summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPavel Březina <pbrezina@redhat.com>2016-01-15 13:00:45 +0100
committerJakub Hrozek <jhrozek@redhat.com>2016-06-20 14:48:47 +0200
commitd3dee2a07f1a8ee9ae6f94e149ced754ef76c248 (patch)
treedcb92cf97dd70a4183d05258b9db0414b91d60a8 /src
parent565b9955cc439ade58cc24a98168060a60f33e7a (diff)
downloadsssd-d3dee2a07f1a8ee9ae6f94e149ced754ef76c248.tar.gz
sssd-d3dee2a07f1a8ee9ae6f94e149ced754ef76c248.tar.xz
sssd-d3dee2a07f1a8ee9ae6f94e149ced754ef76c248.zip
DP: Introduce new interface for backend
Terminology: * Backend: Implemenation of domain * Data Provider: interface between backend and responders * Module: ldap/ipa/ad/... dlopened library that implements dp interface * Target: id/autofs/sudo/... functionality of module Benefits over current code: * data provider is a black box completely separated from backend * method handlers are just simple tevent requests on backend side * no need of spy on be_client * simplified and error proof adding of new responders * simplified adding of new methods * reply to D-Bus message is completely handled by DP code * each target can have several methods defined * properties can be added on objects * each method can have output parameters * modules now support constructor * improved debugging * clear memory hierarchy * ability to chain requests * type safe private data Reviewed-by: Sumit Bose <sbose@redhat.com> Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Diffstat (limited to 'src')
-rw-r--r--src/providers/backend.h3
-rw-r--r--src/providers/data_provider/dp.c129
-rw-r--r--src/providers/data_provider/dp.h162
-rw-r--r--src/providers/data_provider/dp_builtin.c118
-rw-r--r--src/providers/data_provider/dp_builtin.h50
-rw-r--r--src/providers/data_provider/dp_client.c249
-rw-r--r--src/providers/data_provider/dp_custom_data.h32
-rw-r--r--src/providers/data_provider/dp_flags.h29
-rw-r--r--src/providers/data_provider/dp_iface.c49
-rw-r--r--src/providers/data_provider/dp_iface.h34
-rw-r--r--src/providers/data_provider/dp_iface.xml54
-rw-r--r--src/providers/data_provider/dp_iface_generated.c296
-rw-r--r--src/providers/data_provider/dp_iface_generated.h93
-rw-r--r--src/providers/data_provider/dp_methods.c122
-rw-r--r--src/providers/data_provider/dp_modules.c224
-rw-r--r--src/providers/data_provider/dp_private.h247
-rw-r--r--src/providers/data_provider/dp_request.c460
-rw-r--r--src/providers/data_provider/dp_request.h77
-rw-r--r--src/providers/data_provider/dp_request_reply.c372
-rw-r--r--src/providers/data_provider/dp_request_table.c265
-rw-r--r--src/providers/data_provider/dp_responder_iface.h29
-rw-r--r--src/providers/data_provider/dp_sbus.c (renamed from src/providers/dp_sbus.c)0
-rw-r--r--src/providers/data_provider/dp_targets.c534
23 files changed, 3628 insertions, 0 deletions
diff --git a/src/providers/backend.h b/src/providers/backend.h
index cfb9044b5..e91ed9c3e 100644
--- a/src/providers/backend.h
+++ b/src/providers/backend.h
@@ -25,6 +25,7 @@
#include "providers/data_provider.h"
#include "providers/fail_over.h"
#include "providers/be_refresh.h"
+#include "providers/data_provider/dp.h"
#include "util/child_common.h"
#include "db/sysdb.h"
@@ -148,6 +149,8 @@ struct be_ctx {
/* List of ongoing requests */
struct be_req *active_requests;
+
+ struct data_provider *provider;
};
struct bet_ops {
diff --git a/src/providers/data_provider/dp.c b/src/providers/data_provider/dp.c
new file mode 100644
index 000000000..c4590e315
--- /dev/null
+++ b/src/providers/data_provider/dp.c
@@ -0,0 +1,129 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <talloc.h>
+
+#include "config.h"
+#include "providers/data_provider/dp.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/backend.h"
+#include "util/util.h"
+
+static errno_t dp_init_dbus_server(struct data_provider *provider)
+{
+ const char *domain;
+ char *sbus_address;
+ errno_t ret;
+
+ domain = provider->be_ctx->domain->name;
+ ret = dp_get_sbus_address(NULL, &sbus_address, domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not get sbus backend address.\n");
+ return ret;
+ }
+
+ ret = sbus_new_server(provider, provider->ev, sbus_address,
+ provider->uid, provider->gid, true,
+ &provider->srv_conn,
+ dp_client_init, provider);
+ talloc_free(sbus_address);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not set up sbus server.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static int dp_destructor(struct data_provider *provider)
+{
+ enum dp_clients client;
+
+ provider->terminating = true;
+
+ dp_terminate_active_requests(provider);
+
+ for (client = 0; client != DP_CLIENT_SENTINEL; client++) {
+ talloc_zfree(provider->clients[client]);
+ }
+
+ return 0;
+}
+
+errno_t dp_init(struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ uid_t uid,
+ gid_t gid)
+{
+ struct data_provider *provider;
+
+ errno_t ret;
+
+ provider = talloc_zero(be_ctx, struct data_provider);
+ if (provider == NULL) {
+ return ENOMEM;
+ }
+
+ provider->ev = ev;
+ provider->uid = uid;
+ provider->gid = gid;
+ provider->be_ctx = be_ctx;
+
+ ret = dp_req_table_init(provider, &provider->requests.reply_table);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize request table "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = dp_init_modules(provider, &provider->modules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize DP modules "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = dp_init_targets(provider, be_ctx, provider, provider->modules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize DP targets "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ talloc_set_destructor(provider, dp_destructor);
+
+ ret = dp_init_dbus_server(provider);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup service bus [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ be_ctx->provider = provider;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(provider);
+ }
+
+ return ret;
+}
diff --git a/src/providers/data_provider/dp.h b/src/providers/data_provider/dp.h
new file mode 100644
index 000000000..8cdbd7768
--- /dev/null
+++ b/src/providers/data_provider/dp.h
@@ -0,0 +1,162 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 _DP_H_
+#define _DP_H_
+
+#include <stdint.h>
+
+#include "sbus/sssd_dbus.h"
+#include "providers/backend.h"
+#include "providers/data_provider/dp_request.h"
+#include "providers/data_provider/dp_custom_data.h"
+#include "providers/data_provider/dp_flags.h"
+
+struct data_provider;
+struct dp_method;
+
+/**
+ * Module constructor.
+ *
+ * It is possible to create a module data that is passed into all
+ * target initialization functions.
+ */
+typedef errno_t (*dp_module_init_fn)(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ const char *module_name,
+ void **_module_data);
+
+/**
+ * Target initialization function.
+ *
+ * Pointer to dp_method is unique for all targets. Make sure that
+ * dp_set_method is called in all targets even if you are reusing
+ * some existing context or initialization function.
+ */
+typedef errno_t (*dp_target_init_fn)(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ void *module_data,
+ struct dp_method *dp_methods);
+
+enum dp_targets {
+ DPT_ID,
+ DPT_AUTH,
+ DPT_ACCESS,
+ DPT_CHPASS,
+ DPT_SUDO,
+ DPT_AUTOFS,
+ DPT_SELINUX,
+ DPT_HOSTID,
+ DPT_SUBDOMAINS,
+
+ DP_TARGET_SENTINEL
+};
+
+enum dp_methods {
+ DPM_CHECK_ONLINE,
+ DPM_ACCOUNT_HANDLER,
+ DPM_AUTH_HANDLER,
+ DPM_ACCESS_HANDLER,
+ DPM_SELINUX_HANDLER,
+ DPM_SUDO_HANDLER,
+ DPM_AUTOFS_HANDLER,
+ DPM_HOSTID_HANDLER,
+ DPM_DOMAINS_HANDLER,
+
+ DP_METHOD_SENTINEL
+};
+
+/* Method handler. */
+
+struct dp_req_params {
+ struct tevent_context *ev;
+ struct be_ctx *be_ctx;
+ struct sss_domain_info *domain;
+ enum dp_targets target;
+ enum dp_methods method;
+};
+
+typedef struct tevent_req *
+(*dp_req_send_fn)(TALLOC_CTX *mem_ctx, void *method_data, void *request_data,
+ struct dp_req_params *params);
+
+typedef errno_t
+(*dp_req_recv_fn)(TALLOC_CTX *mem_ctx, struct tevent_req *req, void *data);
+
+/* Data provider initialization. */
+
+errno_t dp_init(struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ uid_t uid,
+ gid_t gid);
+
+bool _dp_target_enabled(struct data_provider *provider,
+ const char *module_name,
+ ...);
+
+#define dp_target_enabled(provider, module_name, ...) \
+ _dp_target_enabled(provider, module_name, ##__VA_ARGS__, DP_TARGET_SENTINEL)
+
+struct dp_module *dp_target_module(struct data_provider *provider,
+ enum dp_targets target);
+
+void _dp_set_method(struct dp_method *methods,
+ enum dp_methods method,
+ dp_req_send_fn send_fn,
+ dp_req_recv_fn recv_fn,
+ void *method_data,
+ const char *method_dtype,
+ const char *request_dtype,
+ const char *output_dtype,
+ uint32_t output_size);
+
+/* We check function headers on compile time and data types on run time. This
+ * check requires that both method and request private data are talloc-created
+ * with talloc name set to data type name (which is done by talloc unless
+ * you use _size variations of talloc functions.
+ *
+ * This way we ensure that we always pass correct data and we can access them
+ * directly in request handler without the need to cast them explicitly
+ * from void pointer. */
+#define dp_set_method(methods, method, send_fn, recv_fn, method_data, \
+ method_dtype, req_dtype, output_dtype) \
+ do { \
+ /* Check _send function parameter types. */ \
+ struct tevent_req *(*__send_fn)(TALLOC_CTX *, method_dtype *, \
+ req_dtype *, struct dp_req_params *params) = (send_fn); \
+ \
+ /* Check _recv function parameter types. */ \
+ errno_t (*__recv_fn)(TALLOC_CTX *, struct tevent_req *, \
+ output_dtype *) = (recv_fn); \
+ _dp_set_method(methods, method, (dp_req_send_fn)__send_fn, \
+ (dp_req_recv_fn)__recv_fn, method_data, \
+ #method_dtype, #req_dtype, \
+ #output_dtype, sizeof(output_dtype)); \
+ } while (0)
+
+bool dp_method_enabled(struct data_provider *provider,
+ enum dp_targets target,
+ enum dp_methods method);
+
+void dp_terminate_domain_requests(struct data_provider *provider,
+ const char *domain);
+
+#endif /* _DP_H_ */
diff --git a/src/providers/data_provider/dp_builtin.c b/src/providers/data_provider/dp_builtin.c
new file mode 100644
index 000000000..01d7f6deb
--- /dev/null
+++ b/src/providers/data_provider/dp_builtin.c
@@ -0,0 +1,118 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <security/pam_modules.h>
+#include <talloc.h>
+#include "config.h"
+#include "providers/data_provider/dp.h"
+#include "providers/backend.h"
+#include "util/util.h"
+
+struct dp_access_permit_handler_state {
+ struct pam_data *pd;
+};
+
+struct tevent_req *
+dp_access_permit_handler_send(TALLOC_CTX *mem_ctx,
+ void *data,
+ struct pam_data *pd,
+ struct dp_req_params *params)
+{
+ struct dp_access_permit_handler_state *state;
+ struct tevent_req *req;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dp_access_permit_handler_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->pd = pd;
+ DEBUG(SSSDBG_TRACE_ALL, "Access permit, returning PAM_SUCCESS.\n");
+ state->pd->pam_status = PAM_SUCCESS;
+
+ tevent_req_done(req);
+ tevent_req_post(req, params->ev);
+
+ return req;
+}
+
+errno_t
+dp_access_permit_handler_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct pam_data **_data)
+{
+ struct dp_access_permit_handler_state *state = NULL;
+
+ state = tevent_req_data(req, struct dp_access_permit_handler_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_data = talloc_steal(mem_ctx, state->pd);
+
+ return EOK;
+}
+
+struct dp_access_deny_handler_state {
+ struct pam_data *pd;
+};
+
+struct tevent_req *
+dp_access_deny_handler_send(TALLOC_CTX *mem_ctx,
+ void *data,
+ struct pam_data *pd,
+ struct dp_req_params *params)
+{
+ struct dp_access_deny_handler_state *state;
+ struct tevent_req *req;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dp_access_deny_handler_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->pd = pd;
+ DEBUG(SSSDBG_TRACE_ALL, "Access deny, returning PAM_PERM_DENIED.\n");
+ state->pd->pam_status = PAM_PERM_DENIED;
+
+ tevent_req_done(req);
+ tevent_req_post(req, params->ev);
+
+ return req;
+}
+
+errno_t
+dp_access_deny_handler_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct pam_data **_data)
+{
+ struct dp_access_deny_handler_state *state = NULL;
+
+ state = tevent_req_data(req, struct dp_access_deny_handler_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_data = talloc_steal(mem_ctx, state->pd);
+
+ return EOK;
+}
diff --git a/src/providers/data_provider/dp_builtin.h b/src/providers/data_provider/dp_builtin.h
new file mode 100644
index 000000000..6bd032990
--- /dev/null
+++ b/src/providers/data_provider/dp_builtin.h
@@ -0,0 +1,50 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 _DP_SPECIAL_H_
+#define _DP_SPECIAL_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include "providers/data_provider/dp.h"
+
+struct tevent_req *
+dp_access_permit_handler_send(TALLOC_CTX *mem_ctx,
+ void *data,
+ struct pam_data *pd,
+ struct dp_req_params *params);
+
+errno_t
+dp_access_permit_handler_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct pam_data **_data);
+
+struct tevent_req *
+dp_access_deny_handler_send(TALLOC_CTX *mem_ctx,
+ void *data,
+ struct pam_data *pd,
+ struct dp_req_params *params);
+
+errno_t
+dp_access_deny_handler_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct pam_data **_data);
+
+#endif /* _DP_SPECIAL_H_ */
diff --git a/src/providers/data_provider/dp_client.c b/src/providers/data_provider/dp_client.c
new file mode 100644
index 000000000..7009a5c38
--- /dev/null
+++ b/src/providers/data_provider/dp_client.c
@@ -0,0 +1,249 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 "providers/backend.h"
+#include "providers/data_provider/dp_iface_generated.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/data_provider/dp_iface.h"
+#include "providers/data_provider/dp.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_errors.h"
+#include "util/util.h"
+
+struct dp_client {
+ struct data_provider *provider;
+ struct sbus_connection *conn;
+ struct tevent_timer *timeout;
+ const char *name;
+ bool initialized;
+};
+
+const char *dp_client_to_string(enum dp_clients client)
+{
+ switch (client) {
+ case DPC_NSS:
+ return "NSS";
+ case DPC_PAM:
+ return "PAM";
+ case DPC_IFP:
+ return "InfoPipe";
+ case DPC_PAC:
+ return "PAC";
+ case DPC_SUDO:
+ return "SUDO";
+ case DPC_HOST:
+ return "SSH";
+ case DPC_AUTOFS:
+ return "autofs";
+ case DP_CLIENT_SENTINEL:
+ return "Invalid";
+ }
+
+ return "Invalid";
+}
+
+static int dp_client_destructor(struct dp_client *dp_cli)
+{
+ struct data_provider *provider;
+ enum dp_clients client;
+
+ if (dp_cli->provider == NULL) {
+ return 0;
+ }
+
+ provider = dp_cli->provider;
+
+ for (client = 0; client != DP_CLIENT_SENTINEL; client++) {
+ if (provider->clients[client] == dp_cli) {
+ provider->clients[client] = NULL;
+ DEBUG(SSSDBG_TRACE_FUNC, "Removed %s client\n",
+ dp_client_to_string(client));
+ break;
+ }
+ }
+
+ if (client == DP_CLIENT_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown client removed...\n");
+ }
+
+ return 0;
+}
+
+static int
+dp_client_register(struct sbus_request *sbus_req,
+ void *data,
+ const char *client_name)
+{
+ struct data_provider *provider;
+ struct dp_client *dp_cli;
+ struct DBusError *error;
+ enum dp_clients client;
+ errno_t ret;
+
+ dp_cli = talloc_get_type(data, struct dp_client);
+ if (dp_cli == NULL) {
+ /* Do not send D-Bus error here. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: dp_cli is NULL\n");
+ return EINVAL;
+ }
+
+ provider = dp_cli->provider;
+ dp_cli->name = talloc_strdup(dp_cli, client_name);
+ if (dp_cli->name == NULL) {
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "Cancel DP ID timeout [%p]\n", dp_cli->timeout);
+ talloc_zfree(dp_cli->timeout);
+
+ for (client = 0; client != DP_CLIENT_SENTINEL; client++) {
+ if (strcasecmp(client_name, dp_client_to_string(client)) == 0) {
+ provider->clients[client] = dp_cli;
+ break;
+ }
+ }
+
+ if (client == DP_CLIENT_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown client! [%s]\n", client_name);
+ error = sbus_error_new(sbus_req, SBUS_ERROR_NOT_FOUND,
+ "Unknown client [%s]", client_name);
+
+ /* Kill this client. */
+ talloc_free(dp_cli);
+ return sbus_request_fail_and_finish(sbus_req, error);
+ }
+
+ talloc_set_destructor(dp_cli, dp_client_destructor);
+
+ ret = iface_dp_client_Register_finish(sbus_req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Unable to send ack to the client [%s], "
+ "disconnecting...\n", client_name);
+ sbus_disconnect(sbus_req->conn);
+ return ret;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "Added Frontend client [%s]\n", client_name);
+
+ dp_cli->initialized = true;
+ return EOK;
+}
+
+static void
+dp_client_handshake_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *ptr)
+{
+ struct dp_client *dp_cli;
+
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Client timed out before identification [%p]!\n", te);
+
+ dp_cli = talloc_get_type(ptr, struct dp_client);
+
+ sbus_disconnect(dp_cli->conn);
+ talloc_zfree(dp_cli);
+}
+
+errno_t dp_client_init(struct sbus_connection *conn, void *data)
+{
+ struct data_provider *provider;
+ struct dp_client *dp_cli;
+ struct timeval tv;
+ errno_t ret;
+
+ static struct iface_dp_client iface_dp_client = {
+ { &iface_dp_client_meta, 0 },
+
+ .Register = dp_client_register,
+ };
+
+ provider = talloc_get_type(data, struct data_provider);
+
+ /* When connection is lost we also free the client. */
+ dp_cli = talloc_zero(conn, struct dp_client);
+ if (dp_cli == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory, killing connection.\n");
+ talloc_free(conn);
+ return ENOMEM;
+ }
+
+ dp_cli->provider = provider;
+ dp_cli->conn = conn;
+ dp_cli->initialized = false;
+ dp_cli->timeout = NULL;
+
+ /* Allow access from the SSSD user. */
+ sbus_allow_uid(conn, &provider->uid);
+
+ /* Setup timeout in case client fails to register himself in time. */
+ tv = tevent_timeval_current_ofs(5, 0);
+ dp_cli->timeout = tevent_add_timer(provider->ev, dp_cli, tv,
+ dp_client_handshake_timeout, dp_cli);
+ if (dp_cli->timeout == NULL) {
+ /* Connection is closed in the caller. */
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory, killing connection\n");
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Set-up Backend ID timeout [%p]\n", dp_cli->timeout);
+
+ /* Setup D-Bus interfaces and methods. */
+ ret = sbus_conn_register_iface(conn, &iface_dp_client.vtable,
+ DP_PATH, dp_cli);
+ if (ret != EOK) {
+ /* Connection is closed in the caller. */
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register D-Bus interface, "
+ "killing connection [%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = dp_register_sbus_interface(conn, dp_cli);
+ if (ret != EOK) {
+ /* Connection is closed in the caller. */
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register D-Bus interface, "
+ "killing connection [%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return ret;
+}
+
+struct data_provider *
+dp_client_provider(struct dp_client *dp_cli)
+{
+ if (dp_cli == NULL) {
+ return NULL;
+ }
+
+ return dp_cli->provider;
+}
+
+struct sbus_connection *
+dp_client_conn(struct dp_client *dp_cli)
+{
+ if (dp_cli == NULL) {
+ return NULL;
+ }
+
+ return dp_cli->conn;
+}
diff --git a/src/providers/data_provider/dp_custom_data.h b/src/providers/data_provider/dp_custom_data.h
new file mode 100644
index 000000000..4c03dc93a
--- /dev/null
+++ b/src/providers/data_provider/dp_custom_data.h
@@ -0,0 +1,32 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 _DP_CUSTOM_DATA_H_
+#define _DP_CUSTOM_DATA_H_
+
+#include "providers/data_provider/dp.h"
+
+/* Request handler private data. */
+
+/* Reply private data. */
+
+/* Reply callbacks. */
+
+#endif /* _DP_CUSTOM_DATA_H_ */
diff --git a/src/providers/data_provider/dp_flags.h b/src/providers/data_provider/dp_flags.h
new file mode 100644
index 000000000..52e666d0c
--- /dev/null
+++ b/src/providers/data_provider/dp_flags.h
@@ -0,0 +1,29 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 _DP_FLAGS_H_
+#define _DP_FLAGS_H_
+
+/**
+ * If backend is offline, respond with ERR_OFFLINE immediately.
+ */
+#define DP_FAST_REPLY 0x0001
+
+#endif /* _DP_FLAGS_H_ */
diff --git a/src/providers/data_provider/dp_iface.c b/src/providers/data_provider/dp_iface.c
new file mode 100644
index 000000000..5eaf7e2a3
--- /dev/null
+++ b/src/providers/data_provider/dp_iface.c
@@ -0,0 +1,49 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <dbus/dbus.h>
+
+#include "sbus/sssd_dbus.h"
+#include "providers/data_provider/dp_iface_generated.h"
+#include "providers/data_provider/dp_iface.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/data_provider/dp.h"
+
+struct iface_dp iface_dp = {
+ {&iface_dp_meta, 0},
+ .pamHandler = NULL,
+ .sudoHandler = NULL,
+ .autofsHandler = NULL,
+ .hostHandler = NULL,
+ .getDomains = NULL,
+ .getAccountInfo = NULL
+};
+
+static struct sbus_iface_map dp_map[] = {
+ { DP_PATH, &iface_dp.vtable },
+ { NULL, NULL }
+};
+
+errno_t
+dp_register_sbus_interface(struct sbus_connection *conn,
+ struct dp_client *pvt)
+{
+ return sbus_conn_register_iface_map(conn, dp_map, pvt);
+}
diff --git a/src/providers/data_provider/dp_iface.h b/src/providers/data_provider/dp_iface.h
new file mode 100644
index 000000000..2991bf7aa
--- /dev/null
+++ b/src/providers/data_provider/dp_iface.h
@@ -0,0 +1,34 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 DP_IFACE_H_
+#define DP_IFACE_H_
+
+#include "sbus/sssd_dbus.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/data_provider/dp_responder_iface.h"
+#include "providers/data_provider/dp.h"
+
+#define DP_PATH "/org/freedesktop/sssd/dataprovider"
+
+errno_t dp_register_sbus_interface(struct sbus_connection *conn,
+ struct dp_client *pvt);
+
+#endif /* DP_IFACE_H_ */
diff --git a/src/providers/data_provider/dp_iface.xml b/src/providers/data_provider/dp_iface.xml
new file mode 100644
index 000000000..f508968c8
--- /dev/null
+++ b/src/providers/data_provider/dp_iface.xml
@@ -0,0 +1,54 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.sssd.DataProvider.Client">
+ <annotation value="iface_dp_client" name="org.freedesktop.DBus.GLib.CSymbol"/>
+ <method name="Register">
+ <arg name="Name" type="s" direction="in" />
+ </method>
+ </interface>
+
+ <interface name="org.freedesktop.sssd.dataprovider">
+ <annotation value="iface_dp" name="org.freedesktop.DBus.GLib.CSymbol"/>
+ <method name="pamHandler">
+ <!-- arguments parsed manually, raw handler -->
+ <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
+ </method>
+ <method name="sudoHandler">
+ <!-- arguments parsed manually, raw handler -->
+ <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
+ </method>
+ <method name="autofsHandler">
+ <arg name="dp_flags" type="u" direction="in" />
+ <arg name="mapname" type="s" direction="in" />
+ <arg name="dp_error" type="q" direction="out" />
+ <arg name="error" type="u" direction="out" />
+ <arg name="error_message" type="s" direction="out" />
+ </method>
+ <method name="hostHandler">
+ <arg name="dp_flags" type="u" direction="in" />
+ <arg name="name" type="s" direction="in" />
+ <arg name="alias" type="s" direction="in" />
+ <arg name="dp_error" type="q" direction="out" />
+ <arg name="error" type="u" direction="out" />
+ <arg name="error_message" type="s" direction="out" />
+ </method>
+ <method name="getDomains">
+ <arg name="domain_hint" type="s" direction="in" />
+ <arg name="dp_error" type="q" direction="out" />
+ <arg name="error" type="u" direction="out" />
+ <arg name="error_message" type="s" direction="out" />
+ </method>
+ <method name="getAccountInfo">
+ <arg name="dp_flags" type="u" direction="in" />
+ <arg name="entry_type" type="u" direction="in" />
+ <arg name="attr_type" type="u" direction="in" />
+ <arg name="filter" type="s" direction="in" />
+ <arg name="domain" type="s" direction="in" />
+ <arg name="extra" type="s" direction="in" />
+ <arg name="dp_error" type="q" direction="out" />
+ <arg name="error" type="u" direction="out" />
+ <arg name="error_message" type="s" direction="out" />
+ </method>
+ </interface>
+</node>
diff --git a/src/providers/data_provider/dp_iface_generated.c b/src/providers/data_provider/dp_iface_generated.c
new file mode 100644
index 000000000..7fee5326d
--- /dev/null
+++ b/src/providers/data_provider/dp_iface_generated.c
@@ -0,0 +1,296 @@
+/* The following definitions are auto-generated from dp_iface.xml */
+
+#include "util/util.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_meta.h"
+#include "sbus/sssd_dbus_invokers.h"
+#include "dp_iface_generated.h"
+
+/* invokes a handler with a 's' DBus signature */
+static int invoke_s_method(struct sbus_request *dbus_req, void *function_ptr);
+
+/* invokes a handler with a 'us' DBus signature */
+static int invoke_us_method(struct sbus_request *dbus_req, void *function_ptr);
+
+/* invokes a handler with a 'uss' DBus signature */
+static int invoke_uss_method(struct sbus_request *dbus_req, void *function_ptr);
+
+/* invokes a handler with a 'uuusss' DBus signature */
+static int invoke_uuusss_method(struct sbus_request *dbus_req, void *function_ptr);
+
+/* arguments for org.freedesktop.sssd.DataProvider.Client.Register */
+const struct sbus_arg_meta iface_dp_client_Register__in[] = {
+ { "Name", "s" },
+ { NULL, }
+};
+
+int iface_dp_client_Register_finish(struct sbus_request *req)
+{
+ return sbus_request_return_and_finish(req,
+ DBUS_TYPE_INVALID);
+}
+
+/* methods for org.freedesktop.sssd.DataProvider.Client */
+const struct sbus_method_meta iface_dp_client__methods[] = {
+ {
+ "Register", /* name */
+ iface_dp_client_Register__in,
+ NULL, /* no out_args */
+ offsetof(struct iface_dp_client, Register),
+ invoke_s_method,
+ },
+ { NULL, }
+};
+
+/* interface info for org.freedesktop.sssd.DataProvider.Client */
+const struct sbus_interface_meta iface_dp_client_meta = {
+ "org.freedesktop.sssd.DataProvider.Client", /* name */
+ iface_dp_client__methods,
+ NULL, /* no signals */
+ NULL, /* no properties */
+ sbus_invoke_get_all, /* GetAll invoker */
+};
+
+/* arguments for org.freedesktop.sssd.dataprovider.autofsHandler */
+const struct sbus_arg_meta iface_dp_autofsHandler__in[] = {
+ { "dp_flags", "u" },
+ { "mapname", "s" },
+ { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.dataprovider.autofsHandler */
+const struct sbus_arg_meta iface_dp_autofsHandler__out[] = {
+ { "dp_error", "q" },
+ { "error", "u" },
+ { "error_message", "s" },
+ { NULL, }
+};
+
+int iface_dp_autofsHandler_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message)
+{
+ return sbus_request_return_and_finish(req,
+ DBUS_TYPE_UINT16, &arg_dp_error,
+ DBUS_TYPE_UINT32, &arg_error,
+ DBUS_TYPE_STRING, &arg_error_message,
+ DBUS_TYPE_INVALID);
+}
+
+/* arguments for org.freedesktop.sssd.dataprovider.hostHandler */
+const struct sbus_arg_meta iface_dp_hostHandler__in[] = {
+ { "dp_flags", "u" },
+ { "name", "s" },
+ { "alias", "s" },
+ { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.dataprovider.hostHandler */
+const struct sbus_arg_meta iface_dp_hostHandler__out[] = {
+ { "dp_error", "q" },
+ { "error", "u" },
+ { "error_message", "s" },
+ { NULL, }
+};
+
+int iface_dp_hostHandler_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message)
+{
+ return sbus_request_return_and_finish(req,
+ DBUS_TYPE_UINT16, &arg_dp_error,
+ DBUS_TYPE_UINT32, &arg_error,
+ DBUS_TYPE_STRING, &arg_error_message,
+ DBUS_TYPE_INVALID);
+}
+
+/* arguments for org.freedesktop.sssd.dataprovider.getDomains */
+const struct sbus_arg_meta iface_dp_getDomains__in[] = {
+ { "domain_hint", "s" },
+ { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.dataprovider.getDomains */
+const struct sbus_arg_meta iface_dp_getDomains__out[] = {
+ { "dp_error", "q" },
+ { "error", "u" },
+ { "error_message", "s" },
+ { NULL, }
+};
+
+int iface_dp_getDomains_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message)
+{
+ return sbus_request_return_and_finish(req,
+ DBUS_TYPE_UINT16, &arg_dp_error,
+ DBUS_TYPE_UINT32, &arg_error,
+ DBUS_TYPE_STRING, &arg_error_message,
+ DBUS_TYPE_INVALID);
+}
+
+/* arguments for org.freedesktop.sssd.dataprovider.getAccountInfo */
+const struct sbus_arg_meta iface_dp_getAccountInfo__in[] = {
+ { "dp_flags", "u" },
+ { "entry_type", "u" },
+ { "attr_type", "u" },
+ { "filter", "s" },
+ { "domain", "s" },
+ { "extra", "s" },
+ { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.dataprovider.getAccountInfo */
+const struct sbus_arg_meta iface_dp_getAccountInfo__out[] = {
+ { "dp_error", "q" },
+ { "error", "u" },
+ { "error_message", "s" },
+ { NULL, }
+};
+
+int iface_dp_getAccountInfo_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message)
+{
+ return sbus_request_return_and_finish(req,
+ DBUS_TYPE_UINT16, &arg_dp_error,
+ DBUS_TYPE_UINT32, &arg_error,
+ DBUS_TYPE_STRING, &arg_error_message,
+ DBUS_TYPE_INVALID);
+}
+
+/* methods for org.freedesktop.sssd.dataprovider */
+const struct sbus_method_meta iface_dp__methods[] = {
+ {
+ "pamHandler", /* name */
+ NULL, /* no in_args */
+ NULL, /* no out_args */
+ offsetof(struct iface_dp, pamHandler),
+ NULL, /* no invoker */
+ },
+ {
+ "sudoHandler", /* name */
+ NULL, /* no in_args */
+ NULL, /* no out_args */
+ offsetof(struct iface_dp, sudoHandler),
+ NULL, /* no invoker */
+ },
+ {
+ "autofsHandler", /* name */
+ iface_dp_autofsHandler__in,
+ iface_dp_autofsHandler__out,
+ offsetof(struct iface_dp, autofsHandler),
+ invoke_us_method,
+ },
+ {
+ "hostHandler", /* name */
+ iface_dp_hostHandler__in,
+ iface_dp_hostHandler__out,
+ offsetof(struct iface_dp, hostHandler),
+ invoke_uss_method,
+ },
+ {
+ "getDomains", /* name */
+ iface_dp_getDomains__in,
+ iface_dp_getDomains__out,
+ offsetof(struct iface_dp, getDomains),
+ invoke_s_method,
+ },
+ {
+ "getAccountInfo", /* name */
+ iface_dp_getAccountInfo__in,
+ iface_dp_getAccountInfo__out,
+ offsetof(struct iface_dp, getAccountInfo),
+ invoke_uuusss_method,
+ },
+ { NULL, }
+};
+
+/* interface info for org.freedesktop.sssd.dataprovider */
+const struct sbus_interface_meta iface_dp_meta = {
+ "org.freedesktop.sssd.dataprovider", /* name */
+ iface_dp__methods,
+ NULL, /* no signals */
+ NULL, /* no properties */
+ sbus_invoke_get_all, /* GetAll invoker */
+};
+
+/* invokes a handler with a 's' DBus signature */
+static int invoke_s_method(struct sbus_request *dbus_req, void *function_ptr)
+{
+ const char * arg_0;
+ int (*handler)(struct sbus_request *, void *, const char *) = function_ptr;
+
+ if (!sbus_request_parse_or_finish(dbus_req,
+ DBUS_TYPE_STRING, &arg_0,
+ DBUS_TYPE_INVALID)) {
+ return EOK; /* request handled */
+ }
+
+ return (handler)(dbus_req, dbus_req->intf->handler_data,
+ arg_0);
+}
+
+/* invokes a handler with a 'uss' DBus signature */
+static int invoke_uss_method(struct sbus_request *dbus_req, void *function_ptr)
+{
+ uint32_t arg_0;
+ const char * arg_1;
+ const char * arg_2;
+ int (*handler)(struct sbus_request *, void *, uint32_t, const char *, const char *) = function_ptr;
+
+ if (!sbus_request_parse_or_finish(dbus_req,
+ DBUS_TYPE_UINT32, &arg_0,
+ DBUS_TYPE_STRING, &arg_1,
+ DBUS_TYPE_STRING, &arg_2,
+ DBUS_TYPE_INVALID)) {
+ return EOK; /* request handled */
+ }
+
+ return (handler)(dbus_req, dbus_req->intf->handler_data,
+ arg_0,
+ arg_1,
+ arg_2);
+}
+
+/* invokes a handler with a 'uuusss' DBus signature */
+static int invoke_uuusss_method(struct sbus_request *dbus_req, void *function_ptr)
+{
+ uint32_t arg_0;
+ uint32_t arg_1;
+ uint32_t arg_2;
+ const char * arg_3;
+ const char * arg_4;
+ const char * arg_5;
+ int (*handler)(struct sbus_request *, void *, uint32_t, uint32_t, uint32_t, const char *, const char *, const char *) = function_ptr;
+
+ if (!sbus_request_parse_or_finish(dbus_req,
+ DBUS_TYPE_UINT32, &arg_0,
+ DBUS_TYPE_UINT32, &arg_1,
+ DBUS_TYPE_UINT32, &arg_2,
+ DBUS_TYPE_STRING, &arg_3,
+ DBUS_TYPE_STRING, &arg_4,
+ DBUS_TYPE_STRING, &arg_5,
+ DBUS_TYPE_INVALID)) {
+ return EOK; /* request handled */
+ }
+
+ return (handler)(dbus_req, dbus_req->intf->handler_data,
+ arg_0,
+ arg_1,
+ arg_2,
+ arg_3,
+ arg_4,
+ arg_5);
+}
+
+/* invokes a handler with a 'us' DBus signature */
+static int invoke_us_method(struct sbus_request *dbus_req, void *function_ptr)
+{
+ uint32_t arg_0;
+ const char * arg_1;
+ int (*handler)(struct sbus_request *, void *, uint32_t, const char *) = function_ptr;
+
+ if (!sbus_request_parse_or_finish(dbus_req,
+ DBUS_TYPE_UINT32, &arg_0,
+ DBUS_TYPE_STRING, &arg_1,
+ DBUS_TYPE_INVALID)) {
+ return EOK; /* request handled */
+ }
+
+ return (handler)(dbus_req, dbus_req->intf->handler_data,
+ arg_0,
+ arg_1);
+}
diff --git a/src/providers/data_provider/dp_iface_generated.h b/src/providers/data_provider/dp_iface_generated.h
new file mode 100644
index 000000000..f1bcf17d0
--- /dev/null
+++ b/src/providers/data_provider/dp_iface_generated.h
@@ -0,0 +1,93 @@
+/* The following declarations are auto-generated from dp_iface.xml */
+
+#ifndef __DP_IFACE_XML__
+#define __DP_IFACE_XML__
+
+#include "sbus/sssd_dbus.h"
+
+/* ------------------------------------------------------------------------
+ * DBus Constants
+ *
+ * Various constants of interface and method names mostly for use by clients
+ */
+
+/* constants for org.freedesktop.sssd.DataProvider.Client */
+#define IFACE_DP_CLIENT "org.freedesktop.sssd.DataProvider.Client"
+#define IFACE_DP_CLIENT_REGISTER "Register"
+
+/* constants for org.freedesktop.sssd.dataprovider */
+#define IFACE_DP "org.freedesktop.sssd.dataprovider"
+#define IFACE_DP_PAMHANDLER "pamHandler"
+#define IFACE_DP_SUDOHANDLER "sudoHandler"
+#define IFACE_DP_AUTOFSHANDLER "autofsHandler"
+#define IFACE_DP_HOSTHANDLER "hostHandler"
+#define IFACE_DP_GETDOMAINS "getDomains"
+#define IFACE_DP_GETACCOUNTINFO "getAccountInfo"
+
+/* ------------------------------------------------------------------------
+ * DBus handlers
+ *
+ * These structures are filled in by implementors of the different
+ * dbus interfaces to handle method calls.
+ *
+ * Handler functions of type sbus_msg_handler_fn accept raw messages,
+ * other handlers are typed appropriately. If a handler that is
+ * set to NULL is invoked it will result in a
+ * org.freedesktop.DBus.Error.NotSupported error for the caller.
+ *
+ * Handlers have a matching xxx_finish() function (unless the method has
+ * accepts raw messages). These finish functions the
+ * sbus_request_return_and_finish() with the appropriate arguments to
+ * construct a valid reply. Once a finish function has been called, the
+ * @dbus_req it was called with is freed and no longer valid.
+ */
+
+/* vtable for org.freedesktop.sssd.DataProvider.Client */
+struct iface_dp_client {
+ struct sbus_vtable vtable; /* derive from sbus_vtable */
+ int (*Register)(struct sbus_request *req, void *data, const char *arg_Name);
+};
+
+/* finish function for Register */
+int iface_dp_client_Register_finish(struct sbus_request *req);
+
+/* vtable for org.freedesktop.sssd.dataprovider */
+struct iface_dp {
+ struct sbus_vtable vtable; /* derive from sbus_vtable */
+ sbus_msg_handler_fn pamHandler;
+ sbus_msg_handler_fn sudoHandler;
+ int (*autofsHandler)(struct sbus_request *req, void *data, uint32_t arg_dp_flags, const char *arg_mapname);
+ int (*hostHandler)(struct sbus_request *req, void *data, uint32_t arg_dp_flags, const char *arg_name, const char *arg_alias);
+ int (*getDomains)(struct sbus_request *req, void *data, const char *arg_domain_hint);
+ int (*getAccountInfo)(struct sbus_request *req, void *data, uint32_t arg_dp_flags, uint32_t arg_entry_type, uint32_t arg_attr_type, const char *arg_filter, const char *arg_domain, const char *arg_extra);
+};
+
+/* finish function for autofsHandler */
+int iface_dp_autofsHandler_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message);
+
+/* finish function for hostHandler */
+int iface_dp_hostHandler_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message);
+
+/* finish function for getDomains */
+int iface_dp_getDomains_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message);
+
+/* finish function for getAccountInfo */
+int iface_dp_getAccountInfo_finish(struct sbus_request *req, uint16_t arg_dp_error, uint32_t arg_error, const char *arg_error_message);
+
+/* ------------------------------------------------------------------------
+ * DBus Interface Metadata
+ *
+ * These structure definitions are filled in with the information about
+ * the interfaces, methods, properties and so on.
+ *
+ * The actual definitions are found in the accompanying C file next
+ * to this header.
+ */
+
+/* interface info for org.freedesktop.sssd.DataProvider.Client */
+extern const struct sbus_interface_meta iface_dp_client_meta;
+
+/* interface info for org.freedesktop.sssd.dataprovider */
+extern const struct sbus_interface_meta iface_dp_meta;
+
+#endif /* __DP_IFACE_XML__ */
diff --git a/src/providers/data_provider/dp_methods.c b/src/providers/data_provider/dp_methods.c
new file mode 100644
index 000000000..e4290beee
--- /dev/null
+++ b/src/providers/data_provider/dp_methods.c
@@ -0,0 +1,122 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <talloc.h>
+
+#include "config.h"
+#include "providers/data_provider/dp.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/backend.h"
+#include "util/util.h"
+
+void _dp_set_method(struct dp_method *methods,
+ enum dp_methods method,
+ dp_req_send_fn send_fn,
+ dp_req_recv_fn recv_fn,
+ void *method_data,
+ const char *method_dtype,
+ const char *request_dtype,
+ const char *output_dtype,
+ uint32_t output_size)
+{
+ if (method >= DP_METHOD_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: invalid method %d\n", method);
+ return;
+ }
+
+ /* Each method can be set only once, if we attempt to set it twice it
+ * is a bug in module initialization. */
+ if (methods[method].send_fn != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: method %d is already set!\n", method);
+ return;
+ }
+
+ if (send_fn == NULL || recv_fn == NULL || method_dtype == NULL
+ || request_dtype == NULL || output_dtype == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: one or more required parameter was "
+ "not provided for method %d\n", method);
+ return;
+ }
+
+ methods[method].send_fn = send_fn;
+ methods[method].recv_fn = recv_fn;
+ methods[method].method_data = method_data;
+
+ methods[method].method_dtype = method_dtype;
+ methods[method].request_dtype = request_dtype;
+ methods[method].output_dtype = output_dtype;
+ methods[method].output_size = output_size;
+}
+
+bool dp_method_enabled(struct data_provider *provider,
+ enum dp_targets target,
+ enum dp_methods method)
+{
+ struct dp_target *dp_target;
+
+ if (target >= DP_TARGET_SENTINEL || method >= DP_METHOD_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: Invalid target or method ID\n");
+ return false;
+ }
+
+ dp_target = provider->targets[target];
+ if (dp_target == NULL || dp_target->initialized == false) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Target %s is not configured\n",
+ dp_target_to_string(target));
+ return false;
+ }
+
+ if (dp_target->methods[method].send_fn == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+errno_t dp_find_method(struct data_provider *provider,
+ enum dp_targets target,
+ enum dp_methods method,
+ struct dp_method **_execute)
+{
+ struct dp_method *execute;
+
+ if (target >= DP_TARGET_SENTINEL || method >= DP_METHOD_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: Invalid target or method ID\n");
+ return ERR_INTERNAL;
+ }
+
+ if (!dp_target_initialized(provider->targets, target)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Target [%s] is not initialized\n",
+ dp_target_to_string(target));
+ return ERR_MISSING_DP_TARGET;
+ }
+
+ execute = &provider->targets[target]->methods[method];
+ if (execute->send_fn == NULL || execute->recv_fn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Bug: Invalid combination of target [%s] and method [%d]\n",
+ dp_target_to_string(target), method);
+ return ERR_INTERNAL;
+ }
+
+ *_execute = execute;
+
+ return EOK;
+}
diff --git a/src/providers/data_provider/dp_modules.c b/src/providers/data_provider/dp_modules.c
new file mode 100644
index 000000000..2e6e33ddb
--- /dev/null
+++ b/src/providers/data_provider/dp_modules.c
@@ -0,0 +1,224 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <dlfcn.h>
+#include "config.h"
+#include "providers/data_provider/dp.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/backend.h"
+#include "util/util.h"
+
+/* There can be at most the same number of different modules loaded at
+ * one time as the maximum number of defined targets. */
+#define DP_MAX_MODULES DP_TARGET_SENTINEL
+
+#define DP_MODULE_PATH DATA_PROVIDER_PLUGINS_PATH "/libsss_%s.so"
+#define DP_MODULE_INIT_FN "sssm_%s_init"
+
+static errno_t dp_module_open_lib(struct dp_module *module)
+{
+ char *libpath = NULL;
+ errno_t ret;
+
+ libpath = talloc_asprintf(module, DP_MODULE_PATH, module->name);
+ if (libpath == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Loading module [%s] with path [%s]\n",
+ module->name, libpath);
+
+ module->libhandle = dlopen(libpath, RTLD_NOW);
+ if (module->libhandle == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load module [%s] with path "
+ "[%s]: %s\n", module->name, libpath, dlerror());
+ ret = ELIBACC;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(libpath);
+ return ret;
+}
+
+static errno_t dp_module_run_constructor(struct dp_module *module,
+ struct be_ctx *be_ctx,
+ struct data_provider *provider)
+{
+ char *fn_name;
+ dp_module_init_fn fn;
+ errno_t ret;
+
+ fn_name = talloc_asprintf(module, DP_MODULE_INIT_FN, module->name);
+ if (fn_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return ENOMEM;
+ }
+
+ fn = (dp_module_init_fn)dlsym(module->libhandle, fn_name);
+ if (fn != NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Executing module [%s] constructor.\n",
+ module->name);
+
+ ret = fn(module, be_ctx, provider, module->name, &module->module_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Module [%s] constructor failed "
+ "[%d]: %s\n", module->name, ret, sss_strerror(ret));
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "No constructor found for module [%s].\n",
+ module->name);
+ module->module_data = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(fn_name);
+ return ret;
+}
+
+static errno_t dp_module_find(struct dp_module **modules,
+ const char *name,
+ struct dp_module **_module,
+ unsigned int *_slot)
+{
+ unsigned int slot;
+
+ for (slot = 0; modules[slot] != NULL; slot++) {
+ if (strcmp(modules[slot]->name, name) == 0) {
+ *_module = modules[slot];
+ *_slot = slot;
+
+ return EOK;
+ }
+ }
+
+ if (slot == DP_MAX_MODULES) {
+ /* This should not happen. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "All module slots are taken.\n");
+
+ return ERR_INTERNAL;
+ }
+
+ *_module = NULL;
+ *_slot = slot;
+
+ return EOK;
+}
+
+static struct dp_module *dp_module_create(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ const char *name)
+{
+ struct dp_module *module;
+ errno_t ret;
+
+ module = talloc_zero(mem_ctx, struct dp_module);
+ if (module == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ module->name = talloc_strdup(module, name);
+ if (module->name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = dp_module_open_lib(module);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = dp_module_run_constructor(module, be_ctx, provider);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ module->initialized = true;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(module);
+ return NULL;
+ }
+
+ return module;
+}
+
+struct dp_module *dp_load_module(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ struct dp_module **modules,
+ const char *name)
+{
+ struct dp_module *module;
+ unsigned int free_slot;
+ errno_t ret;
+
+ ret = dp_module_find(modules, name, &module, &free_slot);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ if (module != NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Module [%s] is already loaded.\n", name);
+ return module;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "About to load module [%s].\n", name);
+
+ module = dp_module_create(mem_ctx, be_ctx, provider, name);
+ if (module == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create DP module.\n");
+ return NULL;
+ }
+
+ modules[free_slot] = module;
+
+ return module;
+}
+
+errno_t dp_init_modules(TALLOC_CTX *mem_ctx, struct dp_module ***_modules)
+{
+ struct dp_module **modules;
+
+ modules = talloc_zero_array(mem_ctx, struct dp_module *,
+ DP_MAX_MODULES + 1);
+ if (modules == NULL) {
+ return ENOMEM;
+ }
+
+ *_modules = modules;
+
+ return EOK;
+}
diff --git a/src/providers/data_provider/dp_private.h b/src/providers/data_provider/dp_private.h
new file mode 100644
index 000000000..ece60e7a4
--- /dev/null
+++ b/src/providers/data_provider/dp_private.h
@@ -0,0 +1,247 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 _DP_PRIVATE_H_
+#define _DP_PRIVATE_H_
+
+#include <tevent.h>
+#include <dhash.h>
+#include "sbus/sssd_dbus.h"
+#include "providers/data_provider/dp.h"
+#include "util/util.h"
+
+#define DP_REQ_DEBUG(level, name, fmt, ...) \
+ DEBUG(level, "DP Request [%s]: " fmt "\n", (name ?: "Unknown"), ##__VA_ARGS__)
+
+enum dp_clients {
+ DPC_NSS,
+ DPC_PAM,
+ DPC_IFP,
+ DPC_PAC,
+ DPC_SUDO,
+ DPC_HOST,
+ DPC_AUTOFS,
+
+ DP_CLIENT_SENTINEL
+};
+
+struct dp_req;
+struct dp_client;
+
+struct dp_module {
+ bool initialized;
+ const char *name;
+ void *module_data;
+ void *libhandle;
+};
+
+struct dp_target {
+ const char *name;
+ const char *module_name;
+ bool explicitly_configured;
+
+ bool initialized;
+ enum dp_targets target;
+ struct dp_module *module;
+ struct dp_method *methods;
+};
+
+struct dp_method {
+ dp_req_send_fn send_fn;
+ dp_req_recv_fn recv_fn;
+ void *method_data;
+ const char *method_dtype;
+ const char *request_dtype;
+ const char *output_dtype;
+ uint32_t output_size;
+};
+
+struct data_provider {
+ uid_t uid;
+ gid_t gid;
+ struct be_ctx *be_ctx;
+ struct tevent_context *ev;
+ struct sbus_connection *srv_conn;
+ struct dp_client *clients[DP_CLIENT_SENTINEL];
+ bool terminating;
+
+ struct {
+ /* Numeric identificator that will be assigned to next request. */
+ uint32_t index;
+
+ /* List of all ongoing requests. */
+ uint32_t num_active;
+ struct dp_req *active;
+
+ /* Table containing list of sbus_requests where DP sends reply when
+ * a request is finished. Value of this table is pair
+ * <tevent_req, list of sbus_request>
+ */
+ hash_table_t *reply_table;
+ } requests;
+
+ struct dp_module **modules;
+ struct dp_target **targets;
+};
+
+errno_t dp_find_method(struct data_provider *provider,
+ enum dp_targets target,
+ enum dp_methods method,
+ struct dp_method **_execute);
+
+struct dp_module *dp_load_module(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ struct dp_module **modules,
+ const char *name);
+
+errno_t dp_init_modules(TALLOC_CTX *mem_ctx, struct dp_module ***_modules);
+
+const char *dp_target_to_string(enum dp_targets target);
+
+bool dp_target_initialized(struct dp_target **targets, enum dp_targets type);
+
+errno_t dp_init_targets(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ struct dp_module **modules);
+
+/* Reply callbacks. */
+
+typedef void (*dp_req_post_fn)(const char *req_name,
+ struct data_provider *provider,
+ void *post_data,
+ void *reply_data);
+
+typedef void (*dp_req_reply_fn)(const char *req_name,
+ struct sbus_request *sbus_req,
+ void *data);
+
+void dp_req_reply_default(const char *req_name,
+ struct sbus_request *sbus_req,
+ void *data);
+
+/* Data provider request table. */
+
+struct dp_sbus_req_item;
+
+struct dp_table_value {
+ hash_table_t *table;
+ const char *key;
+
+ struct tevent_req *req;
+ struct dp_sbus_req_item *list;
+};
+
+struct dp_sbus_req_item {
+ struct dp_table_value *parent;
+ struct sbus_request *sbus_req;
+
+ struct dp_sbus_req_item *prev;
+ struct dp_sbus_req_item *next;
+};
+
+char *dp_req_table_key(TALLOC_CTX *mem_ctx,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ const char *custom_part);
+
+errno_t dp_req_table_init(TALLOC_CTX *mem_ctx, hash_table_t **_table);
+
+struct dp_table_value *dp_req_table_lookup(hash_table_t *table,
+ const char *key);
+
+errno_t dp_req_table_add(hash_table_t *table,
+ const char *key,
+ struct tevent_req *req,
+ struct sbus_request *sbus_req);
+
+void dp_req_table_del(hash_table_t *table,
+ const char *key);
+
+void dp_req_table_del_and_free(hash_table_t *table,
+ const char *key);
+
+bool dp_req_table_has_key(hash_table_t *table,
+ const char *key);
+
+/* Data provider request. */
+
+void dp_terminate_active_requests(struct data_provider *provider);
+
+void dp_req_reply_error(struct sbus_request *sbus_req,
+ const char *req_name,
+ errno_t ret);
+
+void _dp_req_with_reply(struct dp_client *dp_cli,
+ const char *domain,
+ const char *request_name,
+ const char *custom_key,
+ struct sbus_request *sbus_req,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ dp_req_post_fn postprocess_fn,
+ void *postprocess_data,
+ dp_req_reply_fn reply_fn,
+ const char *output_dtype);
+
+/**
+ * If @domain is NULL, be_ctx->domain is used.
+ * If req_key is NULL, address of sbus_req is used.
+ *
+ * If @pp_fn (post process function) is set it is call on a successful
+ * DP request before reply is sent.
+ */
+#define dp_req_with_reply_pp(dp_cli, domain, req_name, req_key, sbus_req, \
+ target, method, dp_flags, req_data, pp_fn, \
+ pp_data, pp_dtype, reply_fn, output_dtype) \
+ do { \
+ /* Check postprocess function parameter types. */ \
+ void (*__pp_fn)(const char *, struct data_provider *, \
+ pp_dtype *, output_dtype *) = (pp_fn); \
+ pp_dtype *__pp_data = (pp_data); \
+ \
+ /* Check reply function parameter types. */ \
+ void (*__reply_fn)(const char *, struct sbus_request *, \
+ output_dtype *) = (reply_fn); \
+ \
+ _dp_req_with_reply(dp_cli, domain, req_name, req_key, sbus_req, \
+ target, method, dp_flags, req_data, \
+ (dp_req_post_fn)__pp_fn, __pp_data, \
+ (dp_req_reply_fn)__reply_fn, #output_dtype); \
+ } while(0)
+
+#define dp_req_with_reply(dp_cli, domain, req_name, req_key, sbus_req, target,\
+ method, dp_flags, req_data, reply_fn, \
+ output_dtype) \
+ dp_req_with_reply_pp(dp_cli, domain, req_name, req_key, sbus_req, target, \
+ method, dp_flags, req_data, NULL, NULL, void, \
+ reply_fn, output_dtype)
+
+/* Client shared functions. */
+
+errno_t dp_client_init(struct sbus_connection *conn, void *data);
+struct data_provider *dp_client_provider(struct dp_client *dp_cli);
+struct sbus_connection *dp_client_conn(struct dp_client *dp_cli);
+
+#endif /* _DP_PRIVATE_H_ */
diff --git a/src/providers/data_provider/dp_request.c b/src/providers/data_provider/dp_request.c
new file mode 100644
index 000000000..6c0a0b72d
--- /dev/null
+++ b/src/providers/data_provider/dp_request.c
@@ -0,0 +1,460 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "sbus/sssd_dbus_errors.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/backend.h"
+#include "util/dlinklist.h"
+#include "util/util.h"
+
+struct dp_req {
+ struct data_provider *provider;
+ struct dp_client *client;
+ uint32_t dp_flags;
+
+ struct sss_domain_info *domain;
+
+ enum dp_targets target;
+ enum dp_methods method;
+ struct dp_method *execute;
+ const char *name;
+ uint32_t num;
+
+ struct tevent_req *req;
+ struct tevent_req *handler_req;
+ void *request_data;
+
+ /* Active request list. */
+ struct dp_req *prev;
+ struct dp_req *next;
+};
+
+static bool check_data_type(const char *expected,
+ const char *description,
+ void *ptr)
+{
+ void *tmp;
+
+ /* If ptr is NULL we still return true since it is valid case. */
+ tmp = talloc_check_name(ptr, expected);
+ if (tmp != NULL || ptr == NULL) {
+ return true;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid %s data type provided. Expected [%s], "
+ "got [%s].\n", description, expected, talloc_get_name(ptr));
+
+ return false;
+}
+
+static bool check_method_data(struct dp_method *method,
+ void *request_data)
+{
+ if (!check_data_type(method->method_dtype, "method", method->method_data)) {
+ return false;
+ }
+
+ if (!check_data_type(method->request_dtype, "request", request_data)) {
+ return false;
+ }
+
+ return true;
+}
+
+static int dp_req_destructor(struct dp_req *dp_req)
+{
+ DLIST_REMOVE(dp_req->provider->requests.active, dp_req);
+
+ if (dp_req->provider->requests.num_active == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: there are no active requests!\n");
+ return 0;
+ }
+
+ dp_req->provider->requests.num_active--;
+
+ DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, dp_req->name, "Request removed.");
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Number of active DP request: %u\n",
+ dp_req->provider->requests.num_active);
+
+ return 0;
+}
+
+static errno_t dp_attach_req(struct dp_req *dp_req,
+ struct data_provider *provider,
+ const char *name,
+ uint32_t dp_flags)
+{
+ /* If we run out of numbers we simply overflow. */
+ dp_req->num = provider->requests.index++;
+ dp_req->name = talloc_asprintf(dp_req, "%s #%u", name, dp_req->num);
+ if (dp_req->name == NULL) {
+ return ENOMEM;
+ }
+
+ /* Attach this request to active request list. */
+ DLIST_ADD(provider->requests.active, dp_req);
+ provider->requests.num_active++;
+
+ talloc_set_destructor(dp_req, dp_req_destructor);
+
+ DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, dp_req->name,
+ "New request. Flags [%#.4x].", dp_flags);
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Number of active DP request: %u\n",
+ provider->requests.num_active);
+
+ return EOK;
+}
+
+static errno_t
+dp_req_new(TALLOC_CTX *mem_ctx,
+ struct data_provider *provider,
+ struct dp_client *dp_cli,
+ const char *domainname,
+ const char *name,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ struct tevent_req *req,
+ struct dp_req **_dp_req)
+{
+ struct dp_req *dp_req;
+ struct be_ctx *be_ctx;
+ errno_t ret;
+
+ /* We set output even for error to simplify code flow in the caller. */
+ *_dp_req = NULL;
+
+ dp_req = talloc_zero(mem_ctx, struct dp_req);
+ if (dp_req == NULL) {
+ return ENOMEM;
+ }
+
+ dp_req->provider = provider;
+ dp_req->client = dp_cli;
+ dp_req->dp_flags = dp_flags;
+ dp_req->target = target;
+ dp_req->method = method;
+ dp_req->request_data = request_data;
+ dp_req->req = req;
+
+ ret = dp_attach_req(dp_req, provider, name, dp_flags);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create DP request "
+ "[%s] [%d]: %s\n", name, ret, sss_strerror(ret));
+ talloc_free(dp_req);
+ return ret;
+ }
+
+ /* Now the request is created. We will return it even in case of error
+ * so we can get better debug messages. */
+
+ talloc_steal(dp_req, dp_req->request_data);
+ *_dp_req = dp_req;
+
+ be_ctx = provider->be_ctx;
+ dp_req->domain = be_ctx->domain;
+ if (domainname != NULL) {
+ dp_req->domain = find_domain_by_name(be_ctx->domain, domainname, true);
+ if (dp_req->domain == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain: %s\n", domainname);
+ return ERR_DOMAIN_NOT_FOUND;
+ }
+ }
+
+ ret = dp_find_method(provider, target, method, &dp_req->execute);
+
+ return ret;
+}
+
+static errno_t
+file_dp_request(TALLOC_CTX *mem_ctx,
+ struct data_provider *provider,
+ struct dp_client *dp_cli,
+ const char *domainname,
+ const char *name,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ struct tevent_req *req,
+ struct dp_req **_dp_req)
+{
+ struct dp_req_params *dp_params;
+ dp_req_send_fn send_fn;
+ struct dp_req *dp_req;
+ struct be_ctx *be_ctx;
+ errno_t ret;
+
+ be_ctx = provider->be_ctx;
+
+ ret = dp_req_new(mem_ctx, provider, dp_cli, domainname, name, target,
+ method, dp_flags, request_data, req, &dp_req);
+ if (ret != EOK) {
+ *_dp_req = dp_req;
+ goto done;
+ }
+
+ /* DP request is already created. We will always return it to get nice
+ * debug messages. */
+ *_dp_req = dp_req;
+
+ /* Check that provided data are of correct type. */
+
+ if (!check_method_data(dp_req->execute, dp_req->request_data)) {
+ ret = ERR_INVALID_DATA_TYPE;
+ goto done;
+ }
+
+ /* Process data provider flags */
+
+ if (dp_flags & DP_FAST_REPLY && be_is_offline(be_ctx)) {
+ ret = ERR_OFFLINE;
+ goto done;
+ }
+
+ /* File request */
+
+ dp_params = talloc_zero(dp_req, struct dp_req_params);
+ if (dp_params == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dp_params->ev = provider->ev;
+ dp_params->be_ctx = be_ctx;
+ dp_params->domain = dp_req->domain;
+ dp_params->target = dp_req->target;
+ dp_params->method = dp_req->method;
+
+ send_fn = dp_req->execute->send_fn;
+ dp_req->handler_req = send_fn(dp_req, dp_req->execute->method_data,
+ dp_req->request_data, dp_params);
+ if (dp_req->handler_req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_dp_req = dp_req;
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+struct dp_req_state {
+ struct dp_req *dp_req;
+ dp_req_recv_fn recv_fn;
+ void *output_data;
+};
+
+static void dp_req_done(struct tevent_req *subreq);
+
+struct tevent_req *dp_req_send(TALLOC_CTX *mem_ctx,
+ struct data_provider *provider,
+ struct dp_client *dp_cli,
+ const char *domain,
+ const char *name,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ const char **_request_name)
+{
+ struct dp_req_state *state;
+ const char *request_name;
+ struct tevent_req *req;
+ struct dp_req *dp_req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct dp_req_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ ret = file_dp_request(state, provider, dp_cli, domain, name, target,
+ method, dp_flags, request_data, req, &dp_req);
+
+ if (dp_req == NULL) {
+ /* An error ocurred before request could be created. */
+ if (_request_name != NULL) {
+ *_request_name = "Request Not Yet Created";
+ }
+
+ goto immediately;
+ }
+
+ state->dp_req = dp_req;
+ if (_request_name != NULL) {
+ request_name = talloc_strdup(mem_ctx, dp_req->name);
+ if (request_name == NULL) {
+ *_request_name = "Request Not Yet Created";
+ ret = ENOMEM;
+ goto immediately;
+ }
+ *_request_name = request_name;
+ }
+
+ if (ret != EOK) {
+ goto immediately;
+ }
+
+ state->recv_fn = dp_req->execute->recv_fn;
+ state->output_data = talloc_zero_size(state, dp_req->execute->output_size);
+ if (state->output_data == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+
+ talloc_set_name_const(state->output_data, dp_req->execute->output_dtype);
+
+ tevent_req_set_callback(dp_req->handler_req, dp_req_done, req);
+
+ return req;
+
+immediately:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, provider->ev);
+
+ return req;
+}
+
+static void dp_req_done(struct tevent_req *subreq)
+{
+ struct dp_req_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct dp_req_state);
+
+ ret = state->recv_fn(state->output_data, subreq, state->output_data);
+
+ /* subreq is the same as dp_req->handler_req */
+ talloc_zfree(subreq);
+ state->dp_req->handler_req = NULL;
+
+ DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->dp_req->name,
+ "Request handler finished [%d]: %s", ret, sss_strerror(ret));
+
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t _dp_req_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char *output_dtype,
+ void **_output_data)
+{
+ struct dp_req_state *state;
+
+ state = tevent_req_data(req, struct dp_req_state);
+
+ if (state->dp_req != NULL) {
+ DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->dp_req->name,
+ "Receiving request data.");
+ } else {
+ /* dp_req may be NULL in case we error when filing request */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Receiving data of prematurely interrupted request!\n");
+ }
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (!check_data_type(output_dtype, "output", state->output_data)) {
+ return ERR_INVALID_DATA_TYPE;
+ }
+
+ *_output_data = talloc_steal(mem_ctx, state->output_data);
+
+ return EOK;
+}
+
+static void dp_terminate_request(struct dp_req *dp_req)
+{
+ if (dp_req->handler_req == NULL) {
+ /* This may occur when the handler already finished but the caller
+ * of dp request did not yet recieved data/free dp_req. We just
+ * return here. */
+ return;
+ }
+
+ /* We will end the handler request and mark dp request as terminated. */
+
+ DP_REQ_DEBUG(SSSDBG_TRACE_ALL, dp_req->name, "Terminating.");
+
+ talloc_zfree(dp_req->handler_req);
+ tevent_req_error(dp_req->req, ERR_TERMINATED);
+}
+
+static void dp_terminate_request_list(struct data_provider *provider,
+ const char *domain)
+{
+ struct dp_req *next;
+ struct dp_req *cur;
+
+ if (provider == NULL || provider->requests.active == NULL) {
+ return;
+ }
+
+ for (cur = provider->requests.active; cur != NULL; cur = next) {
+ next = cur->next;
+ if (domain == NULL || strcmp(cur->domain->name, domain) == 0) {
+ dp_terminate_request(cur);
+ }
+ }
+}
+
+void dp_terminate_active_requests(struct data_provider *provider)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Terminating active data provider requests\n");
+
+ dp_terminate_request_list(provider, NULL);
+}
+
+void dp_terminate_domain_requests(struct data_provider *provider,
+ const char *domain)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Terminating active data provider requests "
+ "for domain [%s]\n", domain);
+
+ if (domain == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: domain is NULL!\n");
+ return;
+ }
+
+ dp_terminate_request_list(provider, domain);
+}
diff --git a/src/providers/data_provider/dp_request.h b/src/providers/data_provider/dp_request.h
new file mode 100644
index 000000000..361ab2536
--- /dev/null
+++ b/src/providers/data_provider/dp_request.h
@@ -0,0 +1,77 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 _DP_REQUEST_H_
+#define _DP_REQUEST_H_
+
+#include <talloc.h>
+
+#include "providers/data_provider/dp.h"
+
+struct data_provider;
+struct dp_client;
+enum dp_targets;
+enum dp_methods;
+
+struct tevent_req *dp_req_send(TALLOC_CTX *mem_ctx,
+ struct data_provider *provider,
+ struct dp_client *dp_cli,
+ const char *domain,
+ const char *name,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ const char **_request_name);
+
+errno_t _dp_req_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char *data_type,
+ void **_data);
+
+/**
+ * Returns value of output data.
+ *
+ * @example
+ * struct dp_reply_std reply;
+ * ret = dp_req_recv(mem_ctx, req, struct dp_reply_std, &reply);
+ */
+#define dp_req_recv(mem_ctx, req, data_type, _data) \
+({ \
+ data_type *__value = NULL; \
+ errno_t __ret; \
+ __ret = _dp_req_recv(mem_ctx, req, #data_type, (void**)&__value); \
+ if (__ret == EOK) { \
+ *(_data) = *__value; \
+ } \
+ __ret; \
+})
+
+/**
+ * Returns pointer to output data type.
+ *
+ * @example
+ * struct dp_reply_std *reply;
+ * ret = dp_req_recv_ptr(mem_ctx, req, struct dp_reply_std, &reply);
+ */
+#define dp_req_recv_ptr(mem_ctx, req, data_type, _data) \
+ _dp_req_recv(mem_ctx, req, #data_type, (void**)_data)
+
+#endif /* _DP_REQUEST_H_ */
diff --git a/src/providers/data_provider/dp_request_reply.c b/src/providers/data_provider/dp_request_reply.c
new file mode 100644
index 000000000..27d9654ba
--- /dev/null
+++ b/src/providers/data_provider/dp_request_reply.c
@@ -0,0 +1,372 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "sbus/sssd_dbus_errors.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/backend.h"
+#include "util/dlinklist.h"
+#include "util/sss_utf8.h"
+#include "util/util.h"
+
+void dp_req_reply_default(const char *req_name,
+ struct sbus_request *sbus_req,
+ void *data)
+{
+ DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, req_name, "Replying with empty message");
+
+ sbus_request_return_and_finish(sbus_req, DBUS_TYPE_INVALID);
+}
+
+static DBusError *dp_req_reply_gen_error(TALLOC_CTX *mem_ctx,
+ const char *req_name,
+ errno_t ret)
+{
+ DBusError *error;
+
+ switch (ret) {
+ case EOK:
+ DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
+ "Bug: Success case must be handled by custom handler.");
+ error = sbus_error_new(mem_ctx, SBUS_ERROR_INTERNAL,
+ "Operation succeeded but result was not handled");
+ break;
+ case ERR_OFFLINE:
+ DP_REQ_DEBUG(SSSDBG_MINOR_FAILURE, req_name,
+ "Finished. Backend is currently offline.");
+
+ error = sbus_error_new(mem_ctx, SBUS_ERROR_DP_OFFLINE,
+ "Backend is currently offline");
+ break;
+ case ERR_MISSING_DP_TARGET:
+ DP_REQ_DEBUG(SSSDBG_MINOR_FAILURE, req_name,
+ "Finished. Target is not supported "
+ "with this configuration.");
+
+ error = sbus_error_new(mem_ctx, SBUS_ERROR_DP_NOTSUP,
+ "Target is not supported.");
+ break;
+ default:
+ DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
+ "Finished. Error [%d]: %s", ret, sss_strerror(ret));
+
+ error = sbus_error_new(mem_ctx, SBUS_ERROR_DP_FATAL,
+ "An error occurred [%d]: %s", ret, sss_strerror(ret));
+ break;
+ }
+
+ return error;
+}
+
+void dp_req_reply_error(struct sbus_request *sbus_req,
+ const char *req_name,
+ errno_t ret)
+{
+ DBusError *error;
+
+ error = dp_req_reply_gen_error(sbus_req, req_name, ret);
+ if (error == NULL) {
+ DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
+ "Out of memory, killing request...");
+ talloc_free(sbus_req);
+ return;
+ }
+
+ sbus_request_fail_and_finish(sbus_req, error);
+}
+
+static void dp_req_reply_list_error(struct dp_sbus_req_item *list,
+ const char *req_name,
+ errno_t ret)
+{
+ struct dp_sbus_req_item *next_item;
+ struct dp_sbus_req_item *item;
+ DBusError *error;
+
+ error = dp_req_reply_gen_error(NULL, req_name, ret);
+ if (error == NULL) {
+ DP_REQ_DEBUG(SSSDBG_CRIT_FAILURE, req_name,
+ "Out of memory, killing request...");
+
+ for (item = list; item != NULL; item = next_item) {
+ next_item = item->next;
+ talloc_free(item->sbus_req);
+ }
+
+ return;
+ }
+
+ for (item = list; item != NULL; item = next_item) {
+ next_item = item->next;
+ sbus_request_fail_and_finish(item->sbus_req, error);
+ }
+
+ talloc_free(error);
+ return;
+}
+
+static void dp_req_reply_list_success(struct dp_sbus_req_item *list,
+ dp_req_reply_fn reply_fn,
+ const char *request_name,
+ void *output_data)
+{
+ struct dp_sbus_req_item *next_item;
+ struct dp_sbus_req_item *item;
+
+ DP_REQ_DEBUG(SSSDBG_TRACE_FUNC, request_name, "Finished. Success.");
+
+ for (item = list; item != NULL; item = next_item) {
+ next_item = item->next;
+ reply_fn(request_name, item->sbus_req, output_data);
+ }
+}
+
+struct dp_req_with_reply_state {
+ struct data_provider *provider;
+
+ void *postprocess_data;
+ dp_req_post_fn postprocess_fn;
+
+ const char *output_dtype;
+ dp_req_reply_fn reply_fn;
+ const char *key;
+ const char *name;
+};
+
+static errno_t dp_req_with_reply_step(struct data_provider *provider,
+ struct dp_client *dp_cli,
+ const char *domain,
+ const char *request_name,
+ const char *custom_key,
+ struct sbus_request *sbus_req,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ dp_req_post_fn postprocess_fn,
+ void *postprocess_data,
+ dp_req_reply_fn reply_fn,
+ const char *output_dtype);
+
+static void dp_req_with_reply_done(struct tevent_req *req);
+
+void _dp_req_with_reply(struct dp_client *dp_cli,
+ const char *domain,
+ const char *request_name,
+ const char *custom_key,
+ struct sbus_request *sbus_req,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ dp_req_post_fn postprocess_fn,
+ void *postprocess_data,
+ dp_req_reply_fn reply_fn,
+ const char *output_dtype)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct data_provider *provider;
+ const char *key;
+ bool has_key;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ provider = dp_client_provider(dp_cli);
+
+ if (custom_key == NULL) {
+ /* It may not be always possible or desirable to have a meaningful key
+ * to chain sbus request. In such cases, we generate a unique key from
+ * sbus_req address that allows us to use the same code but the
+ * chaining is logically disabled. */
+ custom_key = talloc_asprintf(tmp_ctx, "%p", sbus_req);
+ if (custom_key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ key = dp_req_table_key(tmp_ctx, target, method, dp_flags, custom_key);
+ if (key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ has_key = dp_req_table_has_key(provider->requests.reply_table, key);
+ if (has_key) {
+ ret = dp_req_table_add(provider->requests.reply_table,
+ key, NULL, sbus_req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to attach sbus request to "
+ "existing data provider request [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Attaching to DP request: %s\n", key);
+
+ ret = EOK;
+ goto done;
+ }
+
+ ret = dp_req_with_reply_step(provider, dp_cli, domain, request_name, key,
+ sbus_req, target, method, dp_flags,
+ request_data, postprocess_fn, postprocess_data,
+ reply_fn, output_dtype);
+
+done:
+ if (ret == ENOMEM) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate memory for "
+ "new DP request, killing D-Bus request...\n");
+ talloc_zfree(sbus_req);
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize "
+ "DP request [%d: %s], killing D-Bus request...\n",
+ ret, sss_strerror(ret));
+ talloc_zfree(sbus_req);
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+static errno_t dp_req_with_reply_step(struct data_provider *provider,
+ struct dp_client *dp_cli,
+ const char *domain,
+ const char *request_name,
+ const char *custom_key,
+ struct sbus_request *sbus_req,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ void *request_data,
+ dp_req_post_fn postprocess_fn,
+ void *postprocess_data,
+ dp_req_reply_fn reply_fn,
+ const char *output_dtype)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct dp_req_with_reply_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ state = talloc_zero(tmp_ctx, struct dp_req_with_reply_state);
+ if (state == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->provider = provider;
+ state->reply_fn = reply_fn;
+ state->key = talloc_strdup(state, custom_key);
+ if (state->key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (postprocess_fn != NULL) {
+ state->postprocess_data = postprocess_data;
+ state->postprocess_fn = postprocess_fn;
+ }
+
+ state->output_dtype = talloc_strdup(state, output_dtype);
+ if (state->output_dtype == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ req = dp_req_send(tmp_ctx, provider, dp_cli, domain, request_name, target,
+ method, dp_flags, request_data, &state->name);
+ if (req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = dp_req_table_add(provider->requests.reply_table,
+ custom_key, req, sbus_req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add request to table "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ tevent_req_set_callback(req, dp_req_with_reply_done, state);
+
+ talloc_steal(provider, req);
+ talloc_steal(req, state);
+ talloc_steal(state, state->name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static void dp_req_with_reply_done(struct tevent_req *req)
+{
+ struct dp_req_with_reply_state *state;
+ struct dp_table_value *value;
+ void *output_data;
+ errno_t ret;
+
+ state = tevent_req_callback_data(req, struct dp_req_with_reply_state);
+
+ value = dp_req_table_lookup(state->provider->requests.reply_table,
+ state->key);
+ if (value == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup table!\n");
+ return;
+ }
+
+ ret = _dp_req_recv(state, req, state->output_dtype, &output_data);
+ if (ret != EOK) {
+ dp_req_reply_list_error(value->list, state->name, ret);
+ goto done;
+ }
+
+ /* Run postprocess function if any. */
+ if (state->postprocess_fn != NULL) {
+ state->postprocess_fn(state->name,
+ state->provider,
+ state->postprocess_data,
+ output_data);
+ }
+
+ /* Reply with data. */
+ dp_req_reply_list_success(value->list, state->reply_fn,
+ state->name, output_data);
+
+done:
+ /* Freeing value will remove it from the table as well. */
+ talloc_free(value);
+ talloc_free(req);
+}
diff --git a/src/providers/data_provider/dp_request_table.c b/src/providers/data_provider/dp_request_table.c
new file mode 100644
index 000000000..970679a91
--- /dev/null
+++ b/src/providers/data_provider/dp_request_table.c
@@ -0,0 +1,265 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <talloc.h>
+#include <tevent.h>
+#include <dhash.h>
+
+#include "sbus/sssd_dbus.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/backend.h"
+#include "util/dlinklist.h"
+#include "util/sss_utf8.h"
+#include "util/util.h"
+
+static int
+dp_sbus_req_item_destructor(struct dp_sbus_req_item *item)
+{
+ DLIST_REMOVE(item->parent->list, item);
+
+ return 0;
+}
+
+static int
+dp_table_value_destructor(struct dp_table_value *value)
+{
+ struct dp_sbus_req_item *next_item;
+ struct dp_sbus_req_item *item;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Removing [%s] from reply table\n", value->key);
+
+ dp_req_table_del(value->table, value->key);
+
+ for (item = value->list; item != NULL; item = next_item) {
+ next_item = item->next;
+ talloc_free(item);
+ }
+
+ return 0;
+}
+
+static struct dp_sbus_req_item *
+dp_sbus_req_item_new(struct dp_table_value *value,
+ struct sbus_request *sbus_req)
+{
+ struct dp_sbus_req_item *item;
+
+ /* Attach to sbus_request so we ensure that this sbus_req is removed
+ * from the list when it is unexpectedly freed, for example when
+ * client connection is dropped. */
+ item = talloc_zero(sbus_req, struct dp_sbus_req_item);
+ if (item == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+ return NULL;
+ }
+
+ item->parent = value;
+ item->sbus_req = sbus_req;
+
+ talloc_set_destructor(item, dp_sbus_req_item_destructor);
+
+ return item;
+}
+
+char *dp_req_table_key(TALLOC_CTX *mem_ctx,
+ enum dp_targets target,
+ enum dp_methods method,
+ uint32_t dp_flags,
+ const char *custom_part)
+{
+ const char *str = custom_part == NULL ? "(null)" : custom_part;
+
+ return talloc_asprintf(mem_ctx, "%u:%u:%#.4x:%s",
+ target, method, dp_flags, str);
+}
+
+errno_t dp_req_table_init(TALLOC_CTX *mem_ctx, hash_table_t **_table)
+{
+ return sss_hash_create(mem_ctx, 100, _table);
+}
+
+struct dp_table_value *dp_req_table_lookup(hash_table_t *table,
+ const char *key)
+{
+ hash_key_t hkey;
+ hash_value_t hvalue;
+ int hret;
+
+ hkey.type = HASH_KEY_STRING;
+ hkey.str = discard_const_p(char, key);
+
+ hret = hash_lookup(table, &hkey, &hvalue);
+ if (hret == HASH_ERROR_KEY_NOT_FOUND) {
+ return NULL;
+ } else if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search hash table [%d]\n", hret);
+ return NULL;
+ }
+
+ return hvalue.ptr;
+}
+
+static errno_t dp_req_table_new_item(hash_table_t *table,
+ const char *key,
+ struct tevent_req *req,
+ struct sbus_request *sbus_req)
+{
+ hash_key_t hkey;
+ hash_value_t hvalue;
+ struct dp_table_value *table_value;
+ errno_t ret;
+ int hret;
+
+ /* Attach it to request. */
+ table_value = talloc_zero(req, struct dp_table_value);
+ if (table_value == NULL) {
+ return ENOMEM;
+ }
+
+ table_value->table = table;
+ table_value->key = talloc_strdup(table_value, key);
+ if (table_value->key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ table_value->req = req;
+ table_value->list = dp_sbus_req_item_new(table_value, sbus_req);
+ if (table_value->list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_set_destructor(table_value, dp_table_value_destructor);
+
+ hkey.type = HASH_KEY_STRING;
+ hkey.str = discard_const_p(char, key);
+
+ hvalue.type = HASH_VALUE_PTR;
+ hvalue.ptr = table_value;
+
+ hret = hash_enter(table, &hkey, &hvalue);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to enter value into hash table "
+ "[%d]\n", hret);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(table_value);
+ }
+
+ return ret;
+}
+
+static errno_t dp_req_table_mod_item(hash_table_t *table,
+ struct dp_table_value *table_value,
+ struct sbus_request *sbus_req)
+{
+ struct dp_sbus_req_item *item;
+
+ item = dp_sbus_req_item_new(table_value, sbus_req);
+ if (item == NULL) {
+ return ENOMEM;
+ }
+
+ DLIST_ADD(table_value->list, item);
+
+ return EOK;
+}
+
+errno_t dp_req_table_add(hash_table_t *table,
+ const char *key,
+ struct tevent_req *req,
+ struct sbus_request *sbus_req)
+{
+ struct dp_table_value *table_value;
+
+ if (sbus_req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "SBUS request cannot be NULL\n");
+ return EINVAL;
+ }
+
+ table_value = dp_req_table_lookup(table, key);
+ if (table_value == NULL) {
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Tevent request cannot be NULL\n");
+ return EINVAL;
+ }
+
+ return dp_req_table_new_item(table, key, req, sbus_req);
+ }
+
+ return dp_req_table_mod_item(table, table_value, sbus_req);
+}
+
+void dp_req_table_del(hash_table_t *table,
+ const char *key)
+{
+ hash_key_t hkey;
+ int hret;
+
+ if (table == NULL || key == NULL) {
+ return;
+ }
+
+ hkey.type = HASH_KEY_STRING;
+ hkey.str = discard_const_p(char, key);
+
+ hret = hash_delete(table, &hkey);
+ if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to remove key from table [%d]\n",
+ hret);
+ }
+
+ return;
+}
+
+void dp_req_table_del_and_free(hash_table_t *table,
+ const char *key)
+{
+ struct dp_table_value *value;
+
+ value = dp_req_table_lookup(table, key);
+ if (value == NULL) {
+ /* We're done here. */
+ return;
+ }
+
+ dp_req_table_del(table, key);
+ talloc_free(value);
+
+ return;
+}
+
+bool dp_req_table_has_key(hash_table_t *table,
+ const char *key)
+{
+ hash_key_t hkey;
+
+ hkey.type = HASH_KEY_STRING;
+ hkey.str = discard_const_p(char, key);
+
+ return hash_has_key(table, &hkey);
+}
diff --git a/src/providers/data_provider/dp_responder_iface.h b/src/providers/data_provider/dp_responder_iface.h
new file mode 100644
index 000000000..7fe6e0d4e
--- /dev/null
+++ b/src/providers/data_provider/dp_responder_iface.h
@@ -0,0 +1,29 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 DP_RESPONDER_IFACE_H_
+#define DP_RESPONDER_IFACE_H_
+
+#include "providers/data_provider/dp_iface_generated.h"
+#include "providers/data_provider/dp_flags.h"
+
+#define DP_PATH "/org/freedesktop/sssd/dataprovider"
+
+#endif /* DP_RESPONDER_IFACE_H_ */
diff --git a/src/providers/dp_sbus.c b/src/providers/data_provider/dp_sbus.c
index 623d7dd9c..623d7dd9c 100644
--- a/src/providers/dp_sbus.c
+++ b/src/providers/data_provider/dp_sbus.c
diff --git a/src/providers/data_provider/dp_targets.c b/src/providers/data_provider/dp_targets.c
new file mode 100644
index 000000000..e19cf93a3
--- /dev/null
+++ b/src/providers/data_provider/dp_targets.c
@@ -0,0 +1,534 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ 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 <talloc.h>
+#include <dlfcn.h>
+
+#include "config.h"
+#include "providers/data_provider/dp.h"
+#include "providers/data_provider/dp_private.h"
+#include "providers/data_provider/dp_builtin.h"
+#include "providers/backend.h"
+#include "util/util.h"
+
+#define DP_TARGET_INIT_FN "sssm_%s_%s_init"
+
+#define DP_PROVIDER_OPT "%s_provider"
+#define DP_ACCESS_PERMIT "permit"
+#define DP_ACCESS_DENY "deny"
+#define DP_NO_PROVIDER "none"
+
+bool _dp_target_enabled(struct data_provider *provider,
+ const char *module_name,
+ ...)
+{
+ struct dp_target *target;
+ enum dp_targets type;
+ va_list ap;
+ bool bret;
+
+ if (provider == NULL || provider->targets == NULL) {
+ return false;
+ }
+
+ bret = false;
+ va_start(ap, module_name);
+ while ((type = va_arg(ap, enum dp_targets)) != DP_TARGET_SENTINEL) {
+ target = provider->targets[type];
+ if (target == NULL || target->module_name == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Uninitialized target %s\n",
+ dp_target_to_string(type));
+ continue;
+ }
+
+ if (module_name == NULL) {
+ bret = true;
+ goto done;
+ }
+
+ if (strcmp(target->module_name, module_name) == 0) {
+ bret = true;
+ goto done;
+ }
+ }
+
+done:
+ va_end(ap);
+ return bret;
+}
+
+struct dp_module *dp_target_module(struct data_provider *provider,
+ enum dp_targets target)
+{
+ if (provider == NULL || provider->targets == NULL) {
+ return NULL;
+ }
+
+ if (target >= DP_TARGET_SENTINEL || provider->targets[target] == NULL) {
+ return NULL;
+ }
+
+ return provider->targets[target]->module;
+}
+
+const char *dp_target_to_string(enum dp_targets target)
+{
+ switch (target) {
+ case DPT_ID:
+ return "id";
+ case DPT_AUTH:
+ return "auth";
+ case DPT_ACCESS:
+ return "access";
+ case DPT_CHPASS:
+ return "chpass";
+ case DPT_SUDO:
+ return "sudo";
+ case DPT_AUTOFS:
+ return "autofs";
+ case DPT_SELINUX:
+ return "selinux";
+ case DPT_HOSTID:
+ return "hostid";
+ case DPT_SUBDOMAINS:
+ return "subdomains";
+ case DP_TARGET_SENTINEL:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+bool dp_target_initialized(struct dp_target **targets, enum dp_targets type)
+{
+ if (targets == NULL || targets[type] == NULL) {
+ return false;
+ }
+
+ return targets[type]->initialized;
+}
+
+static bool dp_target_sudo_enabled(struct be_ctx *be_ctx)
+{
+ TALLOC_CTX *tmp_ctx;
+ char **services;
+ char *module;
+ bool responder_enabled;
+ bool enable;
+ errno_t ret;
+ int i;
+
+ /* Do not disable it in case of error. */
+ enable = true;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return enable;
+ }
+
+ ret = confdb_get_string_as_list(be_ctx->cdb, tmp_ctx,
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_ACTIVE_SERVICES, &services);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read from confdb [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ responder_enabled = false;
+ for (i = 0; services[i] != NULL; i++) {
+ if (strcmp(services[i], "sudo") == 0) {
+ responder_enabled = true;
+ break;
+ }
+ }
+
+ ret = confdb_get_string(be_ctx->cdb, tmp_ctx, be_ctx->conf_path,
+ CONFDB_DOMAIN_SUDO_PROVIDER, NULL, &module);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read from confdb [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (!responder_enabled) {
+ if (module == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "SUDO is not listed in services, "
+ "disabling SUDO module.\n");
+ enable = false;
+ goto done;
+ } else if (strcmp(module, DP_NO_PROVIDER) != 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "SUDO provider is set, but it is not "
+ "listed in active services. SUDO support will not work!\n");
+ enable = true;
+ goto done;
+ }
+ }
+
+ enable = true;
+
+done:
+ talloc_free(tmp_ctx);
+ return enable;
+}
+
+static const char *dp_target_module_name(struct dp_target **targets,
+ enum dp_targets type)
+{
+ if (targets[type] == NULL) {
+ return NULL;
+ }
+
+ return targets[type]->module_name;
+}
+
+static const char *dp_target_default_module(struct dp_target **targets,
+ enum dp_targets target)
+{
+ switch (target) {
+ case DPT_ID:
+ return NULL;
+ case DPT_ACCESS:
+ return "permit";
+ case DPT_CHPASS:
+ return dp_target_module_name(targets, DPT_AUTH);
+ case DP_TARGET_SENTINEL:
+ return NULL;
+ default:
+ return dp_target_module_name(targets, DPT_ID);
+ }
+}
+
+static errno_t dp_target_run_constructor(struct dp_target *target,
+ struct be_ctx *be_ctx)
+{
+ char *fn_name = NULL;
+ dp_target_init_fn fn;
+ char *error;
+ errno_t ret;
+
+ fn_name = talloc_asprintf(target, DP_TARGET_INIT_FN,
+ target->module->name, target->name);
+ if (fn_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return ENOMEM;
+ }
+
+ dlerror(); /* clear any error */
+ fn = (dp_target_init_fn)dlsym(target->module->libhandle, fn_name);
+ if (fn != NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Executing target [%s] constructor\n",
+ target->name);
+
+ ret = fn(target, be_ctx, target->module->module_data, target->methods);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Target [%s] constructor failed "
+ "[%d]: %s\n", target->name, ret, sss_strerror(ret));
+ goto done;
+ }
+ } else {
+ error = dlerror();
+ if (error == NULL || !target->explicitly_configured) {
+ /* Not found. */
+ ret = ELIBBAD;
+ goto done;
+ } else {
+ /* Error. */
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load target [%s] "
+ "constructor: %s\n", target->name, error);
+ ret = ELIBBAD;
+ goto done;
+ }
+ }
+
+ target->initialized = true;
+ ret = EOK;
+
+done:
+ talloc_free(fn_name);
+ return ret;
+}
+
+static errno_t dp_target_special(struct be_ctx *be_ctx,
+ struct dp_target *target,
+ const char *module_name)
+{
+ if (strcasecmp(module_name, DP_NO_PROVIDER) == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Target [%s] is explicitly disabled.\n",
+ target->name);
+ target->initialized = false;
+ target->module = NULL;
+ return EOK;
+ }
+
+ if (target->target == DPT_ACCESS) {
+ if (strcmp(module_name, DP_ACCESS_PERMIT) == 0) {
+ dp_set_method(target->methods, DPM_ACCESS_HANDLER,
+ dp_access_permit_handler_send, dp_access_permit_handler_recv, NULL,
+ void, struct pam_data, struct pam_data *);
+ target->module = NULL;
+ target->initialized = true;
+ return EOK;
+ }
+
+ if (strcmp(module_name, DP_ACCESS_DENY) == 0) {
+ dp_set_method(target->methods, DPM_ACCESS_HANDLER,
+ dp_access_deny_handler_send, dp_access_deny_handler_recv, NULL,
+ void, struct pam_data, struct pam_data *);
+ target->module = NULL;
+ target->initialized = true;
+ return EOK;
+ }
+ }
+
+ if (target->target == DPT_SUDO) {
+ if (dp_target_sudo_enabled(be_ctx)) {
+ return EAGAIN;
+ } else {
+ target->module = NULL;
+ target->initialized = false;
+ return EOK;
+ }
+ }
+
+ return EAGAIN;
+}
+
+static errno_t dp_target_init(struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ struct dp_module **modules,
+ struct dp_target *target)
+{
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Initializing target [%s] with module [%s]\n",
+ target->name, target->module_name);
+
+ /* We have already name, module name and target set. We just load
+ * the module and initialize it. */
+
+ target->methods = talloc_zero_array(target, struct dp_method,
+ DP_METHOD_SENTINEL + 1);
+ if (target->methods == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Handle special cases that do not require opening a module. */
+ ret = dp_target_special(be_ctx, target, target->module_name);
+ if (ret == EOK || ret != EAGAIN) {
+ goto done;
+ }
+
+ /* Load module first. Memory context is modules, not target here. */
+ target->module = dp_load_module(modules, be_ctx, provider, modules,
+ target->module_name);
+ if (target->module == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load module %s\n",
+ target->module_name);
+ ret = ELIBBAD;
+ goto done;
+ }
+
+ /* Run constructor. */
+ ret = dp_target_run_constructor(target, be_ctx);
+ if (!target->explicitly_configured && (ret == ELIBBAD || ret == ENOTSUP)) {
+ /* Target not found but it wasn't explicitly
+ * configured so we shall just continue. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Target [%s] is not supported by "
+ "module [%s].\n", target->name, target->module_name);
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(target->methods);
+ }
+
+ return ret;
+}
+
+static char *dp_get_module_name(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *confdb_ctx,
+ const char *conf_path,
+ struct dp_target **targets,
+ enum dp_targets type,
+ bool *_is_default)
+{
+ const char *name;
+ const char *default_module;
+ char *module;
+ char *option;
+ errno_t ret;
+
+ name = dp_target_to_string(type);
+ if (name == NULL) {
+ return NULL;
+ }
+
+ option = talloc_asprintf(mem_ctx, DP_PROVIDER_OPT, name);
+ if (option == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return NULL;
+ }
+
+ ret = confdb_get_string(confdb_ctx, mem_ctx, conf_path,
+ option, NULL, &module);
+ talloc_free(option);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read provider value "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return NULL;
+ }
+
+ if (module != NULL) {
+ *_is_default = false;
+ return module;
+ }
+
+ *_is_default = true;
+ default_module = dp_target_default_module(targets, type);
+
+ return talloc_strdup(mem_ctx, default_module);
+}
+
+static errno_t dp_load_configuration(struct confdb_ctx *cdb,
+ const char *conf_path,
+ struct dp_target **targets)
+{
+ enum dp_targets type;
+ const char *name;
+ bool is_default;
+ char *module;
+ errno_t ret;
+
+ for (type = 0; type < DP_TARGET_SENTINEL; type++) {
+ name = dp_target_to_string(type);
+ if (name == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ module = dp_get_module_name(NULL, cdb, conf_path, targets,
+ type, &is_default);
+ if (module == NULL) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "No provider is specified for"
+ " [%s]\n", name);
+ continue;
+ } else {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Using [%s] provider for [%s]\n",
+ module, name);
+ }
+
+ targets[type]->explicitly_configured = is_default == false;
+ targets[type]->name = name;
+ targets[type]->target = type;
+ targets[type]->module_name = talloc_steal(targets[type], module);
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t dp_load_targets(struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ struct dp_target **targets,
+ struct dp_module **modules)
+{
+ enum dp_targets type;
+ errno_t ret;
+
+ /* We load the configuration first and store module name to each target.
+ * This way we ensure that we have this information available during
+ * module initialization. */
+
+ ret = dp_load_configuration(be_ctx->cdb, be_ctx->conf_path, targets);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to load DP configuration "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (type = 0; type < DP_TARGET_SENTINEL; type++) {
+ ret = dp_target_init(be_ctx, provider, modules, targets[type]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to load target [%s] "
+ "[%d]: %s.\n", targets[type]->name, ret, sss_strerror(ret));
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+errno_t dp_init_targets(TALLOC_CTX *mem_ctx,
+ struct be_ctx *be_ctx,
+ struct data_provider *provider,
+ struct dp_module **modules)
+{
+ struct dp_target **targets;
+ enum dp_targets type;
+ errno_t ret;
+
+ /* Even though we know the exact number of targets we will allocate
+ * them all dynamically so we can have correct talloc hierarchy where
+ * all private data are attached to the target they belong to. */
+
+ targets = talloc_zero_array(mem_ctx, struct dp_target *,
+ DP_TARGET_SENTINEL + 1);
+ if (targets == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (type = 0; type != DP_TARGET_SENTINEL; type++) {
+ targets[type] = talloc_zero(targets, struct dp_target);
+ if (targets[type] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /* We want this to be already available. */
+ provider->targets = targets;
+
+ ret = dp_load_targets(be_ctx, provider, targets, modules);
+
+done:
+ if (ret != EOK) {
+ provider->targets = NULL;
+ talloc_free(targets);
+ }
+
+ return ret;
+}