summaryrefslogtreecommitdiff
path: root/server/responder/common
diff options
context:
space:
mode:
Diffstat (limited to 'server/responder/common')
-rw-r--r--server/responder/common/responder_cmd.c78
-rw-r--r--server/responder/common/responder_cmd.h71
-rw-r--r--server/responder/common/responder_common.c518
-rw-r--r--server/responder/common/responder_common.h24
-rw-r--r--server/responder/common/responder_dp.c123
-rw-r--r--server/responder/common/responder_dp.h4
-rw-r--r--server/responder/common/responder_packet.c220
-rw-r--r--server/responder/common/responder_packet.h39
8 files changed, 1077 insertions, 0 deletions
diff --git a/server/responder/common/responder_cmd.c b/server/responder/common/responder_cmd.c
new file mode 100644
index 00000000..83d55e65
--- /dev/null
+++ b/server/responder/common/responder_cmd.c
@@ -0,0 +1,78 @@
+/*
+ SSSD
+
+ SSS Client Responder, command parser
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <errno.h>
+#include "util/util.h"
+#include "responder/common/responder_cmd.h"
+#include "responder/common/responder_packet.h"
+
+
+void sss_cmd_done(struct sss_cmd_ctx *nctx)
+{
+ /* now that the packet is in place, unlock queue
+ * making the event writable */
+ EVENT_FD_WRITEABLE(nctx->cctx->cfde);
+
+ /* free all request related data through the talloc hierarchy */
+ talloc_free(nctx);
+}
+
+int sss_cmd_get_version(struct cli_ctx *cctx)
+{
+ struct sss_cmd_ctx *nctx;
+ uint8_t *body;
+ size_t blen;
+ int ret;
+
+ nctx = talloc(cctx, struct sss_cmd_ctx);
+ if (!nctx) {
+ return ENOMEM;
+ }
+ nctx->cctx = cctx;
+
+ /* create response packet */
+ ret = sss_packet_new(cctx->creq, sizeof(uint32_t),
+ sss_packet_get_cmd(cctx->creq->in),
+ &cctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+ sss_packet_get_body(cctx->creq->out, &body, &blen);
+ ((uint32_t *)body)[0] = SSS_PROTOCOL_VERSION;
+
+ sss_cmd_done(nctx);
+ return EOK;
+}
+
+int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds)
+{
+ enum sss_cli_command cmd;
+ int i;
+
+ cmd = sss_packet_get_cmd(cctx->creq->in);
+
+ for (i = 0; sss_cmds[i].cmd != SSS_CLI_NULL; i++) {
+ if (cmd == sss_cmds[i].cmd) {
+ return sss_cmds[i].fn(cctx);
+ }
+ }
+
+ return EINVAL;
+}
diff --git a/server/responder/common/responder_cmd.h b/server/responder/common/responder_cmd.h
new file mode 100644
index 00000000..ee094ad3
--- /dev/null
+++ b/server/responder/common/responder_cmd.h
@@ -0,0 +1,71 @@
+/*
+ SSSD
+
+ SSS Client Responder, header file
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SSSSRV_CMD_H__
+#define __SSSSRV_CMD_H__
+
+#include <stdint.h>
+#include <sys/un.h>
+#include "talloc.h"
+#include "tevent.h"
+#include "ldb.h"
+#include "../sss_client/sss_cli.h"
+
+/* needed until nsssrv.h is updated */
+#ifndef __NSSSRV_H__
+struct cli_request {
+
+ /* original request from the wire */
+ struct sss_packet *in;
+
+ /* reply data */
+ struct sss_packet *out;
+};
+
+struct cli_ctx {
+ struct event_context *ev;
+ struct nss_ctx *nctx;
+ int cfd;
+ struct fd_event *cfde;
+ struct sockaddr_un addr;
+ struct cli_request *creq;
+ struct getent_ctx *gctx;
+};
+#endif
+
+struct sss_cmd_ctx {
+ struct cli_ctx *cctx;
+ const char *domain;
+ const char *name;
+ uid_t id;
+ bool check_expiration;
+};
+
+struct sss_cmd_table {
+ enum sss_cli_command cmd;
+ int (*fn)(struct cli_ctx *cctx);
+};
+
+int sss_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds);
+void sss_cmd_done(struct sss_cmd_ctx *nctx);
+int sss_cmd_get_version(struct cli_ctx *cctx);
+
+#endif /* __SSSSRV_CMD_H__ */
diff --git a/server/responder/common/responder_common.c b/server/responder/common/responder_common.c
new file mode 100644
index 00000000..b5db7d1e
--- /dev/null
+++ b/server/responder/common/responder_common.c
@@ -0,0 +1,518 @@
+/*
+ SSSD
+
+ Common Responder methods
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include "popt.h"
+#include "util/util.h"
+#include "responder/nss/nsssrv.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "dbus/dbus.h"
+#include "sbus/sssd_dbus.h"
+#include "util/btreemap.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder_cmd.h"
+#include "responder/common/responder_dp.h"
+#include "providers/data_provider.h"
+#include "monitor/monitor_sbus.h"
+#include "monitor/monitor_interfaces.h"
+#include "sbus/sbus_client.h"
+
+static void set_nonblocking(int fd)
+{
+ unsigned v;
+ v = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, v | O_NONBLOCK);
+}
+
+static void set_close_on_exec(int fd)
+{
+ unsigned v;
+ v = fcntl(fd, F_GETFD, 0);
+ fcntl(fd, F_SETFD, v | FD_CLOEXEC);
+}
+
+static int client_destructor(struct cli_ctx *ctx)
+{
+ if (ctx->cfd > 0) close(ctx->cfd);
+ return 0;
+}
+
+static void client_send(struct event_context *ev, struct cli_ctx *cctx)
+{
+ int ret;
+
+ ret = sss_packet_send(cctx->creq->out, cctx->cfd);
+ if (ret == EAGAIN) {
+ /* not all data was sent, loop again */
+ return;
+ }
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to read request, aborting client!\n"));
+ talloc_free(cctx);
+ return;
+ }
+
+ /* ok all sent */
+ EVENT_FD_NOT_WRITEABLE(cctx->cfde);
+ EVENT_FD_READABLE(cctx->cfde);
+ talloc_free(cctx->creq);
+ cctx->creq = NULL;
+ return;
+}
+
+static void client_recv(struct event_context *ev, struct cli_ctx *cctx)
+{
+ int ret;
+
+ if (!cctx->creq) {
+ cctx->creq = talloc_zero(cctx, struct cli_request);
+ if (!cctx->creq) {
+ DEBUG(0, ("Failed to alloc request, aborting client!\n"));
+ talloc_free(cctx);
+ return;
+ }
+ }
+
+ if (!cctx->creq->in) {
+ ret = sss_packet_new(cctx->creq, NSS_PACKET_MAX_RECV_SIZE,
+ 0, &cctx->creq->in);
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to alloc request, aborting client!\n"));
+ talloc_free(cctx);
+ return;
+ }
+ }
+
+ ret = sss_packet_recv(cctx->creq->in, cctx->cfd);
+ switch (ret) {
+ case EOK:
+ /* do not read anymore */
+ EVENT_FD_NOT_READABLE(cctx->cfde);
+ /* execute command */
+ ret = sss_cmd_execute(cctx, cctx->nctx->sss_cmds);
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to execute request, aborting client!\n"));
+ talloc_free(cctx);
+ }
+ /* past this point cctx can be freed at any time by callbacks
+ * in case of error, do not use it */
+ return;
+
+ case EAGAIN:
+ /* need to read still some data, loop again */
+ break;
+
+ case EINVAL:
+ DEBUG(6, ("Invalid data from client, closing connection!\n"));
+ talloc_free(cctx);
+ break;
+
+ case ENODATA:
+ DEBUG(5, ("Client disconnected!\n"));
+ talloc_free(cctx);
+ break;
+
+ default:
+ DEBUG(6, ("Failed to read request, aborting client!\n"));
+ talloc_free(cctx);
+ }
+
+ return;
+}
+
+static void client_fd_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *ptr)
+{
+ struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx);
+
+ if (flags & EVENT_FD_READ) {
+ client_recv(ev, cctx);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ client_send(ev, cctx);
+ return;
+ }
+}
+
+static void accept_fd_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *ptr)
+{
+ /* accept and attach new event handler */
+ struct nss_ctx *nctx = talloc_get_type(ptr, struct nss_ctx);
+ struct cli_ctx *cctx;
+ socklen_t len;
+
+ cctx = talloc_zero(nctx, struct cli_ctx);
+ if (!cctx) {
+ struct sockaddr_un addr;
+ int fd;
+ DEBUG(0, ("Out of memory trying to setup client context!\n"));
+ /* accept and close to signal the client we have a problem */
+ memset(&addr, 0, sizeof(addr));
+ len = sizeof(addr);
+ fd = accept(nctx->lfd, (struct sockaddr *)&addr, &len);
+ if (fd == -1) {
+ return;
+ }
+ close(fd);
+ return;
+ }
+
+ len = sizeof(cctx->addr);
+ cctx->cfd = accept(nctx->lfd, (struct sockaddr *)&cctx->addr, &len);
+ if (cctx->cfd == -1) {
+ DEBUG(1, ("Accept failed [%s]", strerror(errno)));
+ talloc_free(cctx);
+ return;
+ }
+
+ cctx->cfde = event_add_fd(ev, cctx, cctx->cfd,
+ EVENT_FD_READ, client_fd_handler, cctx);
+ if (!cctx->cfde) {
+ close(cctx->cfd);
+ talloc_free(cctx);
+ DEBUG(2, ("Failed to queue client handler\n"));
+ }
+
+ cctx->ev = ev;
+ cctx->nctx = nctx;
+
+ talloc_set_destructor(cctx, client_destructor);
+
+ DEBUG(4, ("Client connected!\n"));
+
+ return;
+}
+
+static int sss_sbus_init(struct nss_ctx *nctx)
+{
+ int ret;
+ char *sbus_address;
+ struct service_sbus_ctx *ss_ctx;
+ struct sbus_method_ctx *sm_ctx;
+
+ /* Set up SBUS connection to the monitor */
+ ret = monitor_get_sbus_address(nctx, nctx->cdb, &sbus_address);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not locate monitor address.\n"));
+ return ret;
+ }
+
+ ret = monitor_init_sbus_methods(nctx, nctx->sss_sbus_methods, &sm_ctx);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not initialize SBUS methods.\n"));
+ return ret;
+ }
+
+ ret = sbus_client_init(nctx, nctx->ev,
+ sbus_address, sm_ctx,
+ NULL /* Private Data */,
+ NULL /* Destructor */,
+ &ss_ctx);
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to connect to monitor services.\n"));
+ return ret;
+ }
+
+ /* Set up NSS-specific listeners */
+ /* None currently used */
+
+ nctx->ss_ctx = ss_ctx;
+
+ return EOK;
+}
+
+/* create a unix socket and listen to it */
+static int set_unix_socket(struct nss_ctx *nctx)
+{
+ struct sockaddr_un addr;
+ char *default_pipe;
+ int ret;
+
+ default_pipe = talloc_asprintf(nctx, "%s/%s", PIPE_PATH,
+ nctx->sss_pipe_name);
+ if (!default_pipe) {
+ return ENOMEM;
+ }
+
+ ret = confdb_get_string(nctx->cdb, nctx,
+ nctx->confdb_socket_path, "unixSocket",
+ default_pipe, &nctx->sock_name);
+ if (ret != EOK) {
+ talloc_free(default_pipe);
+ return ret;
+ }
+ talloc_free(default_pipe);
+
+ nctx->lfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (nctx->lfd == -1) {
+ return EIO;
+ }
+
+ /* Set the umask so that permissions are set right on the socket.
+ * It must be readable and writable by anybody on the system. */
+ umask(0111);
+
+ set_nonblocking(nctx->lfd);
+ set_close_on_exec(nctx->lfd);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, nctx->sock_name, sizeof(addr.sun_path));
+
+ /* make sure we have no old sockets around */
+ unlink(nctx->sock_name);
+
+ if (bind(nctx->lfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG(0,("Unable to bind on socket '%s'\n", nctx->sock_name));
+ goto failed;
+ }
+ if (listen(nctx->lfd, 10) != 0) {
+ DEBUG(0,("Unable to listen on socket '%s'\n", nctx->sock_name));
+ goto failed;
+ }
+
+ nctx->lfde = event_add_fd(nctx->ev, nctx, nctx->lfd,
+ EVENT_FD_READ, accept_fd_handler, nctx);
+
+ /* we want default permissions on created files to be very strict,
+ so set our umask to 0177 */
+ umask(0177);
+ return EOK;
+
+failed:
+ /* we want default permissions on created files to be very strict,
+ so set our umask to 0177 */
+ umask(0177);
+ close(nctx->lfd);
+ return EIO;
+}
+
+/* domain names are case insensitive for now
+ * NOTE: this function is not utf-8 safe,
+ * only ASCII names for now */
+static int _domain_comparator(const void *key1, const void *key2)
+{
+ return strcasecmp((const char *)key1, (const char *)key2);
+}
+
+static int sss_init_domains(struct nss_ctx *nctx)
+{
+ char *path;
+ char **domains;
+ char *provider;
+ TALLOC_CTX *tmp_ctx;
+ struct nss_domain_info *info;
+ int ret, i, c;
+ int retval;
+
+ tmp_ctx = talloc_new(nctx);
+ ret = confdb_get_domains(nctx->cdb, tmp_ctx, &domains);
+ if (ret != EOK) {
+ retval = ret;
+ goto done;
+ }
+
+ i = 0;
+ c = 0;
+ while (domains[i] != NULL) {
+ DEBUG(3, ("Adding domain %s to the map\n", domains[i]));
+
+ path = talloc_asprintf(tmp_ctx, "config/domains/%s", domains[i]);
+ if (!path) {
+ retval = ENOMEM;
+ goto done;
+ }
+
+ /* alloc on tmp_ctx, it will be stolen by btreemap_set_value */
+ info = talloc_zero(tmp_ctx, struct nss_domain_info);
+ if (!info) {
+ retval = ENOMEM;
+ goto done;
+ }
+
+ /* Build the basedn for this domain */
+ info->basedn = talloc_asprintf(info, SYSDB_DOM_BASE, domains[i]);
+ DEBUG(3, ("BaseDN: %s\n", info->basedn));
+
+ ret = confdb_get_int(nctx->cdb, tmp_ctx, path,
+ "enumerate", false, &(info->enumerate));
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to fetch enumerate for [%s]!\n", domains[i]));
+ }
+
+ ret = confdb_get_bool(nctx->cdb, tmp_ctx, path,
+ "legacy", false, &(info->legacy));
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to fetch legacy for [%s]!\n", domains[i]));
+ }
+
+ ret = confdb_get_string(nctx->cdb, tmp_ctx, path, "provider",
+ NULL, &provider);
+ if (ret != EOK) {
+ DEBUG(0, ("Failed to fetch provider for [%s]!\n", domains[i]));
+ }
+ if (provider) info->has_provider = true;
+
+ ret = btreemap_set_value(nctx, &nctx->domain_map,
+ domains[i], info,
+ _domain_comparator);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to store domain info, aborting!\n"));
+ retval = ret;
+ goto done;
+ }
+
+ i++;
+ c++;
+ }
+ if (c == 0) {
+ /* No domains configured!
+ * Note: this should never happen, since LOCAL should
+ * always be configured */
+ DEBUG(0, ("No domains configured on this client!\n"));
+ retval = EINVAL;
+ goto done;
+ }
+
+ ret = confdb_get_string(nctx->cdb, nctx,
+ "config/domains", "default",
+ NULL, &nctx->default_domain);
+ if (ret != EOK) {
+ retval = ret;
+ goto done;
+ }
+
+ retval = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return retval;
+}
+
+int sss_process_init(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct confdb_ctx *cdb,
+ struct sbus_method sss_sbus_methods[],
+ struct sss_cmd_table sss_cmds[],
+ const char *sss_pipe_name,
+ const char *confdb_socket_path,
+ struct sbus_method dp_methods[])
+{
+ struct nss_ctx *nctx;
+ int ret;
+
+ nctx = talloc_zero(mem_ctx, struct nss_ctx);
+ if (!nctx) {
+ DEBUG(0, ("fatal error initializing nss_ctx\n"));
+ return ENOMEM;
+ }
+ nctx->ev = ev;
+ nctx->cdb = cdb;
+ nctx->sss_sbus_methods = sss_sbus_methods;
+ nctx->sss_cmds = sss_cmds;
+ nctx->sss_pipe_name = sss_pipe_name;
+ nctx->confdb_socket_path = confdb_socket_path;
+ nctx->dp_methods = dp_methods;
+
+ ret = sss_init_domains(nctx);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error setting up domain map\n"));
+ return ret;
+ }
+
+ ret = sss_sbus_init(nctx);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error setting up message bus\n"));
+ return ret;
+ }
+
+ ret = sss_dp_init(nctx, nctx->dp_methods);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error setting up backend connector\n"));
+ return ret;
+ }
+
+ ret = sysdb_init(nctx, ev, cdb, NULL, &nctx->sysdb);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error initializing nss_ctx\n"));
+ return ret;
+ }
+
+ /* after all initializations we are ready to listen on our socket */
+ ret = set_unix_socket(nctx);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error initializing socket\n"));
+ return ret;
+ }
+
+ nctx->cache_timeout = 600; /* FIXME: read from conf */
+
+ DEBUG(1, ("NSS Initialization complete\n"));
+
+ return EOK;
+}
+
+int sss_parse_name(TALLOC_CTX *memctx,
+ const char *fullname,
+ struct btreemap *domain_map,
+ const char **domain, const char **name) {
+ char *delim;
+ struct btreemap *node;
+ int ret;
+
+ if ((delim = strchr(fullname, NSS_DOMAIN_DELIM)) != NULL) {
+
+ /* Check for registered domain */
+ ret = btreemap_search_key(domain_map, (void *)(delim+1), &node);
+ if (ret != BTREEMAP_FOUND) {
+ /* No such domain was registered. Return EINVAL.
+ * TODO: alternative approach?
+ * Alternatively, we could simply fail down to
+ * below, treating the entire construct as the
+ * full name if the domain is unspecified.
+ */
+ return EINVAL;
+ }
+
+ *name = talloc_strndup(memctx, fullname, delim-fullname);
+ *domain = talloc_strdup(memctx, delim+1);
+ }
+ else {
+ *name = talloc_strdup(memctx, fullname);
+ *domain = NULL;
+ }
+
+ return EOK;
+}
diff --git a/server/responder/common/responder_common.h b/server/responder/common/responder_common.h
new file mode 100644
index 00000000..433b242e
--- /dev/null
+++ b/server/responder/common/responder_common.h
@@ -0,0 +1,24 @@
+#include "sbus/sssd_dbus.h"
+#include "responder/common/responder_cmd.h"
+#include "util/btreemap.h"
+
+/* SSS_DOMAIN_DELIM can be specified in config.h */
+#include "config.h"
+#ifndef SSS_DOMAIN_DELIM
+#define SSS_DOMAIN_DELIM '@'
+#endif
+
+
+int sss_process_init(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct confdb_ctx *cdb,
+ struct sbus_method sss_sbus_methods[],
+ struct sss_cmd_table sss_cmds[],
+ const char *sss_pipe_name,
+ const char *confdb_socket_path,
+ struct sbus_method dp_methods[]);
+
+int sss_parse_name(TALLOC_CTX *memctx,
+ const char *fullname,
+ struct btreemap *domain_map,
+ const char **domain, const char **name);
diff --git a/server/responder/common/responder_dp.c b/server/responder/common/responder_dp.c
new file mode 100644
index 00000000..a024674b
--- /dev/null
+++ b/server/responder/common/responder_dp.c
@@ -0,0 +1,123 @@
+
+#include <sys/time.h>
+#include <time.h>
+#include "responder/nss/nsssrv.h"
+#include "util/util.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder_common.h"
+#include "providers/data_provider.h"
+#include "sbus/sbus_client.h"
+#include "providers/dp_sbus.h"
+
+struct sss_dp_pvt_ctx {
+ struct nss_ctx *nctx;
+ struct sbus_method *methods;
+ time_t last_retry;
+ int retries;
+};
+
+static int sss_dp_conn_destructor(void *data);
+static void sss_dp_reconnect(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval tv, void *data);
+
+static void sss_dp_conn_reconnect(struct sss_dp_pvt_ctx *pvt)
+{
+ struct nss_ctx *nctx;
+ struct timed_event *te;
+ struct timeval tv;
+ struct sbus_method_ctx *sm_ctx;
+ char *sbus_address;
+ time_t now;
+ int ret;
+
+ now = time(NULL);
+
+ /* reset retry if last reconnect was > 60 sec. ago */
+ if (pvt->last_retry + 60 < now) pvt->retries = 0;
+ if (pvt->retries >= 3) {
+ DEBUG(4, ("Too many reconnect retries! Giving up\n"));
+ return;
+ }
+
+ pvt->last_retry = now;
+ pvt->retries++;
+
+ nctx = pvt->nctx;
+
+ ret = dp_get_sbus_address(nctx, nctx->cdb, &sbus_address);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not locate data provider address.\n"));
+ return;
+ }
+
+ ret = dp_init_sbus_methods(nctx, pvt->methods, &sm_ctx);
+ if (ret != EOK) {
+ DEBUG(0, ("Could not initialize SBUS methods.\n"));
+ return;
+ }
+
+ ret = sbus_client_init(nctx, nctx->ev,
+ sbus_address, sm_ctx,
+ pvt, sss_dp_conn_destructor,
+ &nctx->dp_ctx);
+ if (ret != EOK) {
+ DEBUG(4, ("Failed to reconnect [%d(%s)]!\n", ret, strerror(ret)));
+
+ tv.tv_sec = now +5;
+ tv.tv_usec = 0;
+ te = event_add_timed(nctx->ev, nctx, tv, sss_dp_reconnect, pvt);
+ if (te == NULL) {
+ DEBUG(4, ("Failed to add timed event! Giving up\n"));
+ } else {
+ DEBUG(4, ("Retrying in 5 seconds\n"));
+ }
+ }
+}
+
+static void sss_dp_reconnect(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval tv, void *data)
+{
+ struct sss_dp_pvt_ctx *pvt;
+
+ pvt = talloc_get_type(data, struct sss_dp_pvt_ctx);
+
+ sss_dp_conn_reconnect(pvt);
+}
+
+int sss_dp_conn_destructor(void *data)
+{
+ struct sss_dp_pvt_ctx *pvt;
+ struct sbus_conn_ctx *scon;
+
+ scon = talloc_get_type(data, struct sbus_conn_ctx);
+ if (!scon) return 0;
+
+ /* if this is a regular disconnect just quit */
+ if (sbus_conn_disconnecting(scon)) return 0;
+
+ pvt = talloc_get_type(sbus_conn_get_private_data(scon),
+ struct sss_dp_pvt_ctx);
+ if (pvt) return 0;
+
+ sss_dp_conn_reconnect(pvt);
+
+ return 0;
+}
+
+int sss_dp_init(struct nss_ctx *nctx, struct sbus_method *dp_methods)
+{
+ struct sss_dp_pvt_ctx *pvt;
+
+ pvt = talloc_zero(nctx, struct sss_dp_pvt_ctx);
+ if (!pvt) return ENOMEM;
+
+ pvt->nctx = nctx;
+ pvt->methods = dp_methods;
+
+ sss_dp_conn_reconnect(pvt);
+
+ return EOK;
+}
+
diff --git a/server/responder/common/responder_dp.h b/server/responder/common/responder_dp.h
new file mode 100644
index 00000000..2eeddf5b
--- /dev/null
+++ b/server/responder/common/responder_dp.h
@@ -0,0 +1,4 @@
+#include "responder/nss/nsssrv.h"
+#include "sbus/sssd_dbus.h"
+
+int sss_dp_init(struct nss_ctx *nctx, struct sbus_method dp_methods[]);
diff --git a/server/responder/common/responder_packet.c b/server/responder/common/responder_packet.c
new file mode 100644
index 00000000..24762934
--- /dev/null
+++ b/server/responder/common/responder_packet.c
@@ -0,0 +1,220 @@
+/*
+ SSSD
+
+ SSS Client Responder, command parser
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+#include "talloc.h"
+#include "util/util.h"
+#include "responder/common/responder_packet.h"
+
+#define SSSSRV_PACKET_MEM_SIZE 512
+
+struct sss_packet {
+ size_t memsize;
+ uint8_t *buffer;
+
+ /* header */
+ uint32_t *len;
+ uint32_t *cmd;
+ uint32_t *status;
+ uint32_t *reserved;
+
+ uint8_t *body;
+
+ /* io pointer */
+ size_t iop;
+};
+
+/*
+ * Allocate a new packet structure
+ *
+ * - if size is defined use it otherwise the default packet will be
+ * SSSSRV_PACKET_MEM_SIZE bytes.
+ */
+int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size,
+ enum sss_cli_command cmd,
+ struct sss_packet **rpacket)
+{
+ struct sss_packet *packet;
+
+ packet = talloc(mem_ctx, struct sss_packet);
+ if (!packet) return ENOMEM;
+
+ if (size) {
+ int n = (size + SSS_NSS_HEADER_SIZE) % SSSSRV_PACKET_MEM_SIZE;
+ packet->memsize = (n + 1) * SSSSRV_PACKET_MEM_SIZE;
+ } else {
+ packet->memsize = SSSSRV_PACKET_MEM_SIZE;
+ }
+
+ packet->buffer = talloc_size(packet, packet->memsize);
+ if (!packet->buffer) {
+ talloc_free(packet);
+ return ENOMEM;
+ }
+ memset(packet->buffer, 0, SSS_NSS_HEADER_SIZE);
+
+ packet->len = &((uint32_t *)packet->buffer)[0];
+ packet->cmd = &((uint32_t *)packet->buffer)[1];
+ packet->status = &((uint32_t *)packet->buffer)[2];
+ packet->reserved = &((uint32_t *)packet->buffer)[3];
+ packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4];
+
+ *(packet->len) = size + SSS_NSS_HEADER_SIZE;
+ *(packet->cmd) = cmd;
+
+ packet->iop = 0;
+
+ *rpacket = packet;
+
+ return EOK;
+}
+
+/* grows a packet size only in SSSSRV_PACKET_MEM_SIZE chunks */
+int sss_packet_grow(struct sss_packet *packet, size_t size)
+{
+ size_t totlen, len;
+ uint8_t *newmem;
+
+ if (size == 0) {
+ return EOK;
+ }
+
+ totlen = packet->memsize;
+ len = *packet->len + size;
+
+ /* make sure we do not overflow */
+ if (totlen < len) {
+ int n = len % SSSSRV_PACKET_MEM_SIZE + 1;
+ totlen += n * SSSSRV_PACKET_MEM_SIZE;
+ if (totlen < len) {
+ return EINVAL;
+ }
+ }
+
+ if (totlen > packet->memsize) {
+ newmem = talloc_realloc_size(packet, packet->buffer, totlen);
+ if (!newmem) {
+ return ENOMEM;
+ }
+
+ packet->memsize = totlen;
+ packet->buffer = newmem;
+ packet->len = &((uint32_t *)packet->buffer)[0];
+ packet->cmd = &((uint32_t *)packet->buffer)[1];
+ packet->status = &((uint32_t *)packet->buffer)[2];
+ packet->reserved = &((uint32_t *)packet->buffer)[3];
+ packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4];
+ }
+
+ *(packet->len) += size;
+
+ return 0;
+}
+
+int sss_packet_recv(struct sss_packet *packet, int fd)
+{
+ size_t rb;
+ size_t len;
+ void *buf;
+
+ buf = packet->buffer + packet->iop;
+ if (packet->iop > 4) len = *packet->len - packet->iop;
+ else len = packet->memsize - packet->iop;
+
+ /* check for wrapping */
+ if (len > packet->memsize) {
+ return EINVAL;
+ }
+
+ errno = 0;
+ rb = recv(fd, buf, len, 0);
+
+ if (rb == -1 && errno == EAGAIN) {
+ return EAGAIN;
+ }
+
+ if (rb == 0) {
+ return ENODATA;
+ }
+
+ if (*packet->len > packet->memsize) {
+ return EINVAL;
+ }
+
+ packet->iop += rb;
+ if (packet->iop < 4) {
+ return EAGAIN;
+ }
+
+ if (packet->iop < *packet->len) {
+ return EAGAIN;
+ }
+
+ return EOK;
+}
+
+int sss_packet_send(struct sss_packet *packet, int fd)
+{
+ size_t rb;
+ size_t len;
+ void *buf;
+
+ buf = packet->buffer + packet->iop;
+ len = *packet->len - packet->iop;
+
+ errno = 0;
+ rb = send(fd, buf, len, 0);
+
+ if (rb == -1 && errno == EAGAIN) {
+ return EAGAIN;
+ }
+
+ if (rb == 0) {
+ return EIO;
+ }
+
+ packet->iop += rb;
+
+ if (packet->iop < *packet->len) {
+ return EAGAIN;
+ }
+
+ return EOK;
+}
+
+enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet)
+{
+ return (enum sss_cli_command)(*packet->cmd);
+}
+
+void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen)
+{
+ *body = packet->body;
+ *blen = *packet->len - SSS_NSS_HEADER_SIZE;
+}
+
+void sss_packet_set_error(struct sss_packet *packet, int error)
+{
+ *(packet->status) = error;
+}
diff --git a/server/responder/common/responder_packet.h b/server/responder/common/responder_packet.h
new file mode 100644
index 00000000..1a852946
--- /dev/null
+++ b/server/responder/common/responder_packet.h
@@ -0,0 +1,39 @@
+/*
+ SSSD
+
+ SSS Client Responder, header file
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SSSSRV_PACKET_H__
+#define __SSSSRV_PACKET_H__
+
+#include "../sss_client/sss_cli.h"
+
+struct sss_packet;
+
+int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size,
+ enum sss_cli_command cmd,
+ struct sss_packet **rpacket);
+int sss_packet_grow(struct sss_packet *packet, size_t size);
+int sss_packet_recv(struct sss_packet *packet, int fd);
+int sss_packet_send(struct sss_packet *packet, int fd);
+enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet);
+void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen);
+void sss_packet_set_error(struct sss_packet *packet, int error);
+
+#endif /* __SSSSRV_PACKET_H__ */