summaryrefslogtreecommitdiffstats
path: root/src/responder
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/responder/autofs/autofs_private.h74
-rw-r--r--src/responder/autofs/autofssrv.c242
-rw-r--r--src/responder/autofs/autofssrv_cmd.c961
-rw-r--r--src/responder/common/cache_req/cache_req.c1612
-rw-r--r--src/responder/common/cache_req/cache_req.h521
-rw-r--r--src/responder/common/cache_req/cache_req_data.c519
-rw-r--r--src/responder/common/cache_req/cache_req_domain.c317
-rw-r--r--src/responder/common/cache_req/cache_req_domain.h63
-rw-r--r--src/responder/common/cache_req/cache_req_plugin.h331
-rw-r--r--src/responder/common/cache_req/cache_req_private.h227
-rw-r--r--src/responder/common/cache_req/cache_req_result.c274
-rw-r--r--src/responder/common/cache_req/cache_req_search.c643
-rw-r--r--src/responder/common/cache_req/cache_req_sr_overlay.c347
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c161
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c155
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c187
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_common.c192
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_groups.c93
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c126
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c126
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_svc.c106
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_enum_users.c93
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_group_by_filter.c171
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_group_by_id.c246
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_group_by_name.c227
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c242
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c130
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c174
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c170
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c174
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c170
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c160
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_object_by_id.c236
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_object_by_name.c238
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_object_by_sid.c202
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c164
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c143
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_svc_by_name.c185
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_svc_by_port.c159
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_cert.c127
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_filter.c176
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_id.c246
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_name.c256
-rw-r--r--src/responder/common/cache_req/plugins/cache_req_user_by_upn.c158
-rw-r--r--src/responder/common/negcache.c1417
-rw-r--r--src/responder/common/negcache.h174
-rw-r--r--src/responder/common/negcache_files.c105
-rw-r--r--src/responder/common/negcache_files.h35
-rw-r--r--src/responder/common/responder.h450
-rw-r--r--src/responder/common/responder_cmd.c302
-rw-r--r--src/responder/common/responder_common.c2001
-rw-r--r--src/responder/common/responder_dp.c460
-rw-r--r--src/responder/common/responder_get_domains.c841
-rw-r--r--src/responder/common/responder_iface.c161
-rw-r--r--src/responder/common/responder_packet.c364
-rw-r--r--src/responder/common/responder_packet.h51
-rw-r--r--src/responder/common/responder_utils.c527
-rw-r--r--src/responder/ifp/ifp_cache.c270
-rw-r--r--src/responder/ifp/ifp_cache.h63
-rw-r--r--src/responder/ifp/ifp_components.c662
-rw-r--r--src/responder/ifp/ifp_components.h100
-rw-r--r--src/responder/ifp/ifp_domains.c1022
-rw-r--r--src/responder/ifp/ifp_domains.h193
-rw-r--r--src/responder/ifp/ifp_groups.c1169
-rw-r--r--src/responder/ifp/ifp_groups.h156
-rw-r--r--src/responder/ifp/ifp_iface/ifp_iface.c274
-rw-r--r--src/responder/ifp/ifp_iface/ifp_iface.h40
-rw-r--r--src/responder/ifp/ifp_iface/ifp_iface.xml253
-rw-r--r--src/responder/ifp/ifp_iface/ifp_iface_async.h28
-rw-r--r--src/responder/ifp/ifp_iface/ifp_iface_sync.h27
-rw-r--r--src/responder/ifp/ifp_iface/ifp_iface_types.c225
-rw-r--r--src/responder/ifp/ifp_iface/ifp_iface_types.h35
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_arguments.c397
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_arguments.h201
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_client_async.c30
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_client_async.h31
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_client_properties.h180
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c2304
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h637
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_interface.h2083
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_invokers.c3004
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_invokers.h60
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_keygens.c107
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_keygens.h57
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_server.h27
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_symbols.c436
-rw-r--r--src/responder/ifp/ifp_iface/sbus_ifp_symbols.h130
-rw-r--r--src/responder/ifp/ifp_iface_nodes.c166
-rw-r--r--src/responder/ifp/ifp_private.h137
-rw-r--r--src/responder/ifp/ifp_users.c2040
-rw-r--r--src/responder/ifp/ifp_users.h254
-rw-r--r--src/responder/ifp/ifpsrv.c363
-rw-r--r--src/responder/ifp/ifpsrv_cmd.c631
-rw-r--r--src/responder/ifp/ifpsrv_util.c455
-rw-r--r--src/responder/ifp/org.freedesktop.sssd.infopipe.conf47
-rw-r--r--src/responder/ifp/org.freedesktop.sssd.infopipe.service6
-rw-r--r--src/responder/ifp/org.freedesktop.sssd.infopipe.service.in6
-rw-r--r--src/responder/kcm/kcm.c374
-rw-r--r--src/responder/kcm/kcm_renew.c775
-rw-r--r--src/responder/kcm/kcm_renew.h53
-rw-r--r--src/responder/kcm/kcmsrv_ccache.c1724
-rw-r--r--src/responder/kcm/kcmsrv_ccache.h380
-rw-r--r--src/responder/kcm/kcmsrv_ccache_be.h216
-rw-r--r--src/responder/kcm/kcmsrv_ccache_binary.c308
-rw-r--r--src/responder/kcm/kcmsrv_ccache_key.c144
-rw-r--r--src/responder/kcm/kcmsrv_ccache_mem.c826
-rw-r--r--src/responder/kcm/kcmsrv_ccache_pvt.h61
-rw-r--r--src/responder/kcm/kcmsrv_ccache_secdb.c1671
-rw-r--r--src/responder/kcm/kcmsrv_cmd.c667
-rw-r--r--src/responder/kcm/kcmsrv_op_queue.c332
-rw-r--r--src/responder/kcm/kcmsrv_ops.c2458
-rw-r--r--src/responder/kcm/kcmsrv_ops.h67
-rw-r--r--src/responder/kcm/kcmsrv_pvt.h106
-rw-r--r--src/responder/kcm/secrets/config.c92
-rw-r--r--src/responder/kcm/secrets/sec_pvt.h47
-rw-r--r--src/responder/kcm/secrets/secrets.c1229
-rw-r--r--src/responder/kcm/secrets/secrets.h114
-rw-r--r--src/responder/nss/nss_cmd.c1439
-rw-r--r--src/responder/nss/nss_enum.c361
-rw-r--r--src/responder/nss/nss_get_object.c546
-rw-r--r--src/responder/nss/nss_iface.c242
-rw-r--r--src/responder/nss/nss_iface.h31
-rw-r--r--src/responder/nss/nss_private.h155
-rw-r--r--src/responder/nss/nss_protocol.c487
-rw-r--r--src/responder/nss/nss_protocol.h217
-rw-r--r--src/responder/nss/nss_protocol_grent.c495
-rw-r--r--src/responder/nss/nss_protocol_hostent.c299
-rw-r--r--src/responder/nss/nss_protocol_netent.c243
-rw-r--r--src/responder/nss/nss_protocol_netgr.c181
-rw-r--r--src/responder/nss/nss_protocol_pwent.c338
-rw-r--r--src/responder/nss/nss_protocol_sid.c704
-rw-r--r--src/responder/nss/nss_protocol_subid.c60
-rw-r--r--src/responder/nss/nss_protocol_svcent.c270
-rw-r--r--src/responder/nss/nss_utils.c38
-rw-r--r--src/responder/nss/nsssrv.c740
-rw-r--r--src/responder/nss/nsssrv_mmap_cache.c1626
-rw-r--r--src/responder/nss/nsssrv_mmap_cache.h86
-rw-r--r--src/responder/pac/pacsrv.c230
-rw-r--r--src/responder/pac/pacsrv.h42
-rw-r--r--src/responder/pac/pacsrv_cmd.c311
-rw-r--r--src/responder/pam/pam_helpers.c162
-rw-r--r--src/responder/pam/pam_helpers.h42
-rw-r--r--src/responder/pam/pam_prompting_config.c309
-rw-r--r--src/responder/pam/pamsrv.c529
-rw-r--r--src/responder/pam/pamsrv.h173
-rw-r--r--src/responder/pam/pamsrv_cmd.c3085
-rw-r--r--src/responder/pam/pamsrv_dp.c106
-rw-r--r--src/responder/pam/pamsrv_gssapi.c1043
-rw-r--r--src/responder/pam/pamsrv_p11.c1312
-rw-r--r--src/responder/pam/pamsrv_passkey.c1438
-rw-r--r--src/responder/pam/pamsrv_passkey.h83
-rw-r--r--src/responder/ssh/ssh_cert_to_ssh_key.c342
-rw-r--r--src/responder/ssh/ssh_cmd.c409
-rw-r--r--src/responder/ssh/ssh_known_hosts.c333
-rw-r--r--src/responder/ssh/ssh_private.h100
-rw-r--r--src/responder/ssh/ssh_protocol.c252
-rw-r--r--src/responder/ssh/ssh_reply.c429
-rw-r--r--src/responder/ssh/sshsrv.c235
-rw-r--r--src/responder/sudo/sudosrv.c223
-rw-r--r--src/responder/sudo/sudosrv_cmd.c303
-rw-r--r--src/responder/sudo/sudosrv_dp.c265
-rw-r--r--src/responder/sudo/sudosrv_get_sudorules.c887
-rw-r--r--src/responder/sudo/sudosrv_private.h112
-rw-r--r--src/responder/sudo/sudosrv_query.c307
164 files changed, 69982 insertions, 0 deletions
diff --git a/src/responder/autofs/autofs_private.h b/src/responder/autofs/autofs_private.h
new file mode 100644
index 0000000..c15e028
--- /dev/null
+++ b/src/responder/autofs/autofs_private.h
@@ -0,0 +1,74 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2012 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 _AUTOFSSRV_PRIVATE_H_
+#define _AUTOFSSRV_PRIVATE_H_
+
+#include <dhash.h>
+
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+
+#define SSS_AUTOFS_PROTO_VERSION 0x001
+
+struct autofs_ctx {
+ struct resp_ctx *rctx;
+
+ int neg_timeout;
+
+ hash_table_t *maps;
+};
+
+struct autofs_cmd_ctx {
+ struct autofs_ctx *autofs_ctx;
+ struct cli_ctx *cli_ctx;
+
+ const char *mapname;
+ const char *keyname;
+ uint32_t max_entries;
+ uint32_t cursor;
+};
+
+struct autofs_enum_ctx {
+ /* Results. First result is the map objects, next results are map entries. */
+ struct cache_req_result *result;
+
+ /* True if the map was found. */
+ bool found;
+
+ /* False if the result is being created. */
+ bool ready;
+
+ /* Enumeration context key. */
+ const char *key;
+
+ /* Hash table that contains this enumeration context. */
+ hash_table_t *table;
+
+ /* Requests that awaits the data. */
+ struct setent_req_list *notify_list;
+};
+
+struct sss_cmd_table *get_autofs_cmds(void);
+int autofs_connection_setup(struct cli_ctx *cctx);
+
+void autofs_orphan_maps(struct autofs_ctx *actx);
+
+#endif /* _AUTOFSSRV_PRIVATE_H_ */
diff --git a/src/responder/autofs/autofssrv.c b/src/responder/autofs/autofssrv.c
new file mode 100644
index 0000000..1dbbe9f
--- /dev/null
+++ b/src/responder/autofs/autofssrv.c
@@ -0,0 +1,242 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ Autofs responder: the responder server
+
+ 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 <popt.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "providers/data_provider.h"
+#include "responder/autofs/autofs_private.h"
+#include "sss_iface/sss_iface_async.h"
+#include "util/sss_ptr_hash.h"
+
+static errno_t
+autofs_get_config(struct autofs_ctx *actx,
+ struct confdb_ctx *cdb)
+{
+ errno_t ret;
+
+ ret = confdb_get_int(cdb, CONFDB_AUTOFS_CONF_ENTRY,
+ CONFDB_AUTOFS_MAP_NEG_TIMEOUT, 15,
+ &actx->neg_timeout);
+ return ret;
+}
+
+static errno_t
+autofs_clean_hash_table(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct autofs_ctx *actx)
+{
+ autofs_orphan_maps(actx);
+
+ return EOK;
+}
+
+static void
+autofs_maps_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ struct autofs_ctx *autofs_ctx;
+ struct autofs_enum_ctx *enum_ctx;
+
+ autofs_ctx = talloc_get_type(pvt, struct autofs_ctx);
+ enum_ctx = talloc_get_type(item->value.ptr, struct autofs_enum_ctx);
+
+ talloc_unlink(autofs_ctx->maps, enum_ctx);
+}
+
+static errno_t
+autofs_register_service_iface(struct autofs_ctx *autofs_ctx,
+ struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface_svc,
+ sssd_service,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_service, rotateLogs, responder_logrotate, rctx),
+ SBUS_SYNC(METHOD, sssd_service, clearEnumCache, autofs_clean_hash_table, autofs_ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, sssd_service, debug_level, generic_get_debug_level, NULL),
+ SBUS_SYNC(SETTER, sssd_service, debug_level, generic_set_debug_level, NULL)
+ )
+ );
+
+ ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static int
+autofs_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *autofs_cmds;
+ struct autofs_ctx *autofs_ctx;
+ int ret;
+
+ autofs_cmds = get_autofs_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ autofs_cmds,
+ SSS_AUTOFS_SOCKET_NAME, -1, NULL, -1,
+ CONFDB_AUTOFS_CONF_ENTRY,
+ SSS_BUS_AUTOFS, SSS_AUTOFS_SBUS_SERVICE_NAME,
+ autofs_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ autofs_ctx = talloc_zero(rctx, struct autofs_ctx);
+ if (!autofs_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing autofs_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = autofs_get_config(autofs_ctx, cdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Cannot read autofs configuration\n");
+ goto fail;
+ }
+
+ autofs_ctx->rctx = rctx;
+ autofs_ctx->rctx->pvt_ctx = autofs_ctx;
+
+ /* Create the lookup table for setautomntent results */
+ autofs_ctx->maps = sss_ptr_hash_create(autofs_ctx,
+ autofs_maps_delete_cb,
+ autofs_ctx);
+ if (autofs_ctx->maps == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to initialize automount maps hash table\n");
+ ret = EIO;
+ goto fail;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_AUTOFS,
+ SSS_AUTOFS_SBUS_SERVICE_NAME,
+ SSS_AUTOFS_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = autofs_register_service_iface(autofs_ctx, rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "autofs Initialization complete\n");
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_autofs";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ ret = server_setup("autofs", true, 0, uid, gid,
+ CONFDB_AUTOFS_CONF_ENTRY, &main_ctx, true);
+ if (ret != EOK) {
+ return 2;
+ }
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit "
+ "when parent process does\n");
+ }
+
+ ret = autofs_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) {
+ return 3;
+ }
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
diff --git a/src/responder/autofs/autofssrv_cmd.c b/src/responder/autofs/autofssrv_cmd.c
new file mode 100644
index 0000000..7c80909
--- /dev/null
+++ b/src/responder/autofs/autofssrv_cmd.c
@@ -0,0 +1,961 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ Autofs responder: commands
+
+ 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 "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/autofs/autofs_private.h"
+#include "db/sysdb.h"
+#include "db/sysdb_autofs.h"
+#include "confdb/confdb.h"
+#include "sss_iface/sss_iface_async.h"
+#include "util/sss_ptr_hash.h"
+
+static int autofs_cmd_send_error(struct autofs_cmd_ctx *cmdctx, int err)
+{
+ return sss_cmd_send_error(cmdctx->cli_ctx, err);
+}
+
+static int
+autofs_cmd_send_empty(struct autofs_cmd_ctx *cmdctx)
+{
+ return sss_cmd_send_empty(cmdctx->cli_ctx);
+}
+
+static int
+autofs_cmd_done(struct autofs_cmd_ctx *cmdctx, int ret)
+{
+ switch (ret) {
+ case EOK:
+ /* all fine, just return here */
+ break;
+
+ case ENOENT:
+ ret = autofs_cmd_send_empty(cmdctx);
+ if (ret) {
+ return EFAULT;
+ }
+ sss_cmd_done(cmdctx->cli_ctx, cmdctx);
+ break;
+
+ case EAGAIN:
+ /* async processing, just return here */
+ break;
+
+ case EFAULT:
+ /* very bad error */
+ return EFAULT;
+
+ default:
+ ret = autofs_cmd_send_error(cmdctx, ret);
+ if (ret) {
+ return EFAULT;
+ }
+ sss_cmd_done(cmdctx->cli_ctx, cmdctx);
+ break;
+ }
+
+ return EOK;
+}
+
+static errno_t
+autofs_fill_entry(struct ldb_message *entry, struct sss_packet *packet, size_t *rp)
+{
+ errno_t ret;
+ const char *key;
+ size_t keylen;
+ const char *value;
+ size_t valuelen;
+ uint8_t *body;
+ size_t blen;
+ size_t len;
+
+ key = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_KEY, NULL);
+ value = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_VALUE, NULL);
+ if (!key || !value) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Incomplete entry\n");
+ return EINVAL;
+ }
+
+ keylen = 1 + strlen(key);
+ valuelen = 1 + strlen(value);
+ len = sizeof(uint32_t) + sizeof(uint32_t) + keylen + sizeof(uint32_t) + valuelen;
+
+ ret = sss_packet_grow(packet, len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot grow packet\n");
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &blen);
+
+ SAFEALIGN_SET_UINT32(&body[*rp], len, rp);
+ SAFEALIGN_SET_UINT32(&body[*rp], keylen, rp);
+
+ if (keylen == 1) {
+ body[*rp] = '\0';
+ } else {
+ memcpy(&body[*rp], key, keylen);
+ }
+ *rp += keylen;
+
+ SAFEALIGN_SET_UINT32(&body[*rp], valuelen, rp);
+ if (valuelen == 1) {
+ body[*rp] = '\0';
+ } else {
+ memcpy(&body[*rp], value, valuelen);
+ }
+ *rp += valuelen;
+
+ return EOK;
+}
+
+void
+autofs_orphan_maps(struct autofs_ctx *autofs_ctx)
+{
+ /* It will automatically decrease the refcount of enum_ctx through
+ * delete callback. */
+ sss_ptr_hash_delete_all(autofs_ctx->maps, false);
+}
+
+static void
+autofs_enumctx_lifetime_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *pvt)
+{
+ struct autofs_enum_ctx *enum_ctx;
+
+ enum_ctx = talloc_get_type(pvt, struct autofs_enum_ctx);
+
+ /* Remove it from the table. It will automatically decrease the refcount. */
+ sss_ptr_hash_delete(enum_ctx->table, enum_ctx->key, false);
+}
+
+static void
+autofs_set_enumctx_lifetime(struct autofs_ctx *autofs_ctx,
+ struct autofs_enum_ctx *enum_ctx,
+ uint32_t lifetime)
+{
+ struct timeval tv;
+ struct tevent_timer *te;
+
+ tv = tevent_timeval_current_ofs(lifetime, 0);
+ te = tevent_add_timer(autofs_ctx->rctx->ev, enum_ctx, tv,
+ autofs_enumctx_lifetime_timeout, enum_ctx);
+ if (te == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not set up life timer for autofs maps. "
+ "Entries may become stale.\n");
+ }
+}
+
+static struct autofs_enum_ctx *
+autofs_create_enumeration_context(TALLOC_CTX *mem_ctx,
+ struct autofs_ctx *autofs_ctx,
+ const char *mapname)
+{
+ struct autofs_enum_ctx *enum_ctx;
+ errno_t ret;
+
+ enum_ctx = talloc_zero(mem_ctx, struct autofs_enum_ctx);
+ if (enum_ctx == NULL) {
+ return NULL;
+ }
+
+ enum_ctx->ready = false;
+ enum_ctx->table = autofs_ctx->maps;
+
+ enum_ctx->key = talloc_strdup(enum_ctx, mapname);
+ if (enum_ctx->key == NULL) {
+ talloc_free(enum_ctx);
+ return NULL;
+ }
+
+ ret = sss_ptr_hash_add(autofs_ctx->maps, mapname,
+ enum_ctx, struct autofs_enum_ctx);
+ if (ret != EOK) {
+ talloc_free(enum_ctx);
+ return NULL;
+ }
+
+ return enum_ctx;
+}
+
+static void
+autofs_orphan_master_map(struct autofs_ctx *autofs_ctx,
+ const char *mapname)
+{
+ struct sss_domain_info *dom;
+ errno_t ret;
+
+ if (strcmp(mapname, "auto.master") != 0) {
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Invalidating master map\n");
+
+ /* Remove and invalidate all maps. */
+ autofs_orphan_maps(autofs_ctx);
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Invalidating autofs maps\n");
+ for (dom = autofs_ctx->rctx->domains;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ ret = sysdb_invalidate_autofs_maps(dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to invalidate maps in "
+ "%s [%d]: %s\n", dom->name, ret, sss_strerror(ret));
+ }
+ }
+}
+
+struct autofs_setent_state {
+ struct autofs_ctx *autofs_ctx;
+ struct autofs_enum_ctx *enum_ctx;
+};
+
+static void autofs_setent_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+autofs_setent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct autofs_ctx *autofs_ctx,
+ const char *mapname)
+{
+ struct autofs_setent_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct autofs_setent_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->autofs_ctx = autofs_ctx;
+
+ /* Lookup current results if available. */
+ state->enum_ctx = sss_ptr_hash_lookup(autofs_ctx->maps, mapname,
+ struct autofs_enum_ctx);
+ if (state->enum_ctx != NULL) {
+ if (state->enum_ctx->ready) {
+ ret = EOK;
+ goto done;
+ }
+
+ /* Map is still being created. We will watch the request. */
+ ret = setent_add_ref(state, &state->enum_ctx->notify_list, req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to watch enumeration request "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EAGAIN;
+ goto done;
+ }
+
+ /* Map does not yet exist. Create the enumeration object and fetch data. */
+ state->enum_ctx = autofs_create_enumeration_context(state, autofs_ctx, mapname);
+ if (state->enum_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create enumeration context!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = cache_req_autofs_map_entries_send(mem_ctx, ev, autofs_ctx->rctx,
+ autofs_ctx->rctx->ncache,
+ 0, NULL, mapname);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, autofs_setent_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void autofs_setent_done(struct tevent_req *subreq)
+{
+ struct autofs_setent_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct autofs_setent_state);
+
+ ret = cache_req_autofs_map_entries_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+
+ switch (ret) {
+ case EOK:
+ state->enum_ctx->found = true;
+ state->enum_ctx->result = talloc_steal(state->enum_ctx, result);
+ autofs_set_enumctx_lifetime(state->autofs_ctx, state->enum_ctx,
+ state->enum_ctx->result->domain->autofsmap_timeout);
+ break;
+ case ENOENT:
+ state->enum_ctx->found = false;
+ state->enum_ctx->result = NULL;
+ autofs_set_enumctx_lifetime(state->autofs_ctx, state->enum_ctx,
+ state->autofs_ctx->neg_timeout);
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to get map data [%d]: %s\n",
+ ret, sss_strerror(ret));
+
+ setent_notify(&state->enum_ctx->notify_list, ret);
+ talloc_zfree(state->enum_ctx);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->enum_ctx->ready = true;
+
+ /* Make the enumeration context disappear with maps table. */
+ talloc_steal(state->autofs_ctx->maps, state->enum_ctx);
+
+ setent_notify_done(&state->enum_ctx->notify_list);
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+autofs_setent_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct autofs_enum_ctx **_enum_ctx)
+{
+ struct autofs_setent_state *state;
+ state = tevent_req_data(req, struct autofs_setent_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_enum_ctx = talloc_reference(mem_ctx, state->enum_ctx);
+
+ return EOK;
+}
+
+static errno_t
+autofs_read_setautomntent_input(struct cli_ctx *cli_ctx,
+ const char **_mapname)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* if not terminated fail */
+ if (body[blen - 1] != '\0') {
+ return EINVAL;
+ }
+
+ /* If the body isn't valid UTF-8, fail */
+ if (!sss_utf8_check(body, blen - 1)) {
+ return EINVAL;
+ }
+
+ *_mapname = (const char *)body;
+
+ return EOK;
+}
+
+static errno_t
+autofs_write_setautomntent_output(struct cli_ctx *cli_ctx,
+ struct cache_req_result *result)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (result == NULL || result->count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Map was not found\n");
+ return sss_cmd_empty_packet(pctx->creq->out);
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Map found\n");
+
+ ret = sss_packet_grow(pctx->creq->out, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ sss_packet_get_body(pctx->creq->out, &body, &blen);
+
+ /* Got some results */
+ SAFEALIGN_SETMEM_UINT32(body, 1, NULL);
+
+ /* Reserved padding */
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
+
+ return EOK;
+}
+
+static void
+sss_autofs_cmd_setautomntent_done(struct tevent_req *req);
+
+static int
+sss_autofs_cmd_setautomntent(struct cli_ctx *cli_ctx)
+{
+ struct autofs_cmd_ctx *cmd_ctx;
+ struct autofs_ctx *autofs_ctx;
+ struct tevent_req *req;
+ errno_t ret;
+
+ autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx);
+
+ cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n");
+ return ENOMEM;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+ cmd_ctx->autofs_ctx = autofs_ctx;
+
+ ret = autofs_read_setautomntent_input(cli_ctx, &cmd_ctx->mapname);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ autofs_orphan_master_map(autofs_ctx, cmd_ctx->mapname);
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Obtaining autofs map %s\n",
+ cmd_ctx->mapname);
+
+ req = cache_req_autofs_map_by_name_send(cli_ctx, cli_ctx->ev,
+ autofs_ctx->rctx,
+ autofs_ctx->rctx->ncache, 0, NULL,
+ cmd_ctx->mapname);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_map_by_name_send failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, sss_autofs_cmd_setautomntent_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ return autofs_cmd_done(cmd_ctx, ret);
+}
+
+static void
+sss_autofs_cmd_setautomntent_done(struct tevent_req *req)
+{
+ struct cache_req_result *result;
+ struct autofs_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx);
+
+ ret = cache_req_autofs_map_by_name_recv(cmd_ctx, req, &result);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ autofs_cmd_done(cmd_ctx, ret);
+ return;
+ }
+
+ ret = autofs_write_setautomntent_output(cmd_ctx->cli_ctx, result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ autofs_cmd_done(cmd_ctx, ret);
+ return;
+ }
+
+ sss_cmd_done(cmd_ctx->cli_ctx, NULL);
+}
+
+static int
+sss_autofs_cmd_endautomntent(struct cli_ctx *client)
+{
+ struct cli_protocol *pctx;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "endautomntent called\n");
+
+ pctx = talloc_get_type(client->protocol_ctx, struct cli_protocol);
+
+ /* create response packet */
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+
+ if (ret != EOK) {
+ return ret;
+ }
+
+ sss_cmd_done(client, NULL);
+ return EOK;
+}
+
+static errno_t
+autofs_read_getautomntent_input(struct cli_ctx *cli_ctx,
+ const char **_mapname,
+ uint32_t *_cursor,
+ uint32_t *_max_entries)
+{
+ struct cli_protocol *pctx;
+ const char *mapname;
+ uint32_t namelen;
+ uint8_t *body;
+ size_t blen;
+ size_t c = 0;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ SAFEALIGN_COPY_UINT32_CHECK(&namelen, body+c, blen, &c);
+ if (namelen == 0 || namelen > blen - c) {
+ return EINVAL;
+ }
+
+ mapname = (const char *)body + c;
+
+ /* if not null-terminated fail */
+ if (mapname[namelen] != '\0') {
+ return EINVAL;
+ }
+
+ /* If the name isn't valid UTF-8, fail */
+ if (!sss_utf8_check((const uint8_t *)mapname, namelen - 1)) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32_CHECK(_cursor, body + c + namelen + 1, blen, &c);
+ SAFEALIGN_COPY_UINT32_CHECK(_max_entries, body + c + namelen + 1, blen, &c);
+ *_mapname = mapname;
+
+ return EOK;
+}
+
+static errno_t
+autofs_write_getautomntent_output(struct cli_ctx *cli_ctx,
+ struct autofs_enum_ctx *enum_ctx,
+ uint32_t cursor,
+ uint32_t max_entries)
+{
+ struct cli_protocol *pctx;
+ struct ldb_message **entries;
+ struct ldb_message *entry;
+ size_t count;
+ size_t num_entries;
+ uint8_t *body;
+ size_t blen;
+ size_t rp;
+ uint32_t i;
+ uint32_t stop;
+ uint32_t left;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ count = enum_ctx->found ? enum_ctx->result->count - 1 : 0;
+ entries = count > 0 ? enum_ctx->result->msgs + 1 : NULL;
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (!enum_ctx->found || count == 0 || cursor >= count) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No entries was not found\n");
+ return sss_cmd_empty_packet(pctx->creq->out);
+ }
+
+ /* allocate memory for number of entries in the packet */
+ ret = sss_packet_grow(pctx->creq->out, sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot grow packet\n");
+ return ret;
+ }
+
+ rp = sizeof(uint32_t); /* We will first write the elements. */
+ left = count - cursor;
+ stop = max_entries < left ? max_entries : left;
+
+ num_entries = 0;
+ for (i = 0; i < stop; i++) {
+ entry = entries[cursor];
+ cursor++;
+
+ ret = autofs_fill_entry(entry, pctx->creq->out, &rp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot fill entry %d/%d, skipping\n", i, stop);
+ continue;
+ }
+ num_entries++;
+ }
+
+ /* packet grows in fill_autofs_entry, body pointer may change,
+ * thus we have to obtain it here */
+ sss_packet_get_body(pctx->creq->out, &body, &blen);
+
+ rp = 0;
+ SAFEALIGN_SET_UINT32(&body[rp], num_entries, &rp);
+
+ return EOK;
+}
+
+static void
+sss_autofs_cmd_getautomntent_done(struct tevent_req *req);
+
+static int
+sss_autofs_cmd_getautomntent(struct cli_ctx *cli_ctx)
+{
+ struct autofs_cmd_ctx *cmd_ctx;
+ struct autofs_ctx *autofs_ctx;
+ struct tevent_req *req;
+ errno_t ret;
+
+ autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx);
+
+ cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n");
+ return ENOMEM;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+ cmd_ctx->autofs_ctx = autofs_ctx;
+
+ ret = autofs_read_getautomntent_input(cli_ctx, &cmd_ctx->mapname,
+ &cmd_ctx->cursor,
+ &cmd_ctx->max_entries);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Obtaining enumeration context for %s\n",
+ cmd_ctx->mapname);
+
+ req = autofs_setent_send(cli_ctx, cli_ctx->ev, autofs_ctx, cmd_ctx->mapname);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "autofs_setent_send failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, sss_autofs_cmd_getautomntent_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ return autofs_cmd_done(cmd_ctx, ret);
+}
+
+static void
+sss_autofs_cmd_getautomntent_done(struct tevent_req *req)
+{
+ struct autofs_enum_ctx *enum_ctx;
+ struct autofs_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx);
+
+ ret = autofs_setent_recv(cmd_ctx, req, &enum_ctx);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ autofs_cmd_done(cmd_ctx, ret);
+ return;
+ }
+
+ ret = autofs_write_getautomntent_output(cmd_ctx->cli_ctx, enum_ctx,
+ cmd_ctx->cursor,
+ cmd_ctx->max_entries);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ autofs_cmd_done(cmd_ctx, ret);
+ return;
+ }
+
+ sss_cmd_done(cmd_ctx->cli_ctx, NULL);
+}
+
+static errno_t
+autofs_read_getautomntbyname_input(struct cli_ctx *cli_ctx,
+ const char **_mapname,
+ const char **_keyname)
+{
+ struct cli_protocol *pctx;
+ const char *mapname;
+ const char *keyname;
+ uint32_t namelen;
+ uint32_t keylen;
+ uint8_t *body;
+ size_t blen;
+ size_t c = 0;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* Get map name. */
+ SAFEALIGN_COPY_UINT32_CHECK(&namelen, body + c, blen, &c);
+ if (namelen == 0 || namelen > blen - c) {
+ return EINVAL;
+ }
+
+ mapname = (const char *) body + c;
+
+ /* if not null-terminated fail */
+ if (mapname[namelen] != '\0') {
+ return EINVAL;
+ }
+
+ /* If the name isn't valid UTF-8, fail */
+ if (!sss_utf8_check((const uint8_t *)mapname, namelen - 1)) {
+ return EINVAL;
+ }
+
+ c += namelen + 1;
+
+ /* Get key name. */
+ SAFEALIGN_COPY_UINT32_CHECK(&keylen, body + c, blen, &c);
+ if (keylen == 0 || keylen > blen - c) {
+ return EINVAL;
+ }
+
+ keyname = (const char *) body + c;
+
+ /* if not null-terminated fail */
+ if (keyname[keylen] != '\0') {
+ return EINVAL;
+ }
+
+ /* If the key isn't valid UTF-8, fail */
+ if (!sss_utf8_check((const uint8_t *)keyname, keylen - 1)) {
+ return EINVAL;
+ }
+
+ *_mapname = mapname;
+ *_keyname = keyname;
+
+ return EOK;
+}
+
+static errno_t
+autofs_write_getautomntbyname_output(struct cli_ctx *cli_ctx,
+ struct cache_req_result *result,
+ const char *keyname)
+{
+ struct cli_protocol *pctx;
+ struct ldb_message *entry;
+ const char *value;
+ size_t value_len;
+ size_t len;
+ uint8_t *body;
+ size_t blen;
+ size_t rp;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ if (result == NULL || result->count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Key [%s] was not found\n", keyname);
+ return sss_cmd_empty_packet(pctx->creq->out);
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found key [%s]\n", keyname);
+ entry = result->msgs[0];
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ value = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_VALUE, NULL);
+ if (value == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No entry value found in [%s]\n", keyname);
+ return EINVAL;
+ }
+
+ value_len = 1 + strlen(value);
+ len = sizeof(uint32_t) + sizeof(uint32_t) + value_len;
+
+ ret = sss_packet_grow(pctx->creq->out, len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ sss_packet_get_body(pctx->creq->out, &body, &blen);
+
+ rp = 0;
+ SAFEALIGN_SET_UINT32(&body[rp], len, &rp);
+
+ SAFEALIGN_SET_UINT32(&body[rp], value_len, &rp);
+ if (value_len == 1) {
+ body[rp] = '\0';
+ } else {
+ memcpy(&body[rp], value, value_len);
+ }
+
+ return EOK;
+}
+
+static void
+sss_autofs_cmd_getautomntbyname_done(struct tevent_req *req);
+
+static int
+sss_autofs_cmd_getautomntbyname(struct cli_ctx *cli_ctx)
+{
+ struct autofs_cmd_ctx *cmd_ctx;
+ struct autofs_ctx *autofs_ctx;
+ struct tevent_req *req;
+ errno_t ret;
+
+ autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx);
+
+ cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n");
+ return ENOMEM;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+ cmd_ctx->autofs_ctx = autofs_ctx;
+
+ ret = autofs_read_getautomntbyname_input(cli_ctx, &cmd_ctx->mapname,
+ &cmd_ctx->keyname);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Obtaining autofs entry %s:%s\n",
+ cmd_ctx->mapname, cmd_ctx->keyname);
+
+ req = cache_req_autofs_entry_by_name_send(cli_ctx, cli_ctx->ev,
+ autofs_ctx->rctx,
+ autofs_ctx->rctx->ncache, 0, NULL,
+ cmd_ctx->mapname,
+ cmd_ctx->keyname);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_entry_by_name_send failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, sss_autofs_cmd_getautomntbyname_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ return autofs_cmd_done(cmd_ctx, ret);
+}
+
+static void
+sss_autofs_cmd_getautomntbyname_done(struct tevent_req *req)
+{
+ struct cache_req_result *result;
+ struct autofs_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx);
+
+ ret = cache_req_autofs_entry_by_name_recv(cmd_ctx, req, &result);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ autofs_cmd_done(cmd_ctx, ret);
+ return;
+ }
+
+ ret = autofs_write_getautomntbyname_output(cmd_ctx->cli_ctx, result,
+ cmd_ctx->keyname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ autofs_cmd_done(cmd_ctx, ret);
+ return;
+ }
+
+ sss_cmd_done(cmd_ctx->cli_ctx, NULL);
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version autofs_cli_protocol_version[] = {
+ { SSS_AUTOFS_PROTO_VERSION, NULL, NULL }
+ };
+
+ return autofs_cli_protocol_version;
+}
+
+struct sss_cmd_table *get_autofs_cmds(void)
+{
+ static struct sss_cmd_table autofs_cmds[] = {
+ { SSS_GET_VERSION, sss_cmd_get_version },
+ { SSS_AUTOFS_SETAUTOMNTENT, sss_autofs_cmd_setautomntent },
+ { SSS_AUTOFS_GETAUTOMNTENT, sss_autofs_cmd_getautomntent },
+ { SSS_AUTOFS_GETAUTOMNTBYNAME, sss_autofs_cmd_getautomntbyname },
+ { SSS_AUTOFS_ENDAUTOMNTENT, sss_autofs_cmd_endautomntent },
+ { SSS_CLI_NULL, NULL}
+ };
+
+ return autofs_cmds;
+}
+
+int autofs_connection_setup(struct cli_ctx *cctx)
+{
+ int ret;
+
+ ret = sss_connection_setup(cctx);
+ if (ret != EOK) return ret;
+
+ return EOK;
+}
diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
new file mode 100644
index 0000000..b827595
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req.c
@@ -0,0 +1,1612 @@
+/*
+ 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 <ldb.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "util/sss_chain_id.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req_private.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const struct cache_req_plugin *
+cache_req_get_plugin(enum cache_req_type type)
+{
+ static const struct cache_req_plugin *plugins[CACHE_REQ_SENTINEL] = {
+ &cache_req_user_by_name,
+ &cache_req_user_by_upn,
+ &cache_req_user_by_id,
+ &cache_req_user_by_cert,
+ &cache_req_user_by_filter,
+
+ &cache_req_group_by_name,
+ &cache_req_group_by_id,
+ &cache_req_group_by_filter,
+
+ &cache_req_initgroups_by_name,
+ &cache_req_initgroups_by_upn,
+
+#ifdef BUILD_SUBID
+ &cache_req_subid_ranges_by_name,
+#endif
+
+ &cache_req_object_by_sid,
+ &cache_req_object_by_name,
+ &cache_req_object_by_id,
+
+ &cache_req_enum_users,
+ &cache_req_enum_groups,
+ &cache_req_enum_svc,
+ &cache_req_enum_ip_hosts,
+ &cache_req_enum_ip_networks,
+
+ &cache_req_svc_by_name,
+ &cache_req_svc_by_port,
+
+ &cache_req_netgroup_by_name,
+
+ &cache_req_ssh_host_id_by_name,
+
+ &cache_req_autofs_map_entries,
+ &cache_req_autofs_map_by_name,
+ &cache_req_autofs_entry_by_name,
+
+ &cache_req_ip_host_by_name,
+ &cache_req_ip_host_by_addr,
+ &cache_req_ip_network_by_name,
+ &cache_req_ip_network_by_addr,
+ };
+
+ if (type >= CACHE_REQ_SENTINEL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Bug: invalid plugin type!");
+ return NULL;
+ }
+
+ return plugins[type];
+}
+
+static errno_t cache_req_set_plugin(struct cache_req *cr,
+ enum cache_req_type type)
+{
+ const struct cache_req_plugin *plugin;
+
+ plugin = cache_req_get_plugin(type);
+ if (plugin == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Bug: unset plugin!");
+ return EINVAL;
+ }
+
+ cr->reqname = plugin->name;
+ cr->plugin = plugin;
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, "Setting \"%s\" plugin\n",
+ plugin->name);
+
+ return EOK;
+}
+
+static const char *
+cache_req_dom_type_as_str(struct cache_req *cr)
+{
+ if (cr == NULL) {
+ return "BUG: Invalid cache_req pointer\n";
+ }
+ switch (cr->req_dom_type) {
+ case CACHE_REQ_POSIX_DOM:
+ return "POSIX-only";
+ case CACHE_REQ_APPLICATION_DOM:
+ return "Application-only";
+ case CACHE_REQ_ANY_DOM:
+ return "Any";
+ }
+
+ return "Unknown";
+}
+
+static struct cache_req *
+cache_req_create(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct cache_req_data *data,
+ struct sss_nc_ctx *ncache,
+ int midpoint,
+ enum cache_req_dom_type req_dom_type)
+{
+ struct cache_req *cr;
+ bool bypass_cache;
+ errno_t ret;
+
+ cr = talloc_zero(mem_ctx, struct cache_req);
+ if (cr == NULL) {
+ return NULL;
+ }
+
+ cr->rctx = rctx;
+ cr->data = data;
+ cr->ncache = ncache;
+ cr->midpoint = midpoint;
+ cr->req_dom_type = req_dom_type;
+ cr->req_start = time(NULL);
+
+ /* It is perfectly fine to just overflow here. */
+ cr->reqid = rctx->cache_req_num++;
+
+ ret = cache_req_set_plugin(cr, data->type);
+ if (ret != EOK) {
+ talloc_free(cr);
+ return NULL;
+ }
+
+ bypass_cache = cr->plugin->bypass_cache || cr->data->bypass_cache;
+ if (bypass_cache && cr->data->bypass_dp) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Cannot bypass cache and dp at the same time!");
+ talloc_free(cr);
+ return NULL;
+ }
+ if (rctx->cache_first) {
+ cr->cache_behavior = CACHE_REQ_CACHE_FIRST;
+ }
+ /* it is ok to override cache_first here */
+ if (bypass_cache) {
+ cr->cache_behavior = CACHE_REQ_BYPASS_CACHE;
+ } else if (cr->data->bypass_dp) {
+ cr->cache_behavior = CACHE_REQ_BYPASS_PROVIDER;
+ }
+
+ return cr;
+}
+
+static errno_t
+cache_req_set_name(struct cache_req *cr, const char *name)
+{
+ const char *dup_name;
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Setting name [%s]\n", name);
+
+ dup_name = talloc_strdup(cr->data, name);
+ if (dup_name == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, "Unable to set name!\n");
+ return ENOMEM;
+ }
+
+ talloc_zfree(cr->data->name.name);
+ cr->data->name.name = dup_name;
+
+ return EOK;
+}
+
+static bool
+cache_req_validate_domain_enumeration(struct cache_req *cr,
+ struct sss_domain_info *domain)
+{
+ if (!cr->plugin->require_enumeration) {
+ return true;
+ }
+
+ if (domain->enumerate == false) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Domain %s does not support "
+ "enumeration, skipping...\n", domain->name);
+ if (cr->rctx->enumeration_warn_logged == false) {
+ sss_log(SSS_LOG_NOTICE, "Enumeration requested but not enabled\n");
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Enumeration requested but not enabled\n");
+ cr->rctx->enumeration_warn_logged = true;
+ }
+ return false;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Domain %s supports enumeration\n",
+ domain->name);
+
+ return true;
+}
+
+static bool
+cache_req_validate_domain_type(struct cache_req *cr,
+ struct sss_domain_info *domain)
+{
+ bool valid = false;
+
+ switch (cr->req_dom_type) {
+ case CACHE_REQ_POSIX_DOM:
+ valid = domain->type == DOM_TYPE_POSIX ? true : false;
+ break;
+ case CACHE_REQ_APPLICATION_DOM:
+ valid = domain->type == DOM_TYPE_APPLICATION ? true : false;
+ break;
+ case CACHE_REQ_ANY_DOM:
+ valid = true;
+ break;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Request type %s for domain %s type %s is %svalid\n",
+ cache_req_dom_type_as_str(cr),
+ domain->name,
+ sss_domain_type_str(domain),
+ valid ? "" : "not ");
+ return valid;
+}
+
+static bool
+cache_req_validate_domain(struct cache_req *cr,
+ struct sss_domain_info *domain)
+{
+ bool ok;
+
+ ok = cache_req_validate_domain_enumeration(cr, domain);
+ if (ok == false) {
+ return false;
+ }
+
+ ok = cache_req_validate_domain_type(cr, domain);
+ if (ok == false) {
+ return false;
+ }
+
+ ok = !cr->data->hybrid_lookup || domain->mpg_mode == MPG_HYBRID;
+ if (ok == false) {
+ return false;
+ }
+
+ return true;
+}
+
+static errno_t
+cache_req_is_well_known_object(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_result **_result)
+{
+ errno_t ret;
+
+ if (cr->plugin->is_well_known_fn == NULL) {
+ return ENOENT;
+ }
+
+ ret = cr->plugin->is_well_known_fn(mem_ctx, cr, cr->data, _result);
+ if (ret == EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Object is well known!\n");
+ (*_result)->well_known_object = true;
+ } else if (ret != ENOENT) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Unable to prepare data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static errno_t
+cache_req_prepare_domain_data(struct cache_req *cr,
+ struct sss_domain_info *domain)
+{
+ errno_t ret;
+
+ if (cr->plugin->prepare_domain_data_fn == NULL) {
+ return EOK;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Preparing input data for domain [%s] rules\n",
+ domain->name);
+
+ ret = cr->plugin->prepare_domain_data_fn(cr, cr->data, domain);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Unable to prepare data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t
+cache_req_create_debug_name(struct cache_req *cr,
+ struct sss_domain_info *domain)
+{
+ if (cr->plugin->create_debug_name_fn == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Bug: no create debug name function specified!\n");
+ return ERR_INTERNAL;
+ }
+
+ talloc_zfree(cr->debugobj);
+
+ cr->debugobj = cr->plugin->create_debug_name_fn(cr, cr->data, domain);
+ if (cr->debugobj == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Unable to create debug name!\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static errno_t
+cache_req_set_domain(struct cache_req *cr,
+ struct sss_domain_info *domain)
+{
+ errno_t ret;
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Using domain [%s]\n", domain->name);
+
+ ret = cache_req_prepare_domain_data(cr, domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = cache_req_create_debug_name(cr, domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ cr->domain = domain;
+
+ return EOK;
+}
+
+static void cache_req_global_ncache_add(struct cache_req *cr)
+{
+ errno_t ret;
+
+ if (cr->plugin->global_ncache_add_fn == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr,
+ "This request type does not support "
+ "global negative cache\n");
+ return;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Adding [%s] to global "
+ "negative cache\n", cr->debugobj);
+
+ ret = cr->plugin->global_ncache_add_fn(cr->ncache, cr->data);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
+ "Cannot set negative cache for [%s] [%d]: %s\n",
+ cr->debugobj, ret, sss_strerror(ret));
+ /* not fatal */
+ }
+
+ return;
+}
+
+static bool cache_req_check_acct_domain_lookup_type(struct cache_req *cr,
+ struct sss_domain_info *dom)
+{
+ struct sss_domain_info *head;
+ int nret;
+
+ head = get_domains_head(dom);
+ if (head == NULL) {
+ return false;
+ }
+
+ nret = sss_ncache_check_domain_locate_type(cr->rctx->ncache,
+ head,
+ cr->plugin->name);
+ if (nret == ENOENT) {
+ return true;
+ }
+ return false;
+}
+
+static errno_t cache_req_set_acct_domain_lookup_type(struct cache_req *cr,
+ struct sss_domain_info *dom)
+{
+ struct sss_domain_info *head;
+
+ head = get_domains_head(dom);
+ if (head == NULL) {
+ return EINVAL;
+ }
+
+ return sss_ncache_set_domain_locate_type(cr->rctx->ncache,
+ head,
+ cr->plugin->name);
+}
+
+static void cache_req_domain_set_locate_flag(struct cache_req_domain *domains,
+ struct cache_req *cr)
+{
+ struct cache_req_domain *crd_iter;
+
+ DLIST_FOR_EACH(crd_iter, domains) {
+ if (cache_req_check_acct_domain_lookup_type(cr, crd_iter->domain)) {
+ crd_iter->locate_domain = true;
+ }
+ }
+}
+
+static bool
+cache_req_assume_upn(struct cache_req *cr)
+{
+ errno_t ret;
+
+ if (cr->plugin->allow_switch_to_upn == false
+ || cr->data->name.input == NULL
+ || strchr(cr->data->name.input, '@') == NULL) {
+ return false;
+ }
+
+ ret = cache_req_set_plugin(cr, cr->plugin->upn_equivalent);
+ if (ret != EOK) {
+ return false;
+ }
+
+ ret = cache_req_set_name(cr, cr->data->name.input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_set_name() failed\n");
+ return false;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Assuming UPN [%s]\n",
+ cr->data->name.input);
+
+ return true;
+}
+
+struct cache_req_locate_dom_state {
+ /* input data */
+ struct tevent_context *ev;
+ struct cache_req *cr;
+ struct cache_req_domain *req_domains;
+
+ /* Return values in case the first cache lookup succeeds */
+ struct ldb_result *result;
+ bool dp_success;
+};
+
+static void cache_req_locate_dom_cache_done(struct tevent_req *subreq);
+static void cache_req_locate_dom_done(struct tevent_req *subreq);
+static void cache_req_locate_dom_mark_neg_all(
+ struct cache_req_locate_dom_state *state);
+static void cache_req_locate_dom_mark_neg_domains(
+ struct cache_req_locate_dom_state *state,
+ const char *found_domain_name);
+
+static struct tevent_req *cache_req_locate_dom_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr,
+ struct cache_req_domain *req_domains)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct cache_req_locate_dom_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cache_req_locate_dom_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+ state->ev = ev;
+ state->cr = cr;
+ state->req_domains = req_domains;
+
+ /* It is wasteful to run the domain locator request if the results are
+ * present in the cache, because the domain locator always contacts
+ * the DP. Therefore, first run a cache-only search and only if the
+ * requested data is not available, run the locator
+ *
+ * FIXME - this could be optimized further if we are running the
+ * second iteration with cache_first, then we don't need to search
+ * again
+ */
+ subreq = cache_req_search_send(state,
+ state->ev,
+ state->cr,
+ false,
+ true);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+ tevent_req_set_callback(subreq, cache_req_locate_dom_cache_done, req);
+
+ return req;
+
+immediately:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void cache_req_locate_dom_cache_done(struct tevent_req *subreq)
+{
+ struct cache_req_locate_dom_state *state = NULL;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_locate_dom_state);
+
+ ret = cache_req_search_recv(state, subreq, &state->result, &state->dp_success);
+ talloc_zfree(subreq);
+
+ switch (ret) {
+ case EOK:
+ /* Just finish the request and let the caller handle the result */
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Result found in the cache\n");
+ tevent_req_done(req);
+ return;
+ case ERR_ID_OUTSIDE_RANGE:
+ case ENOENT:
+ /* Not cached and locator was requested, run the locator
+ * DP request plugin
+ */
+ subreq = cache_req_locate_domain_send(state,
+ state->ev,
+ state->cr);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, cache_req_locate_dom_done, req);
+ return;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "cache_req_search_recv returned [%d]: %s\n", ret, sss_strerror(ret));
+ break;
+ }
+
+ tevent_req_error(req, ret);
+ return;
+}
+
+static void cache_req_locate_dom_done(struct tevent_req *subreq)
+{
+ struct cache_req_locate_dom_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+ char *found_domain_name;
+ int nret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_locate_dom_state);
+
+ ret = cache_req_locate_domain_recv(state, subreq, &found_domain_name);
+ talloc_zfree(subreq);
+ switch (ret) {
+ case ERR_GET_ACCT_DOM_NOT_SUPPORTED:
+ nret = cache_req_set_acct_domain_lookup_type(state->cr,
+ state->cr->domain);
+ if (nret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to disable domain locating functionality for %s\n",
+ state->cr->plugin->name);
+ }
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Disabled domain locating functionality for %s\n",
+ state->cr->plugin->name);
+ break;
+ case ERR_NOT_FOUND:
+ cache_req_locate_dom_mark_neg_all(state);
+ break;
+ case EOK:
+ cache_req_locate_dom_mark_neg_domains(state, found_domain_name);
+ break;
+ default:
+ /* We explicitly ignore errors here */
+ break;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static void cache_req_locate_dom_mark_neg_all(
+ struct cache_req_locate_dom_state *state)
+{
+ struct cache_req_domain *iter;
+
+ DLIST_FOR_EACH(iter, state->req_domains) {
+ if (get_domains_head(state->cr->domain) != get_domains_head(iter->domain)) {
+ /* Only add to negative cache for domains from the same "main"
+ * domain" */
+ continue;
+ }
+ cache_req_search_ncache_add_to_domain(state->cr, iter->domain);
+ }
+}
+
+static void cache_req_locate_dom_mark_neg_domains(
+ struct cache_req_locate_dom_state *state,
+ const char *found_domain_name)
+{
+ struct sss_domain_info *found_domain;
+ struct cache_req_domain *iter;
+
+ found_domain = find_domain_by_name(get_domains_head(state->cr->domain),
+ found_domain_name,
+ true);
+ if (found_domain == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot find domain %s\n", found_domain_name);
+ return;
+ }
+
+ /* Set negcache in all subdomains of the one being examined
+ * except the found one */
+ DLIST_FOR_EACH(iter, state->req_domains) {
+ if (strcasecmp(found_domain_name,
+ iter->domain->name) == 0) {
+ continue;
+ }
+
+ if (get_domains_head(found_domain) != get_domains_head(iter->domain)) {
+ /* Don't set negative cache for domains outside the main
+ * domain/subdomain tree b/c the locator request is not
+ * authoritative for them
+ */
+ continue;
+ }
+ cache_req_search_ncache_add_to_domain(state->cr, iter->domain);
+ }
+}
+
+static errno_t cache_req_locate_dom_cache_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ldb_result **_result,
+ bool *_dp_success)
+{
+ struct cache_req_locate_dom_state *state;
+
+ state = tevent_req_data(req, struct cache_req_locate_dom_state);
+
+ if (_dp_success != NULL) {
+ *_dp_success = state->dp_success;
+ }
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_result != NULL) {
+ *_result = talloc_steal(mem_ctx, state->result);
+ }
+
+ return EOK;
+}
+
+struct cache_req_search_domains_state {
+ /* input data */
+ struct tevent_context *ev;
+ struct cache_req *cr;
+
+ /* work data */
+ struct cache_req_domain *cr_domain;
+ struct cache_req_domain *req_domains;
+ struct sss_domain_info *selected_domain;
+ struct cache_req_result **results;
+ size_t num_results;
+ bool check_next;
+ bool dp_success;
+ bool first_iteration;
+};
+
+static errno_t cache_req_search_domains_next(struct tevent_req *req);
+static errno_t cache_req_handle_result(struct tevent_req *req,
+ struct ldb_result *result);
+
+static void cache_req_search_domains_locate_done(struct tevent_req *subreq);
+
+static void cache_req_search_domains_done(struct tevent_req *subreq);
+
+static bool
+cache_req_dp_contacted(struct cache_req_search_domains_state *state)
+{
+ switch (state->cr->cache_behavior) {
+ case CACHE_REQ_CACHE_FIRST:
+ if (state->first_iteration) {
+ /* This is the first iteration so provider was bypassed. */
+ return false;
+ }
+
+ /* This is the second iteration so the provider was contacted. */
+ return true;
+ case CACHE_REQ_BYPASS_PROVIDER:
+ return false;
+ default:
+ /* Other schemas talks to provider immediately. */
+ return true;
+ }
+}
+
+struct tevent_req *
+cache_req_search_domains_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr,
+ struct cache_req_domain *cr_domain,
+ bool check_next,
+ bool first_iteration)
+{
+ struct tevent_req *req;
+ struct cache_req_search_domains_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cache_req_search_domains_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->cr = cr;
+
+ state->cr_domain = cr_domain;
+ state->req_domains = cr_domain;
+ state->check_next = check_next;
+ state->dp_success = true;
+ state->first_iteration = first_iteration;
+
+ if (cr->plugin->dp_get_domain_send_fn != NULL
+ && ((state->check_next && cr_domain->next != NULL)
+ || ((state->cr->cache_behavior == CACHE_REQ_CACHE_FIRST)
+ && !first_iteration))) {
+ /* If the request is not qualified with a domain name AND
+ * there are multiple domains to search OR if this is the second
+ * pass during the "check-cache-first" schema, it makes sense
+ * to try to run the domain-locator plugin
+ */
+ cache_req_domain_set_locate_flag(cr_domain, cr);
+ }
+
+ ret = cache_req_search_domains_next(req);
+ if (ret == EAGAIN) {
+ return req;
+ }
+
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t cache_req_search_domains_next(struct tevent_req *req)
+{
+ struct cache_req_search_domains_state *state;
+ struct tevent_req *subreq;
+ struct cache_req *cr;
+ struct sss_domain_info *domain;
+ uint32_t next_domain_flag;
+ bool is_domain_valid;
+ bool allow_no_fqn;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct cache_req_search_domains_state);
+ cr = state->cr;
+
+ next_domain_flag = cr->plugin->get_next_domain_flags;
+ allow_no_fqn = cr->plugin->allow_missing_fqn;
+
+ while (state->cr_domain != NULL) {
+ domain = state->cr_domain->domain;
+
+ if (domain == NULL) {
+ break;
+ }
+
+ /* As the cr_domain list is a flatten version of the domains
+ * list, we have to ensure to only go through the subdomains in
+ * case it's specified in the plugin to do so.
+ */
+ if (next_domain_flag == 0 && IS_SUBDOMAIN(domain)) {
+ state->cr_domain = state->cr_domain->next;
+ continue;
+ }
+
+ /* Check if this domain is valid for this request. */
+ is_domain_valid = cache_req_validate_domain(cr, domain);
+ if (!is_domain_valid) {
+ state->cr_domain = state->cr_domain->next;
+ continue;
+ }
+
+ /* If not specified otherwise, we skip domains that require fully
+ * qualified names on domain less search. We do not descend into
+ * subdomains here since those are implicitly qualified.
+ */
+ if (state->check_next && !allow_no_fqn && state->cr_domain->fqnames) {
+ state->cr_domain = state->cr_domain->next;
+ continue;
+ }
+
+ state->selected_domain = domain;
+
+ ret = cache_req_set_domain(cr, domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (state->cr_domain->locate_domain) {
+ subreq = cache_req_locate_dom_send(state,
+ state->ev,
+ cr,
+ state->req_domains);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, cache_req_search_domains_locate_done, req);
+ return EAGAIN;
+ }
+
+ subreq = cache_req_search_send(state, state->ev, cr,
+ state->first_iteration,
+ false);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, cache_req_search_domains_done, req);
+
+ /* we will continue with the following domain the next time */
+ if (state->check_next) {
+ state->cr_domain = state->cr_domain->next;
+ }
+
+ return EAGAIN;
+ }
+
+ /* If we've got some result from previous searches we want to return
+ * EOK here so the whole cache request is successfully finished. */
+ if (state->num_results > 0) {
+ return EOK;
+ }
+
+ /* We have searched all available domains and no result was found.
+ *
+ * If the plug-in uses a negative cache which is shared among all domains
+ * (e.g. unique identifiers such as user or group id or sid), we add it
+ * here and return object not found error.
+ *
+ * However, we can only set the negative cache if all data provider
+ * requests succeeded because only then we can be sure that it does
+ * not exist-
+ */
+ if (cache_req_dp_contacted(state) && state->dp_success) {
+ cache_req_global_ncache_add(cr);
+ }
+
+ return ENOENT;
+}
+
+static void cache_req_search_domains_locate_done(struct tevent_req *subreq)
+{
+ struct cache_req_search_domains_state *state;
+ struct ldb_result *result = NULL;
+ struct tevent_req *req;
+ bool dp_success;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_search_domains_state);
+
+ ret = cache_req_locate_dom_cache_recv(state, subreq, &result, &dp_success);
+ talloc_zfree(subreq);
+
+ /* Remember if any DP request fails, but here it shouldn't matter
+ * as the only DP request that should realistically happen is midpoint
+ * refresh */
+ state->dp_success = !dp_success ? false : state->dp_success;
+
+ /* Don't locate the domain again */
+ state->cr_domain->locate_domain = false;
+
+ switch (ret) {
+ case EOK:
+ if (result != NULL) {
+ /* Handle result as normally */
+ ret = cache_req_handle_result(req, result);
+ if (ret != EAGAIN) {
+ goto done;
+ }
+ }
+ break;
+ default:
+ /* Some serious error has happened. Finish. */
+ goto done;
+ }
+
+ /* This is a domain less search, continue with the next domain. */
+ ret = cache_req_search_domains_next(req);
+
+done:
+ switch (ret) {
+ case EOK:
+ tevent_req_done(req);
+ break;
+ case EAGAIN:
+ break;
+ default:
+ tevent_req_error(req, ret);
+ break;
+ }
+ return;
+}
+
+static errno_t cache_req_handle_result(struct tevent_req *req,
+ struct ldb_result *result)
+{
+ struct cache_req_search_domains_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct cache_req_search_domains_state);
+
+ /* We got some data from this search. Save it. */
+ ret = cache_req_create_and_add_result(state,
+ state->cr,
+ state->selected_domain,
+ result,
+ state->cr->data->name.lookup,
+ &state->results,
+ &state->num_results);
+ if (ret != EOK) {
+ /* We were unable to save data. */
+ return ret;
+ }
+
+ if (!state->check_next || !state->cr->plugin->search_all_domains) {
+ /* We are not interested in more results. */
+ return EOK;
+ }
+
+ return EAGAIN;
+}
+
+static void cache_req_search_domains_done(struct tevent_req *subreq)
+{
+ struct cache_req_search_domains_state *state;
+ struct ldb_result *result;
+ struct tevent_req *req;
+ bool dp_success;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_search_domains_state);
+
+ ret = cache_req_search_recv(state, subreq, &result, &dp_success);
+ talloc_zfree(subreq);
+
+ /* Remember if any DP request fails, if DP was contacted. */
+ if (cache_req_dp_contacted(state)) {
+ state->dp_success = !dp_success ? false : state->dp_success;
+ }
+
+ switch (ret) {
+ case EOK:
+ ret = cache_req_handle_result(req, result);
+ if (ret != EAGAIN) {
+ goto done;
+ }
+ break;
+ case ERR_ID_OUTSIDE_RANGE:
+ case ENOENT:
+ if (state->check_next == false) {
+ if (cache_req_dp_contacted(state)
+ && !state->dp_success
+ && state->cr->data->propogate_offline_status) {
+ /* Not found and data provider request failed so we were
+ * unable to fetch the data. */
+ ret = ERR_OFFLINE;
+ goto done;
+ }
+
+ /* Not found. */
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* Continue with next domain. */
+ break;
+ default:
+ /* Some serious error has happened. Finish. */
+ goto done;
+ }
+
+ /* This is a domain less search, continue with the next domain. */
+ ret = cache_req_search_domains_next(req);
+
+done:
+ if (ret == ENOENT && state->results != NULL) {
+ /* We have at least one result. */
+ ret = EOK;
+ }
+
+ switch (ret) {
+ case EOK:
+ tevent_req_done(req);
+ break;
+ case EAGAIN:
+ break;
+ default:
+ if (cache_req_dp_contacted(state)
+ && ret == ENOENT
+ && !state->dp_success
+ && state->cr->data->propogate_offline_status) {
+ /* Not found and data provider request failed so we were
+ * unable to fetch the data. */
+ ret = ERR_OFFLINE;
+ }
+ tevent_req_error(req, ret);
+ break;
+ }
+
+ return;
+}
+
+static errno_t
+cache_req_search_domains_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result ***_results,
+ size_t *_num_results)
+{
+ struct cache_req_search_domains_state *state;
+
+ state = tevent_req_data(req, struct cache_req_search_domains_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_results != NULL) {
+ *_results = talloc_steal(mem_ctx, state->results);
+ }
+ if (_num_results != NULL) {
+ *_num_results = state->num_results;
+ }
+
+ return EOK;
+}
+
+struct cache_req_state {
+ /* input data */
+ struct tevent_context *ev;
+ struct cache_req *cr;
+ const char *domain_name;
+
+ /* work data */
+ struct cache_req_domain *cr_domains;
+ struct cache_req_result **results;
+ size_t num_results;
+ bool first_iteration;
+};
+
+static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req *cr,
+ const char *domain);
+
+static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req *cr,
+ const char *domain);
+
+static void cache_req_domains_updated(struct tevent_req *subreq);
+
+static void cache_req_input_parsed(struct tevent_req *subreq);
+
+static errno_t cache_req_select_domains(struct tevent_req *req,
+ const char *domain_name,
+ char **requested_domains);
+
+static errno_t
+cache_req_search_domains(struct tevent_req *req,
+ struct cache_req_domain *oredered_domain,
+ bool check_next);
+
+static void cache_req_process_result(struct tevent_req *subreq);
+
+static void cache_req_done(struct tevent_req *subreq);
+
+struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int midpoint,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ struct cache_req_data *data)
+{
+ struct cache_req_state *state;
+ struct cache_req_result *result;
+ struct cache_req *cr;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct cache_req_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->cr = cr = cache_req_create(state, rctx, data,
+ ncache, midpoint, req_dom_type);
+ if (state->cr == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ state->first_iteration = true;
+
+ SSS_REQ_TRACE_CID_CR(SSSDBG_TRACE_FUNC, cr, "New request [CID #%lu] '%s'\n",
+ sss_chain_id_get(), cr->reqname);
+
+ ret = cache_req_is_well_known_object(state, cr, &result);
+ if (ret == EOK) {
+ ret = cache_req_add_result(state, result, &state->results,
+ &state->num_results);
+ goto done;
+ } else if (ret != ENOENT) {
+ goto done;
+ }
+
+ state->domain_name = domain;
+ ret = cache_req_process_input(state, req, cr, domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = cache_req_select_domains(req, state->domain_name,
+ cr->data->requested_domains);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req *cr,
+ const char *domain)
+{
+ struct tevent_req *subreq;
+ const char *default_domain;
+ errno_t ret;
+
+ if (cr->data->name.input == NULL) {
+ /* Call cache_req_update_domains() in order to get a up to date list
+ * of domains and subdomains, if needed. Otherwise just return EOK as
+ * the input was not a name, thus there's no need to process it
+ * further. */
+ return cache_req_update_domains(mem_ctx, req, cr, domain);
+ }
+
+ if (cr->plugin->parse_name == false) {
+ /* Call cache_req_update_domains() in order to get a up to date list
+ * of domains and subdomains, if needed. Otherwise, just use the input
+ * name as it is. */
+ ret = cache_req_update_domains(mem_ctx, req, cr, domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return cache_req_set_name(cr, cr->data->name.input);
+ }
+
+ default_domain = NULL;
+ if (!cr->plugin->ignore_default_domain) {
+ default_domain = cr->rctx->default_domain;
+ }
+
+ /* Parse name since it may contain a domain name. */
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Parsing input name [%s]\n", cr->data->name.input);
+
+ subreq = sss_parse_inp_send(mem_ctx, cr->rctx, default_domain,
+ cr->data->name.input);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_inp_send() failed\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, cache_req_input_parsed, req);
+
+ return EAGAIN;
+}
+
+static errno_t cache_req_update_domains(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req *cr,
+ const char *domain)
+{
+ struct tevent_req *subreq;
+
+ if (cr->rctx->get_domains_last_call.tv_sec != 0) {
+ return EOK;
+ }
+
+ subreq = sss_dp_get_domains_send(mem_ctx, cr->rctx, false, domain);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, cache_req_domains_updated, req);
+ return EAGAIN;
+}
+
+static void cache_req_domains_updated(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct cache_req_state *state;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_state);
+
+ ret = sss_dp_get_domains_recv(subreq);
+ talloc_free(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (state->cr->data->name.input == NULL) {
+ /* Input was not name, there is no need to process it further. */
+ goto immediately;
+ }
+
+ if (state->cr->plugin->parse_name == false || state->domain_name != NULL) {
+ /* We do not want to parse the name. */
+ ret = cache_req_set_name(state->cr, state->cr->data->name.input);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+immediately:
+ ret = cache_req_select_domains(req, state->domain_name,
+ state->cr->data->requested_domains);
+
+done:
+ if (ret != EOK && ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+}
+
+static void cache_req_input_parsed(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct cache_req_state *state;
+ char *name;
+ char *domain = NULL;
+ bool maybe_upn;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_state);
+
+ ret = sss_parse_inp_recv(subreq, state, &name, &domain);
+
+ if (state->domain_name != NULL && domain != NULL
+ && strcmp(state->domain_name, domain) != 0){
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Mismatch between input domain name [%s] and parsed domain name [%s]\n",
+ state->domain_name, domain);
+ tevent_req_error(req, ERR_INPUT_PARSE);
+ return;
+ }
+
+ switch (ret) {
+ case EOK:
+ ret = cache_req_set_name(state->cr, name);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ break;
+ case ERR_DOMAIN_NOT_FOUND:
+ maybe_upn = cache_req_assume_upn(state->cr);
+ if (!maybe_upn) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ domain = NULL;
+ break;
+ default:
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->domain_name == NULL) {
+ state->domain_name = domain;
+ }
+ ret = cache_req_select_domains(req, state->domain_name,
+ state->cr->data->requested_domains);
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+}
+
+static errno_t cache_req_select_domains(struct tevent_req *req,
+ const char *domain_name,
+ char **requested_domains)
+{
+ struct cache_req_state *state = NULL;
+ struct cache_req_domain *cr_domain;
+ bool check_next;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct cache_req_state);
+
+ if (state->cr->cache_behavior != CACHE_REQ_CACHE_FIRST) {
+
+ if (!state->first_iteration) {
+ /* We're done here. */
+ return EOK;
+ }
+ }
+
+ ret = cache_req_domain_copy_cr_domains(state,
+ state->cr->rctx->cr_domains,
+ requested_domains,
+ &state->cr_domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_copy_cr_domains() failed\n");
+ return EINVAL;
+ }
+
+ if (domain_name != NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Performing a single domain search\n");
+
+ cr_domain = cache_req_domain_get_domain_by_name(
+ state->cr_domains, domain_name);
+ if (cr_domain == NULL) {
+ return ERR_DOMAIN_NOT_FOUND;
+ }
+ check_next = false;
+ } else {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Performing a multi-domain search\n");
+
+ cr_domain = state->cr_domains;
+ check_next = true;
+ }
+
+ return cache_req_search_domains(req, cr_domain, check_next);
+}
+
+static errno_t
+cache_req_search_domains(struct tevent_req *req,
+ struct cache_req_domain *cr_domain,
+ bool check_next)
+{
+ struct tevent_req *subreq;
+ struct cache_req_state *state = NULL;
+ const char *cache_action;
+ const char *provider_action;
+
+ state = tevent_req_data(req, struct cache_req_state);
+
+ switch (state->cr->cache_behavior) {
+ case CACHE_REQ_CACHE_FIRST:
+ cache_action = (state->first_iteration) ? "check" : "bypass";
+ provider_action = (state->first_iteration) ? "bypass" : "check";
+ break;
+ case CACHE_REQ_BYPASS_CACHE:
+ cache_action = "bypass";
+ provider_action = "check";
+ break;
+ case CACHE_REQ_BYPASS_PROVIDER:
+ cache_action = "check";
+ provider_action = "bypass";
+ break;
+ default:
+ cache_action = "check";
+ provider_action = "check";
+ break;
+ }
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Search will %s the cache and %s the data provider\n",
+ cache_action, provider_action);
+
+ subreq = cache_req_search_domains_send(state, state->ev, state->cr,
+ cr_domain, check_next,
+ state->first_iteration);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, cache_req_process_result, req);
+ return EAGAIN;
+}
+
+static void cache_req_process_result(struct tevent_req *subreq)
+{
+ struct cache_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 cache_req_state);
+
+ ret = cache_req_search_domains_recv(state, subreq,
+ &state->results, &state->num_results);
+ talloc_zfree(subreq);
+
+ if (ret == ENOENT && state->first_iteration) {
+ /* Try again different search schema. */
+ state->first_iteration = false;
+ ret = cache_req_select_domains(req, state->domain_name,
+ state->cr->data->requested_domains);
+ if (ret == EOK) {
+ /* We're done searching and we have found nothing. */
+ ret = ENOENT;
+ }
+ }
+
+ /* Have have tried all domains and found nothing. Let's try UPN search. */
+ if (ret == ENOENT) {
+ if (state->domain_name != NULL) {
+ /* Lookup domain was specified as input. Since we haven't
+ * found anything yet we may want to try UPN search with
+ * some plug-ins. */
+
+ if (cache_req_assume_upn(state->cr)) {
+ /* Try UPN now. */
+ state->first_iteration = true;
+ ret = cache_req_select_domains(req, NULL,
+ state->cr->data->requested_domains);
+ }
+ }
+ }
+
+ /* Overlay each result with session recording flag */
+ if (ret == EOK) {
+ subreq = cache_req_sr_overlay_send(state, state->ev, state->cr,
+ state->results,
+ state->num_results);
+ if (subreq == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Failed creating a session recording "
+ "overlay request\n");
+ ret = ENOMEM;
+ } else {
+ tevent_req_set_callback(subreq, cache_req_done, req);
+ ret = EAGAIN;
+ }
+ }
+
+ switch (ret) {
+ case EAGAIN:
+ break;
+ case ENOENT:
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Not found\n");
+ tevent_req_error(req, ret);
+ break;
+ default:
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Finished: Error %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ break;
+ }
+
+ return;
+}
+
+static void cache_req_done(struct tevent_req *subreq)
+{
+ struct cache_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 cache_req_state);
+ ret = cache_req_sr_overlay_recv(subreq);
+ talloc_zfree(subreq);
+
+ switch (ret) {
+ case EOK:
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Success\n");
+ tevent_req_done(req);
+ break;
+ default:
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Finished: Error %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ break;
+ }
+}
+
+uint32_t cache_req_get_reqid(struct tevent_req *req)
+{
+ const struct cache_req_state *state;
+
+ state = tevent_req_data(req, struct cache_req_state);
+
+ if (state && state->cr) {
+ return state->cr->reqid;
+ }
+
+ return 0;
+}
+
+errno_t cache_req_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result ***_results)
+{
+ struct cache_req_state *state;
+
+ state = tevent_req_data(req, struct cache_req_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_results != NULL) {
+ *_results = talloc_steal(mem_ctx, state->results);
+ }
+
+ return EOK;
+}
+
+errno_t cache_req_single_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result **_result)
+{
+ struct cache_req_state *state;
+
+ state = tevent_req_data(req, struct cache_req_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_result != NULL) {
+ *_result = talloc_steal(mem_ctx, state->results[0]);
+ }
+
+ return EOK;
+}
+
+struct tevent_req *
+cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ struct cache_req_data *data)
+{
+ struct tevent_req *req;
+
+ req = cache_req_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ req_dom_type, domain, data);
+ if (req == NULL) {
+ talloc_zfree(data);
+ return NULL;
+ }
+
+ talloc_steal(req, data);
+
+ return req;
+}
diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h
new file mode 100644
index 0000000..a0c6879
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req.h
@@ -0,0 +1,521 @@
+/*
+ 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 _CACHE_REQ_H_
+#define _CACHE_REQ_H_
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/negcache.h"
+
+enum cache_req_type {
+ CACHE_REQ_USER_BY_NAME,
+ CACHE_REQ_USER_BY_UPN,
+ CACHE_REQ_USER_BY_ID,
+ CACHE_REQ_USER_BY_CERT,
+ CACHE_REQ_USER_BY_FILTER,
+
+ CACHE_REQ_GROUP_BY_NAME,
+ CACHE_REQ_GROUP_BY_ID,
+ CACHE_REQ_GROUP_BY_FILTER,
+
+ CACHE_REQ_INITGROUPS,
+ CACHE_REQ_INITGROUPS_BY_UPN,
+
+#ifdef BUILD_SUBID
+ CACHE_REQ_SUBID_RANGES_BY_NAME,
+#endif
+
+ CACHE_REQ_OBJECT_BY_SID,
+ CACHE_REQ_OBJECT_BY_NAME,
+ CACHE_REQ_OBJECT_BY_ID,
+
+ CACHE_REQ_ENUM_USERS,
+ CACHE_REQ_ENUM_GROUPS,
+ CACHE_REQ_ENUM_SVC,
+ CACHE_REQ_ENUM_HOST,
+ CACHE_REQ_ENUM_IP_NETWORK,
+
+ CACHE_REQ_SVC_BY_NAME,
+ CACHE_REQ_SVC_BY_PORT,
+
+ CACHE_REQ_NETGROUP_BY_NAME,
+
+ CACHE_REQ_SSH_HOST_ID_BY_NAME,
+
+ CACHE_REQ_AUTOFS_MAP_ENTRIES,
+ CACHE_REQ_AUTOFS_MAP_BY_NAME,
+ CACHE_REQ_AUTOFS_ENTRY_BY_NAME,
+
+ CACHE_REQ_IP_HOST_BY_NAME,
+ CACHE_REQ_IP_HOST_BY_ADDR,
+ CACHE_REQ_IP_NETWORK_BY_NAME,
+ CACHE_REQ_IP_NETWORK_BY_ADDR,
+
+ CACHE_REQ_SENTINEL
+};
+
+/* Whether to limit the request type to a certain domain type
+ * (POSIX/non-POSIX)
+ */
+enum cache_req_dom_type {
+ /* Only look up data in POSIX domains */
+ CACHE_REQ_POSIX_DOM,
+ /* Only look up data in application domains */
+ CACHE_REQ_APPLICATION_DOM,
+ /* Look up data in any domain type */
+ CACHE_REQ_ANY_DOM
+};
+
+/* Controls behavior about how to use cached information during
+ * a lookup, this is to fine tune some behaviors for specific
+ * situations
+ */
+enum cache_req_behavior {
+ CACHE_REQ_NORMAL,
+ CACHE_REQ_CACHE_FIRST,
+ CACHE_REQ_BYPASS_CACHE,
+ CACHE_REQ_BYPASS_PROVIDER,
+};
+
+/* Input data. */
+
+struct cache_req_data;
+
+struct cache_req_data *
+cache_req_data_attr(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *attr,
+ const char *filter);
+
+struct cache_req_data *
+cache_req_data_name(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name);
+
+struct cache_req_data *
+cache_req_data_name_attrs(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name,
+ const char **attrs);
+
+struct cache_req_data *
+cache_req_data_id(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ uint32_t id);
+
+struct cache_req_data *
+cache_req_data_id_attrs(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ uint32_t id,
+ const char **attrs);
+
+struct cache_req_data *
+cache_req_data_cert(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *cert);
+
+struct cache_req_data *
+cache_req_data_sid(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *sid,
+ const char **attrs);
+
+struct cache_req_data *
+cache_req_data_addr(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ uint32_t af,
+ uint32_t addrlen,
+ uint8_t *addr);
+
+struct cache_req_data *
+cache_req_data_enum(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type);
+
+struct cache_req_data *
+cache_req_data_svc(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name,
+ const char *protocol,
+ uint16_t port);
+
+struct cache_req_data *
+cache_req_data_ssh_host_id(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name,
+ const char *alias,
+ const char **attrs);
+
+struct cache_req_data *
+cache_req_data_autofs_entry(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *mapname,
+ const char *entryname);
+
+void
+cache_req_data_set_bypass_cache(struct cache_req_data *data,
+ bool bypass_cache);
+
+void
+cache_req_data_set_bypass_dp(struct cache_req_data *data,
+ bool bypass_dp);
+
+void
+cache_req_data_set_requested_domains(struct cache_req_data *data,
+ char **requested_domains);
+
+void
+cache_req_data_set_propogate_offline_status(struct cache_req_data *data,
+ bool propogate_offline_status);
+
+void
+cache_req_data_set_hybrid_lookup(struct cache_req_data *data,
+ bool hybrid_lookup);
+
+enum cache_req_type
+cache_req_data_get_type(struct cache_req_data *data);
+
+/* Output data. */
+
+struct cache_req_result {
+ /**
+ * SSSD domain where the result was obtained.
+ */
+ struct sss_domain_info *domain;
+
+ /**
+ * Result from ldb lookup.
+ */
+ struct ldb_result *ldb_result;
+
+ /**
+ * Shortcuts into ldb_result. This shortens the code a little since
+ * callers usually don't don't need to work with ldb_result directly.
+ */
+ unsigned int count;
+ struct ldb_message **msgs;
+
+ /**
+ * If name was used as a lookup parameter, @lookup_name contains name
+ * normalized to @domain rules.
+ */
+ const char *lookup_name;
+
+ /**
+ * If true the result contain attributes of a well known object.
+ * Since this result is manually created it may not contain all
+ * requested attributes, depending on the plug-in.
+ */
+ bool well_known_object;
+
+ /* If this is a well known object, it may not be part of any particular
+ * SSSD domain, but still may be associated with a well known domain
+ * name such as "BUILTIN", or "LOCAL AUTHORITY".
+ */
+ const char *well_known_domain;
+};
+
+/**
+ * Shallow copy of cache request result, limiting the result to a maximum
+ * numbers of records.
+ */
+struct cache_req_result *
+cache_req_copy_limited_result(TALLOC_CTX *mem_ctx,
+ struct cache_req_result *result,
+ uint32_t start,
+ uint32_t limit);
+
+/* Generic request. */
+
+struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int midpoint,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ struct cache_req_data *data);
+
+uint32_t cache_req_get_reqid(struct tevent_req *req);
+
+errno_t cache_req_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result ***_results);
+
+errno_t cache_req_single_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result **_result);
+
+/* Plug-ins. */
+
+struct tevent_req *
+cache_req_user_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *name);
+
+#define cache_req_user_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs);
+
+#define cache_req_user_by_name_attrs_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_user_by_upn_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *upn);
+
+#define cache_req_user_by_upn_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result);
+
+struct tevent_req *
+cache_req_user_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ uid_t uid);
+
+#define cache_req_user_by_id_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result);
+
+struct tevent_req *
+cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *pem_cert);
+
+#define cache_req_user_by_cert_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *name);
+
+#define cache_req_group_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_group_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ gid_t gid);
+
+#define cache_req_group_by_id_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *name);
+
+#define cache_req_initgr_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *attr,
+ const char *filter);
+
+#define cache_req_user_by_filter_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *filter);
+
+#define cache_req_group_by_filter_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_object_by_sid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *sid,
+ const char **attrs);
+
+#define cache_req_object_by_sid_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_object_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs);
+
+#define cache_req_object_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_object_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ uint32_t id,
+ const char **attrs);
+
+#define cache_req_object_by_id_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_svc_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char *protocol);
+
+#define cache_req_svc_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_svc_by_port_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ uint16_t port,
+ const char *protocol);
+
+#define cache_req_svc_by_port_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_netgroup_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name);
+
+#define cache_req_netgroup_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_ssh_host_id_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char *alias,
+ const char **attrs);
+
+#define cache_req_ssh_host_id_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_autofs_map_entries_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name);
+
+#define cache_req_autofs_map_entries_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_autofs_map_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name);
+
+#define cache_req_autofs_map_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+struct tevent_req *
+cache_req_autofs_entry_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *mapname,
+ const char *entryname);
+
+#define cache_req_autofs_entry_by_name_recv(mem_ctx, req, _result) \
+ cache_req_single_domain_recv(mem_ctx, req, _result)
+
+#endif /* _CACHE_REQ_H_ */
diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c
new file mode 100644
index 0000000..c506de5
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_data.c
@@ -0,0 +1,519 @@
+/*
+ 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 "db/sysdb.h"
+#include "responder/common/cache_req/cache_req_private.h"
+
+static const char **
+cache_req_data_create_attrs(TALLOC_CTX *mem_ctx,
+ const char **requested)
+{
+ static const char *defattrs[] = { SYSDB_DEFAULT_ATTRS, SYSDB_NAME,
+ OVERRIDE_PREFIX SYSDB_NAME,
+ SYSDB_DEFAULT_OVERRIDE_NAME };
+ static size_t defnum = sizeof(defattrs) / sizeof(defattrs[0]);
+ const char **attrs;
+ size_t reqnum;
+ size_t total;
+ size_t i;
+
+ for (reqnum = 0; requested[reqnum] != NULL; reqnum++);
+
+ total = defnum + reqnum;
+
+ /* We always want to get default attributes. */
+ attrs = talloc_zero_array(mem_ctx, const char *, total + 1);
+ if (attrs == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; i < reqnum; i++) {
+ attrs[i] = talloc_strdup(attrs, requested[i]);
+ if (attrs[i] == NULL) {
+ talloc_free(attrs);
+ return NULL;
+ }
+ }
+
+ for (/* continue */; i < total; i++) {
+ attrs[i] = talloc_strdup(attrs, defattrs[i - reqnum]);
+ if (attrs[i] == NULL) {
+ talloc_free(attrs);
+ return NULL;
+ }
+ }
+
+ return attrs;
+}
+
+static struct cache_req_data *
+cache_req_data_create(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const struct cache_req_data *input)
+{
+ struct cache_req_data *data;
+ errno_t ret;
+
+ data = talloc_zero(mem_ctx, struct cache_req_data);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+ return NULL;
+ }
+
+ data->type = type;
+ data->svc.name = &data->name;
+
+ switch (type) {
+ case CACHE_REQ_USER_BY_FILTER:
+ if (input->name.attr == NULL) {
+ data->name.attr = NULL;
+ } else {
+ data->name.attr = talloc_strdup(data, input->name.attr);
+ if (data->name.attr == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ /* Fallthrough */
+ case CACHE_REQ_USER_BY_NAME:
+ case CACHE_REQ_USER_BY_UPN:
+ case CACHE_REQ_GROUP_BY_NAME:
+ case CACHE_REQ_GROUP_BY_FILTER:
+ case CACHE_REQ_INITGROUPS:
+ case CACHE_REQ_INITGROUPS_BY_UPN:
+#ifdef BUILD_SUBID
+ case CACHE_REQ_SUBID_RANGES_BY_NAME:
+#endif
+ case CACHE_REQ_NETGROUP_BY_NAME:
+ case CACHE_REQ_OBJECT_BY_NAME:
+ case CACHE_REQ_AUTOFS_MAP_ENTRIES:
+ case CACHE_REQ_AUTOFS_MAP_BY_NAME:
+ case CACHE_REQ_IP_HOST_BY_NAME:
+ case CACHE_REQ_IP_NETWORK_BY_NAME:
+ if (input->name.input == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ data->name.input = talloc_strdup(data, input->name.input);
+ if (data->name.input == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case CACHE_REQ_IP_HOST_BY_ADDR:
+ case CACHE_REQ_IP_NETWORK_BY_ADDR:
+ data->addr.af = input->addr.af;
+ data->addr.len = input->addr.len;
+ data->addr.data = talloc_memdup(data, input->addr.data,
+ input->addr.len);
+ if (data->addr.data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case CACHE_REQ_USER_BY_CERT:
+ if (input->cert == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: certificate cannot be NULL!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ data->cert = talloc_strdup(data, input->cert);
+ if (data->cert == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case CACHE_REQ_USER_BY_ID:
+ case CACHE_REQ_GROUP_BY_ID:
+ case CACHE_REQ_OBJECT_BY_ID:
+ data->id = input->id;
+ break;
+ case CACHE_REQ_OBJECT_BY_SID:
+ if (input->sid == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: SID cannot be NULL!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ data->sid = talloc_strdup(data, input->sid);
+ if (data->sid == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case CACHE_REQ_ENUM_USERS:
+ case CACHE_REQ_ENUM_GROUPS:
+ case CACHE_REQ_ENUM_SVC:
+ case CACHE_REQ_ENUM_HOST:
+ case CACHE_REQ_ENUM_IP_NETWORK:
+ break;
+ case CACHE_REQ_SVC_BY_NAME:
+ if ((input->svc.name == NULL) || (input->svc.name->input == NULL)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ data->svc.name->input = talloc_strdup(data, input->svc.name->input);
+ if (data->svc.name->input == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (input->svc.protocol.name == NULL) {
+ break;
+ }
+
+ data->svc.protocol.name = talloc_strdup(data, input->svc.protocol.name);
+ if (data->svc.protocol.name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ break;
+ case CACHE_REQ_SVC_BY_PORT:
+ if (input->svc.port == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: port cannot be 0!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ data->svc.port = input->svc.port;
+
+ if (input->svc.protocol.name == NULL) {
+ break;
+ }
+
+ data->svc.protocol.name = talloc_strdup(data, input->svc.protocol.name);
+ if (data->svc.protocol.name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ break;
+ case CACHE_REQ_SSH_HOST_ID_BY_NAME:
+ if (input->name.input == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ data->name.input = talloc_strdup(data, input->name.input);
+ if (data->name.input == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (input->alias == NULL) {
+ break;
+ }
+
+ data->alias = talloc_strdup(data, input->alias);
+ if (data->alias == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case CACHE_REQ_AUTOFS_ENTRY_BY_NAME:
+ if (input->name.input == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ data->name.input = talloc_strdup(data, input->name.input);
+ if (data->name.input == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ data->autofs_entry_name = talloc_strdup(data, input->autofs_entry_name);
+ if (data->autofs_entry_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ case CACHE_REQ_SENTINEL:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid cache request type!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (input->attrs != NULL) {
+ data->attrs = cache_req_data_create_attrs(data, input->attrs);
+ if (data->attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_zfree(data);
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create cache_req data "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return NULL;
+ }
+
+ return data;
+}
+
+struct cache_req_data *
+cache_req_data_name(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name)
+{
+ struct cache_req_data input = {0};
+
+ input.name.input = name;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_name_attrs(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name,
+ const char **attrs)
+{
+ struct cache_req_data input = { 0 };
+
+ input.name.input = name;
+ input.attrs = attrs;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_attr(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *attr,
+ const char *filter)
+{
+ struct cache_req_data input = {0};
+
+ input.name.input = filter;
+ input.name.attr = attr;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_id(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ uint32_t id)
+{
+ struct cache_req_data input = {0};
+
+ input.id = id;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_id_attrs(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ uint32_t id,
+ const char **attrs)
+{
+ struct cache_req_data input = { 0 };
+
+ input.id = id;
+ input.attrs = attrs;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_cert(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *cert)
+{
+ struct cache_req_data input = {0};
+
+ input.cert = cert;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_sid(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *sid,
+ const char **attrs)
+{
+ struct cache_req_data input = {0};
+
+ input.sid = sid;
+ input.attrs = attrs;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_enum(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type)
+{
+ struct cache_req_data input = { 0 };
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_svc(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name,
+ const char *protocol,
+ uint16_t port)
+{
+ struct cache_req_data input = { 0 };
+
+ input.name.input = name;
+ input.svc.name = &input.name;
+ input.svc.protocol.name = protocol;
+ input.svc.port = port;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_ssh_host_id(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *name,
+ const char *alias,
+ const char **attrs)
+{
+ struct cache_req_data input = {0};
+
+ input.name.input = name;
+ input.alias = alias;
+ input.attrs = attrs;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_addr(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ uint32_t af,
+ uint32_t addrlen,
+ uint8_t *addr)
+{
+ struct cache_req_data input = {0};
+
+ input.addr.af = af;
+ input.addr.len = addrlen;
+ input.addr.data = addr;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_autofs_entry(TALLOC_CTX *mem_ctx,
+ enum cache_req_type type,
+ const char *mapname,
+ const char *entryname)
+{
+ struct cache_req_data input = {0};
+
+ input.name.input = mapname;
+ input.autofs_entry_name = entryname;
+
+ return cache_req_data_create(mem_ctx, type, &input);
+}
+
+void
+cache_req_data_set_bypass_cache(struct cache_req_data *data,
+ bool bypass_cache)
+{
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n");
+ return;
+ }
+
+ data->bypass_cache = bypass_cache;
+}
+
+void
+cache_req_data_set_bypass_dp(struct cache_req_data *data,
+ bool bypass_dp)
+{
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n");
+ return;
+ }
+
+ data->bypass_dp = bypass_dp;
+}
+
+void
+cache_req_data_set_requested_domains(struct cache_req_data *data,
+ char **requested_domains)
+{
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n");
+ return;
+ }
+
+ data->requested_domains = requested_domains;
+}
+
+void
+cache_req_data_set_propogate_offline_status(struct cache_req_data *data,
+ bool propogate_offline_status)
+{
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n");
+ return;
+ }
+
+ data->propogate_offline_status = propogate_offline_status;
+}
+
+void
+cache_req_data_set_hybrid_lookup(struct cache_req_data *data,
+ bool hybrid_lookup)
+{
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n");
+ return;
+ }
+
+ data->hybrid_lookup = hybrid_lookup;
+}
+
+
+enum cache_req_type
+cache_req_data_get_type(struct cache_req_data *data)
+{
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_data should never be NULL\n");
+ return CACHE_REQ_SENTINEL;
+ }
+
+ return data->type;
+}
diff --git a/src/responder/common/cache_req/cache_req_domain.c b/src/responder/common/cache_req/cache_req_domain.c
new file mode 100644
index 0000000..1f3b690
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_domain.c
@@ -0,0 +1,317 @@
+/*
+ Authors:
+ Fabiano Fidêncio <fidencio@redhat.com>
+
+ Copyright (C) 2017 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 "responder/common/cache_req/cache_req_domain.h"
+
+struct cache_req_domain *
+cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
+ const char *name)
+{
+ struct cache_req_domain *dom;
+ struct cache_req_domain *ret = NULL;
+
+ DLIST_FOR_EACH(dom, domains) {
+ if (sss_domain_get_state(dom->domain) == DOM_DISABLED) {
+ continue;
+ }
+
+ if (strcasecmp(dom->domain->name, name) == 0 ||
+ (dom->domain->flat_name != NULL &&
+ strcasecmp(dom->domain->flat_name, name) == 0)) {
+ ret = dom;
+ break;
+ }
+ }
+
+ if (ret == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown domains [%s].\n", name);
+ }
+
+ return ret;
+}
+
+errno_t
+cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
+ struct cache_req_domain *src,
+ char **requested_domains,
+ struct cache_req_domain **_dest)
+{
+ struct cache_req_domain *cr_domains = NULL;
+ struct cache_req_domain *cr_domain;
+ struct cache_req_domain *iter;
+ errno_t ret;
+
+ if (src == NULL) {
+ return EINVAL;
+ }
+
+ DLIST_FOR_EACH(iter, src) {
+ if (requested_domains != NULL
+ && !string_in_list(iter->domain->name, requested_domains,
+ false)) {
+ continue;
+ }
+
+ cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
+ if (cr_domain == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cr_domain->domain = iter->domain;
+ cr_domain->fqnames = iter->fqnames;
+
+ DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
+ }
+
+ if (cr_domains == NULL) {
+ if (requested_domains != NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "No requested domains found, "
+ "please check configuration options for typos.\n");
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to copy domains.\n");
+ }
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_dest = cr_domains;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ cache_req_domain_list_zfree(&cr_domains);
+ }
+
+ return ret;
+}
+
+void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains)
+{
+ struct cache_req_domain *p, *q, *r;
+
+ DLIST_FOR_EACH_SAFE(p, q, *cr_domains) {
+ r = p;
+ DLIST_REMOVE(*cr_domains, p);
+ talloc_zfree(r);
+ }
+
+ *cr_domains = NULL;
+}
+
+static bool
+cache_req_domain_use_fqnames(struct sss_domain_info *domain,
+ bool enforce_non_fqnames)
+{
+ struct sss_domain_info *head;
+
+ head = get_domains_head(domain);
+
+ /*
+ * In order to decide whether fully_qualified_names must be used on the
+ * lookups we have to take into consideration:
+ * - use_fully_qualified_name value of the head of the domains;
+ * (head->fqnames)
+ * - the presence of a domains' resolution order list;
+ * (non_fqnames_enforced)
+ *
+ * The relationship between those two can be described by:
+ * - head->fqnames:
+ * - true: in this case doesn't matter whether it's enforced or not,
+ * fully-qualified-names will _always_ be used
+ * - false: in this case (which is also the default case), the usage
+ * depends on it being enforced;
+ *
+ * - enforce_non_fqnames:
+ * - true: in this case, the usage of fully-qualified-names is not
+ * needed;
+ * - false: in this case, the usage of fully-qualified-names will be
+ * done accordingly to what's set for the domain itself.
+ */
+ if (head->fqnames) {
+ return true;
+ } else if (enforce_non_fqnames) {
+ return false;
+ } else {
+ return domain->fqnames;
+ }
+}
+
+static struct cache_req_domain *
+cache_req_domain_new_list_from_string_list(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ char **resolution_order)
+{
+ struct cache_req_domain *cr_domains = NULL;
+ struct cache_req_domain *cr_domain;
+ struct sss_domain_info *dom;
+ char *name;
+ int flag = SSS_GND_ALL_DOMAINS;
+ int i;
+ bool enforce_non_fqnames = false;
+ bool files_provider = false;
+ errno_t ret;
+
+ /* Firstly, in case a domains' resolution order is passed ... iterate over
+ * the list adding its domains to the flatten cache req domains' list */
+ if (resolution_order != NULL) {
+ enforce_non_fqnames = true;
+ for (i = 0; resolution_order[i] != NULL; i++) {
+ name = resolution_order[i];
+ for (dom = domains; dom; dom = get_next_domain(dom, flag)) {
+ if (strcasecmp(name, dom->name) != 0) {
+ continue;
+ }
+
+ cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
+ if (cr_domain == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ cr_domain->domain = dom;
+ cr_domain->fqnames =
+ cache_req_domain_use_fqnames(dom, enforce_non_fqnames);
+
+ /* when using the domain resolution order, using shortnames as
+ * input is allowed by default. However, we really want to use
+ * the fully qualified name as output in order to avoid
+ * conflicts whith users who have the very same name. */
+ sss_domain_info_set_output_fqnames(cr_domain->domain, true);
+
+ DLIST_ADD_END(cr_domains, cr_domain,
+ struct cache_req_domain *);
+ break;
+ }
+ }
+ }
+
+ /* Then iterate through all the other domains (and subdomains) and add them
+ * to the flatten cache req domains' list */
+ for (dom = domains; dom; dom = get_next_domain(dom, flag)) {
+ if (string_in_list(dom->name, resolution_order, false)) {
+ continue;
+ }
+
+ files_provider = is_files_provider(dom);
+
+ cr_domain = talloc_zero(mem_ctx, struct cache_req_domain);
+ if (cr_domain == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ cr_domain->domain = dom;
+ cr_domain->fqnames =
+ cache_req_domain_use_fqnames(dom, enforce_non_fqnames);
+
+ /* when using the domain resolution order, using shortnames as input
+ * is allowed by default. However, we really want to use the fully
+ * qualified name as output in order to avoid conflicts whith users
+ * who have the very same name.
+ *
+ * NOTE: we do *not* want to use fully qualified names for the
+ * files provider.*/
+ if (resolution_order != NULL) {
+ if (!files_provider) {
+ sss_domain_info_set_output_fqnames(cr_domain->domain, true);
+ }
+ }
+
+ /* The implicit files provider should always be searched firstly,
+ * doesn't matter whether the domain_resolution_order set!
+ *
+ * By doing this we avoid querying other domains for local users.
+ */
+ if (files_provider) {
+ DLIST_ADD(cr_domains, cr_domain);
+ continue;
+ }
+
+ DLIST_ADD_END(cr_domains, cr_domain, struct cache_req_domain *);
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ cache_req_domain_list_zfree(&cr_domains);
+ }
+
+ return cr_domains;
+}
+
+errno_t
+cache_req_domain_new_list_from_domain_resolution_order(
+ TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ const char *domain_resolution_order,
+ struct cache_req_domain **_cr_domains)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct cache_req_domain *cr_domains;
+ char **list = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ if (domain_resolution_order != NULL) {
+ if (strcmp(domain_resolution_order, ":") != 0) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Domain resolution order list (split by ':'): \"%s\"\n",
+ domain_resolution_order);
+
+ ret = split_on_separator(tmp_ctx, domain_resolution_order, ':',
+ true, true, &list, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "split_on_separator() failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Domain resolution order list: ':' "
+ "(do not use any specific order)\n");
+ }
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Domain resolution order list: not set\n");
+ }
+
+ cr_domains = cache_req_domain_new_list_from_string_list(mem_ctx, domains,
+ list);
+ if (cr_domains == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "cache_req_domain_new_list_from_domain_resolution_order() "
+ "failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_cr_domains = cr_domains;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/responder/common/cache_req/cache_req_domain.h b/src/responder/common/cache_req/cache_req_domain.h
new file mode 100644
index 0000000..72e4995
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_domain.h
@@ -0,0 +1,63 @@
+/*
+ Authors:
+ Fabiano Fidêncio <fidencio@redhat.com>
+
+ Copyright (C) 2017 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 _CACHE_REQ_DOMAIN_H_
+#define _CACHE_REQ_DOMAIN_H_
+
+#include "responder/common/responder.h"
+
+struct cache_req_domain {
+ struct sss_domain_info *domain;
+ bool fqnames;
+ bool locate_domain;
+
+ struct cache_req_domain *prev;
+ struct cache_req_domain *next;
+};
+
+struct cache_req_domain *
+cache_req_domain_get_domain_by_name(struct cache_req_domain *domains,
+ const char *name);
+
+/*
+ * This function may have a side effect of setting the output_fqnames' domain
+ * property when it's called.
+ *
+ * It happens as the output_fqnames' domain property must only be set depending
+ * on whether a domain resolution order is set or not, and the saner place to
+ * set it to all domains is when flattening those (thus, in this function).
+ */
+errno_t
+cache_req_domain_new_list_from_domain_resolution_order(
+ TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ const char *domain_resolution_order,
+ struct cache_req_domain **_cr_domains);
+
+errno_t
+cache_req_domain_copy_cr_domains(TALLOC_CTX *mem_ctx,
+ struct cache_req_domain *src,
+ char **requested_domains,
+ struct cache_req_domain **_dest);
+
+void cache_req_domain_list_zfree(struct cache_req_domain **cr_domains);
+
+
+#endif /* _CACHE_REQ_DOMAIN_H_ */
diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h
new file mode 100644
index 0000000..f86a020
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_plugin.h
@@ -0,0 +1,331 @@
+/*
+ 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 _CACHE_REQ_PLUGIN_H_
+#define _CACHE_REQ_PLUGIN_H_
+
+#include "responder/common/cache_req/cache_req_private.h"
+#include "sss_iface/sss_iface_async.h"
+
+enum cache_object_status {
+ CACHE_OBJECT_VALID,
+ CACHE_OBJECT_EXPIRED,
+ CACHE_OBJECT_MISSING,
+ CACHE_OBJECT_MIDPOINT
+};
+
+/**
+ * Create cache request result manually, if the searched object is well known
+ * and thus can not be found in the cache.
+ *
+ *
+ * @return EOK If it is a well known object and a result was created.
+ * @return ENOENT If it is not a well known object.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_is_well_known_result_fn)(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct cache_req_result **_result);
+
+/**
+ * Prepare domain data. Some plug-ins may require to alter lookup data
+ * per specific domain rules, such as case sensitivity, fully qualified
+ * format etc.
+ *
+ * @return EOK If everything went fine.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_prepare_domain_data_fn)(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain);
+
+/**
+ * Create an object debug name that is used in debug messages to identify
+ * this object.
+ *
+ * @return Debug name or NULL in case of an error.
+ **/
+typedef const char *
+(*cache_req_create_debug_name_fn)(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain);
+
+/**
+ * Check if an object is stored in negative cache.
+ *
+ * @return EOK If the object is not found.
+ * @return EEXIST If the object is found in negative cache.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_ncache_check_fn)(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data);
+
+/**
+ * Add an object into negative cache.
+ *
+ * @return EOK If everything went fine.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_ncache_add_fn)(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data);
+
+/**
+ * Filter the result through the negative cache.
+ *
+ * This is useful for plugins which don't use name as an input
+ * token but can be affected by filter_users and filter_groups
+ * options.
+ */
+typedef errno_t
+(*cache_req_ncache_filter_fn)(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ const char *name);
+
+/**
+ * Add an object into global negative cache.
+ *
+ * @return EOK If everything went fine.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_global_ncache_add_fn)(struct sss_nc_ctx *ncache,
+ struct cache_req_data *data);
+
+/**
+ * Lookup object in sysdb.
+ *
+ * @return EOK If the object is found.
+ * @return ENOENT If the object is not found.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_lookup_fn)(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result);
+
+/**
+ * Send Data Provider request.
+ *
+ * @return Tevent request on success.
+ * @return NULL on error.
+ */
+typedef struct tevent_req *
+(*cache_req_dp_send_fn)(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result);
+
+/**
+ * Process result of Data Provider request.
+ *
+ * Do not free subreq! It will be freed in the caller.
+ *
+ * @return True if data provider request succeeded.
+ * @return False if there was an error.
+ */
+typedef bool
+(*cache_req_dp_recv_fn)(struct tevent_req *subreq,
+ struct cache_req *cr);
+
+/**
+ * Check whether the results of the domain locator can still
+ * be considered valid or whether it is time to call the request
+ * again.
+ *
+ * @param resp_ctx The responder context.
+ * @param domain The domain to check. This should be the domain-head,
+ * because the locator works across a domain and its
+ * subdomains.
+ * @param data The cache request data that contains primarily the key
+ * to look for.
+ *
+ * @return True if the locator plugin should be ran again.
+ * @return False if the lookup should just proceed with the
+ * data that is already in the negative cache.
+ */
+typedef bool
+(*cache_req_dp_get_domain_check_fn)(struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data);
+/**
+ * Send Data Provider request to locate the domain
+ * of an entry
+ *
+ * @param resp_ctx The responder context.
+ * @param domain The domain to check. This should be the domain-head,
+ * because the locator works across a domain and its
+ * subdomains.
+ * @param data The cache request data that contains primarily the key
+ * to look for.
+ *
+ *
+ * @return Tevent request on success.
+ * @return NULL on error.
+ */
+typedef struct tevent_req *
+(*cache_req_dp_get_domain_send_fn)(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data);
+
+/**
+ * Process result of Data Provider find-domain request.
+ *
+ * Do not free subreq! It will be freed in the caller.
+ *
+ * @param mem_ctx The memory context that owns the _found_domain
+ * result parameter.
+ * @param subreq The request to finish.
+ * @param cr The cache_req being processed.
+ * @param _found_domain The domain the request account belongs to. This
+ * parameter can be NULL even on success, in that
+ * case the account was not found and no lookups are
+ * needed, all domains can be skipped in this case.
+ *
+ * @return EOK if the request did not encounter any error. In this
+ * case, the _found_domain parameter can be considered authoritative,
+ * regarless of its value
+ * @return errno on error. _found_domain should be NULL in this case.
+ */
+typedef errno_t
+(*cache_req_dp_get_domain_recv_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_req *subreq,
+ struct cache_req *cr,
+ char **_found_domain);
+
+struct cache_req_plugin {
+ /**
+ * Plugin name.
+ */
+ const char *name;
+
+ /**
+ * Expiration timestamp attribute name.
+ */
+ const char *attr_expiration;
+
+ /**
+ * Flags that are passed to get_next_domain().
+ */
+ uint32_t get_next_domain_flags;
+
+ /**
+ * True if input name should be parsed for domain.
+ */
+ bool parse_name;
+
+ /**
+ * True if default domain suffix should be ignored when parsing name.
+ */
+ bool ignore_default_domain;
+
+ /**
+ * True if we always contact data provider.
+ */
+ bool bypass_cache;
+
+ /**
+ * True if only one result is expected.
+ */
+ bool only_one_result;
+
+ /**
+ * If true, cache request will iterate over all domains on domain-less
+ * search and merge acquired results.
+ */
+ bool search_all_domains;
+
+ /**
+ * True if only domains with enumeration enabled are searched.
+ */
+ bool require_enumeration;
+
+ /**
+ * Allow missing domain part even if domain requires fully qualified name
+ * on domain less searches.
+ */
+ bool allow_missing_fqn;
+
+ /**
+ * True if this plugin can be swapped for equivalent search with UPN.
+ */
+ bool allow_switch_to_upn;
+ enum cache_req_type upn_equivalent;
+
+ /* Operations */
+ cache_req_is_well_known_result_fn is_well_known_fn;
+ cache_req_prepare_domain_data_fn prepare_domain_data_fn;
+ cache_req_create_debug_name_fn create_debug_name_fn;
+ cache_req_global_ncache_add_fn global_ncache_add_fn;
+ cache_req_ncache_check_fn ncache_check_fn;
+ cache_req_ncache_add_fn ncache_add_fn;
+ cache_req_ncache_filter_fn ncache_filter_fn;
+ cache_req_lookup_fn lookup_fn;
+ cache_req_dp_send_fn dp_send_fn;
+ cache_req_dp_recv_fn dp_recv_fn;
+ cache_req_dp_get_domain_check_fn dp_get_domain_check_fn;
+ cache_req_dp_get_domain_send_fn dp_get_domain_send_fn;
+ cache_req_dp_get_domain_recv_fn dp_get_domain_recv_fn;
+};
+
+extern const struct cache_req_plugin cache_req_user_by_name;
+extern const struct cache_req_plugin cache_req_user_by_upn;
+extern const struct cache_req_plugin cache_req_user_by_id;
+extern const struct cache_req_plugin cache_req_group_by_name;
+extern const struct cache_req_plugin cache_req_group_by_id;
+extern const struct cache_req_plugin cache_req_initgroups_by_name;
+extern const struct cache_req_plugin cache_req_initgroups_by_upn;
+#ifdef BUILD_SUBID
+extern const struct cache_req_plugin cache_req_subid_ranges_by_name;
+#endif
+extern const struct cache_req_plugin cache_req_user_by_cert;
+extern const struct cache_req_plugin cache_req_user_by_filter;
+extern const struct cache_req_plugin cache_req_group_by_filter;
+extern const struct cache_req_plugin cache_req_object_by_sid;
+extern const struct cache_req_plugin cache_req_object_by_name;
+extern const struct cache_req_plugin cache_req_object_by_id;
+extern const struct cache_req_plugin cache_req_enum_users;
+extern const struct cache_req_plugin cache_req_enum_groups;
+extern const struct cache_req_plugin cache_req_enum_svc;
+extern const struct cache_req_plugin cache_req_enum_ip_hosts;
+extern const struct cache_req_plugin cache_req_enum_ip_networks;
+extern const struct cache_req_plugin cache_req_svc_by_name;
+extern const struct cache_req_plugin cache_req_svc_by_port;
+extern const struct cache_req_plugin cache_req_netgroup_by_name;
+extern const struct cache_req_plugin cache_req_ssh_host_id_by_name;
+extern const struct cache_req_plugin cache_req_autofs_map_entries;
+extern const struct cache_req_plugin cache_req_autofs_map_by_name;
+extern const struct cache_req_plugin cache_req_autofs_entry_by_name;
+extern const struct cache_req_plugin cache_req_ip_host_by_name;
+extern const struct cache_req_plugin cache_req_ip_host_by_addr;
+extern const struct cache_req_plugin cache_req_ip_network_by_name;
+extern const struct cache_req_plugin cache_req_ip_network_by_addr;
+
+#endif /* _CACHE_REQ_PLUGIN_H_ */
diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
new file mode 100644
index 0000000..22f197b
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_private.h
@@ -0,0 +1,227 @@
+/*
+ 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 _CACHE_REQ_PRIVATE_H_
+#define _CACHE_REQ_PRIVATE_H_
+
+#include <stdint.h>
+
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+
+#define CACHE_REQ_DEBUG(level, cr, fmt, ...) \
+ DEBUG(level, "CR #%u: " fmt, (cr)->reqid, ##__VA_ARGS__)
+
+/* Tracing message, changing this can break log parsing tools */
+#define SSS_REQ_TRACE_CID_CR(level, cr, fmt, ...) \
+ CACHE_REQ_DEBUG(level, cr, "REQ_TRACE: " fmt, ##__VA_ARGS__)
+
+struct cache_req {
+ /* Provided input. */
+ struct cache_req_data *data;
+
+ const struct cache_req_plugin *plugin;
+ struct resp_ctx *rctx;
+ struct sss_nc_ctx *ncache;
+ int midpoint;
+
+ /* Domain related information. */
+ struct sss_domain_info *domain;
+
+ /* wanted cache behavior */
+ enum cache_req_behavior cache_behavior;
+
+ /* Only contact domains with this type */
+ enum cache_req_dom_type req_dom_type;
+
+ /* Debug information */
+ uint32_t reqid;
+ const char *reqname;
+ const char *debugobj;
+
+ /* Time when the request started. Useful for by-filter lookups */
+ time_t req_start;
+};
+
+/**
+ * Structure to hold the information the user passed as parameter
+ * and some strings after processing this information.
+ */
+struct cache_req_parsed_name {
+ const char *input; /* Original input. */
+ const char *name; /* Parsed name or UPN. */
+ const char *lookup; /* Converted per domain rules. */
+ const char *attr; /* Attribute name when looking for an attribute */
+};
+
+/**
+ * Structure to hold the input strings that cannot contain domain
+ * part but are transferred per each domain's case sensitivity.
+ */
+struct cache_req_cased_name {
+ const char *name; /* Parsed name or UPN. */
+ const char *lookup; /* Converted per domain rules. */
+};
+
+/* Input data. */
+struct cache_req_data {
+ enum cache_req_type type;
+ struct cache_req_parsed_name name;
+ uint32_t id;
+ const char *cert;
+ const char *sid;
+ const char *alias;
+ const char **attrs;
+ const char *autofs_entry_name;
+
+ struct {
+ struct cache_req_parsed_name *name;
+ struct cache_req_cased_name protocol;
+ uint16_t port;
+ } svc;
+
+ struct {
+ uint32_t af;
+ uint32_t len;
+ uint8_t *data;
+ } addr;
+
+ bool bypass_cache;
+ bool bypass_dp;
+
+ /* if set, only search in the listed domains */
+ char **requested_domains;
+
+ /* if set, ERR_OFFLINE is returned if data provider is offline */
+ bool propogate_offline_status;
+
+ /* if set, only domains with MPG_HYBRID are searched */
+ bool hybrid_lookup;
+};
+
+struct tevent_req *
+cache_req_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr,
+ bool first_iteration,
+ bool cache_only_override);
+
+errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ldb_result **_result,
+ bool *_dp_success);
+
+struct tevent_req *cache_req_locate_domain_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr);
+errno_t cache_req_locate_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_found_domain);
+
+struct tevent_req *
+cache_req_steal_data_and_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ struct cache_req_data *data);
+
+void cache_req_search_ncache_add_to_domain(struct cache_req *cr,
+ struct sss_domain_info *domain);
+
+errno_t
+cache_req_add_result(TALLOC_CTX *mem_ctx,
+ struct cache_req_result *new_result,
+ struct cache_req_result ***_results,
+ size_t *_num_results);
+
+struct cache_req_result *
+cache_req_create_result(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result *ldb_result,
+ const char *lookup_name,
+ const char *well_known_domain);
+
+errno_t
+cache_req_create_and_add_result(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct sss_domain_info *domain,
+ struct ldb_result *ldb_result,
+ const char *name,
+ struct cache_req_result ***_results,
+ size_t *_num_results);
+
+struct ldb_result *
+cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx,
+ struct ldb_message **ldb_msgs,
+ size_t ldb_msg_count);
+
+struct ldb_result *
+cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx,
+ struct ldb_message *ldb_msg);
+
+struct cache_req_result *
+cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *ldb_msg,
+ const char *lookup_name,
+ const char *well_known_domain);
+
+struct tevent_req *
+cache_req_sr_overlay_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr,
+ struct cache_req_result **results,
+ size_t num_results);
+
+errno_t
+cache_req_sr_overlay_recv(struct tevent_req *req);
+
+/* Plug-in common. */
+
+struct cache_req_result *
+cache_req_well_known_sid_result(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ const char *domname,
+ const char *sid,
+ const char *name);
+
+bool
+cache_req_common_process_dp_reply(struct cache_req *cr,
+ errno_t ret,
+ uint16_t err_maj,
+ uint32_t err_min,
+ const char *err_msg);
+
+bool
+cache_req_common_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr);
+
+errno_t
+cache_req_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *subreq,
+ struct cache_req *cr,
+ char **_domain);
+
+errno_t cache_req_idminmax_check(struct cache_req_data *data,
+ struct sss_domain_info *domain);
+#endif /* _CACHE_REQ_PRIVATE_H_ */
diff --git a/src/responder/common/cache_req/cache_req_result.c b/src/responder/common/cache_req/cache_req_result.c
new file mode 100644
index 0000000..c1a3732
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_result.c
@@ -0,0 +1,274 @@
+/*
+ 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 <ldb.h>
+#include <talloc.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "responder/common/cache_req/cache_req_private.h"
+
+errno_t
+cache_req_add_result(TALLOC_CTX *mem_ctx,
+ struct cache_req_result *new_result,
+ struct cache_req_result ***_results,
+ size_t *_num_results)
+{
+ struct cache_req_result **results = *_results;
+ size_t idx;
+ size_t count;
+
+ /* Make space for new results. */
+ idx = *_num_results;
+ count = *_num_results + 1;
+
+ results = talloc_realloc(mem_ctx, results, struct cache_req_result *,
+ count + 1);
+ if (results == NULL) {
+ return ENOMEM;
+ }
+
+ results[idx] = talloc_steal(results, new_result);
+ results[idx + 1] = NULL;
+
+ *_results = results;
+ *_num_results = count;
+
+ return EOK;
+}
+
+struct cache_req_result *
+cache_req_create_result(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result *ldb_result,
+ const char *lookup_name,
+ const char *well_known_domain)
+{
+ struct cache_req_result *result;
+
+ result = talloc_zero(mem_ctx, struct cache_req_result);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ result->domain = domain;
+ result->ldb_result = talloc_steal(result, ldb_result);
+ result->count = ldb_result != NULL ? ldb_result->count : 0;
+ result->msgs = ldb_result != NULL ? ldb_result->msgs : NULL;
+
+ if (lookup_name != NULL) {
+ result->lookup_name = talloc_strdup(result, lookup_name);
+ if (result->lookup_name == NULL) {
+ talloc_free(result);
+ return NULL;
+ }
+ }
+
+ if (well_known_domain != NULL) {
+ result->well_known_domain = talloc_strdup(result, well_known_domain);
+ if (result->well_known_domain == NULL) {
+ talloc_free(result);
+ return NULL;
+ }
+ }
+
+ return result;
+}
+
+errno_t
+cache_req_create_and_add_result(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct sss_domain_info *domain,
+ struct ldb_result *ldb_result,
+ const char *name,
+ struct cache_req_result ***_results,
+ size_t *_num_results)
+{
+ struct cache_req_result *item;
+ errno_t ret;
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Found %u entries in domain %s\n",
+ ldb_result->count, domain->name);
+
+ item = cache_req_create_result(mem_ctx, domain, ldb_result, name, NULL);
+ if (item == NULL) {
+ return ENOMEM;
+ }
+
+ ret = cache_req_add_result(mem_ctx, item, _results, _num_results);
+ if (ret != EOK) {
+ talloc_free(item);
+ }
+
+ return ret;
+}
+
+struct ldb_result *
+cache_req_create_ldb_result_from_msg_list(TALLOC_CTX *mem_ctx,
+ struct ldb_message **ldb_msgs,
+ size_t ldb_msg_count)
+{
+ struct ldb_result *ldb_result;
+
+ if (ldb_msgs == NULL || ldb_msgs[0] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n");
+ return NULL;
+ }
+
+ ldb_result = talloc_zero(NULL, struct ldb_result);
+ if (ldb_result == NULL) {
+ return NULL;
+ }
+
+ ldb_result->extended = NULL;
+ ldb_result->controls = NULL;
+ ldb_result->refs = NULL;
+ ldb_result->count = ldb_msg_count;
+ ldb_result->msgs = talloc_zero_array(ldb_result, struct ldb_message *,
+ ldb_msg_count + 1);
+ if (ldb_result->msgs == NULL) {
+ talloc_free(ldb_result);
+ return NULL;
+ }
+
+ for (size_t i = 0; i < ldb_msg_count; i++) {
+ ldb_result->msgs[i] = talloc_steal(ldb_result->msgs, ldb_msgs[i]);
+ }
+
+ return ldb_result;
+}
+
+struct ldb_result *
+cache_req_create_ldb_result_from_msg(TALLOC_CTX *mem_ctx,
+ struct ldb_message *ldb_msg)
+{
+ struct ldb_result *ldb_result;
+
+ if (ldb_msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n");
+ return NULL;
+ }
+
+ ldb_result = talloc_zero(NULL, struct ldb_result);
+ if (ldb_result == NULL) {
+ return NULL;
+ }
+
+ ldb_result->extended = NULL;
+ ldb_result->controls = NULL;
+ ldb_result->refs = NULL;
+ ldb_result->count = 1;
+ ldb_result->msgs = talloc_zero_array(ldb_result, struct ldb_message *, 2);
+ if (ldb_result->msgs == NULL) {
+ talloc_free(ldb_result);
+ return NULL;
+ }
+
+ ldb_result->msgs[0] = talloc_steal(ldb_result->msgs, ldb_msg);
+
+ return ldb_result;
+}
+
+struct cache_req_result *
+cache_req_create_result_from_msg(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *ldb_msg,
+ const char *lookup_name,
+ const char *well_known_domain)
+{
+ struct cache_req_result *result;
+ struct ldb_result *ldb_result;
+
+ if (ldb_msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No message set!\n");
+ return NULL;
+ }
+
+ ldb_result = cache_req_create_ldb_result_from_msg(mem_ctx, ldb_msg);
+ if (ldb_result == NULL) {
+ return NULL;
+ }
+
+ result = cache_req_create_result(mem_ctx, domain, ldb_result,
+ lookup_name, well_known_domain);
+ if (result == NULL) {
+ talloc_free(ldb_result);
+ return NULL;
+ }
+
+ return result;
+}
+
+struct cache_req_result *
+cache_req_copy_limited_result(TALLOC_CTX *mem_ctx,
+ struct cache_req_result *result,
+ uint32_t start,
+ uint32_t limit)
+{
+ struct cache_req_result *out = NULL;
+ struct ldb_result *ldb_result;
+ unsigned int left;
+ errno_t ret;
+
+ if (start >= result->count) {
+ ret = ERANGE;
+ goto done;
+ }
+
+ out = talloc_zero(mem_ctx, struct cache_req_result);
+ if (out == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ldb_result = talloc_zero(out, struct ldb_result);
+ if (ldb_result == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ left = result->count - start;
+
+ ldb_result->extended = result->ldb_result->extended;
+ ldb_result->controls = result->ldb_result->controls;
+ ldb_result->refs = result->ldb_result->refs;
+ ldb_result->msgs = &(result->ldb_result->msgs[start]);
+ ldb_result->count = left < limit ? left : limit;
+
+ out->domain = result->domain;
+ out->ldb_result = ldb_result;
+ out->lookup_name = result->lookup_name;
+ out->count = ldb_result->count;
+ out->msgs = ldb_result->msgs;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create cache request result "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+
+ talloc_free(out);
+ return NULL;
+ }
+
+ return out;
+}
diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
new file mode 100644
index 0000000..d800f47
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_search.c
@@ -0,0 +1,643 @@
+/*
+ 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 <ldb.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "responder/common/cache_req/cache_req_private.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+#include "db/sysdb.h"
+
+static errno_t cache_req_search_ncache(struct cache_req *cr)
+{
+ errno_t ret;
+
+ if (cr->plugin->ncache_check_fn == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr,
+ "This request type does not support negative cache\n");
+ return EOK;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Checking negative cache for [%s]\n",
+ cr->debugobj);
+
+ ret = cr->plugin->ncache_check_fn(cr->ncache, cr->domain, cr->data);
+ if (ret == EEXIST) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "[%s] does not exist (negative cache)\n",
+ cr->debugobj);
+ return ENOENT;
+ } else if (ret != EOK && ret != ENOENT) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Unable to check negative cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "[%s] is not present in negative cache\n",
+ cr->debugobj);
+
+ return EOK;
+}
+
+void cache_req_search_ncache_add_to_domain(struct cache_req *cr,
+ struct sss_domain_info *domain)
+{
+ errno_t ret;
+
+ if (cr->plugin->ncache_add_fn == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr,
+ "This request type does not support negative cache\n");
+ return;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Adding [%s] to negative cache\n",
+ cr->debugobj);
+
+ ret = cr->plugin->ncache_add_fn(cr->ncache, domain, cr->data);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
+ "Cannot set negative cache for [%s] [%d]: %s\n",
+ cr->debugobj, ret, sss_strerror(ret));
+ /* not fatal */
+ }
+
+ return;
+}
+
+static void cache_req_search_ncache_add(struct cache_req *cr)
+{
+ return cache_req_search_ncache_add_to_domain(cr, cr->domain);
+}
+
+static errno_t cache_req_search_ncache_filter(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct ldb_result **_result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *filtered_result;
+ struct ldb_message **msgs;
+ size_t msg_count;
+ const char *name;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ if (cr->plugin->ncache_filter_fn == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "This request type does not support filtering "
+ "result by negative cache\n");
+
+ ret = EOK;
+ goto done;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Filtering out results by negative cache\n");
+
+ msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, (*_result)->count);
+ msg_count = 0;
+
+ for (size_t i = 0; i < (*_result)->count; i++) {
+ name = sss_get_name_from_msg(cr->domain, (*_result)->msgs[i]);
+ if (name == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "sss_get_name_from_msg() returned NULL, which should never "
+ "happen in this scenario!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = cr->plugin->ncache_filter_fn(cr->ncache, cr->domain, name);
+ if (ret == EEXIST) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "[%s] filtered out! (negative cache)\n",
+ name);
+ continue;
+ } else if (ret != EOK && ret != ENOENT) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Unable to check negative cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ msgs[msg_count] = talloc_steal(msgs, (*_result)->msgs[i]);
+ msg_count++;
+ }
+
+ if (msg_count == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ filtered_result = cache_req_create_ldb_result_from_msg_list(tmp_ctx, msgs,
+ msg_count);
+ if (filtered_result == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(*_result);
+ *_result = talloc_steal(mem_ctx, filtered_result);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int
+cache_req_should_be_in_cache(struct cache_req *cr,
+ struct ldb_result *result)
+{
+ id_t id = 0;
+
+ if (result == NULL || result->count != 1) {
+ /* can't decide so keep it */
+ return EOK;
+ }
+
+ id = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_UIDNUM, 0);
+ if (id && OUT_OF_ID_RANGE(id, cr->domain->id_min, cr->domain->id_max)) {
+ return ERR_ID_OUTSIDE_RANGE;
+ }
+
+ id = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_GIDNUM, 0);
+ if (id && OUT_OF_ID_RANGE(id, cr->domain->id_min, cr->domain->id_max)) {
+ return ERR_ID_OUTSIDE_RANGE;
+ }
+
+ return EOK;
+}
+
+static errno_t cache_req_search_cache(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct ldb_result **_result)
+{
+ struct ldb_result *result = NULL;
+ errno_t ret;
+
+ if (cr->plugin->lookup_fn == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Bug: No cache lookup function specified\n");
+ return ERR_INTERNAL;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Looking up [%s] in cache\n",
+ cr->debugobj);
+
+ ret = cr->plugin->lookup_fn(mem_ctx, cr, cr->data, cr->domain, &result);
+ if (ret == EOK && (result == NULL || result->count == 0)) {
+ ret = ENOENT;
+ }
+
+ if (ret == EOK) {
+ ret = cache_req_should_be_in_cache(cr, result);
+ }
+
+ switch (ret) {
+ case EOK:
+ if (cr->plugin->only_one_result && result->count > 1) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Multiple objects were found when "
+ "only one was expected!\n");
+ ret = ERR_MULTIPLE_ENTRIES;
+ goto done;
+ }
+
+ *_result = result;
+ break;
+ case ERR_ID_OUTSIDE_RANGE:
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "ID [%s] was filtered out\n",
+ cr->debugobj);
+ break;
+ case ENOENT:
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Object [%s] was not found in cache\n",
+ cr->debugobj);
+ break;
+ default:
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Unable to lookup [%s] in cache [%d]: %s\n",
+ cr->debugobj, ret, sss_strerror(ret));
+ break;
+ }
+
+done:
+ if (ret != EOK) {
+ talloc_free(result);
+ }
+
+ return ret;
+}
+
+static enum cache_object_status
+cache_req_expiration_status(struct cache_req *cr,
+ struct ldb_result *result)
+{
+ time_t expire;
+ errno_t ret;
+
+ if (result == NULL || result->count == 0 || cr->plugin->bypass_cache) {
+ return CACHE_OBJECT_MISSING;
+ }
+
+ expire = ldb_msg_find_attr_as_uint64(result->msgs[0],
+ cr->plugin->attr_expiration, 0);
+
+ ret = sss_cmd_check_cache(result->msgs[0], cr->midpoint, expire);
+ if (ret == EOK) {
+ return CACHE_OBJECT_VALID;
+ } else if (ret == EAGAIN) {
+ return CACHE_OBJECT_MIDPOINT;
+ }
+
+ return CACHE_OBJECT_EXPIRED;
+}
+
+struct cache_req_search_state {
+ /* input data */
+ struct tevent_context *ev;
+ struct resp_ctx *rctx;
+ struct cache_req *cr;
+
+ /* output data */
+ struct ldb_result *result;
+ bool dp_success;
+};
+
+static errno_t cache_req_search_dp(struct tevent_req *req,
+ enum cache_object_status status);
+static void cache_req_search_oob_done(struct tevent_req *subreq);
+static void cache_req_search_done(struct tevent_req *subreq);
+
+struct tevent_req *
+cache_req_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr,
+ bool first_iteration,
+ bool cache_only_override)
+{
+ struct cache_req_search_state *state;
+ enum cache_object_status status;
+ struct tevent_req *req;
+ bool bypass_cache = false;
+ bool bypass_dp = false;
+ bool skip_refresh = false;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct cache_req_search_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Looking up %s\n", cr->debugobj);
+
+ state->ev = ev;
+ state->cr = cr;
+
+ ret = cache_req_search_ncache(cr);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (cache_only_override) {
+ bypass_dp = true;
+ } else {
+ switch (cr->cache_behavior) {
+ case CACHE_REQ_CACHE_FIRST:
+ bypass_cache = first_iteration ? false : true;
+ bypass_dp = first_iteration ? true : false;
+ break;
+ case CACHE_REQ_BYPASS_CACHE:
+ bypass_cache = true;
+ break;
+ case CACHE_REQ_BYPASS_PROVIDER:
+ bypass_dp = true;
+ skip_refresh = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* If bypass_cache is enabled we always contact data provider before
+ * searching the cache. Thus we set expiration status to missing,
+ * which will trigger data provider request later.
+ *
+ * If disabled, we want to search the cache here to see if the
+ * object is already cached and valid or if data provider needs
+ * to be contacted.
+ */
+ state->result = NULL;
+ status = CACHE_OBJECT_MISSING;
+ if (!bypass_cache) {
+ ret = cache_req_search_cache(state, cr, &state->result);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ status = cache_req_expiration_status(cr, state->result);
+ if (status == CACHE_OBJECT_VALID) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Returning [%s] from cache\n", cr->debugobj);
+ ret = EOK;
+ goto done;
+ }
+
+ /* For the CACHE_REQ_CACHE_FIRST case, if bypass_dp is true but we
+ * found the object in this domain, we will contact the data provider
+ * anyway to refresh it so we can return it without searching the rest
+ * of the domains.
+ */
+ if (status != CACHE_OBJECT_MISSING && !skip_refresh) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Object found, but needs to be refreshed.\n");
+ bypass_dp = false;
+ } else {
+ ret = ENOENT;
+ }
+ }
+
+ if (!bypass_dp) {
+ ret = cache_req_search_dp(req, status);
+ }
+
+ if (ret != EAGAIN) {
+ goto done;
+ }
+
+ return req;
+
+done:
+ if (ret == EOK) {
+ ret = cache_req_search_ncache_filter(state, cr, &state->result);
+ }
+
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static errno_t cache_req_search_dp(struct tevent_req *req,
+ enum cache_object_status status)
+{
+ struct cache_req_search_state *state;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct cache_req_search_state);
+
+ switch (status) {
+ case CACHE_OBJECT_MIDPOINT:
+ /* Out of band update. The calling function will return the cached
+ * entry immediately. We need to use rctx so the request is not
+ * removed when state is freed. */
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Performing midpoint cache update of [%s]\n",
+ state->cr->debugobj);
+
+ subreq = state->cr->plugin->dp_send_fn(state->rctx, state->cr,
+ state->cr->data,
+ state->cr->domain,
+ state->result);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory sending out-of-band "
+ "data provider request\n");
+ /* This is non-fatal, so we'll continue here */
+ } else {
+ tevent_req_set_callback(subreq, cache_req_search_oob_done, req);
+ }
+
+ ret = EOK;
+ break;
+ case CACHE_OBJECT_EXPIRED:
+ case CACHE_OBJECT_MISSING:
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Looking up [%s] in data provider\n",
+ state->cr->debugobj);
+
+ subreq = state->cr->plugin->dp_send_fn(state->cr, state->cr,
+ state->cr->data,
+ state->cr->domain,
+ state->result);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Out of memory sending data provider request\n");
+ ret = ENOMEM;
+ break;
+ }
+
+ tevent_req_set_callback(subreq, cache_req_search_done, req);
+ ret = EAGAIN;
+ break;
+ default:
+ /* error */
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Unexpected status [%d]\n", status);
+ ret = ERR_INTERNAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void cache_req_search_oob_done(struct tevent_req *subreq)
+{
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Out of band request finished\n");
+ talloc_zfree(subreq);
+
+ return;
+}
+
+static void cache_req_search_done(struct tevent_req *subreq)
+{
+ struct cache_req_search_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_search_state);
+
+ state->dp_success = state->cr->plugin->dp_recv_fn(subreq, state->cr);
+ talloc_zfree(subreq);
+
+ /* Do not try to read from cache if the domain is inconsistent */
+ if (sss_domain_get_state(state->cr->domain) == DOM_INCONSISTENT) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Domain inconsistent, "
+ "we will not return cached data\n");
+
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* Get result from cache again. */
+ ret = cache_req_search_cache(state, state->cr, &state->result);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ /* Only store entry in negative cache if DP request succeeded
+ * because only then we know that the entry does not exist. */
+ if (state->dp_success) {
+ cache_req_search_ncache_add(state->cr);
+ }
+ }
+ goto done;
+ }
+
+ /* ret == EOK */
+ ret = cache_req_search_ncache_filter(state, state->cr, &state->result);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+ "Returning updated object [%s]\n", state->cr->debugobj);
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ldb_result **_result,
+ bool *_dp_success)
+{
+ struct cache_req_search_state *state = NULL;
+ state = tevent_req_data(req, struct cache_req_search_state);
+
+ *_dp_success = state->dp_success;
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_result = talloc_steal(mem_ctx, state->result);
+
+ return EOK;
+}
+
+struct cache_req_locate_domain_state {
+ struct cache_req *cr;
+
+ char *found_domain;
+};
+
+static void cache_req_locate_domain_done(struct tevent_req *subreq);
+
+struct tevent_req *cache_req_locate_domain_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr)
+{
+ struct cache_req_locate_domain_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ errno_t ret;
+ bool should_run;
+
+ req = tevent_req_create(mem_ctx, &state, struct cache_req_locate_domain_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+ state->cr = cr;
+
+ should_run = cr->plugin->dp_get_domain_check_fn(cr->rctx,
+ get_domains_head(cr->domain),
+ cr->data);
+ if (should_run == false) {
+ /* The request was tried too recently, don't issue a new one
+ * as its results are still valid
+ */
+ ret = ERR_GET_ACCT_DOM_CACHED;
+ goto immediate;
+ }
+
+ subreq = cr->plugin->dp_get_domain_send_fn(state,
+ cr->rctx,
+ get_domains_head(cr->domain),
+ cr->data);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, cache_req_locate_domain_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void cache_req_locate_domain_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct cache_req_locate_domain_state *state;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_locate_domain_state);
+
+ ret = state->cr->plugin->dp_get_domain_recv_fn(state,
+ subreq,
+ state->cr,
+ &state->found_domain);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t cache_req_locate_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_found_domain)
+{
+ struct cache_req_locate_domain_state *state = NULL;
+
+ state = tevent_req_data(req, struct cache_req_locate_domain_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_found_domain = talloc_steal(mem_ctx, state->found_domain);
+ return EOK;
+}
diff --git a/src/responder/common/cache_req/cache_req_sr_overlay.c b/src/responder/common/cache_req/cache_req_sr_overlay.c
new file mode 100644
index 0000000..8deb06a
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_sr_overlay.c
@@ -0,0 +1,347 @@
+/*
+ Authors:
+ Nikolai Kondrashov <Nikolai.Kondrashov@redhat.com>
+
+ Copyright (C) 2017 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 "db/sysdb.h"
+#include "responder/common/cache_req/cache_req_private.h"
+
+struct cache_req_sr_overlay_state {
+ /* Input data */
+ struct tevent_context *ev;
+ struct cache_req *cr;
+ struct cache_req_result **results;
+ size_t num_results;
+ /* Work data */
+ size_t res_idx;
+ size_t msg_idx;
+};
+
+static errno_t cache_req_sr_overlay_match_users(
+ struct cache_req_sr_overlay_state *state);
+
+static errno_t cache_req_sr_overlay_match_users(
+ struct cache_req_sr_overlay_state *state);
+
+static struct tevent_req *cache_req_sr_overlay_match_all_step_send(
+ struct cache_req_sr_overlay_state *state);
+
+static void cache_req_sr_overlay_match_all_step_done(
+ struct tevent_req *subreq);
+
+struct tevent_req *cache_req_sr_overlay_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cache_req *cr,
+ struct cache_req_result **results,
+ size_t num_results)
+{
+ errno_t ret = EOK;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct cache_req_sr_overlay_state *state;
+ struct resp_ctx *rctx = cr->rctx;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cache_req_sr_overlay_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->cr = cr;
+ state->results = results;
+ state->num_results = num_results;
+
+ /* If session recording is selective */
+ if (rctx->sr_conf.scope != SESSION_RECORDING_SCOPE_NONE) {
+ /* If it's a request for a user/users */
+ switch (cr->data->type) {
+ case CACHE_REQ_USER_BY_NAME:
+ case CACHE_REQ_USER_BY_UPN:
+ case CACHE_REQ_USER_BY_ID:
+ case CACHE_REQ_ENUM_USERS:
+ /* If we have group names to match against */
+ if ((rctx->sr_conf.groups != NULL &&
+ rctx->sr_conf.groups[0] != NULL) ||
+ (rctx->sr_conf.exclude_groups != NULL &&
+ rctx->sr_conf.exclude_groups[0] != NULL)) {
+ /* Pull and match group and user names for each user entry */
+ subreq = cache_req_sr_overlay_match_all_step_send(state);
+ if (subreq == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Failed allocating a session recording "
+ "user overlay request\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(
+ subreq, cache_req_sr_overlay_match_all_step_done, req);
+ ret = EAGAIN;
+ } else {
+ /* Only match user names for each user entry */
+ ret = cache_req_sr_overlay_match_users(state);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+done:
+ if (ret != EAGAIN) {
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t cache_req_sr_overlay_match_users(
+ struct cache_req_sr_overlay_state *state)
+{
+ struct cache_req *cr;
+ struct resp_ctx *rctx;
+ errno_t ret;
+ int lret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct cache_req_result *result;
+ struct ldb_message *msg;
+ const char *name;
+ char *output_name;
+ char **conf_user;
+ char **conf_exclude_user;
+ bool enabled;
+ char *enabled_str;
+
+ cr = state->cr;
+ rctx = cr->rctx;
+
+ /* Create per-message talloc context */
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Failed creating temporary talloc context\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* For each result */
+ for (state->res_idx = 0;
+ state->res_idx < state->num_results;
+ state->res_idx++) {
+ result = state->results[state->res_idx];
+
+ /* For each message */
+ for (state->msg_idx = 0;
+ state->msg_idx < result->count;
+ state->msg_idx++) {
+ msg = result->msgs[state->msg_idx];
+
+ /* Format output username */
+ name = sss_get_name_from_msg(result->domain, msg);
+ ret = sss_output_fqname(tmp_ctx, result->domain, name,
+ rctx->override_space,
+ &output_name);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Failed formatting output username from %s: %s\n",
+ name, sss_strerror(ret));
+ goto done;
+ }
+
+ /* For each user name in session recording config */
+ enabled = false;
+ conf_user = rctx->sr_conf.users;
+ if (rctx->sr_conf.scope == SESSION_RECORDING_SCOPE_SOME) {
+ if (conf_user != NULL) {
+ for (; *conf_user != NULL; conf_user++) {
+ /* If it matches the requested user name */
+ if (strcmp(*conf_user, output_name) == 0) {
+ enabled = true;
+ break;
+ }
+ }
+ }
+ /* For each exclude user name in session recording config */
+ } else if (rctx->sr_conf.scope == SESSION_RECORDING_SCOPE_ALL) {
+ enabled = true;
+ conf_exclude_user = rctx->sr_conf.exclude_users;
+ if (conf_exclude_user != NULL) {
+ for (; *conf_exclude_user != NULL; conf_exclude_user++) {
+ /* If it matches the requested user name */
+ if (strcmp(*conf_exclude_user, output_name) == 0) {
+ enabled = false;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Set sessionRecording attribute to enabled value */
+ ldb_msg_remove_attr(msg, SYSDB_SESSION_RECORDING);
+ enabled_str = talloc_strdup(tmp_ctx, enabled ? "TRUE" : "FALSE");
+ if (enabled_str == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Failed to allocate a %s attribute value\n",
+ SYSDB_SESSION_RECORDING);
+ ret = ENOMEM;
+ goto done;
+ }
+ lret = ldb_msg_add_string(msg, SYSDB_SESSION_RECORDING, enabled_str);
+ if (lret != LDB_SUCCESS) {
+ ret = sss_ldb_error_to_errno(lret);
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Failed adding %s attribute: %s\n",
+ SYSDB_SESSION_RECORDING, sss_strerror(ret));
+ goto done;
+ }
+ talloc_steal(msg, enabled_str);
+
+ /* Free per-message allocations */
+ talloc_free_children(tmp_ctx);
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static struct tevent_req *cache_req_sr_overlay_match_all_step_send(
+ struct cache_req_sr_overlay_state *state)
+{
+ struct cache_req *cr = state->cr;
+ struct cache_req_result *result =
+ state->results[state->res_idx];
+ const char *name;
+
+ name = ldb_msg_find_attr_as_string(result->msgs[state->msg_idx],
+ SYSDB_NAME, NULL);
+ return cache_req_initgr_by_name_send(state, state->ev, cr->rctx, cr->ncache,
+ cr->midpoint, CACHE_REQ_ANY_DOM,
+ NULL, name);
+}
+
+static void cache_req_sr_overlay_match_all_step_done(
+ struct tevent_req *subreq)
+{
+ int lret;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct tevent_req *req;
+ struct cache_req_sr_overlay_state *state;
+ struct cache_req_result *result;
+ struct ldb_message *msg;
+ const char *enabled;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct cache_req_sr_overlay_state);
+ msg = state->results[state->res_idx]->
+ msgs[state->msg_idx];
+
+ /* Create temporary allocation context */
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Failed creating temporary talloc context\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Get initgroups result */
+ ret = cache_req_initgr_by_name_recv(tmp_ctx, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Failed retrieving initgr request results: %s\n",
+ sss_strerror(ret));
+ goto done;
+ }
+
+ /* Overwrite sessionRecording attribute */
+ ldb_msg_remove_attr(msg, SYSDB_SESSION_RECORDING);
+ enabled = ldb_msg_find_attr_as_string(result->msgs[0],
+ SYSDB_SESSION_RECORDING, NULL);
+ if (enabled != NULL) {
+ char *enabled_copy;
+ enabled_copy = talloc_strdup(tmp_ctx, enabled);
+ if (enabled_copy == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Failed to allocate a copy of %s attribute\n",
+ SYSDB_SESSION_RECORDING);
+ ret = ENOMEM;
+ goto done;
+ }
+ lret = ldb_msg_add_string(msg, SYSDB_SESSION_RECORDING, enabled_copy);
+ if (lret != LDB_SUCCESS) {
+ ret = sss_ldb_error_to_errno(lret);
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Failed adding %s attribute: %s\n",
+ SYSDB_SESSION_RECORDING, sss_strerror(ret));
+ goto done;
+ }
+ talloc_steal(msg, enabled_copy);
+ }
+
+ /* Move onto next entry, if any */
+ state->msg_idx++;
+ if (state->msg_idx >=
+ state->results[state->res_idx]->count) {
+ state->res_idx++;
+ if (state->res_idx >= state->num_results) {
+ ret = EOK;
+ goto done;
+ }
+ state->msg_idx = 0;
+ }
+
+ /* Schedule next entry overlay */
+ subreq = cache_req_sr_overlay_match_all_step_send(state);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+ "Failed allocating a session recording "
+ "user overlay request\n");
+ goto done;
+ }
+ tevent_req_set_callback(subreq,
+ cache_req_sr_overlay_match_all_step_done, req);
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+ talloc_free(tmp_ctx);
+}
+
+errno_t cache_req_sr_overlay_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c b/src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c
new file mode 100644
index 0000000..b2b0a06
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_autofs_entry_by_name.c
@@ -0,0 +1,161 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2019 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_autofs.h"
+#include "util/util.h"
+#include "util/sss_chain_id.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_autofs_entry_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_asprintf(mem_ctx, "%s:%s",
+ data->name.name,
+ data->autofs_entry_name);
+}
+
+static errno_t
+cache_req_autofs_entry_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ struct ldb_message *entry;
+ struct ldb_result *result;
+ errno_t ret;
+
+ ret = sysdb_get_autofsentry(mem_ctx, domain, data->name.name,
+ data->autofs_entry_name, &entry);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ result = cache_req_create_ldb_result_from_msg(mem_ctx, entry);
+ if (result == NULL) {
+ talloc_free(entry);
+ return ENOMEM;
+ }
+
+ *_result = talloc_steal(mem_ctx, result);
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_autofs_entry_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ domain->name);
+ return NULL;
+ }
+
+ return sbus_call_dp_autofs_GetEntry_send(mem_ctx, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH,
+ 0, data->name.name,
+ data->autofs_entry_name,
+ sss_chain_id_get());
+}
+
+bool
+cache_req_autofs_entry_by_name_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ errno_t ret;
+
+ ret = sbus_call_dp_autofs_GetEntry_recv(subreq);
+
+ if (ret == ERR_MISSING_DP_TARGET || ret == ENOENT) {
+ ret = EOK;
+ }
+
+ return ret == EOK;
+}
+
+const struct cache_req_plugin cache_req_autofs_entry_by_name = {
+ .name = "Get autofs entry",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_autofs_entry_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_autofs_entry_by_name_lookup,
+ .dp_send_fn = cache_req_autofs_entry_by_name_dp_send,
+ .dp_recv_fn = cache_req_autofs_entry_by_name_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_autofs_entry_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *mapname,
+ const char *entryname)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_autofs_entry(mem_ctx, CACHE_REQ_AUTOFS_ENTRY_BY_NAME,
+ mapname, entryname);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ cache_req_data_set_propogate_offline_status(data, true);
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c b/src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c
new file mode 100644
index 0000000..23b11b1
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_autofs_map_by_name.c
@@ -0,0 +1,155 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2019 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_autofs.h"
+#include "util/util.h"
+#include "util/sss_chain_id.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_autofs_map_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.name);
+}
+
+static errno_t
+cache_req_autofs_map_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ struct ldb_message *map;
+ struct ldb_result *result;
+ errno_t ret;
+
+ ret = sysdb_get_map_byname(mem_ctx, domain, data->name.name, &map);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ result = cache_req_create_ldb_result_from_msg(mem_ctx, map);
+ if (result == NULL) {
+ talloc_free(map);
+ return ENOMEM;
+ }
+
+ *_result = talloc_steal(mem_ctx, result);
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_autofs_map_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ domain->name);
+ return NULL;
+ }
+
+ return sbus_call_dp_autofs_GetMap_send(mem_ctx, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH,
+ 0, data->name.name,
+ sss_chain_id_get());
+}
+
+bool
+cache_req_autofs_map_by_name_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ errno_t ret;
+
+ ret = sbus_call_dp_autofs_GetMap_recv(subreq);
+
+ if (ret == ERR_MISSING_DP_TARGET || ret == ENOENT) {
+ ret = EOK;
+ }
+
+ return ret == EOK;
+}
+
+const struct cache_req_plugin cache_req_autofs_map_by_name = {
+ .name = "Get autofs map",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_autofs_map_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_autofs_map_by_name_lookup,
+ .dp_send_fn = cache_req_autofs_map_by_name_dp_send,
+ .dp_recv_fn = cache_req_autofs_map_by_name_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_autofs_map_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_AUTOFS_MAP_BY_NAME, name);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ cache_req_data_set_propogate_offline_status(data, true);
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c b/src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c
new file mode 100644
index 0000000..18c08ca
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_autofs_map_entries.c
@@ -0,0 +1,187 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2019 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_autofs.h"
+#include "util/util.h"
+#include "util/sss_chain_id.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_autofs_map_entries_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.name);
+}
+
+static errno_t
+cache_req_autofs_map_entries_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *map;
+ struct ldb_message **mounts;
+ struct ldb_message **msgs;
+ struct ldb_result *result;
+ size_t count;
+ size_t i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_get_map_byname(tmp_ctx, domain, data->name.name, &map);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_autofs_entries_by_map(tmp_ctx, domain, data->name.name,
+ &count, &mounts);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ msgs = talloc_zero_array(tmp_ctx, struct ldb_message *, count + 1);
+ if (msgs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msgs[0] = talloc_steal(msgs, map);
+ for (i = 0; i < count; i++) {
+ msgs[i + 1] = talloc_steal(msgs, mounts[i]);
+ }
+
+ result = cache_req_create_ldb_result_from_msg_list(tmp_ctx, msgs, count + 1);
+ if (result == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_result = talloc_steal(mem_ctx, result);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static struct tevent_req *
+cache_req_autofs_map_entries_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ domain->name);
+ return NULL;
+ }
+
+ return sbus_call_dp_autofs_Enumerate_send(mem_ctx, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH,
+ 0, data->name.name,
+ sss_chain_id_get());
+}
+
+bool
+cache_req_autofs_map_entries_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ errno_t ret;
+
+ ret = sbus_call_dp_autofs_Enumerate_recv(subreq);
+
+ if (ret == ERR_MISSING_DP_TARGET || ret == ENOENT) {
+ ret = EOK;
+ }
+
+ return ret == EOK;
+}
+
+const struct cache_req_plugin cache_req_autofs_map_entries = {
+ .name = "Get autofs entries",
+ .attr_expiration = SYSDB_ENUM_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_autofs_map_entries_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_autofs_map_entries_lookup,
+ .dp_send_fn = cache_req_autofs_map_entries_dp_send,
+ .dp_recv_fn = cache_req_autofs_map_entries_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_autofs_map_entries_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_AUTOFS_MAP_ENTRIES, name);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ cache_req_data_set_propogate_offline_status(data, true);
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c
new file mode 100644
index 0000000..7eb0921
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_common.c
@@ -0,0 +1,192 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+errno_t cache_req_idminmax_check(struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ if (((domain->id_min != 0) && (data->id < domain->id_min)) ||
+ ((domain->id_max != 0) && (data->id > domain->id_max))) {
+ DEBUG(SSSDBG_FUNC_DATA, "id exceeds min/max boundaries\n");
+ return ERR_ID_OUTSIDE_RANGE;
+ }
+ return EOK;
+}
+
+static struct ldb_message *
+cache_req_well_known_sid_msg(TALLOC_CTX *mem_ctx,
+ const char *sid,
+ const char *name)
+{
+ struct ldb_message *msg;
+ const char *dup_sid;
+ const char *dup_name;
+ int ldberr;
+
+ msg = ldb_msg_new(NULL);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ dup_sid = talloc_strdup(msg, sid);
+ if (dup_sid == NULL) {
+ ldberr = LDB_ERR_OTHER;
+ goto done;
+ }
+
+ dup_name = talloc_strdup(msg, name);
+ if (name == NULL) {
+ ldberr = LDB_ERR_OTHER;
+ goto done;
+ }
+
+ ldberr = ldb_msg_add_string(msg, SYSDB_OBJECTCATEGORY, SYSDB_GROUP_CLASS);
+ if (ldberr != LDB_SUCCESS) {
+ goto done;
+ }
+
+ ldberr = ldb_msg_add_string(msg, SYSDB_NAME, dup_name);
+ if (ldberr != LDB_SUCCESS) {
+ goto done;
+ }
+
+ ldberr = ldb_msg_add_string(msg, SYSDB_SID_STR, dup_sid);
+ if (ldberr != LDB_SUCCESS) {
+ goto done;
+ }
+
+done:
+ if (ldberr != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+struct cache_req_result *
+cache_req_well_known_sid_result(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ const char *domname,
+ const char *sid,
+ const char *name)
+{
+ struct cache_req_result *result;
+ struct sss_domain_info *domain;
+ struct ldb_message *msg;
+
+ msg = cache_req_well_known_sid_msg(NULL, sid, name);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ if (domname != NULL) {
+ domain = find_domain_by_name(cr->rctx->domains, domname, true);
+ } else {
+ domain = NULL;
+ }
+
+ result = cache_req_create_result_from_msg(mem_ctx, domain, msg,
+ name, domname);
+ if (result == NULL) {
+ talloc_free(msg);
+ }
+
+ return result;
+}
+
+bool
+cache_req_common_process_dp_reply(struct cache_req *cr,
+ errno_t ret,
+ uint16_t err_maj,
+ uint32_t err_min,
+ const char *err_msg)
+{
+ bool bret;
+
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_IMPORTANT_INFO, cr,
+ "Could not get account info [%d]: %s\n",
+ ret, sss_strerror(ret));
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Due to an error we will return cached data\n");
+
+ bret = false;
+ goto done;
+ }
+
+ if (err_maj) {
+ CACHE_REQ_DEBUG(SSSDBG_IMPORTANT_INFO, cr,
+ "Data Provider Error: %u, %u, %s\n",
+ (unsigned int)err_maj, (unsigned int)err_min, err_msg);
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+ "Due to an error we will return cached data\n");
+
+ bret = false;
+ goto done;
+ }
+
+ bret = true;
+
+done:
+ return bret;
+}
+
+bool
+cache_req_common_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ const char *err_msg;
+ uint16_t err_maj;
+ uint32_t err_min;
+ errno_t ret;
+ bool bret;
+
+ /* Use subreq as memory context so err_msg is freed with it. */
+ ret = sss_dp_get_account_recv(subreq, subreq, &err_maj, &err_min, &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+errno_t
+cache_req_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *subreq,
+ struct cache_req *cr,
+ char **_domain)
+{
+ errno_t ret;
+
+ ret = sss_dp_get_account_domain_recv(mem_ctx, subreq, _domain);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
+ "Could not get account domain [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+ return ret;
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_groups.c b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
new file mode 100644
index 0000000..a0d1028
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_enum_groups.c
@@ -0,0 +1,93 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_enum_groups_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, "Groups enumeration");
+}
+
+static errno_t
+cache_req_enum_groups_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_enumgrent_with_views(mem_ctx, domain, _result);
+}
+
+static struct tevent_req *
+cache_req_enum_groups_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_GROUP, NULL, 0, NULL);
+}
+
+static errno_t
+cache_req_enum_groups_ncache_filter(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ return sss_ncache_check_group(ncache, domain, name);
+}
+
+const struct cache_req_plugin cache_req_enum_groups = {
+ .name = "Enumerate groups",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = true,
+ .only_one_result = false,
+ .search_all_domains = true,
+ .require_enumeration = true,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_enum_groups_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = cache_req_enum_groups_ncache_filter,
+ .lookup_fn = cache_req_enum_groups_lookup,
+ .dp_send_fn = cache_req_enum_groups_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c b/src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c
new file mode 100644
index 0000000..ae468b3
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_enum_ip_hosts.c
@@ -0,0 +1,126 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_iphosts.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_enum_host_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, "IP hosts enumeration");
+}
+
+static errno_t
+cache_req_enum_host_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_enumhostent(mem_ctx, domain, _result);
+}
+
+static struct tevent_req *
+cache_req_enum_host_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true,
+ BE_REQ_HOST, BE_FILTER_ENUM, NULL);
+}
+
+static bool
+cache_req_enum_host_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ bool bret;
+ uint16_t err_maj;
+ uint32_t err_min;
+ errno_t ret;
+ const char *err_msg;
+
+ ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min,
+ &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+const struct cache_req_plugin cache_req_enum_ip_hosts = {
+ .name = "Enumerate IP hosts",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = true,
+ .only_one_result = false,
+ .search_all_domains = true,
+ .require_enumeration = true,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_enum_host_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_enum_host_lookup,
+ .dp_send_fn = cache_req_enum_host_dp_send,
+ .dp_recv_fn = cache_req_enum_host_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_enum_ip_hosts_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_enum(mem_ctx, CACHE_REQ_ENUM_HOST);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain, data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c b/src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c
new file mode 100644
index 0000000..e03bf6a
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_enum_ip_networks.c
@@ -0,0 +1,126 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_ipnetworks.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_enum_ip_networks_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, "IP networks enumeration");
+}
+
+static errno_t
+cache_req_enum_ip_networks_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_enumnetent(mem_ctx, domain, _result);
+}
+
+static struct tevent_req *
+cache_req_enum_ip_networks_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true,
+ BE_REQ_IP_NETWORK, BE_FILTER_ENUM, NULL);
+}
+
+static bool
+cache_req_enum_ip_networks_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ bool bret;
+ uint16_t err_maj;
+ uint32_t err_min;
+ errno_t ret;
+ const char *err_msg;
+
+ ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min,
+ &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+const struct cache_req_plugin cache_req_enum_ip_networks = {
+ .name = "Enumerate IP networks",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = true,
+ .only_one_result = false,
+ .search_all_domains = true,
+ .require_enumeration = true,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_enum_ip_networks_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_enum_ip_networks_lookup,
+ .dp_send_fn = cache_req_enum_ip_networks_dp_send,
+ .dp_recv_fn = cache_req_enum_ip_networks_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_enum_ip_networks_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_enum(mem_ctx, CACHE_REQ_ENUM_IP_NETWORK);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain, data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_svc.c b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
new file mode 100644
index 0000000..282dc1c
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_enum_svc.c
@@ -0,0 +1,106 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_services.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_enum_svc_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, "Services enumeration");
+}
+
+static errno_t
+cache_req_enum_svc_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_enumservent(mem_ctx, domain, _result);
+}
+
+static struct tevent_req *
+cache_req_enum_svc_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_SERVICES, NULL, 0, NULL);
+}
+
+const struct cache_req_plugin cache_req_enum_svc = {
+ .name = "Enumerate services",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = true,
+ .only_one_result = false,
+ .search_all_domains = true,
+ .require_enumeration = true,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_enum_svc_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_enum_svc_lookup,
+ .dp_send_fn = cache_req_enum_svc_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_enum_svc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_enum(mem_ctx, CACHE_REQ_ENUM_SVC);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain, data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_enum_users.c b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
new file mode 100644
index 0000000..87da79f
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_enum_users.c
@@ -0,0 +1,93 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_enum_users_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, "Users enumeration");
+}
+
+static errno_t
+cache_req_enum_users_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_enumpwent_with_views(mem_ctx, domain, _result);
+}
+
+static struct tevent_req *
+cache_req_enum_users_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_USER, NULL, 0, NULL);
+}
+
+static errno_t
+cache_req_enum_users_ncache_filter(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ return sss_ncache_check_user(ncache, domain, name);
+}
+
+const struct cache_req_plugin cache_req_enum_users = {
+ .name = "Enumerate users",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = true,
+ .only_one_result = false,
+ .search_all_domains = true,
+ .require_enumeration = true,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_enum_users_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = cache_req_enum_users_ncache_filter,
+ .lookup_fn = cache_req_enum_users_lookup,
+ .dp_send_fn = cache_req_enum_users_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
new file mode 100644
index 0000000..1292f18
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_filter.c
@@ -0,0 +1,171 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_group_by_filter_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_group_by_filter_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_group_by_filter_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ char *recent_filter;
+ errno_t ret;
+
+ /* The "files" provider updates the record if /etc/passwd or /etc/group
+ * is touched. It does not perform any per-request update.
+ * Therefore the last update flag is not updated if no file was touched
+ * and we cannot use this optimization.
+ */
+ if (is_files_provider(domain)) {
+ recent_filter = NULL;
+ } else {
+ recent_filter = talloc_asprintf(mem_ctx, "(%s>=%lu)", SYSDB_LAST_UPDATE,
+ cr->req_start);
+ if (recent_filter == NULL) {
+ return ENOMEM;
+ }
+ }
+
+ ret = sysdb_enumgrent_filter_with_views(mem_ctx, domain, data->name.lookup,
+ recent_filter, _result);
+ talloc_free(recent_filter);
+
+ return ret;
+}
+
+static struct tevent_req *
+cache_req_group_by_filter_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_WILDCARD_GROUP,
+ cr->data->name.lookup, cr->data->id, NULL);
+}
+
+const struct cache_req_plugin cache_req_group_by_filter = {
+ .name = "Group by filter",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = true,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_group_by_filter_prepare_domain_data,
+ .create_debug_name_fn = cache_req_group_by_filter_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_group_by_filter_lookup,
+ .dp_send_fn = cache_req_group_by_filter_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_group_by_filter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *filter)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_GROUP_BY_FILTER, filter);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL,
+ 0,
+ req_dom_type, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
new file mode 100644
index 0000000..bb72ad9
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
@@ -0,0 +1,246 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_group_by_id_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_asprintf(mem_ctx, "GID:%"PRIu32"@%s", data->id, domain->name);
+}
+
+static errno_t
+cache_req_group_by_id_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ errno_t ret;
+
+ if (domain != NULL) {
+ ret = sss_ncache_check_gid(ncache, domain, data->id);
+ if (ret == EEXIST) {
+ return ret;
+ }
+ }
+
+ return sss_ncache_check_gid(ncache, NULL, data->id);
+}
+
+static errno_t
+cache_req_group_by_id_ncache_filter(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ return sss_ncache_check_group(ncache, domain, name);
+}
+
+static errno_t
+cache_req_group_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_gid(ncache, false, NULL, data->id);
+}
+
+static errno_t
+cache_req_group_by_id_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_gid(ncache, false, domain, data->id);
+}
+
+static errno_t
+cache_req_group_by_id_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ errno_t ret;
+
+ ret = cache_req_idminmax_check(data, domain);
+ if (ret != EOK) {
+ return ret;
+ }
+ return sysdb_getgrgid_with_views(mem_ctx, domain, data->id, _result);
+}
+
+static errno_t
+cache_req_group_by_id_dpreq_params(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct ldb_result *result,
+ const char **_string,
+ uint32_t *_id,
+ const char **_flag)
+{
+ uint32_t id;
+
+ *_id = cr->data->id;
+ *_string = NULL;
+ *_flag = NULL;
+
+ if (!DOM_HAS_VIEWS(cr->domain)) {
+ return EOK;
+ }
+
+ /* We must search with views. */
+ if (result == NULL || result->count == 0) {
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* If domain has views we will try to use original values instead of the
+ * overridden ones. This is a must for the LOCAL view since we can't look
+ * it up otherwise. But it is also a shortcut for non-local views where
+ * we will not fail over to the overridden value. */
+
+ id = ldb_msg_find_attr_as_uint64(result->msgs[0], SYSDB_GIDNUM, 0);
+ if (id == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0\n");
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* Now we have the original name and id. We don't have to search with
+ * views unless some error occurred. */
+ *_id = id;
+
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_group_by_id_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ const char *string;
+ const char *flag;
+ uint32_t id;
+ errno_t ret;
+
+ ret = cache_req_group_by_id_dpreq_params(mem_ctx, cr, result,
+ &string, &id, &flag);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_GROUP, string, id, flag);
+}
+
+static bool
+cache_req_group_by_id_get_domain_check(struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id);
+ if (nret == EEXIST) {
+ return false;
+ }
+
+ return true;
+}
+
+static struct tevent_req *
+cache_req_group_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id);
+ if (nret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set negative cache, this might result in performance degradation\n");
+ /* Not fatal */
+ }
+
+ return sss_dp_get_account_domain_send(mem_ctx,
+ rctx,
+ domain,
+ true, /* fast_reply */
+ SSS_DP_GROUP,
+ data->id,
+ NULL);
+}
+
+const struct cache_req_plugin cache_req_group_by_id = {
+ .name = "Group by ID",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_group_by_id_create_debug_name,
+ .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add,
+ .ncache_check_fn = cache_req_group_by_id_ncache_check,
+ .ncache_add_fn = cache_req_group_by_id_ncache_add,
+ .ncache_filter_fn = cache_req_group_by_id_ncache_filter,
+ .lookup_fn = cache_req_group_by_id_lookup,
+ .dp_send_fn = cache_req_group_by_id_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = cache_req_group_by_id_get_domain_check,
+ .dp_get_domain_send_fn = cache_req_group_by_id_get_domain_send,
+ .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv,
+};
+
+struct tevent_req *
+cache_req_group_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ gid_t gid)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_id(mem_ctx, CACHE_REQ_GROUP_BY_ID, gid);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_name.c b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
new file mode 100644
index 0000000..3be0d5e
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_name.c
@@ -0,0 +1,227 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_group_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_create_internal_fqname(tmp_ctx, name, domain->name);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_group_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_group_by_name_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_group(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_group_by_name_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_group(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_group_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_getgrnam_with_views(mem_ctx, domain, data->name.lookup,
+ _result);
+}
+
+static errno_t
+cache_req_group_by_name_dpreq_params(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct ldb_result *result,
+ const char **_string,
+ uint32_t *_id,
+ const char **_flag)
+{
+ const char *name;
+
+ *_id = 0;
+ *_string = cr->data->name.lookup;
+ *_flag = NULL;
+
+ if (!DOM_HAS_VIEWS(cr->domain)) {
+ return EOK;
+ }
+
+ /* We must search with views. */
+ if (result == NULL || result->count == 0) {
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* If domain has views we will try to use original values instead of the
+ * overridden ones. This is a must for the LOCAL view since we can't look
+ * it up otherwise. But it is also a shortcut for non-local views where
+ * we will not fail over to the overridden value. */
+
+ name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n");
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* Now we have the original name and id. We don't have to search with
+ * views unless some error occurred. */
+ *_string = talloc_steal(mem_ctx, name);
+
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_group_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ const char *string;
+ const char *flag;
+ uint32_t id;
+ errno_t ret;
+
+ ret = cache_req_group_by_name_dpreq_params(mem_ctx, cr, result,
+ &string, &id, &flag);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_GROUP, string, id, flag);
+}
+
+const struct cache_req_plugin cache_req_group_by_name = {
+ .name = "Group by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_group_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_group_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_group_by_name_ncache_check,
+ .ncache_add_fn = cache_req_group_by_name_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_group_by_name_lookup,
+ .dp_send_fn = cache_req_group_by_name_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *name)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_GROUP_BY_NAME, name);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ req_dom_type, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
new file mode 100644
index 0000000..c5bea9d
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_name.c
@@ -0,0 +1,242 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_initgroups_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_create_internal_fqname(tmp_ctx, name, domain->name);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_initgroups_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_initgroups_by_name_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_user(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_initgroups_by_name_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_user(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_initgroups_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_initgroups_with_views(mem_ctx, domain, data->name.lookup,
+ _result);
+}
+
+static errno_t
+cache_req_initgroups_by_name_dpreq_params(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct ldb_result *result,
+ const char **_string,
+ uint32_t *_id,
+ const char **_flag)
+{
+ struct ldb_result *user;
+ const char *name;
+ errno_t ret;
+
+ *_id = 0;
+ *_string = cr->data->name.lookup;
+ *_flag = NULL;
+
+ if (!DOM_HAS_VIEWS(cr->domain)) {
+ return EOK;
+ }
+
+ /* We must search with views. */
+ if (result == NULL || result->count == 0) {
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* If domain has views we will try to use original values instead of the
+ * overridden ones. This is a must for the LOCAL view since we can't look
+ * it up otherwise. But it is also a shortcut for non-local views where
+ * we will not fail over to the overridden value. */
+
+ ret = sysdb_getpwnam_with_views(NULL, cr->domain,
+ cr->data->name.lookup, &user);
+ if (ret != EOK || user == NULL || user->count != 1) {
+ /* Case where the user is not found has been already handled. If
+ * this is not OK, it is an error. */
+ CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+ "Unable to match initgroups user [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ name = ldb_msg_find_attr_as_string(user->msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n");
+ talloc_free(user);
+ return ERR_INTERNAL;
+ }
+
+ /* Now we have the original name. We don't have to search with
+ * views unless some error occurred. */
+ *_string = talloc_steal(mem_ctx, name);
+
+ talloc_free(user);
+
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_initgroups_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ const char *string;
+ const char *flag;
+ uint32_t id;
+ errno_t ret;
+
+ ret = cache_req_initgroups_by_name_dpreq_params(mem_ctx, cr, result,
+ &string, &id, &flag);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_INITGROUPS, string, id, flag);
+}
+
+const struct cache_req_plugin cache_req_initgroups_by_name = {
+ .name = "Initgroups by name",
+ .attr_expiration = SYSDB_INITGR_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = true,
+ .upn_equivalent = CACHE_REQ_INITGROUPS_BY_UPN,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_initgroups_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_initgroups_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_initgroups_by_name_ncache_check,
+ .ncache_add_fn = cache_req_initgroups_by_name_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_initgroups_by_name_lookup,
+ .dp_send_fn = cache_req_initgroups_by_name_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *name)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_INITGROUPS, name);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ req_dom_type, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
new file mode 100644
index 0000000..9bd00f3
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_initgroups_by_upn.c
@@ -0,0 +1,130 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_initgroups_by_upn_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *name;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed UPN is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ /* When looking up UPNs we don't want to reverse-replace spaces,
+ * just search whatever the user passed in. strdup the name so we
+ * can safely steal it later.
+ */
+ name = talloc_strdup(data, cr->data->name.name);
+ if (name == NULL) {
+ return ENOMEM;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ return EOK;
+}
+
+static const char *
+cache_req_initgroups_by_upn_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_initgroups_by_upn_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_upn(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_initgroups_by_upn_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_upn(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_initgroups_by_upn_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_initgroups_by_upn(mem_ctx, domain, data->name.lookup,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_initgroups_by_upn_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_INITGROUPS, cr->data->name.lookup,
+ 0, EXTRA_NAME_IS_UPN);
+}
+
+const struct cache_req_plugin cache_req_initgroups_by_upn = {
+ .name = "Initgroups by UPN",
+ .attr_expiration = SYSDB_INITGR_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_initgroups_by_upn_prepare_domain_data,
+ .create_debug_name_fn = cache_req_initgroups_by_upn_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_initgroups_by_upn_ncache_check,
+ .ncache_add_fn = cache_req_initgroups_by_upn_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_initgroups_by_upn_lookup,
+ .dp_send_fn = cache_req_initgroups_by_upn_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c
new file mode 100644
index 0000000..324d20e
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_addr.c
@@ -0,0 +1,174 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <ldb.h>
+#include <arpa/inet.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_iphosts.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_ip_host_by_addr_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ char buf[INET6_ADDRSTRLEN];
+ const char *addr;
+
+ if (data->addr.len == 0 || data->addr.data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: address is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ if (data->addr.af != AF_INET && data->addr.af != AF_INET6) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bad address family [%d]\n", data->addr.af);
+ return EAFNOSUPPORT;
+ }
+
+ addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN);
+ if (addr == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse address: %s\n",
+ strerror(errno));
+ return ERR_INTERNAL;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_strdup(data, addr);
+
+ return EOK;
+}
+
+static const char *
+cache_req_ip_host_by_addr_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *addr = NULL;
+ char buf[INET6_ADDRSTRLEN];
+
+ addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN);
+ if (addr == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse network address: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, addr);
+}
+
+static errno_t
+cache_req_ip_host_by_addr_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_gethostbyaddr(mem_ctx, domain, data->name.lookup,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_ip_host_by_addr_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true,
+ BE_REQ_HOST, BE_FILTER_ADDR,
+ data->name.lookup);
+}
+
+static bool
+cache_req_ip_host_by_addr_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ bool bret;
+ uint16_t err_maj;
+ uint32_t err_min;
+ errno_t ret;
+ const char *err_msg;
+
+ ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min,
+ &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+const struct cache_req_plugin cache_req_ip_host_by_addr = {
+ .name = "IP host by address",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_ip_host_by_addr_prepare_domain_data,
+ .create_debug_name_fn = cache_req_ip_host_by_addr_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_ip_host_by_addr_lookup,
+ .dp_send_fn = cache_req_ip_host_by_addr_dp_send,
+ .dp_recv_fn = cache_req_ip_host_by_addr_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_ip_host_by_addr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_HOST_BY_NAME,
+ name, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c
new file mode 100644
index 0000000..b5f33ee
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_ip_host_by_name.c
@@ -0,0 +1,170 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_iphosts.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_ip_host_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Host names always case insensitive */
+ name = sss_get_cased_name(tmp_ctx, data->name.name, false);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_ip_host_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *name = data->name.lookup;
+
+ return talloc_asprintf(mem_ctx, "%s@%s", name, domain->name);
+}
+
+static errno_t
+cache_req_ip_host_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_gethostbyname(mem_ctx, domain, data->name.lookup,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_ip_host_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true,
+ BE_REQ_HOST, BE_FILTER_NAME,
+ data->name.lookup);
+}
+
+static bool
+cache_req_ip_host_by_name_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ bool bret;
+ uint16_t err_maj;
+ uint32_t err_min;
+ errno_t ret;
+ const char *err_msg;
+
+ ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min,
+ &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+const struct cache_req_plugin cache_req_ip_host_by_name = {
+ .name = "IP host by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_ip_host_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_ip_host_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_ip_host_by_name_lookup,
+ .dp_send_fn = cache_req_ip_host_by_name_dp_send,
+ .dp_recv_fn = cache_req_ip_host_by_name_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_ip_host_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_HOST_BY_NAME,
+ name, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c
new file mode 100644
index 0000000..0ad7f61
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_addr.c
@@ -0,0 +1,174 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <ldb.h>
+#include <arpa/inet.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_ipnetworks.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_ip_network_by_addr_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ char buf[INET6_ADDRSTRLEN];
+ const char *addr;
+
+ if (data->addr.len == 0 || data->addr.data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: address is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ if (data->addr.af != AF_INET && data->addr.af != AF_INET6) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bad address family [%d]\n", data->addr.af);
+ return EAFNOSUPPORT;
+ }
+
+ addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN);
+ if (addr == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse address: %s\n",
+ strerror(errno));
+ return ERR_INTERNAL;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_strdup(data, addr);
+
+ return EOK;
+}
+
+static const char *
+cache_req_ip_network_by_addr_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *addr = NULL;
+ char buf[INET6_ADDRSTRLEN];
+
+ addr = inet_ntop(data->addr.af, data->addr.data, buf, INET6_ADDRSTRLEN);
+ if (addr == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse network address: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, addr);
+}
+
+static errno_t
+cache_req_ip_network_by_addr_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_getipnetworkbyaddr(mem_ctx, domain, data->name.lookup,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_ip_network_by_addr_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true,
+ BE_REQ_IP_NETWORK, BE_FILTER_ADDR,
+ data->name.lookup);
+}
+
+static bool
+cache_req_ip_network_by_addr_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ bool bret;
+ uint16_t err_maj;
+ uint32_t err_min;
+ errno_t ret;
+ const char *err_msg;
+
+ ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min,
+ &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+const struct cache_req_plugin cache_req_ip_network_by_addr = {
+ .name = "IP network by address",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_ip_network_by_addr_prepare_domain_data,
+ .create_debug_name_fn = cache_req_ip_network_by_addr_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_ip_network_by_addr_lookup,
+ .dp_send_fn = cache_req_ip_network_by_addr_dp_send,
+ .dp_recv_fn = cache_req_ip_network_by_addr_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_ip_network_by_addr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_NETWORK_BY_NAME,
+ name, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c
new file mode 100644
index 0000000..c02bc06
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_ip_network_by_name.c
@@ -0,0 +1,170 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_ipnetworks.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_ip_network_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Network names always case insensitive */
+ name = sss_get_cased_name(tmp_ctx, data->name.name, false);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_ip_network_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *name = data->name.lookup;
+
+ return talloc_asprintf(mem_ctx, "%s@%s", name, domain->name);
+}
+
+static errno_t
+cache_req_ip_network_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_getipnetworkbyname(mem_ctx, domain, data->name.lookup,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_ip_network_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_resolver_get_send(mem_ctx, cr->rctx, domain, true,
+ BE_REQ_IP_NETWORK, BE_FILTER_NAME,
+ data->name.lookup);
+}
+
+static bool
+cache_req_ip_network_by_name_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ bool bret;
+ uint16_t err_maj;
+ uint32_t err_min;
+ errno_t ret;
+ const char *err_msg;
+
+ ret = sss_dp_resolver_get_recv(subreq, subreq, &err_maj, &err_min,
+ &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+const struct cache_req_plugin cache_req_ip_network_by_name = {
+ .name = "IP network by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_ip_network_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_ip_network_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_ip_network_by_name_lookup,
+ .dp_send_fn = cache_req_ip_network_by_name_dp_send,
+ .dp_recv_fn = cache_req_ip_network_by_name_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_ip_network_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_IP_NETWORK_BY_NAME,
+ name, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
new file mode 100644
index 0000000..d370d34
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_netgroup_by_name.c
@@ -0,0 +1,160 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_netgroup_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_netgroup_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_asprintf(mem_ctx, "%s@%s", data->name.lookup, domain->name);
+}
+
+static errno_t
+cache_req_netgroup_by_name_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_netgr(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_netgroup_by_name_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_netgr(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_netgroup_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_getnetgr(mem_ctx, domain, data->name.lookup, _result);
+}
+
+static struct tevent_req *
+cache_req_netgroup_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_NETGR, cr->data->name.lookup,
+ 0, NULL);
+}
+
+const struct cache_req_plugin cache_req_netgroup_by_name = {
+ .name = "Netgroup by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_netgroup_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_netgroup_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_netgroup_by_name_ncache_check,
+ .ncache_add_fn = cache_req_netgroup_by_name_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_netgroup_by_name_lookup,
+ .dp_send_fn = cache_req_netgroup_by_name_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_netgroup_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_NETGROUP_BY_NAME, name);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
new file mode 100644
index 0000000..2bff990
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
@@ -0,0 +1,236 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_object_by_id_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_asprintf(mem_ctx, "ID:%"PRIu32"@%s", data->id, domain->name);
+}
+
+static errno_t
+cache_req_object_by_id_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ errno_t ret;
+
+ ret = sss_ncache_check_uid(ncache, domain, data->id);
+ if (ret == EEXIST) {
+ ret = sss_ncache_check_gid(ncache, domain, data->id);
+ }
+
+ return ret;
+}
+
+static errno_t
+cache_req_object_by_id_ncache_filter(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ errno_t ret;
+
+ ret = sss_ncache_check_user(ncache, domain, name);
+ if (ret == EEXIST) {
+ ret = sss_ncache_check_group(ncache, domain, name);
+ }
+
+ return ret;
+}
+
+static errno_t
+cache_req_object_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
+ struct cache_req_data *data)
+{
+ errno_t ret;
+
+ ret = sss_ncache_set_uid(ncache, false, NULL, data->id);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_ncache_set_gid(ncache, false, NULL, data->id);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t
+cache_req_object_by_id_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ errno_t ret;
+
+ ret = sss_ncache_set_uid(ncache, false, domain, data->id);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_ncache_set_gid(ncache, false, domain, data->id);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t
+cache_req_object_by_id_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ errno_t ret;
+
+ ret = cache_req_idminmax_check(data, domain);
+ if (ret != EOK) {
+ return ret;
+ }
+ return sysdb_search_object_by_id(mem_ctx, domain, data->id,
+ data->attrs, _result);
+}
+
+static struct tevent_req *
+cache_req_object_by_id_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_USER_AND_GROUP, NULL,
+ cr->data->id, NULL);
+}
+
+static bool
+cache_req_object_by_id_get_domain_check(struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id);
+ if (nret == EEXIST) {
+ nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id);
+ if (nret == EEXIST) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static struct tevent_req *
+cache_req_object_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id);
+ if (nret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set negative cache, this might result in "
+ "performance degradation\n");
+ /* Not fatal */
+ }
+
+ nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id);
+ if (nret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set negative cache, this might result in "
+ "performance degradation\n");
+ /* Not fatal */
+ }
+
+ return sss_dp_get_account_domain_send(mem_ctx,
+ rctx,
+ domain,
+ true, /* fast_reply */
+ SSS_DP_USER_AND_GROUP,
+ data->id,
+ NULL);
+}
+
+const struct cache_req_plugin cache_req_object_by_id = {
+ .name = "Object by ID",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_object_by_id_create_debug_name,
+ .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add,
+ .ncache_check_fn = cache_req_object_by_id_ncache_check,
+ .ncache_add_fn = cache_req_object_by_id_ncache_add,
+ .ncache_filter_fn = cache_req_object_by_id_ncache_filter,
+ .lookup_fn = cache_req_object_by_id_lookup,
+ .dp_send_fn = cache_req_object_by_id_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = cache_req_object_by_id_get_domain_check,
+ .dp_get_domain_send_fn = cache_req_object_by_id_get_domain_send,
+ .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv,
+};
+
+struct tevent_req *
+cache_req_object_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ uint32_t id,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_id_attrs(mem_ctx, CACHE_REQ_OBJECT_BY_ID, id, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_name.c b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
new file mode 100644
index 0000000..907f1f3
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_name.c
@@ -0,0 +1,238 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_object_by_name_well_known(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct cache_req_result **_result)
+{
+ struct cache_req_result *result;
+ const char *sid;
+ char *domname;
+ char *name;
+ errno_t ret;
+
+ ret = sss_parse_name(mem_ctx, cr->rctx->global_names,
+ data->name.input, &domname, &name);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr, "Unable to parse name "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (domname == NULL || name == NULL) {
+ CACHE_REQ_DEBUG(SSSDBG_FUNC_DATA, cr, "Unable to split [%s] in "
+ "name and domain part. Skipping detection of "
+ "well-known name.\n", data->name.input);
+ return ENOENT;
+ }
+
+ ret = name_to_well_known_sid(domname, name, &sid);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ result = cache_req_well_known_sid_result(mem_ctx, cr, domname, sid, name);
+ talloc_free(domname);
+ talloc_free(name);
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ *_result = result;
+
+ return EOK;
+}
+
+static errno_t
+cache_req_object_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_create_internal_fqname(tmp_ctx, name, domain->name);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_object_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_object_by_name_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ errno_t ret;
+
+ ret = sss_ncache_check_user(ncache, domain, data->name.lookup);
+ if (ret == EEXIST) {
+ ret = sss_ncache_check_group(ncache, domain, data->name.lookup);
+ }
+
+ return ret;
+}
+
+static errno_t
+cache_req_object_by_name_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ errno_t ret;
+
+ ret = sss_ncache_set_user(ncache, false, domain, data->name.lookup);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_ncache_set_group(ncache, false, domain, data->name.lookup);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t
+cache_req_object_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_search_object_by_name(mem_ctx, domain, data->name.lookup,
+ data->attrs, _result);
+}
+
+static struct tevent_req *
+cache_req_object_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_USER_AND_GROUP,
+ cr->data->name.lookup, 0, NULL);
+}
+
+const struct cache_req_plugin cache_req_object_by_name = {
+ .name = "Object by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = true,
+ .upn_equivalent = CACHE_REQ_USER_BY_UPN,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = cache_req_object_by_name_well_known,
+ .prepare_domain_data_fn = cache_req_object_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_object_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_object_by_name_ncache_check,
+ .ncache_add_fn = cache_req_object_by_name_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_object_by_name_lookup,
+ .dp_send_fn = cache_req_object_by_name_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_object_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_OBJECT_BY_NAME,
+ name, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
new file mode 100644
index 0000000..db731b1
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_sid.c
@@ -0,0 +1,202 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_object_by_sid_well_known(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct cache_req_result **_result)
+{
+ struct cache_req_result *result;
+ const char *domname;
+ const char *name;
+ errno_t ret;
+
+ ret = well_known_sid_to_name(data->sid, &domname, &name);
+ if (ret != EOK) {
+ CACHE_REQ_DEBUG(SSSDBG_TRACE_ALL, cr,
+ "SID [%s] is not a Well-Known SID.\n", data->sid);
+ return ret;
+ }
+
+ result = cache_req_well_known_sid_result(mem_ctx, cr, domname,
+ data->sid, name);
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ *_result = result;
+
+ return EOK;
+}
+
+static const char *
+cache_req_object_by_sid_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_asprintf(mem_ctx, "SID:%s@%s", data->sid, domain->name);
+}
+
+static errno_t
+cache_req_object_by_sid_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_sid(ncache, domain, data->sid);
+}
+
+static errno_t
+cache_req_object_by_sid_global_ncache_add(struct sss_nc_ctx *ncache,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_sid(ncache, false, NULL, data->sid);
+}
+
+static errno_t
+cache_req_object_by_sid_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_sid(ncache, false, domain, data->sid);
+}
+
+static errno_t
+cache_req_object_by_sid_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_search_object_by_sid(mem_ctx, domain, data->sid, data->attrs,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_object_by_sid_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_SECID, cr->data->sid, 0, NULL);
+}
+
+static bool
+cache_req_object_by_sid_get_domain_check(struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_check_locate_sid(rctx->ncache, domain, data->sid);
+ if (nret == EEXIST) {
+ return false;
+ }
+
+ return true;
+}
+
+static struct tevent_req *
+cache_req_object_by_sid_get_domain_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_set_locate_sid(rctx->ncache, domain, data->sid);
+ if (nret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set negative cache, this might result in "
+ "performance degradation\n");
+ /* Not fatal */
+ }
+
+ return sss_dp_get_account_domain_send(mem_ctx,
+ rctx,
+ domain,
+ true, /* fast_reply */
+ SSS_DP_SECID,
+ 0,
+ data->sid);
+}
+
+
+const struct cache_req_plugin cache_req_object_by_sid = {
+ .name = "Object by SID",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = cache_req_object_by_sid_well_known,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_object_by_sid_create_debug_name,
+ .global_ncache_add_fn = cache_req_object_by_sid_global_ncache_add,
+ .ncache_check_fn = cache_req_object_by_sid_ncache_check,
+ .ncache_add_fn = cache_req_object_by_sid_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_object_by_sid_lookup,
+ .dp_send_fn = cache_req_object_by_sid_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = cache_req_object_by_sid_get_domain_check,
+ .dp_get_domain_send_fn = cache_req_object_by_sid_get_domain_send,
+ .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv,
+};
+
+struct tevent_req *
+cache_req_object_by_sid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *sid,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_sid(mem_ctx, CACHE_REQ_OBJECT_BY_SID, sid, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c b/src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c
new file mode 100644
index 0000000..29f52f1
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_ssh_host_id_by_name.c
@@ -0,0 +1,164 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb_ssh.h"
+#include "util/util.h"
+#include "util/sss_chain_id.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_host_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.name);
+}
+
+static errno_t
+cache_req_host_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+#ifdef BUILD_SSH
+ struct ldb_result *result;
+ struct ldb_message *msg;
+ errno_t ret;
+
+ ret = sysdb_get_ssh_host(mem_ctx, domain, data->name.name,
+ data->attrs, &msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ result = cache_req_create_ldb_result_from_msg(mem_ctx, msg);
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ *_result = result;
+
+ return EOK;
+#else
+ return ERR_INTERNAL;
+#endif /* BUILD_SSH */
+}
+
+static struct tevent_req *
+cache_req_host_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ ret = sss_dp_get_domain_conn(cr->rctx, domain->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ domain->name);
+ return NULL;
+ }
+
+ return sbus_call_dp_dp_hostHandler_send(mem_ctx, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH,
+ 0, data->name.name, data->alias,
+ sss_chain_id_get());
+}
+
+static bool
+cache_req_host_by_name_dp_recv(struct tevent_req *subreq,
+ struct cache_req *cr)
+{
+ const char *err_msg;
+ dbus_uint16_t err_maj;
+ dbus_uint32_t err_min;
+ errno_t ret;
+ bool bret;
+
+ /* Use subreq as memory context so err_msg is freed with it. */
+ ret = sbus_call_dp_dp_hostHandler_recv(subreq, subreq, &err_maj,
+ &err_min, &err_msg);
+ bret = cache_req_common_process_dp_reply(cr, ret, err_maj,
+ err_min, err_msg);
+
+ return bret;
+}
+
+const struct cache_req_plugin cache_req_ssh_host_id_by_name = {
+ .name = "SSH Host ID by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = true,
+ .bypass_cache = true,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = 0,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_host_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_host_by_name_lookup,
+ .dp_send_fn = cache_req_host_by_name_dp_send,
+ .dp_recv_fn = cache_req_host_by_name_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_ssh_host_id_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char *alias,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_ssh_host_id(mem_ctx, CACHE_REQ_SSH_HOST_ID_BY_NAME,
+ name, alias, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c b/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c
new file mode 100644
index 0000000..5485271
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_subid_ranges_by_name.c
@@ -0,0 +1,143 @@
+/*
+ Copyright (C) 2021 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_subid.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_subid_ranges_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_subid_ranges_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_subid_ranges_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ struct ldb_result *result;
+ struct ldb_message *msg;
+ errno_t ret;
+
+ ret = sysdb_get_subid_ranges(mem_ctx, domain, data->name.name,
+ data->attrs, &msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ result = cache_req_create_ldb_result_from_msg(mem_ctx, msg);
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ *_result = result;
+
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_subid_ranges_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ /* Views aren't yet supported */
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_SUBID_RANGES, cr->data->name.lookup, 0, NULL);
+}
+
+const struct cache_req_plugin cache_req_subid_ranges_by_name = {
+ .name = "SubID ranges by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_subid_ranges_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_subid_ranges_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_subid_ranges_by_name_lookup,
+ .dp_send_fn = cache_req_subid_ranges_by_name_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
new file mode 100644
index 0000000..5b17051
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_name.c
@@ -0,0 +1,185 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_services.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_svc_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ const char *protocol;
+ errno_t ret;
+
+ if (data->svc.name->name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, data->svc.name->name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (data->svc.protocol.name == NULL) {
+ protocol = NULL;
+ } else {
+ protocol = sss_get_cased_name(tmp_ctx, data->svc.protocol.name,
+ domain->case_sensitive);
+ if (protocol == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ talloc_zfree(data->svc.name->lookup);
+ talloc_zfree(data->svc.protocol.lookup);
+ data->svc.name->lookup = talloc_steal(data, name);
+ data->svc.protocol.lookup = talloc_steal(data, protocol);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_svc_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *protocol = data->svc.protocol.lookup;
+ const char *name = data->svc.name->lookup;
+
+ protocol = protocol == NULL ? "<ANY>" : protocol;
+
+ return talloc_asprintf(mem_ctx, "%s %s@%s", protocol, name, domain->name);
+}
+
+static errno_t
+cache_req_svc_by_name_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_service(ncache, domain, data->svc.name->lookup,
+ data->svc.protocol.lookup);
+}
+
+static errno_t
+cache_req_svc_by_name_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_service_name(ncache, false, domain,
+ data->svc.name->lookup,
+ data->svc.protocol.lookup);
+}
+
+static errno_t
+cache_req_svc_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_getservbyname(mem_ctx, domain, data->svc.name->lookup,
+ data->svc.protocol.lookup, _result);
+}
+
+static struct tevent_req *
+cache_req_svc_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_SERVICES, cr->data->svc.name->lookup,
+ 0, cr->data->svc.protocol.lookup);
+}
+
+const struct cache_req_plugin cache_req_svc_by_name = {
+ .name = "Service by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_svc_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_svc_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_svc_by_name_ncache_check,
+ .ncache_add_fn = cache_req_svc_by_name_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_svc_by_name_lookup,
+ .dp_send_fn = cache_req_svc_by_name_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_svc_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char *protocol)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_svc(mem_ctx, CACHE_REQ_SVC_BY_NAME, name, protocol, 0);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
new file mode 100644
index 0000000..4c005df
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_svc_by_port.c
@@ -0,0 +1,159 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "db/sysdb_services.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_svc_by_port_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *protocol;
+
+ if (data->svc.protocol.name == NULL) {
+ return EOK;
+ }
+
+ protocol = sss_get_cased_name(NULL, data->svc.protocol.name,
+ domain->case_sensitive);
+ if (protocol == NULL) {
+ return ENOMEM;
+ }
+
+ talloc_zfree(data->svc.protocol.lookup);
+ data->svc.protocol.lookup = talloc_steal(data, protocol);
+
+ return EOK;
+}
+
+static const char *
+cache_req_svc_by_port_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *protocol = data->svc.protocol.lookup;
+
+ protocol = protocol == NULL ? "<ANY>" : protocol;
+
+ return talloc_asprintf(mem_ctx, "%s %u@%s", protocol,
+ data->svc.port, domain->name);
+}
+
+static errno_t
+cache_req_svc_by_port_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_service_port(ncache, domain, data->svc.port,
+ data->svc.protocol.lookup);
+}
+
+static errno_t
+cache_req_svc_by_port_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_service_port(ncache, false, domain,
+ data->svc.port,
+ data->svc.protocol.lookup);
+}
+
+static errno_t
+cache_req_svc_by_port_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_getservbyport(mem_ctx, domain, data->svc.port,
+ data->svc.protocol.lookup, _result);
+}
+
+static struct tevent_req *
+cache_req_svc_by_port_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_SERVICES, NULL, cr->data->svc.port,
+ cr->data->svc.protocol.lookup);
+}
+
+const struct cache_req_plugin cache_req_svc_by_port = {
+ .name = "Service by port",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_svc_by_port_prepare_domain_data,
+ .create_debug_name_fn = cache_req_svc_by_port_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_svc_by_port_ncache_check,
+ .ncache_add_fn = cache_req_svc_by_port_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_svc_by_port_lookup,
+ .dp_send_fn = cache_req_svc_by_port_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_svc_by_port_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ uint16_t port,
+ const char *protocol)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_svc(mem_ctx, CACHE_REQ_SVC_BY_PORT,
+ NULL, protocol, port);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
new file mode 100644
index 0000000..a2dc1fa
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_cert.c
@@ -0,0 +1,127 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_user_by_cert_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ /* Certificates might be quite long, thus we only use
+ * the last 10 characters for logging. */
+ return talloc_asprintf(mem_ctx, "CERT:%s@%s",
+ get_last_x_chars(data->cert, 10), domain->name);
+}
+
+static errno_t
+cache_req_user_by_cert_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_cert(ncache, data->cert);
+}
+
+static errno_t
+cache_req_user_by_cert_global_ncache_add(struct sss_nc_ctx *ncache,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_cert(ncache, false, data->cert);
+}
+
+static errno_t
+cache_req_user_by_cert_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ return sysdb_search_user_by_cert_with_views(mem_ctx, domain, data->cert,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_user_by_cert_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_CERT, cr->data->cert, 0, NULL);
+}
+
+const struct cache_req_plugin cache_req_user_by_cert = {
+ .name = "User by certificate",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = false,
+ .search_all_domains = true,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_user_by_cert_create_debug_name,
+ .global_ncache_add_fn = cache_req_user_by_cert_global_ncache_add,
+ .ncache_check_fn = cache_req_user_by_cert_ncache_check,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_user_by_cert_lookup,
+ .dp_send_fn = cache_req_user_by_cert_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *pem_cert)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_cert(mem_ctx, CACHE_REQ_USER_BY_CERT, pem_cert);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ req_dom_type, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
new file mode 100644
index 0000000..11ea9ec
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_filter.c
@@ -0,0 +1,176 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_user_by_filter_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_user_by_filter_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_filter_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ char *recent_filter;
+ const char *attr = (data->name.attr == NULL ? SYSDB_NAME : data->name.attr);
+ errno_t ret;
+
+ /* The "files" provider updates the record if /etc/passwd or /etc/group
+ * is touched. It does not perform any per-request update.
+ * Therefore the last update flag is not updated if no file was touched
+ * and we cannot use this optimization.
+ * Neither it is possible to use it when asking for a non-"name" attribute
+ * as it could not be present in the timestamp cache.
+ */
+ if (is_files_provider(domain) || data->name.attr != NULL) {
+ recent_filter = NULL;
+ } else {
+ recent_filter = talloc_asprintf(mem_ctx, "(%s>=%lu)", SYSDB_LAST_UPDATE,
+ cr->req_start);
+ if (recent_filter == NULL) {
+ return ENOMEM;
+ }
+ }
+
+ ret = sysdb_enumpwent_filter_with_views(mem_ctx, domain,
+ attr, data->name.lookup,
+ recent_filter, _result);
+ talloc_free(recent_filter);
+
+ return ret;
+}
+
+static struct tevent_req *
+cache_req_user_by_filter_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_WILDCARD_USER, cr->data->name.lookup,
+ cr->data->id, NULL);
+}
+
+const struct cache_req_plugin cache_req_user_by_filter = {
+ .name = "User by filter",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = true,
+ .only_one_result = false,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_user_by_filter_prepare_domain_data,
+ .create_debug_name_fn = cache_req_user_by_filter_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = NULL,
+ .ncache_add_fn = NULL,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_user_by_filter_lookup,
+ .dp_send_fn = cache_req_user_by_filter_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_user_by_filter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *attr,
+ const char *filter)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_attr(mem_ctx, CACHE_REQ_USER_BY_FILTER, attr, filter);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, NULL,
+ 0,
+ req_dom_type, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
new file mode 100644
index 0000000..fee5736
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
@@ -0,0 +1,246 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_user_by_id_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_asprintf(mem_ctx, "UID:%"PRIu32"@%s", data->id, domain->name);
+}
+
+static errno_t
+cache_req_user_by_id_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ errno_t ret;
+
+ if (domain != NULL) {
+ ret = sss_ncache_check_uid(ncache, domain, data->id);
+ if (ret == EEXIST) {
+ return ret;
+ }
+ }
+
+ return sss_ncache_check_uid(ncache, NULL, data->id);
+}
+
+static errno_t
+cache_req_user_by_id_ncache_filter(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ return sss_ncache_check_user(ncache, domain, name);
+}
+
+static errno_t
+cache_req_user_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_uid(ncache, false, NULL, data->id);
+}
+
+static errno_t
+cache_req_user_by_id_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_uid(ncache, false, domain, data->id);
+}
+
+static errno_t
+cache_req_user_by_id_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ errno_t ret;
+ ret = cache_req_idminmax_check(data, domain);
+ if (ret != EOK) {
+ return ret;
+ }
+ return sysdb_getpwuid_with_views(mem_ctx, domain, data->id, _result);
+}
+
+static errno_t
+cache_req_user_by_id_dpreq_params(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct ldb_result *result,
+ const char **_string,
+ uint32_t *_id,
+ const char **_flag)
+{
+ uint32_t id;
+
+ *_id = cr->data->id;
+ *_string = NULL;
+ *_flag = NULL;
+
+ if (!DOM_HAS_VIEWS(cr->domain)) {
+ return EOK;
+ }
+
+ /* We must search with views. */
+ if (result == NULL || result->count == 0) {
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* If domain has views we will try to use original values instead of the
+ * overridden ones. This is a must for the LOCAL view since we can't look
+ * it up otherwise. But it is also a shortcut for non-local views where
+ * we will not fail over to the overridden value. */
+
+ id = ldb_msg_find_attr_as_uint64(result->msgs[0], SYSDB_UIDNUM, 0);
+ if (id == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0\n");
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* Now we have the original name and id. We don't have to search with
+ * views unless some error occurred. */
+ *_id = id;
+
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_user_by_id_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ const char *string;
+ const char *flag;
+ uint32_t id;
+ errno_t ret;
+
+ ret = cache_req_user_by_id_dpreq_params(mem_ctx, cr, result,
+ &string, &id, &flag);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_USER, string, id, flag);
+}
+
+static bool
+cache_req_user_by_id_get_domain_check(struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id);
+ if (nret == EEXIST) {
+ return false;
+ }
+
+ return true;
+}
+
+static struct tevent_req *
+cache_req_user_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ int nret;
+
+ nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id);
+ if (nret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot set negative cache, this might result in "
+ "performance degradation\n");
+ /* Not fatal */
+ }
+
+ return sss_dp_get_account_domain_send(mem_ctx,
+ rctx,
+ domain,
+ true, /* fast_reply */
+ SSS_DP_USER,
+ data->id,
+ NULL);
+}
+
+const struct cache_req_plugin cache_req_user_by_id = {
+ .name = "User by ID",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = NULL,
+ .create_debug_name_fn = cache_req_user_by_id_create_debug_name,
+ .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add,
+ .ncache_check_fn = cache_req_user_by_id_ncache_check,
+ .ncache_add_fn = cache_req_user_by_id_ncache_add,
+ .ncache_filter_fn = cache_req_user_by_id_ncache_filter,
+ .lookup_fn = cache_req_user_by_id_lookup,
+ .dp_send_fn = cache_req_user_by_id_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = cache_req_user_by_id_get_domain_check,
+ .dp_get_domain_send_fn = cache_req_user_by_id_get_domain_send,
+ .dp_get_domain_recv_fn = cache_req_common_get_acct_domain_recv,
+};
+
+struct tevent_req *
+cache_req_user_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ uid_t uid)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_id(mem_ctx, CACHE_REQ_USER_BY_ID, uid);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
new file mode 100644
index 0000000..d24a222
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
@@ -0,0 +1,256 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_user_by_name_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+ domain->case_sensitive);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = sss_create_internal_fqname(tmp_ctx, name, domain->name);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *
+cache_req_user_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_name_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_user(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_name_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_user(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_name_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ if (data->attrs == NULL) {
+ return sysdb_getpwnam_with_views(mem_ctx, domain, data->name.lookup,
+ _result);
+ }
+
+ return sysdb_get_user_attr_with_views(mem_ctx, domain, data->name.lookup,
+ data->attrs, _result);
+}
+
+static errno_t
+cache_req_user_by_name_dpreq_params(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct ldb_result *result,
+ const char **_string,
+ uint32_t *_id,
+ const char **_flag)
+{
+ const char *name;
+
+ *_id = 0;
+ *_string = cr->data->name.lookup;
+ *_flag = NULL;
+
+ if (!DOM_HAS_VIEWS(cr->domain)) {
+ return EOK;
+ }
+
+ /* We must search with views. */
+ if (result == NULL || result->count == 0) {
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* If domain has views we will try to use original values instead of the
+ * overridden ones. This is a must for the LOCAL view since we can't look
+ * it up otherwise. But it is also a shortcut for non-local views where
+ * we will not fail over to the overridden value. */
+
+ name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n");
+ *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+ return EOK;
+ }
+
+ /* Now we have the original name and id. We don't have to search with
+ * views unless some error occurred. */
+ *_string = talloc_steal(mem_ctx, name);
+
+ return EOK;
+}
+
+static struct tevent_req *
+cache_req_user_by_name_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ const char *string;
+ const char *flag;
+ uint32_t id;
+ errno_t ret;
+
+ ret = cache_req_user_by_name_dpreq_params(mem_ctx, cr, result,
+ &string, &id, &flag);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_USER, string, id, flag);
+}
+
+const struct cache_req_plugin cache_req_user_by_name = {
+ .name = "User by name",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = true,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = false,
+ .allow_switch_to_upn = true,
+ .upn_equivalent = CACHE_REQ_USER_BY_UPN,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_user_by_name_prepare_domain_data,
+ .create_debug_name_fn = cache_req_user_by_name_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_user_by_name_ncache_check,
+ .ncache_add_fn = cache_req_user_by_name_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_user_by_name_lookup,
+ .dp_send_fn = cache_req_user_by_name_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_user_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *name)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_USER_BY_NAME, name);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ req_dom_type, domain,
+ data);
+}
+
+struct tevent_req *
+cache_req_user_by_name_attrs_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ const char *domain,
+ const char *name,
+ const char **attrs)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name_attrs(mem_ctx, CACHE_REQ_USER_BY_NAME,
+ name, attrs);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, domain,
+ data);
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
new file mode 100644
index 0000000..037994c
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
@@ -0,0 +1,158 @@
+/*
+ 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 <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_user_by_upn_prepare_domain_data(struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ const char *name;
+
+ if (cr->data->name.name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed UPN is NULL?\n");
+ return ERR_INTERNAL;
+ }
+
+ /* When looking up UPNs we don't want to reverse-replace spaces,
+ * just search whatever the user passed in. strdup the name so we
+ * can safely steal it later.
+ */
+ name = talloc_strdup(data, cr->data->name.name);
+ if (name == NULL) {
+ return ENOMEM;
+ }
+
+ talloc_zfree(data->name.lookup);
+ data->name.lookup = talloc_steal(data, name);
+
+ return EOK;
+}
+
+static const char *
+cache_req_user_by_upn_create_debug_name(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain)
+{
+ return talloc_strdup(mem_ctx, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_upn_ncache_check(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_check_upn(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_upn_ncache_add(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ struct cache_req_data *data)
+{
+ return sss_ncache_set_upn(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_upn_lookup(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result **_result)
+{
+ if (data->attrs == NULL) {
+ return sysdb_getpwupn(mem_ctx, domain, true, data->name.lookup, _result);
+ }
+
+ return sysdb_search_user_by_upn_res(mem_ctx, domain, true,
+ data->name.lookup, data->attrs,
+ _result);
+}
+
+static struct tevent_req *
+cache_req_user_by_upn_dp_send(TALLOC_CTX *mem_ctx,
+ struct cache_req *cr,
+ struct cache_req_data *data,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ return sss_dp_get_account_send(mem_ctx, cr->rctx, domain, true,
+ SSS_DP_USER, cr->data->name.lookup,
+ 0, EXTRA_NAME_IS_UPN);
+}
+
+const struct cache_req_plugin cache_req_user_by_upn = {
+ .name = "User by UPN",
+ .attr_expiration = SYSDB_CACHE_EXPIRE,
+ .parse_name = false,
+ .ignore_default_domain = false,
+ .bypass_cache = false,
+ .only_one_result = true,
+ .search_all_domains = false,
+ .require_enumeration = false,
+ .allow_missing_fqn = true,
+ .allow_switch_to_upn = false,
+ .upn_equivalent = CACHE_REQ_SENTINEL,
+ .get_next_domain_flags = SSS_GND_DESCEND,
+
+ .is_well_known_fn = NULL,
+ .prepare_domain_data_fn = cache_req_user_by_upn_prepare_domain_data,
+ .create_debug_name_fn = cache_req_user_by_upn_create_debug_name,
+ .global_ncache_add_fn = NULL,
+ .ncache_check_fn = cache_req_user_by_upn_ncache_check,
+ .ncache_add_fn = cache_req_user_by_upn_ncache_add,
+ .ncache_filter_fn = NULL,
+ .lookup_fn = cache_req_user_by_upn_lookup,
+ .dp_send_fn = cache_req_user_by_upn_dp_send,
+ .dp_recv_fn = cache_req_common_dp_recv,
+ .dp_get_domain_check_fn = NULL,
+ .dp_get_domain_send_fn = NULL,
+ .dp_get_domain_recv_fn = NULL,
+};
+
+struct tevent_req *
+cache_req_user_by_upn_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ int cache_refresh_percent,
+ enum cache_req_dom_type req_dom_type,
+ const char *domain,
+ const char *upn)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(mem_ctx, CACHE_REQ_USER_BY_UPN, upn);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return cache_req_steal_data_and_send(mem_ctx, ev, rctx, ncache,
+ cache_refresh_percent,
+ req_dom_type, domain,
+ data);
+}
diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c
new file mode 100644
index 0000000..9ed0196
--- /dev/null
+++ b/src/responder/common/negcache.c
@@ -0,0 +1,1417 @@
+/*
+ SSSD
+
+ NSS Responder
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <fcntl.h>
+#include <time.h>
+#include "tdb.h"
+#include "util/util.h"
+#include "util/nss_dl_load.h"
+#include "confdb/confdb.h"
+#include "responder/common/negcache_files.h"
+#include "responder/common/responder.h"
+#include "responder/common/negcache.h"
+
+
+#define NC_ENTRY_PREFIX "NCE/"
+#define NC_USER_PREFIX NC_ENTRY_PREFIX"USER"
+#define NC_GROUP_PREFIX NC_ENTRY_PREFIX"GROUP"
+#define NC_NETGROUP_PREFIX NC_ENTRY_PREFIX"NETGR"
+#define NC_SERVICE_PREFIX NC_ENTRY_PREFIX"SERVICE"
+#define NC_UID_PREFIX NC_ENTRY_PREFIX"UID"
+#define NC_GID_PREFIX NC_ENTRY_PREFIX"GID"
+#define NC_SID_PREFIX NC_ENTRY_PREFIX"SID"
+#define NC_CERT_PREFIX NC_ENTRY_PREFIX"CERT"
+#define NC_DOMAIN_ACCT_LOCATE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE"
+#define NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE_TYPE"
+
+struct sss_nc_ctx {
+ struct tdb_context *tdb;
+ uint32_t timeout;
+ uint32_t local_timeout;
+ struct sss_nss_ops ops;
+};
+
+typedef int (*ncache_set_byname_fn_t)(struct sss_nc_ctx *, bool,
+ const char *, const char *);
+
+static int sss_ncache_set_ent(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name,
+ ncache_set_byname_fn_t setter);
+
+static int string_to_tdb_data(char *str, TDB_DATA *ret)
+{
+ if (!str || !ret) return EINVAL;
+
+ ret->dptr = (uint8_t *)str;
+ ret->dsize = strlen(str)+1;
+
+ return EOK;
+}
+
+static errno_t ncache_load_nss_symbols(struct sss_nss_ops *ops)
+{
+ errno_t ret;
+ struct sss_nss_symbols syms[] = {
+ {(void*)&ops->getpwnam_r, true, "getpwnam_r" },
+ {(void*)&ops->getpwuid_r, true, "getpwuid_r" },
+ {(void*)&ops->getgrnam_r, true, "getgrnam_r" },
+ {(void*)&ops->getgrgid_r, true, "getgrgid_r" }
+ };
+ size_t nsyms = sizeof(syms) / sizeof(struct sss_nss_symbols);
+
+ ret = sss_load_nss_symbols(ops, "files", syms, nsyms);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+int sss_ncache_init(TALLOC_CTX *memctx, uint32_t timeout,
+ uint32_t local_timeout, struct sss_nc_ctx **_ctx)
+{
+ errno_t ret;
+ struct sss_nc_ctx *ctx;
+
+ ctx = talloc_zero(memctx, struct sss_nc_ctx);
+ if (!ctx) return ENOMEM;
+
+ ret = ncache_load_nss_symbols(&ctx->ops);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load NSS symbols [%d]: %s\n",
+ ret, sss_strerror(ret));
+ talloc_free(ctx);
+ return ret;
+ }
+
+ errno = 0;
+ /* open a memory only tdb with default hash size */
+ ctx->tdb = tdb_open("memcache", 0, TDB_INTERNAL, O_RDWR|O_CREAT, 0);
+ if (!ctx->tdb) return errno;
+
+ ctx->timeout = timeout;
+ ctx->local_timeout = local_timeout;
+
+ *_ctx = ctx;
+ return EOK;
+};
+
+uint32_t sss_ncache_get_timeout(struct sss_nc_ctx *ctx)
+{
+ return ctx->timeout;
+}
+
+static int sss_ncache_check_str(struct sss_nc_ctx *ctx, char *str)
+{
+ TDB_DATA key;
+ TDB_DATA data;
+ unsigned long long int timestamp;
+ bool expired = false;
+ char *ep;
+ int ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Checking negative cache for [%s]\n", str);
+
+ data.dptr = NULL;
+
+ ret = string_to_tdb_data(str, &key);
+ if (ret != EOK) goto done;
+
+ data = tdb_fetch(ctx->tdb, key);
+
+ if (!data.dptr) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ errno = 0;
+ timestamp = strtoull((const char *)data.dptr, &ep, 10);
+ if (errno != 0 || *ep != '\0') {
+ /* Malformed entry, remove it and return no entry */
+ expired = true;
+ goto done;
+ }
+
+ if (timestamp == 0) {
+ /* a 0 timestamp means this is a permanent entry */
+ ret = EEXIST;
+ goto done;
+ }
+
+ if (timestamp >= time(NULL)) {
+ /* still valid */
+ ret = EEXIST;
+ goto done;
+ }
+
+ expired = true;
+
+done:
+ if (expired) {
+ /* expired, remove and return no entry */
+ tdb_delete(ctx->tdb, key);
+ ret = ENOENT;
+ }
+
+ free(data.dptr);
+ return ret;
+}
+
+static int sss_ncache_set_str(struct sss_nc_ctx *ctx, char *str,
+ bool permanent, bool use_local_negative)
+{
+ TDB_DATA key;
+ TDB_DATA data;
+ char *timest;
+ unsigned long long int timell;
+ int ret;
+
+ ret = string_to_tdb_data(str, &key);
+ if (ret != EOK) return ret;
+
+ if (permanent) {
+ timest = talloc_strdup(ctx, "0");
+ } else {
+ if (use_local_negative == true && ctx->local_timeout > ctx->timeout) {
+ timell = ctx->local_timeout;
+ } else {
+ /* EOK is tested in cwrap based unit test */
+ if (ctx->timeout == 0) {
+ return EOK;
+ }
+ timell = ctx->timeout;
+ }
+ timell += (unsigned long long int)time(NULL);
+ timest = talloc_asprintf(ctx, "%llu", timell);
+ }
+ if (!timest) return ENOMEM;
+
+ ret = string_to_tdb_data(timest, &data);
+ if (ret != EOK) goto done;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding [%s] to negative cache%s\n",
+ str, permanent?" permanently":"");
+
+ ret = tdb_store(ctx->tdb, key, data, TDB_REPLACE);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Negative cache failed to set entry: [%s]\n",
+ tdb_errorstr(ctx->tdb));
+ ret = EFAULT;
+ }
+
+done:
+ talloc_free(timest);
+ return ret;
+}
+
+static int sss_ncache_check_user_int(struct sss_nc_ctx *ctx, const char *domain,
+ const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_check_group_int(struct sss_nc_ctx *ctx,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_check_netgr_int(struct sss_nc_ctx *ctx,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_check_service_int(struct sss_nc_ctx *ctx,
+ const char *domain,
+ const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s",
+ NC_SERVICE_PREFIX,
+ domain,
+ name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+typedef int (*ncache_check_byname_fn_t)(struct sss_nc_ctx *, const char *,
+ const char *);
+
+static int sss_cache_check_ent(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom, const char *name,
+ ncache_check_byname_fn_t checker)
+{
+ char *lower;
+ errno_t ret;
+
+ if (dom->case_sensitive == false) {
+ lower = sss_tc_utf8_str_tolower(ctx, name);
+ if (!lower) return ENOMEM;
+ ret = checker(ctx, dom->name, lower);
+ talloc_free(lower);
+ } else {
+ ret = checker(ctx, dom->name, name);
+ }
+
+ return ret;
+}
+
+int sss_ncache_check_user(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_user_int);
+}
+
+int sss_ncache_check_upn(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ char *neg_cache_name = NULL;
+ errno_t ret;
+
+ neg_cache_name = talloc_asprintf(ctx, "@%s", name);
+ if (neg_cache_name == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_cache_check_ent(ctx, dom, neg_cache_name,
+ sss_ncache_check_user_int);
+ talloc_free(neg_cache_name);
+
+ return ret;
+}
+
+int sss_ncache_check_group(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_group_int);
+}
+
+int sss_ncache_check_netgr(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_netgr_int);
+}
+
+static int sss_ncache_set_service_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_SERVICE_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_service_name(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom,
+ const char *name, const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%s:%s",
+ name,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_ncache_set_ent(ctx, permanent, dom,
+ service_and_protocol,
+ sss_ncache_set_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+int sss_ncache_check_service(struct sss_nc_ctx *ctx,struct sss_domain_info *dom,
+ const char *name, const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%s:%s",
+ name,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_cache_check_ent(ctx, dom, service_and_protocol,
+ sss_ncache_check_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+int sss_ncache_set_service_port(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom,
+ uint16_t port, const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%ul:%s",
+ port,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_ncache_set_ent(ctx, permanent, dom,
+ service_and_protocol,
+ sss_ncache_set_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+int sss_ncache_check_service_port(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uint16_t port,
+ const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%ul:%s",
+ port,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_cache_check_ent(ctx, dom, service_and_protocol,
+ sss_ncache_check_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+
+
+int sss_ncache_check_uid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ uid_t uid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIuid, NC_UID_PREFIX, dom->name,
+ uid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIuid, NC_UID_PREFIX, uid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_gid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ gid_t gid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIgid, NC_GID_PREFIX, dom->name,
+ gid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIgid, NC_GID_PREFIX, gid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_sid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_SID_PREFIX, dom->name, sid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%s", NC_SID_PREFIX, sid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%s", NC_CERT_PREFIX, cert);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+
+static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_user_local_by_name(&ctx->ops, name);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_group_local_by_name(&ctx->ops, name);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_set_netgr_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_set_ent(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name,
+ ncache_set_byname_fn_t setter)
+{
+ char *lower;
+ errno_t ret;
+
+ if (dom->case_sensitive == false) {
+ lower = sss_tc_utf8_str_tolower(ctx, name);
+ if (!lower) return ENOMEM;
+ ret = setter(ctx, permanent, dom->name, lower);
+ talloc_free(lower);
+ } else {
+ ret = setter(ctx, permanent, dom->name, name);
+ }
+
+ return ret;
+}
+
+
+int sss_ncache_set_user(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_user_int);
+}
+
+int sss_ncache_set_upn(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ char *neg_cache_name = NULL;
+ errno_t ret;
+
+ neg_cache_name = talloc_asprintf(ctx, "@%s", name);
+ if (neg_cache_name == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_ent(ctx, permanent, dom, neg_cache_name,
+ sss_ncache_set_user_int);
+ talloc_free(neg_cache_name);
+
+ return ret;
+}
+
+int sss_ncache_set_group(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_group_int);
+}
+
+int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_netgr_int);
+}
+
+int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, uid_t uid)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIuid, NC_UID_PREFIX, dom->name,
+ uid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIuid, NC_UID_PREFIX, uid);
+ }
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_user_local_by_uid(&ctx->ops, uid);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, gid_t gid)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIgid, NC_GID_PREFIX, dom->name,
+ gid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIgid, NC_GID_PREFIX, gid);
+ }
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_group_local_by_gid(&ctx->ops, gid);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_sid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_SID_PREFIX, dom->name, sid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%s", NC_SID_PREFIX, sid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_cert(struct sss_nc_ctx *ctx, bool permanent,
+ const char *cert)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%s", NC_CERT_PREFIX, cert);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+static char *domain_lookup_type_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *lookup_type)
+{
+ return talloc_asprintf(mem_ctx,
+ "%s/%s/%s",
+ NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX,
+ dom->name,
+ lookup_type);
+}
+
+int sss_ncache_set_domain_locate_type(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *lookup_type)
+{
+ char *str;
+ int ret;
+
+ str = domain_lookup_type_str(ctx, dom, lookup_type);
+ if (!str) return ENOMEM;
+
+ /* Permanent cache is always used here, because the lookup
+ * type's (getgrgid, getpwuid, ..) support locating an entry's domain
+ * doesn't change
+ */
+ ret = sss_ncache_set_str(ctx, str, true, false);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_domain_locate_type(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *lookup_type)
+{
+ char *str;
+ int ret;
+
+ str = domain_lookup_type_str(ctx, dom, lookup_type);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+static char *locate_gid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ gid_t gid)
+{
+ return talloc_asprintf(mem_ctx,
+ "%s/%s/%s/%"SPRIgid,
+ NC_DOMAIN_ACCT_LOCATE_PREFIX,
+ NC_GID_PREFIX,
+ dom->name,
+ gid);
+}
+
+int sss_ncache_set_locate_gid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ gid_t gid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_gid_str(ctx, dom, gid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_str(ctx, str, false, false);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_locate_gid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ gid_t gid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_gid_str(ctx, dom, gid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+static char *locate_uid_str(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid)
+{
+ return talloc_asprintf(ctx,
+ "%s/%s/%s/%"SPRIuid,
+ NC_DOMAIN_ACCT_LOCATE_PREFIX,
+ NC_UID_PREFIX,
+ dom->name,
+ uid);
+}
+
+int sss_ncache_set_locate_uid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_uid_str(ctx, dom, uid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_str(ctx, str, false, false);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_locate_uid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_uid_str(ctx, dom, uid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+static char *locate_sid_str(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid)
+{
+ return talloc_asprintf(ctx,
+ "%s/%s/%s/%s",
+ NC_DOMAIN_ACCT_LOCATE_PREFIX,
+ NC_SID_PREFIX,
+ dom->name,
+ sid);
+}
+
+int sss_ncache_check_locate_sid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_sid_str(ctx, dom, sid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_locate_sid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_sid_str(ctx, dom, sid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_str(ctx, str, false, false);
+ talloc_free(str);
+ return ret;
+}
+
+static int delete_permanent(struct tdb_context *tdb,
+ TDB_DATA key, TDB_DATA data, void *state)
+{
+ unsigned long long int timestamp;
+ bool remove_key = false;
+ char *ep;
+
+ if (strncmp((char *)key.dptr,
+ NC_ENTRY_PREFIX, sizeof(NC_ENTRY_PREFIX) - 1) != 0) {
+ /* not interested in this key */
+ return 0;
+ }
+
+ errno = 0;
+ timestamp = strtoull((const char *)data.dptr, &ep, 10);
+ if (errno != 0 || *ep != '\0') {
+ /* Malformed entry, remove it */
+ remove_key = true;
+ goto done;
+ }
+
+ if (timestamp == 0) {
+ /* a 0 timestamp means this is a permanent entry */
+ remove_key = true;
+ }
+
+done:
+ if (remove_key) {
+ return tdb_delete(tdb, key);
+ }
+
+ return 0;
+}
+
+int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx)
+{
+ int ret;
+
+ ret = tdb_traverse(ctx->tdb, delete_permanent, NULL);
+ if (ret < 0)
+ return EIO;
+
+ return EOK;
+}
+
+static int delete_prefix(struct tdb_context *tdb,
+ TDB_DATA key, TDB_DATA data, void *state)
+{
+ const char *prefix = (const char *) state;
+ unsigned long long int timestamp;
+ char *ep = NULL;
+
+ if (strncmp((char *)key.dptr, prefix, strlen(prefix) - 1) != 0) {
+ /* not interested in this key */
+ return 0;
+ }
+
+ errno = 0;
+ timestamp = strtoull((const char *)data.dptr, &ep, 10);
+ if ((errno == 0) && (*ep == '\0') && (timestamp == 0)) {
+ /* skip permanent entries */
+ return 0;
+ }
+
+ return tdb_delete(tdb, key);
+}
+
+static int sss_ncache_reset_pfx(struct sss_nc_ctx *ctx,
+ const char **prefixes)
+{
+ int ret;
+
+ if (prefixes == NULL) {
+ return EOK;
+ }
+
+ for (int i = 0; prefixes[i] != NULL; i++) {
+ ret = tdb_traverse(ctx->tdb,
+ delete_prefix,
+ discard_const(prefixes[i]));
+ if (ret < 0) {
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
+int sss_ncache_reset_users(struct sss_nc_ctx *ctx)
+{
+ const char *prefixes[] = {
+ NC_USER_PREFIX,
+ NC_UID_PREFIX,
+ NULL,
+ };
+
+ return sss_ncache_reset_pfx(ctx, prefixes);
+}
+
+int sss_ncache_reset_groups(struct sss_nc_ctx *ctx)
+{
+ const char *prefixes[] = {
+ NC_GROUP_PREFIX,
+ NC_GID_PREFIX,
+ NULL,
+ };
+
+ return sss_ncache_reset_pfx(ctx, prefixes);
+}
+
+errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
+ struct confdb_ctx *cdb,
+ struct resp_ctx *rctx)
+{
+ errno_t ret;
+ char **filter_list = NULL;
+ char **default_list = NULL;
+ char *name = NULL;
+ struct sss_domain_info *dom = NULL;
+ struct sss_domain_info *domain_list = rctx->domains;
+ struct sss_domain_info *ddom;
+ char *domainname = NULL;
+ char *conf_path = NULL;
+ TALLOC_CTX *tmpctx = talloc_new(NULL);
+ int i;
+ char *fqname = NULL;
+
+ if (tmpctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Populate domain-specific negative cache user entries */
+ for (dom = domain_list; dom; dom = get_next_domain(dom, 0)) {
+ conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL,
+ dom->name);
+ if (!conf_path) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(filter_list);
+ ret = confdb_get_string_as_list(cdb, tmpctx, conf_path,
+ CONFDB_NSS_FILTER_USERS,
+ &filter_list);
+ if (ret == ENOENT) continue;
+ if (ret != EOK) goto done;
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL,
+ filter_list[i],
+ &domainname, &name);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Can add [%s] only as UPN to negcache because the "
+ "required domain is not known yet\n", filter_list[i]);
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterUsers list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+
+ /* Check domain and its sub-domains */
+ for (ddom = dom; ddom != NULL;
+ ddom = get_next_domain(ddom, SSS_GND_ALL_SUBDOMAINS)) {
+
+ if (domainname && strcmp(domainname, ddom->name)) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Mismatch between domain name (%s) and name "
+ "set in FQN (%s), assuming %s is UPN\n",
+ ddom->name, domainname, filter_list[i]);
+ ret = sss_ncache_set_upn(ncache, true, ddom, filter_list[i]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ncache_set_upn failed (%d [%s]), ignored\n",
+ ret, sss_strerror(ret));
+ }
+ continue;
+ }
+
+ fqname = sss_create_internal_fqname(tmpctx, name, ddom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_upn(ncache, true, ddom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ncache_set_upn failed (%d [%s]), ignored\n",
+ ret, sss_strerror(ret));
+ }
+ ret = sss_ncache_set_user(ncache, true, ddom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent user filter for [%s]"
+ " (%d [%s])\n", filter_list[i],
+ ret, sss_strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ talloc_zfree(filter_list);
+ /* Populate non domain-specific negative cache user entries */
+ ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_FILTER_USERS, &filter_list);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL, filter_list[i],
+ &domainname, &name);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Can add [%s] only as UPN to negcache because the "
+ "required domain is not known yet\n", filter_list[i]);
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterUsers list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+ if (domainname) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Adding [%s] to UPN negative cache of all domains.\n",
+ filter_list[i]);
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ ret = sss_ncache_set_upn(ncache, true, dom, filter_list[i]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ncache_set_upn failed (%d [%s]), ignored\n",
+ ret, sss_strerror(ret));
+ }
+ }
+
+ /* Add name to domain specific cache for known domain names */
+ dom = responder_get_domain(rctx, domainname);
+ if (dom != NULL) {
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_user(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent user filter for [%s]"
+ " (%d [%s])\n", filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ } else {
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_upn(ncache, true, dom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent upn filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, filter_list[i],
+ ret, strerror(ret));
+ }
+
+ ret = sss_ncache_set_user(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent user filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ /* Populate domain-specific negative cache group entries */
+ for (dom = domain_list; dom; dom = get_next_domain(dom, 0)) {
+ conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, dom->name);
+ if (!conf_path) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(filter_list);
+ ret = confdb_get_string_as_list(cdb, tmpctx, conf_path,
+ CONFDB_NSS_FILTER_GROUPS, &filter_list);
+ if (ret == ENOENT) continue;
+ if (ret != EOK) goto done;
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL, filter_list[i],
+ &domainname, &name);
+ if (ret != EOK) {
+ /* Groups do not have UPNs, so domain names, if present,
+ * must be known */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterGroups list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+
+ /* Check domain and its sub-domains */
+ for (ddom = dom;
+ ddom != NULL && (ddom == dom || ddom->parent != NULL);
+ ddom = get_next_domain(ddom, SSS_GND_ALL_DOMAINS)) {
+ if (domainname && strcmp(domainname, ddom->name)) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Mismatch between domain name (%s) and name "
+ "set in FQN (%s), skipping group %s\n",
+ ddom->name, domainname, name);
+ continue;
+ }
+
+ fqname = sss_create_internal_fqname(tmpctx, name, ddom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, ddom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent group filter for [%s]"
+ " (%d [%s])\n", filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ talloc_zfree(filter_list);
+ /* Populate non domain-specific negative cache group entries */
+ ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_FILTER_GROUPS, &filter_list);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL, filter_list[i],
+ &domainname, &name);
+ if (ret != EOK) {
+ /* Groups do not have UPNs, so domain names, if present,
+ * must be known */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterGroups list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+ if (domainname) {
+ dom = responder_get_domain(rctx, domainname);
+ if (!dom) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid domain name [%s]\n", domainname);
+ continue;
+ }
+
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent group filter for"
+ " [%s] (%d [%s])\n", filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ } else {
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent group filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ /* SSSD doesn't handle "root", thus it'll be added to the negative cache
+ * nonetheless what's already added there. */
+ default_list = talloc_array(tmpctx, char *, 2);
+ if (default_list == NULL) {
+ ret= ENOMEM;
+ goto done;
+ }
+ default_list[0] = talloc_strdup(tmpctx, "root");
+ if (default_list[0] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ default_list[1] = NULL;
+
+ /* Populate negative cache users and groups entries for the
+ * "default_list" */
+ for (i = 0; (default_list != NULL && default_list[i] != NULL); i++) {
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ fqname = sss_create_internal_fqname(tmpctx,
+ default_list[i],
+ dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_user(ncache, true, dom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent user filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, default_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, dom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent group filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, default_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+
+ /* Also add "root" uid and gid to the negative cache */
+ ret = sss_ncache_set_uid(ncache, true, NULL, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent uid filter for root (0) "
+ "(%d [%s])\n",
+ ret, strerror(ret));
+ }
+
+ ret = sss_ncache_set_gid(ncache, true, NULL, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent gid filter for root (0) "
+ "(%d [%s])\n",
+ ret, strerror(ret));
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmpctx);
+ return ret;
+}
+
+/* Reset permanent negcache after checking the domains */
+errno_t sss_ncache_reset_repopulate_permanent(struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache)
+{
+ int ret;
+
+ ret = sss_ncache_reset_permanent(ncache);
+ if (ret == EOK) {
+ ret = sss_ncache_prepopulate(ncache, rctx->cdb, rctx);
+ }
+
+ return ret;
+}
diff --git a/src/responder/common/negcache.h b/src/responder/common/negcache.h
new file mode 100644
index 0000000..29c0e6b
--- /dev/null
+++ b/src/responder/common/negcache.h
@@ -0,0 +1,174 @@
+/*
+ SSSD
+
+ NSS Responder
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NSS_NEG_CACHE_H_
+#define _NSS_NEG_CACHE_H_
+
+struct sss_nc_ctx;
+
+/* init the in memory negative cache */
+int sss_ncache_init(TALLOC_CTX *memctx, uint32_t timeout,
+ uint32_t local_timeout, struct sss_nc_ctx **_ctx);
+
+uint32_t sss_ncache_get_timeout(struct sss_nc_ctx *ctx);
+
+/* check if the user is expired according to the passed in time to live */
+int sss_ncache_check_user(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name);
+int sss_ncache_check_upn(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name);
+int sss_ncache_check_group(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name);
+int sss_ncache_check_netgr(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name);
+int sss_ncache_check_uid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ uid_t uid);
+int sss_ncache_check_gid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ gid_t gid);
+int sss_ncache_check_sid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *sid);
+int sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert);
+
+int sss_ncache_check_service(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *name,
+ const char *proto);
+int sss_ncache_check_service_port(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uint16_t port,
+ const char *proto);
+
+/* add a new neg-cache entry setting the timestamp to "now" unless
+ * "permanent" is set to true, in which case the timestamps is set to 0
+ * and the negative cache never expires (used to permanently filter out
+ * users and groups) */
+int sss_ncache_set_user(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name);
+int sss_ncache_set_upn(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name);
+int sss_ncache_set_group(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name);
+int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name);
+int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, uid_t uid);
+int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, gid_t gid);
+int sss_ncache_set_sid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *sid);
+int sss_ncache_set_cert(struct sss_nc_ctx *ctx, bool permanent,
+ const char *cert);
+int sss_ncache_set_service_name(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom,
+ const char *name, const char *proto);
+int sss_ncache_set_service_port(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom,
+ uint16_t port, const char *proto);
+/*
+ * Mark the lookup_type as not supporting the negative cache. This
+ * would be used by the corresponding checker to avoid needless
+ * subsequent calls to the locator for configurations that do not
+ * support the locator plugin.
+ *
+ * @param ctx The negative cache.
+ * @param dom The top-level domain. It is expected that the caller
+ * would use the top-level domain head here, because
+ * this negative cache is "per-request-type" which is the
+ * same for all subdomains of a domain.
+ * @param lookup_type Lookup type, e.g. getpwuid, getgrnam.
+ *
+ * @return EOK on success, errno on failure.
+ */
+int sss_ncache_set_domain_locate_type(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *lookup_type);
+/*
+ * Check if the lookup_type supports the domain locator request.
+ *
+ * @param ctx The negative cache.
+ * @param dom The top-level domain. It is expected that the caller
+ * would use the top-level domain head here, because
+ * this negative cache is "per-request-type" which is the
+ * same for all subdomains of a domain.
+ * @param lookup_type Lookup type, e.g. getpwuid, getgrnam.
+ *
+ * @return ENOENT if the request supports the locator (or we
+ * haven't checked yet), EEXIST if the request does
+ * not support the domain locator request.
+ */
+int sss_ncache_check_domain_locate_type(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *key);
+
+/*
+ * Call these two functions to mark a GID as checked until the negative
+ * cache expires. This function is used to avoid a situation where
+ * GID would be found in a subsequent domain, so any request that
+ * searches for this GID again (even if it was cached) would first
+ * run the locator again.
+ *
+ * While this negative cache entry is valid, it is expected that
+ * the negatively cached entries in the domain's GID negative
+ * cache (if any) are valid.
+ *
+ * The sss_ncache_set_locate_gid() is called by the locator request
+ * when it finishes, the sss_ncache_check_locate_gid() is called
+ * by the caller of the locator request to find if the locator
+ * should be called at all.
+ */
+int sss_ncache_set_locate_gid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ gid_t gid);
+int sss_ncache_check_locate_gid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ gid_t gid);
+int sss_ncache_check_locate_uid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid);
+int sss_ncache_set_locate_uid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid);
+int sss_ncache_check_locate_sid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid);
+int sss_ncache_set_locate_sid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid);
+
+int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx);
+/* sss_ncache_reset_[users/groups] skips permanent entries */
+int sss_ncache_reset_users(struct sss_nc_ctx *ctx);
+int sss_ncache_reset_groups(struct sss_nc_ctx *ctx);
+
+struct resp_ctx;
+
+/* Set up the negative cache with values from filter_users and
+ * filter_groups in the sssd.conf
+ */
+errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
+ struct confdb_ctx *cdb,
+ struct resp_ctx *rctx);
+
+/* Flush the negcache permament entries and then repopulate them */
+errno_t sss_ncache_reset_repopulate_permanent(struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache);
+
+#endif /* _NSS_NEG_CACHE_H_ */
diff --git a/src/responder/common/negcache_files.c b/src/responder/common/negcache_files.c
new file mode 100644
index 0000000..f22796a
--- /dev/null
+++ b/src/responder/common/negcache_files.c
@@ -0,0 +1,105 @@
+/*
+ SSSD
+
+ NSS Responder
+
+ Copyright (C) Petr Čech <pcech@redhat.com> 2016
+
+ 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 "util/util.h"
+#include "util/nss_dl_load.h"
+#include "responder/common/negcache_files.h"
+
+#define BUFFER_SIZE 16384
+static char s_nss_buffer[BUFFER_SIZE];
+
+bool is_user_local_by_name(const struct sss_nss_ops *ops, const char *name)
+{
+ struct passwd pwd = { 0 };
+ int errnop;
+ enum nss_status ret;
+ char *shortname = NULL;
+ int parse_ret;
+
+ parse_ret = sss_parse_internal_fqname(NULL, name, &shortname, NULL);
+ if (parse_ret != EOK) {
+ return false;
+ }
+
+ ret = ops->getpwnam_r(shortname, &pwd, s_nss_buffer, BUFFER_SIZE, &errnop);
+ talloc_free(shortname);
+ if (ret == NSS_STATUS_SUCCESS) {
+ DEBUG(SSSDBG_TRACE_FUNC, "User %s is a local user\n", name);
+ return true;
+ }
+
+ return false;
+}
+
+bool is_user_local_by_uid(const struct sss_nss_ops *ops, uid_t uid)
+{
+ struct passwd pwd = { 0 };
+ int errnop;
+ enum nss_status ret;
+
+ ret = ops->getpwuid_r(uid, &pwd, s_nss_buffer, BUFFER_SIZE, &errnop);
+ if (ret == NSS_STATUS_SUCCESS) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "User with UID %"SPRIuid" is a local user\n", uid);
+ return true;
+ }
+
+ return false;
+}
+
+bool is_group_local_by_name(const struct sss_nss_ops *ops, const char *name)
+{
+ struct group grp = { 0 };
+ int errnop;
+ enum nss_status ret;
+ char *shortname = NULL;
+ int parse_ret;
+
+ parse_ret = sss_parse_internal_fqname(NULL, name, &shortname, NULL);
+ if (parse_ret != EOK) {
+ return false;
+ }
+
+ ret = ops->getgrnam_r(shortname, &grp, s_nss_buffer, BUFFER_SIZE, &errnop);
+ talloc_free(shortname);
+ if (ret == NSS_STATUS_SUCCESS) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Group %s is a local group\n", name);
+ return true;
+ }
+
+ return false;
+}
+
+bool is_group_local_by_gid(const struct sss_nss_ops *ops, uid_t gid)
+{
+ struct group grp = { 0 };
+ int errnop;
+ enum nss_status ret;
+
+ ret = ops->getgrgid_r(gid, &grp, s_nss_buffer, BUFFER_SIZE, &errnop);
+ if (ret == NSS_STATUS_SUCCESS) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Group with GID %"SPRIgid" is a local group\n", gid);
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/responder/common/negcache_files.h b/src/responder/common/negcache_files.h
new file mode 100644
index 0000000..a3e18de
--- /dev/null
+++ b/src/responder/common/negcache_files.h
@@ -0,0 +1,35 @@
+/*
+ SSSD
+
+ NSS Responder
+
+ Copyright (C) Petr Čech <pcech@redhat.com> 2016
+
+ 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 _NEGCACHE_FILES_H_
+#define _NEGCACHE_FILES_H_
+
+#include <stdbool.h>
+
+struct sss_nss_ops;
+
+bool is_user_local_by_name(const struct sss_nss_ops *ops, const char *name);
+bool is_user_local_by_uid(const struct sss_nss_ops *ops, uid_t uid);
+
+bool is_group_local_by_name(const struct sss_nss_ops *ops, const char *name);
+bool is_group_local_by_gid(const struct sss_nss_ops *ops, uid_t gid);
+
+#endif /* _NEGCACHE_FILES_H_ */
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
new file mode 100644
index 0000000..5f04d25
--- /dev/null
+++ b/src/responder/common/responder.h
@@ -0,0 +1,450 @@
+/*
+ SSSD
+
+ SSS Client Responder, header file
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SSS_RESPONDER_H__
+#define __SSS_RESPONDER_H__
+
+#include "config.h"
+
+#include <stdint.h>
+#include <sys/un.h>
+#include <sys/resource.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <ldb.h>
+#include <dhash.h>
+
+#include "util/sss_regexp.h"
+#include "sss_iface/sss_iface_async.h"
+#include "responder/common/negcache.h"
+#include "sss_client/sss_cli.h"
+#include "responder/common/cache_req/cache_req_domain.h"
+#include "util/session_recording.h"
+
+extern hash_table_t *dp_requests;
+
+/* we want default permissions on created files to be very strict,
+ * so set our umask to 0177 */
+#define DFL_RSP_UMASK SSS_DFL_UMASK
+
+/* Public sockets must be readable and writable by anybody on the system.
+ * So we set umask to 0111. */
+#define SCKT_RSP_UMASK 0111
+
+/* needed until nsssrv.h is updated */
+struct cli_request {
+
+ /* original request from the wire */
+ struct sss_packet *in;
+
+ /* reply data */
+ struct sss_packet *out;
+};
+
+struct cli_protocol_version {
+ uint32_t version;
+ const char *date;
+ const char *description;
+};
+
+struct cli_protocol {
+ struct cli_request *creq;
+ struct cli_protocol_version *cli_protocol_version;
+};
+
+struct resp_ctx;
+
+struct be_conn {
+ struct be_conn *next;
+ struct be_conn *prev;
+
+ struct resp_ctx *rctx;
+
+ const char *cli_name;
+ struct sss_domain_info *domain;
+
+ char *bus_name;
+ char *sbus_address;
+ struct sbus_connection *conn;
+};
+
+struct resp_ctx {
+ struct tevent_context *ev;
+ struct tevent_fd *lfde;
+ int lfd;
+ struct tevent_fd *priv_lfde;
+ int priv_lfd;
+ struct confdb_ctx *cdb;
+ const char *sock_name;
+ const char *priv_sock_name;
+
+ struct sss_nc_ctx *ncache;
+ struct sss_names_ctx *global_names;
+
+ struct sbus_connection *mon_conn;
+ struct be_conn *be_conns;
+
+ struct sss_domain_info *domains;
+ int domains_timeout;
+ int client_idle_timeout;
+
+ struct cache_req_domain *cr_domains;
+ const char *domain_resolution_order;
+
+ time_t last_request_time;
+ int idle_timeout;
+ struct tevent_timer *idle;
+
+ struct sss_cmd_table *sss_cmds;
+ const char *sss_pipe_name;
+ const char *confdb_service_path;
+
+ struct timeval get_domains_last_call;
+
+ size_t allowed_uids_count;
+ uid_t *allowed_uids;
+
+ char *default_domain;
+ char override_space;
+
+ char **allowed_shells;
+ char *override_shell;
+ char **vetoed_shells;
+ char **etc_shells;
+ char *shell_fallback;
+ char *default_shell;
+
+ struct session_recording_conf sr_conf;
+
+ uint32_t cache_req_num;
+ uint32_t client_id_num;
+
+ void *pvt_ctx;
+
+ bool shutting_down;
+ bool socket_activated;
+ bool dbus_activated;
+ bool cache_first;
+ bool enumeration_warn_logged;
+};
+
+struct cli_creds;
+
+struct cli_ctx {
+ struct tevent_context *ev;
+ struct resp_ctx *rctx;
+ int cfd;
+ struct tevent_fd *cfde;
+ tevent_fd_handler_t cfd_handler;
+ struct sockaddr_un addr;
+ int priv;
+
+ struct cli_creds *creds;
+ char *cmd_line;
+
+ void *protocol_ctx;
+ void *state_ctx;
+
+ struct tevent_timer *idle;
+ time_t last_request_time;
+ uint32_t client_id_num;
+};
+
+struct sss_cmd_table {
+ enum sss_cli_command cmd;
+ int (*fn)(struct cli_ctx *cctx);
+};
+
+/* from generated code */
+struct mon_cli_iface;
+
+/*
+ * responder_common.c
+ *
+ */
+
+typedef int (*connection_setup_t)(struct cli_ctx *cctx);
+
+int sss_connection_setup(struct cli_ctx *cctx);
+
+void sss_client_fd_handler(void *ptr,
+ void (*recv_fn) (struct cli_ctx *cctx),
+ void (*send_fn) (struct cli_ctx *cctx),
+ uint16_t flags);
+
+int sss_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ struct sss_cmd_table sss_cmds[],
+ const char *sss_pipe_name,
+ int pipe_fd,
+ const char *sss_priv_pipe_name,
+ int priv_pipe_fd,
+ const char *confdb_service_path,
+ const char *conn_name,
+ const char *svc_name,
+ connection_setup_t conn_setup,
+ struct resp_ctx **responder_ctx);
+
+int sss_dp_get_domain_conn(struct resp_ctx *rctx, const char *domain,
+ struct be_conn **_conn);
+struct sss_domain_info *
+responder_get_domain(struct resp_ctx *rctx, const char *domain);
+
+errno_t responder_get_domain_by_id(struct resp_ctx *rctx, const char *id,
+ struct sss_domain_info **_ret_dom);
+
+int create_pipe_fd(const char *sock_name, int *_fd, mode_t umaskval);
+int activate_unix_sockets(struct resp_ctx *rctx,
+ connection_setup_t conn_setup);
+
+/* responder_cmd.c */
+int sss_cmd_empty_packet(struct sss_packet *packet);
+int sss_cmd_send_empty(struct cli_ctx *cctx);
+int sss_cmd_send_error(struct cli_ctx *cctx, int err);
+void sss_cmd_done(struct cli_ctx *cctx, void *freectx);
+int sss_cmd_get_version(struct cli_ctx *cctx);
+int sss_cmd_execute(struct cli_ctx *cctx,
+ enum sss_cli_command cmd,
+ struct sss_cmd_table *sss_cmds);
+struct cli_protocol_version *register_cli_protocol_version(void);
+
+struct setent_req_list;
+
+/* A facility for notifying setent requests */
+struct tevent_req *setent_get_req(struct setent_req_list *sl);
+errno_t setent_add_ref(TALLOC_CTX *memctx,
+ struct setent_req_list **list,
+ struct tevent_req *req);
+void setent_notify(struct setent_req_list **list, errno_t err);
+void setent_notify_done(struct setent_req_list **list);
+
+errno_t
+sss_cmd_check_cache(struct ldb_message *msg,
+ int cache_refresh_percent,
+ uint64_t cache_expire);
+
+void handle_requests_after_reconnect(struct resp_ctx *rctx);
+
+errno_t
+responder_logrotate(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct resp_ctx *rctx);
+
+/* Send a request to the data provider
+ * Once this function is called, the communication
+ * with the data provider will always run to
+ * completion. Freeing the returned tevent_req will
+ * cancel the notification of completion, but not
+ * the data provider action.
+ */
+
+enum sss_dp_acct_type {
+ SSS_DP_USER = 1,
+ SSS_DP_GROUP,
+ SSS_DP_INITGROUPS,
+ SSS_DP_SUBID_RANGES,
+ SSS_DP_NETGR,
+ SSS_DP_SERVICES,
+ SSS_DP_SECID,
+ SSS_DP_USER_AND_GROUP,
+ SSS_DP_CERT,
+ SSS_DP_WILDCARD_USER,
+ SSS_DP_WILDCARD_GROUP,
+};
+
+struct tevent_req *
+sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_acct_type type,
+ const char *opt_name,
+ uint32_t opt_id,
+ const char *extra);
+errno_t
+sss_dp_get_account_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char **_error_message);
+
+struct tevent_req *
+sss_dp_resolver_get_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ uint32_t entry_type,
+ uint32_t filter_type,
+ const char *filter_value);
+
+errno_t
+sss_dp_resolver_get_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char **_error_message);
+
+bool sss_utf8_check(const uint8_t *s, size_t n);
+
+void responder_set_fd_limit(rlim_t fd_limit);
+
+errno_t reset_client_idle_timer(struct cli_ctx *cctx);
+
+errno_t responder_setup_idle_timeout_config(struct resp_ctx *rctx);
+
+#define GET_DOMAINS_DEFAULT_TIMEOUT 60
+
+struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ bool force,
+ const char *hint);
+
+errno_t sss_dp_get_domains_recv(struct tevent_req *req);
+
+/*
+ * Call a getAccountDomain request
+ *
+ * Only requests by ID are supported.
+ *
+ * @param mem_ctx Parent memory context
+ * @param rctx Responder context
+ * @param domain The SSSD domain we're querying. The response can
+ * be either NULL or come from any of domain's subdomains
+ * or domain itself
+ * @param type Either SSS_DP_USER, SSS_DP_GROUP or SSS_DP_SECID, other
+ * types are not supported at the moment
+ * @param opt_id The ID number we're trying to locate
+ * @param opt_str The SID number we're trying to locate
+ *
+ * @return A tevent request or NULL if allocating the request fails.
+ */
+struct tevent_req *sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ bool fast_reply,
+ enum sss_dp_acct_type type,
+ uint32_t opt_id,
+ const char *opt_str);
+
+/* Receive a getAccountDomain request result
+ *
+ * @param mem_ctx The memory context that will own the contents of _domain
+ * @param req The request that had finished
+ * @para _domain Either NULL (the request did not match any domain) or
+ * a string that corresponds to either the input domain
+ * or any of its subdomains
+ *
+ * @return EOK on success, errno otherwise
+ */
+errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_domain);
+
+typedef void (get_domains_callback_fn_t)(void *);
+errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *optional_ncache,
+ get_domains_callback_fn_t *callback,
+ void *callback_pvt);
+
+errno_t csv_string_to_uid_array(TALLOC_CTX *mem_ctx, const char *csv_string,
+ size_t *_uid_count, uid_t **_uids);
+
+uid_t client_euid(struct cli_creds *creds);
+errno_t check_allowed_uids(uid_t uid, size_t allowed_uids_count,
+ const uid_t *allowed_uids);
+
+struct tevent_req *
+sss_parse_inp_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ const char *default_domain,
+ const char *rawinp);
+
+errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char **_name, char **_domname);
+
+const char **parse_attr_list_ex(TALLOC_CTX *mem_ctx, const char *conf_str,
+ const char **defaults);
+
+char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool name_is_upn,
+ const char *orig_name);
+
+errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx);
+
+const char *
+sss_resp_get_shell_override(struct ldb_message *msg,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain);
+
+/**
+ * Helper functions to format output names
+ */
+
+/* Format orig_name into a sized_string in output format as prescribed
+ * by the name_dom domain
+ */
+int sized_output_name(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ const char *orig_name,
+ struct sss_domain_info *name_dom,
+ struct sized_string **_name);
+
+/* Format orig_name into a sized_string in output format as prescribed
+ * by the domain read from the fully qualified name.
+ */
+int sized_domain_name(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ const char *member_name,
+ struct sized_string **_name);
+
+/* Given a ldb_result structure that contains a result of sysdb_initgroups
+ * where some groups might be just 'stubs' that don't have a name, but only
+ * a SID and a GID, resolve those incomplete groups into full group objects
+ */
+struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ struct ldb_result *initgr_res);
+
+int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ldb_result **_initgr_named_res);
+
+/**
+ * Register common responder sbus interface on connection.
+ */
+errno_t
+sss_resp_register_sbus_iface(struct sbus_connection *conn,
+ struct resp_ctx *rctx);
+
+/**
+ * Register common service sbus interface on monitor connection.
+ */
+errno_t
+sss_resp_register_service_iface(struct resp_ctx *rctx);
+
+#endif /* __SSS_RESPONDER_H__ */
diff --git a/src/responder/common/responder_cmd.c b/src/responder/common/responder_cmd.c
new file mode 100644
index 0000000..bd05ca2
--- /dev/null
+++ b/src/responder/common/responder_cmd.c
@@ -0,0 +1,302 @@
+/*
+ SSSD
+
+ SSS Client Responder, command parser
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <errno.h>
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+
+
+int sss_cmd_send_error(struct cli_ctx *cctx, int err)
+{
+ struct cli_protocol *pctx;
+ int ret;
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+ if (!pctx) return EINVAL;
+
+ /* create response packet */
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create new packet: %d\n", ret);
+ return ret;
+ }
+
+ sss_packet_set_error(pctx->creq->out, err);
+ return EOK;
+}
+
+int sss_cmd_empty_packet(struct sss_packet *packet)
+{
+ uint8_t *body;
+ size_t blen;
+ int ret;
+
+ ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
+ if (ret != EOK) return ret;
+
+ sss_packet_get_body(packet, &body, &blen);
+
+ /* num results */
+ SAFEALIGN_SETMEM_UINT32(body, 0, NULL);
+
+ /* reserved */
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
+
+ return EOK;
+}
+
+int sss_cmd_send_empty(struct cli_ctx *cctx)
+{
+ struct cli_protocol *pctx;
+ int ret;
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+ if (!pctx) return EINVAL;
+
+ /* create response packet */
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_cmd_empty_packet(pctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+ return EOK;
+}
+
+void sss_cmd_done(struct cli_ctx *cctx, void *freectx)
+{
+ /* now that the packet is in place, unlock queue
+ * making the event writable */
+ TEVENT_FD_WRITEABLE(cctx->cfde);
+
+ /* free all request related data through the talloc hierarchy */
+ talloc_free(freectx);
+}
+
+int sss_cmd_get_version(struct cli_ctx *cctx)
+{
+ struct cli_protocol *pctx;
+ uint8_t *req_body;
+ size_t req_blen;
+ uint8_t *body;
+ size_t blen;
+ int ret;
+ uint32_t client_version;
+ uint32_t protocol_version;
+ int i;
+ static struct cli_protocol_version *cli_protocol_version = NULL;
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+ if (!pctx) return EINVAL;
+
+ pctx->cli_protocol_version = NULL;
+
+ if (cli_protocol_version == NULL) {
+ cli_protocol_version = register_cli_protocol_version();
+ }
+
+ if (cli_protocol_version != NULL) {
+ pctx->cli_protocol_version = &cli_protocol_version[0];
+
+ sss_packet_get_body(pctx->creq->in, &req_body, &req_blen);
+ if (req_blen == sizeof(uint32_t)) {
+ memcpy(&client_version, req_body, sizeof(uint32_t));
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Received client version [%d].\n", client_version);
+
+ i=0;
+ while(cli_protocol_version[i].version>0) {
+ if (cli_protocol_version[i].version == client_version) {
+ pctx->cli_protocol_version = &cli_protocol_version[i];
+ break;
+ }
+ i++;
+ }
+ }
+ }
+
+ /* create response packet */
+ ret = sss_packet_new(pctx->creq, sizeof(uint32_t),
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ return ret;
+ }
+ sss_packet_get_body(pctx->creq->out, &body, &blen);
+
+ protocol_version = (pctx->cli_protocol_version != NULL)
+ ? pctx->cli_protocol_version->version : 0;
+
+ SAFEALIGN_COPY_UINT32(body, &protocol_version, NULL);
+ DEBUG(SSSDBG_FUNC_DATA, "Offered version [%d].\n", protocol_version);
+
+ sss_cmd_done(cctx, NULL);
+ return EOK;
+}
+
+int sss_cmd_execute(struct cli_ctx *cctx,
+ enum sss_cli_command cmd,
+ struct sss_cmd_table *sss_cmds)
+{
+ int i;
+
+ for (i = 0; sss_cmds[i].cmd != SSS_CLI_NULL; i++) {
+ if (cmd == sss_cmds[i].cmd) {
+ return sss_cmds[i].fn(cctx);
+ }
+ }
+
+ return EINVAL;
+}
+struct setent_req_list {
+ struct setent_req_list *prev;
+ struct setent_req_list *next;
+ /* Need to modify the list from a talloc destructor */
+ struct setent_req_list **head;
+
+ struct tevent_req *req;
+};
+
+struct tevent_req *
+setent_get_req(struct setent_req_list *sl)
+{
+ return sl->req;
+}
+
+int setent_remove_ref(TALLOC_CTX *ctx)
+{
+ struct setent_req_list *entry =
+ talloc_get_type(ctx, struct setent_req_list);
+ DLIST_REMOVE(*(entry->head), entry);
+ return 0;
+}
+
+errno_t setent_add_ref(TALLOC_CTX *memctx,
+ struct setent_req_list **list,
+ struct tevent_req *req)
+{
+ struct setent_req_list *entry;
+
+ entry = talloc_zero(memctx, struct setent_req_list);
+ if (!entry) {
+ return ENOMEM;
+ }
+
+ entry->req = req;
+ DLIST_ADD_END(*list, entry, struct setent_req_list *);
+ entry->head = list;
+
+ talloc_set_destructor((TALLOC_CTX *)entry, setent_remove_ref);
+ return EOK;
+}
+
+void setent_notify(struct setent_req_list **list, errno_t err)
+{
+ struct setent_req_list *reql;
+
+ /* Notify the waiting clients */
+ while ((reql = *list) != NULL) {
+ /* Each tevent_req_done() call will free
+ * the request, removing it from the list.
+ */
+ if (err == EOK) {
+ tevent_req_done(reql->req);
+ } else {
+ tevent_req_error(reql->req, err);
+ }
+
+ if (reql == *list) {
+ /* The consumer failed to free the
+ * request. Log a bug and continue.
+ */
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "BUG: a callback did not free its request. "
+ "May leak memory\n");
+ /* Skip to the next since a memory leak is non-fatal */
+ *list = (*list)->next;
+ }
+ }
+}
+
+void setent_notify_done(struct setent_req_list **list)
+{
+ return setent_notify(list, EOK);
+}
+
+/*
+ * Return values:
+ * EOK - cache hit
+ * EAGAIN - cache hit, but schedule off band update
+ * ENOENT - cache miss
+ */
+errno_t
+sss_cmd_check_cache(struct ldb_message *msg,
+ int cache_refresh_percent,
+ uint64_t cache_expire)
+{
+ uint64_t lastUpdate;
+ uint64_t midpoint_refresh = 0;
+ time_t now;
+
+ now = time(NULL);
+ lastUpdate = ldb_msg_find_attr_as_uint64(msg, SYSDB_LAST_UPDATE, 0);
+ midpoint_refresh = 0;
+
+ if(cache_refresh_percent) {
+ midpoint_refresh = lastUpdate +
+ (cache_expire - lastUpdate)*cache_refresh_percent/100.0;
+ if (midpoint_refresh - lastUpdate < 10) {
+ /* If the percentage results in an expiration
+ * less than ten seconds after the lastUpdate time,
+ * that's too often we will simply set it to 10s
+ */
+ midpoint_refresh = lastUpdate+10;
+ }
+ }
+
+ if (cache_expire > now) {
+ /* cache still valid */
+
+ if (midpoint_refresh && midpoint_refresh < now) {
+ /* We're past the cache refresh timeout
+ * We'll return the value from the cache, but we'll also
+ * queue the cache entry for update out-of-band.
+ */
+ return EAGAIN;
+ } else {
+ /* Cache is still valid. */
+ return EOK;
+ }
+ }
+
+ /* Cache needs to be updated */
+ return ENOENT;
+}
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
new file mode 100644
index 0000000..ac0e727
--- /dev/null
+++ b/src/responder/common/responder_common.c
@@ -0,0 +1,2001 @@
+/*
+ SSSD
+
+ Common Responder methods
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <popt.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "providers/data_provider.h"
+#include "util/util_creds.h"
+#include "sss_iface/sss_iface_async.h"
+#include "util/sss_chain_id_tevent.h"
+#include "util/sss_chain_id.h"
+
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+#define SHELL_REALLOC_INCREMENT 5
+#define SHELL_REALLOC_MAX 50
+
+static errno_t set_close_on_exec(int fd)
+{
+ int v;
+ int ferr;
+ errno_t error;
+
+ /* Get the current flags for this file descriptor */
+ v = fcntl(fd, F_GETFD, 0);
+
+ errno = 0;
+ /* Set the close-on-exec flags on this fd */
+ ferr = fcntl(fd, F_SETFD, v | FD_CLOEXEC);
+ if (ferr < 0) {
+ error = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to set fd close-on-exec: [%d][%s]\n",
+ error, strerror(error));
+ return error;
+ }
+ return EOK;
+}
+
+static void client_close_fn(struct tevent_context *ev,
+ struct tevent_fd *fde, int fd,
+ void *ptr)
+{
+ errno_t ret;
+ struct cli_ctx *ctx = talloc_get_type(ptr, struct cli_ctx);
+
+ if ((ctx->cfd > 0) && close(ctx->cfd) < 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to close fd [%d]: [%s]\n",
+ ctx->cfd, strerror(ret));
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Terminated client [%p][%d]\n",
+ ctx, ctx->cfd);
+
+ ctx->cfd = -1;
+}
+
+static errno_t get_client_cred(struct cli_ctx *cctx)
+{
+ SEC_CTX secctx;
+ int ret;
+
+ cctx->creds = talloc_zero(cctx, struct cli_creds);
+ if (!cctx->creds) return ENOMEM;
+
+#ifdef HAVE_UCRED
+ socklen_t client_cred_len = sizeof(struct ucred);
+ char proc_path[32];
+ char cmd_line[255] = { 0 };
+ int proc_fd;
+
+ cctx->creds->ucred.uid = -1;
+ cctx->creds->ucred.gid = -1;
+ cctx->creds->ucred.pid = -1;
+
+ ret = getsockopt(cctx->cfd, SOL_SOCKET, SO_PEERCRED, &cctx->creds->ucred,
+ &client_cred_len);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "getsockopt failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+ if (client_cred_len != sizeof(struct ucred)) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "getsockopt returned unexpected message size.\n");
+ return ENOMSG;
+ }
+
+ if (cctx->creds->ucred.pid > -1) {
+ snprintf(proc_path, sizeof(proc_path), "/proc/%d/cmdline",
+ (int)cctx->creds->ucred.pid);
+ proc_fd = open(proc_path, O_RDONLY);
+ if (proc_fd != -1) {
+ if (sss_fd_nonblocking(proc_fd) == EOK) {
+ ret = read(proc_fd, cmd_line, sizeof(cmd_line)-1);
+ if (ret > 0) {
+ cmd_line[ret] = 0;
+ cctx->cmd_line = talloc_strdup(cctx, cmd_line);
+ }
+ }
+ close(proc_fd);
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Client [%p][%d] creds: euid[%d] egid[%d] pid[%d] cmd_line['%s'].\n",
+ cctx, cctx->cfd,
+ cctx->creds->ucred.uid, cctx->creds->ucred.gid,
+ cctx->creds->ucred.pid, cmd_line);
+#endif
+
+ ret = SELINUX_getpeercon(cctx->cfd, &secctx);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The following failure is expected to happen in case SELinux is disabled:\n"
+ "SELINUX_getpeercon failed [%d][%s].\n"
+ "Please, consider enabling SELinux in your system.\n", ret, strerror(ret));
+ /* This is not fatal, as SELinux may simply be disabled */
+ ret = EOK;
+ } else {
+ cctx->creds->selinux_ctx = SELINUX_context_new(secctx);
+ SELINUX_freecon(secctx);
+ }
+
+ return ret;
+}
+
+uid_t client_euid(struct cli_creds *creds)
+{
+ if (!creds) return -1;
+ return cli_creds_get_uid(creds);
+}
+
+errno_t check_allowed_uids(uid_t uid, size_t allowed_uids_count,
+ const uid_t *allowed_uids)
+{
+ size_t c;
+
+ if (allowed_uids == NULL) {
+ return EINVAL;
+ }
+
+ for (c = 0; c < allowed_uids_count; c++) {
+ if (uid == allowed_uids[c]) {
+ return EOK;
+ }
+ }
+
+ return EACCES;
+}
+
+errno_t csv_string_to_uid_array(TALLOC_CTX *mem_ctx, const char *csv_string,
+ size_t *_uid_count, uid_t **_uids)
+{
+ int ret;
+ size_t c;
+ char **list = NULL;
+ int list_size;
+ uid_t *uids = NULL;
+ char *endptr;
+ const char *envvar;
+ bool loops_were_allowed;
+
+ envvar = getenv("_SSS_LOOPS");
+ loops_were_allowed = (envvar == NULL || strcmp(envvar, "NO") != 0);
+
+ if (!loops_were_allowed) {
+ ret = unsetenv("_SSS_LOOPS");
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to unset _SSS_LOOPS.\n");
+ goto done;
+ }
+ }
+
+ ret = split_on_separator(mem_ctx, csv_string, ',', true, false,
+ &list, &list_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ uids = talloc_array(mem_ctx, uint32_t, list_size);
+ if (uids == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; c < list_size; c++) {
+ if (*list[c] == '\0') {
+ DEBUG(SSSDBG_OP_FAILURE, "Empty list item.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ uids[c] = strtouint32(list[c], &endptr, 10);
+ if ((errno != 0) || (*endptr != '\0') || (list[c] == endptr)) {
+ ret = errno;
+ if (ret == ERANGE) {
+ DEBUG(SSSDBG_OP_FAILURE, "List item [%s] is out of range.\n",
+ list[c]);
+ goto done;
+ }
+
+ ret = sss_user_by_name_or_uid(list[c], &uids[c], NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "List item [%s] is neither a valid "
+ "UID nor a user name which could be "
+ "resolved by getpwnam().\n", list[c]);
+ sss_log(SSS_LOG_WARNING, "List item [%s] is neither a valid "
+ "UID nor a user name which could be "
+ "resolved by getpwnam().\n", list[c]);
+ goto done;
+ }
+ }
+ }
+
+ *_uid_count = list_size;
+ *_uids = uids;
+
+ ret = EOK;
+
+done:
+ if (!loops_were_allowed) {
+ if (setenv("_SSS_LOOPS", "NO" , 0) != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to restore _SSS_LOOPS.\n");
+ }
+ }
+ talloc_free(list);
+ if (ret != EOK) {
+ talloc_free(uids);
+ }
+
+ return ret;
+}
+
+static void client_send(struct cli_ctx *cctx)
+{
+ struct cli_protocol *pctx;
+ int ret;
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_send(pctx->creq->out, cctx->cfd);
+ if (ret == EAGAIN) {
+ /* not all data was sent, loop again */
+ return;
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n");
+ talloc_free(cctx);
+ return;
+ }
+
+ /* ok all sent */
+ TEVENT_FD_NOT_WRITEABLE(cctx->cfde);
+ TEVENT_FD_READABLE(cctx->cfde);
+ talloc_zfree(pctx->creq);
+ return;
+}
+
+static int client_cmd_execute(struct cli_ctx *cctx, struct sss_cmd_table *sss_cmds)
+{
+ struct cli_protocol *pctx;
+ enum sss_cli_command cmd;
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+ cmd = sss_packet_get_cmd(pctx->creq->in);
+ return sss_cmd_execute(cctx, cmd, sss_cmds);
+}
+
+static void client_recv(struct cli_ctx *cctx)
+{
+ struct cli_protocol *pctx;
+ int ret;
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+
+ if (!pctx->creq) {
+ pctx->creq = talloc_zero(cctx, struct cli_request);
+ if (!pctx->creq) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to alloc request, aborting client!\n");
+ talloc_free(cctx);
+ return;
+ }
+ }
+
+ if (!pctx->creq->in) {
+ ret = sss_packet_new(pctx->creq, SSS_PACKET_MAX_RECV_SIZE,
+ 0, &pctx->creq->in);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to alloc request, aborting client!\n");
+ talloc_free(cctx);
+ return;
+ }
+ }
+
+ ret = sss_packet_recv(pctx->creq->in, cctx->cfd);
+ switch (ret) {
+ case EOK:
+ /* do not read anymore */
+ TEVENT_FD_NOT_READABLE(cctx->cfde);
+ /* execute command */
+ ret = client_cmd_execute(cctx, cctx->rctx->sss_cmds);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to execute request, aborting client!\n");
+ talloc_free(cctx);
+ }
+ /* past this point cctx can be freed at any time by callbacks
+ * in case of error, do not use it */
+ return;
+
+ case EAGAIN:
+ /* need to read still some data, loop again */
+ break;
+
+ case EINVAL:
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Invalid data from client, closing connection!\n");
+ talloc_free(cctx);
+ break;
+
+ case ENODATA:
+ DEBUG(SSSDBG_FUNC_DATA, "Client disconnected!\n");
+ talloc_free(cctx);
+ break;
+
+ default:
+ DEBUG(SSSDBG_TRACE_FUNC, "Failed to read request, aborting client!\n");
+ talloc_free(cctx);
+ }
+
+ return;
+}
+
+static errno_t schedule_responder_idle_timer(struct resp_ctx *rctx);
+
+static void responder_idle_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *data)
+{
+ struct resp_ctx *rctx;
+ time_t now;
+
+ rctx = talloc_get_type(data, struct resp_ctx);
+
+ now = time(NULL);
+ if (rctx->last_request_time > now) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Time shift detected, re-scheduling the responder timeout\n");
+ goto end;
+ }
+
+ if ((now - rctx->last_request_time) >= rctx->idle_timeout) {
+ /* This responder is idle. Terminate it */
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Terminating idle responder [%p]\n", rctx);
+
+ talloc_free(rctx);
+
+ orderly_shutdown(0);
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Re-scheduling the idle timeout [%s] for the responder [%p]\n",
+ CONFDB_RESPONDER_IDLE_TIMEOUT, rctx);
+
+end:
+ schedule_responder_idle_timer(rctx);
+}
+
+static errno_t schedule_responder_idle_timer(struct resp_ctx *rctx)
+{
+ struct timeval tv;
+
+ tv = tevent_timeval_current_ofs(rctx->idle_timeout / 2, 0);
+
+ talloc_zfree(rctx->idle);
+ rctx->idle = tevent_add_timer(rctx->ev,
+ rctx,
+ tv,
+ responder_idle_handler,
+ rctx);
+ if (rctx->idle == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to allocate time event: responder [%p] shutdown timeout\n",
+ rctx);
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Re-scheduling the idle timeout [%s] for the responder [%p]\n",
+ CONFDB_RESPONDER_IDLE_TIMEOUT, rctx);
+
+ return EOK;
+}
+
+static errno_t setup_responder_idle_timer(struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ rctx->last_request_time = time(NULL);
+
+ ret = schedule_responder_idle_timer(rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Error scheduling the idle timeout [%s] for the responder [%p]: "
+ "%d [%s]\n",
+ CONFDB_RESPONDER_IDLE_TIMEOUT, rctx, ret, sss_strerror(ret));
+ return ret;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Setting up the idle timeout [%s] for the responder [%p]\n",
+ CONFDB_RESPONDER_IDLE_TIMEOUT, rctx);
+
+ return EOK;
+}
+
+static void client_fd_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *ptr)
+{
+ sss_client_fd_handler(ptr, client_recv, client_send, flags);
+}
+
+static errno_t setup_client_idle_timer(struct cli_ctx *cctx);
+
+static int cli_ctx_destructor(struct cli_ctx *cctx)
+{
+ if (cctx->creds == NULL) {
+ return 0;
+ }
+
+ if (cctx->creds->selinux_ctx == NULL) {
+ return 0;
+ }
+
+ SELINUX_context_free(cctx->creds->selinux_ctx);
+ cctx->creds->selinux_ctx = NULL;
+
+ return 0;
+}
+
+struct accept_fd_ctx {
+ struct resp_ctx *rctx;
+ bool is_private;
+ connection_setup_t connection_setup;
+};
+
+/*
+ * Use this function only before the client context is established
+ */
+static void accept_and_terminate_cli(int fd)
+{
+ struct sockaddr_un addr;
+ int client_fd;
+ socklen_t len;
+
+ /* accept and close to signal the client we have a problem */
+ memset(&addr, 0, sizeof(addr));
+ len = sizeof(addr);
+ client_fd = accept(fd, (struct sockaddr *)&addr, &len);
+ if (client_fd == -1) {
+ return;
+ }
+ close(client_fd);
+ return;
+}
+
+static void accept_fd_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *ptr)
+{
+ static uid_t last_violator_uid = (uid_t)-1;
+ /* accept and attach new event handler */
+ struct accept_fd_ctx *accept_ctx =
+ talloc_get_type(ptr, struct accept_fd_ctx);
+ struct resp_ctx *rctx = accept_ctx->rctx;
+ struct cli_ctx *cctx;
+ socklen_t len;
+ struct stat stat_buf;
+ int ret;
+ int fd = accept_ctx->is_private ? rctx->priv_lfd : rctx->lfd;
+
+ rctx->client_id_num++;
+ if (accept_ctx->is_private) {
+ ret = stat(rctx->priv_sock_name, &stat_buf);
+ if (ret == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "stat on privileged pipe failed: [%d][%s].\n",
+ errno, strerror(errno));
+ accept_and_terminate_cli(fd);
+ return;
+ }
+
+ if ( ! (stat_buf.st_uid == 0 && stat_buf.st_gid == 0 &&
+ (stat_buf.st_mode&(S_IFSOCK|S_IRUSR|S_IWUSR)) == stat_buf.st_mode)) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "privileged pipe has an illegal status.\n");
+ accept_and_terminate_cli(fd);
+ return;
+ }
+ }
+
+ cctx = talloc_zero(rctx, struct cli_ctx);
+ if (!cctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Out of memory trying to setup client context%s!\n",
+ accept_ctx->is_private ? " on privileged pipe": "");
+ accept_and_terminate_cli(fd);
+ return;
+ }
+
+ talloc_set_destructor(cctx, cli_ctx_destructor);
+
+ cctx->client_id_num = rctx->client_id_num;
+
+ len = sizeof(cctx->addr);
+ cctx->cfd = accept(fd, (struct sockaddr *)&cctx->addr, &len);
+ if (cctx->cfd == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Accept failed [%s]\n", strerror(errno));
+ talloc_free(cctx);
+ return;
+ }
+
+ cctx->priv = accept_ctx->is_private;
+
+ ret = get_client_cred(cctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_client_cred failed, "
+ "client cred may not be available.\n");
+ }
+
+ if (rctx->allowed_uids_count != 0) {
+ if (client_euid(cctx->creds) == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "allowed_uids configured, " \
+ "but platform does not support " \
+ "reading peer credential from the " \
+ "socket. Access denied.\n");
+ close(cctx->cfd);
+ talloc_free(cctx);
+ return;
+ }
+
+ ret = check_allowed_uids(client_euid(cctx->creds), rctx->allowed_uids_count,
+ rctx->allowed_uids);
+ if (ret != EOK) {
+ if (ret == EACCES) {
+ if (client_euid(cctx->creds) != last_violator_uid) {
+ last_violator_uid = client_euid(cctx->creds);
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Access denied for uid [%"SPRIuid"].\n",
+ last_violator_uid);
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "check_allowed_uids failed.\n");
+ }
+ close(cctx->cfd);
+ talloc_free(cctx);
+ return;
+ }
+ }
+
+ ret = accept_ctx->connection_setup(cctx);
+ if (ret != EOK) {
+ close(cctx->cfd);
+ talloc_free(cctx);
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to setup client handler%s\n",
+ accept_ctx->is_private ? " on privileged pipe" : "");
+ return;
+ }
+
+ cctx->cfde = tevent_add_fd(ev, cctx, cctx->cfd,
+ TEVENT_FD_READ, cctx->cfd_handler,
+ cctx);
+ if (!cctx->cfde) {
+ close(cctx->cfd);
+ talloc_free(cctx);
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to queue client handler%s\n",
+ accept_ctx->is_private ? " on privileged pipe" : "");
+ return;
+ }
+ tevent_fd_set_close_fn(cctx->cfde, client_close_fn);
+
+ cctx->ev = ev;
+ cctx->rctx = rctx;
+
+ /* Record the new time and set up the idle timer */
+ ret = reset_client_idle_timer(cctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not create idle timer for client. "
+ "This connection may not auto-terminate\n");
+ /* Non-fatal, continue */
+ }
+
+ ret = setup_client_idle_timer(cctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not create idle timer for client. "
+ "This connection may not auto-terminate\n");
+ /* Non-fatal, continue */
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "[CID#%u] Client [cmd %s][uid %u][%p][%d] connected%s!\n",
+ cctx->client_id_num, cctx->cmd_line, client_euid(cctx->creds),
+ cctx, cctx->cfd, accept_ctx->is_private ? " to privileged pipe" : "");
+
+ return;
+}
+
+static void client_idle_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *data)
+{
+ time_t now = time(NULL);
+ struct cli_ctx *cctx = talloc_get_type(data, struct cli_ctx);
+
+ if (cctx->last_request_time > now) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Time shift detected, re-scheduling the client timeout [%s].\n",
+ CONFDB_RESPONDER_CLI_IDLE_TIMEOUT);
+ goto done;
+ }
+
+ if ((now - cctx->last_request_time) > cctx->rctx->client_idle_timeout) {
+ /* This connection is idle. Terminate it */
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Terminating idle client [%p][%d]\n",
+ cctx, cctx->cfd);
+
+ /* The cli_ctx destructor will handle the rest */
+ talloc_free(cctx);
+ return;
+ }
+
+done:
+ setup_client_idle_timer(cctx);
+}
+
+errno_t reset_client_idle_timer(struct cli_ctx *cctx)
+{
+ cctx->last_request_time = time(NULL);
+
+ return EOK;
+}
+
+static errno_t setup_client_idle_timer(struct cli_ctx *cctx)
+{
+ struct timeval tv =
+ tevent_timeval_current_ofs(cctx->rctx->client_idle_timeout/2, 0);
+
+ talloc_zfree(cctx->idle);
+
+ cctx->idle = tevent_add_timer(cctx->ev, cctx, tv, client_idle_handler, cctx);
+ if (!cctx->idle) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Idle timer re-set for client [%p][%d]\n",
+ cctx, cctx->cfd);
+
+ return EOK;
+}
+
+static void
+sss_dp_on_reconnect(struct sbus_connection *conn,
+ enum sbus_reconnect_status status,
+ struct be_conn *be_conn);
+
+static void
+sss_dp_init_done(struct tevent_req *req);
+
+static errno_t
+sss_dp_init(struct resp_ctx *rctx,
+ const char *conn_name,
+ const char *cli_name,
+ struct sss_domain_info *domain)
+{
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ int max_retries;
+ errno_t ret;
+
+ ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path,
+ CONFDB_SERVICE_RECON_RETRIES, 3, &max_retries);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read confdb [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ be_conn = talloc_zero(rctx, struct be_conn);
+ if (!be_conn) return ENOMEM;
+
+ be_conn->cli_name = cli_name;
+ be_conn->domain = domain;
+ be_conn->rctx = rctx;
+
+ be_conn->sbus_address = sss_iface_domain_address(be_conn, domain);
+ if (be_conn->sbus_address == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not locate DP address.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ be_conn->bus_name = sss_iface_domain_bus(be_conn, domain);
+ if (be_conn->bus_name == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not locate DP address.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_iface_connect_address(be_conn, rctx->ev, conn_name,
+ be_conn->sbus_address, NULL,
+ &be_conn->conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to connect to backend server.\n");
+ goto done;
+ }
+
+ ret = sss_resp_register_sbus_iface(be_conn->conn, rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Cannot register generic responder "
+ "interface [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ sbus_reconnect_enable(be_conn->conn, max_retries, sss_dp_on_reconnect,
+ be_conn);
+
+ DLIST_ADD_END(rctx->be_conns, be_conn, struct be_conn *);
+
+ /* Identify ourselves to the DP */
+ req = sbus_call_dp_client_Register_send(be_conn, be_conn->conn,
+ be_conn->bus_name,
+ SSS_BUS_PATH, cli_name);
+ if (req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, sss_dp_init_done, be_conn);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(be_conn);
+ }
+
+ return ret;
+}
+
+static void
+sss_dp_on_reconnect(struct sbus_connection *conn,
+ enum sbus_reconnect_status status,
+ struct be_conn *be_conn)
+{
+ struct tevent_req *req;
+
+ if (status != SBUS_RECONNECT_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not reconnect to %s provider.\n",
+ be_conn->domain->name);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Reconnected to the Data Provider.\n");
+
+ /* Identify ourselves to the DP */
+ req = sbus_call_dp_client_Register_send(be_conn, be_conn->conn,
+ be_conn->bus_name,
+ SSS_BUS_PATH,
+ be_conn->cli_name);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sbus_call_dp_client_Register_send() failed\n");
+ return;
+ }
+
+ tevent_req_set_callback(req, sss_dp_init_done, be_conn);
+}
+
+static void
+sss_dp_init_done(struct tevent_req *req)
+{
+ errno_t ret;
+
+ ret = sbus_call_dp_client_Register_recv(req);
+ talloc_zfree(req);
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register client with DP\n");
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Client is registered with DP\n");
+}
+
+int create_pipe_fd(const char *sock_name, int *_fd, mode_t umaskval)
+{
+ struct sockaddr_un addr;
+ mode_t orig_umaskval;
+ errno_t ret;
+ int fd;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ return EIO;
+ }
+
+ orig_umaskval = umask(umaskval);
+
+ ret = sss_fd_nonblocking(fd);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = set_close_on_exec(fd);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path) - 1);
+ addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
+
+ /* make sure we have no old sockets around */
+ ret = unlink(sock_name);
+ if (ret != 0 && errno != ENOENT) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot remove old socket (errno=%d [%s]), bind might fail!\n",
+ ret, sss_strerror(ret));
+ }
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to bind on socket '%s' [%d]: %s\n",
+ sock_name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (listen(fd, 128) == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to listen on socket '%s' [%d]: %s\n",
+ sock_name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ /* restore previous umask value */
+ umask(orig_umaskval);
+ if (ret == EOK) {
+ *_fd = fd;
+ } else {
+ close(fd);
+ }
+ return ret;
+}
+
+/* create a unix socket and listen to it */
+static int set_unix_socket(struct resp_ctx *rctx,
+ connection_setup_t conn_setup)
+{
+ errno_t ret;
+ struct accept_fd_ctx *accept_ctx = NULL;
+
+/* for future use */
+#if 0
+ char *default_pipe;
+ int ret;
+
+ default_pipe = talloc_asprintf(rctx, "%s/%s", PIPE_PATH,
+ rctx->sss_pipe_name);
+ if (!default_pipe) {
+ return ENOMEM;
+ }
+
+ ret = confdb_get_string(rctx->cdb, rctx,
+ rctx->confdb_socket_path, "unixSocket",
+ default_pipe, &rctx->sock_name);
+ if (ret != EOK) {
+ talloc_free(default_pipe);
+ return ret;
+ }
+ talloc_free(default_pipe);
+
+ default_pipe = talloc_asprintf(rctx, "%s/private/%s", PIPE_PATH,
+ rctx->sss_pipe_name);
+ if (!default_pipe) {
+ return ENOMEM;
+ }
+
+ ret = confdb_get_string(rctx->cdb, rctx,
+ rctx->confdb_socket_path, "privUnixSocket",
+ default_pipe, &rctx->priv_sock_name);
+ if (ret != EOK) {
+ talloc_free(default_pipe);
+ return ret;
+ }
+ talloc_free(default_pipe);
+#endif
+
+ if (rctx->sock_name != NULL ) {
+ /* Set the umask so that permissions are set right on the socket.
+ * It must be readable and writable by anybody on the system. */
+ if (rctx->lfd == -1) {
+ ret = create_pipe_fd(rctx->sock_name, &rctx->lfd, SCKT_RSP_UMASK);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ accept_ctx = talloc_zero(rctx, struct accept_fd_ctx);
+ if(!accept_ctx) goto failed;
+ accept_ctx->rctx = rctx;
+ accept_ctx->is_private = false;
+ accept_ctx->connection_setup = conn_setup;
+
+ rctx->lfde = tevent_add_fd(rctx->ev, rctx, rctx->lfd,
+ TEVENT_FD_READ, accept_fd_handler,
+ accept_ctx);
+ if (!rctx->lfde) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to queue handler on pipe\n");
+ goto failed;
+ }
+ }
+
+ if (rctx->priv_sock_name != NULL ) {
+ /* create privileged pipe */
+ if (rctx->priv_lfd == -1) {
+ ret = create_pipe_fd(rctx->priv_sock_name, &rctx->priv_lfd,
+ DFL_RSP_UMASK);
+ if (ret != EOK) {
+ goto failed;
+ }
+ }
+
+ accept_ctx = talloc_zero(rctx, struct accept_fd_ctx);
+ if(!accept_ctx) goto failed;
+ accept_ctx->rctx = rctx;
+ accept_ctx->is_private = true;
+ accept_ctx->connection_setup = conn_setup;
+
+ rctx->priv_lfde = tevent_add_fd(rctx->ev, rctx, rctx->priv_lfd,
+ TEVENT_FD_READ, accept_fd_handler,
+ accept_ctx);
+ if (!rctx->priv_lfde) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to queue handler on privileged pipe\n");
+ goto failed;
+ }
+ }
+
+ return EOK;
+
+failed:
+ if (rctx->lfd >= 0) close(rctx->lfd);
+ if (rctx->priv_lfd >= 0) close(rctx->priv_lfd);
+ return EIO;
+}
+
+int activate_unix_sockets(struct resp_ctx *rctx,
+ connection_setup_t conn_setup)
+{
+ int ret;
+
+#ifdef HAVE_SYSTEMD
+ struct sockaddr_un sockaddr;
+ socklen_t sockaddr_len = sizeof(sockaddr);
+
+ if (rctx->lfd == -1 && rctx->priv_lfd == -1) {
+ int numfds = (rctx->sock_name ? 1 : 0)
+ + (rctx->priv_sock_name ? 1 : 0);
+ /* but if systemd support is available, check if the sockets
+ * have been opened for us, via socket activation */
+ ret = sd_listen_fds(1);
+ if (ret < 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unexpected error probing for active sockets. "
+ "Will proceed with no sockets. [Error %d (%s)]\n",
+ -ret, sss_strerror(-ret));
+ } else if (ret > numfds) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Too many activated sockets have been found, "
+ "expected %d, found %d\n", numfds, ret);
+ ret = E2BIG;
+ goto done;
+ }
+
+ if (ret == numfds) {
+ rctx->lfd = SD_LISTEN_FDS_START;
+ ret = sd_is_socket_unix(rctx->lfd, SOCK_STREAM, 1, NULL, 0);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Activated socket is not a UNIX listening socket\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = getsockname(rctx->lfd, (struct sockaddr *) &sockaddr, &sockaddr_len);
+ if (ret == EOK) {
+ if (rctx->sock_name &&
+ memcmp(rctx->sock_name, sockaddr.sun_path, strlen(rctx->sock_name)) != 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Warning: socket path defined in systemd unit (%s) and sssd.conf (%s) don't match\n",
+ sockaddr.sun_path, rctx->sock_name);
+ }
+ }
+
+ ret = sss_fd_nonblocking(rctx->lfd);
+ if (ret != EOK) goto done;
+ if (numfds == 2) {
+ rctx->priv_lfd = SD_LISTEN_FDS_START + 1;
+ ret = sd_is_socket_unix(rctx->priv_lfd, SOCK_STREAM, 1, NULL, 0);
+ if (ret < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Activated priv socket is not a UNIX listening socket\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sss_fd_nonblocking(rctx->priv_lfd);
+ if (ret != EOK) goto done;
+ }
+ }
+ }
+#endif
+
+ ret = set_unix_socket(rctx, conn_setup);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Fatal error initializing sockets\n");
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+void sss_client_fd_handler(void *ptr,
+ void (*recv_fn) (struct cli_ctx *cctx),
+ void (*send_fn) (struct cli_ctx *cctx),
+ uint16_t flags)
+{
+ errno_t ret;
+ uint64_t old_chain_id;
+ struct cli_ctx *cctx = talloc_get_type(ptr, struct cli_ctx);
+
+ /* Always reset the responder idle timer on any activity */
+ cctx->rctx->last_request_time = time(NULL);
+
+ /* Always reset the client idle timer on any activity */
+ ret = reset_client_idle_timer(cctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not create idle timer for the client. "
+ "This connection may not auto-terminate.\n");
+ /* Non-fatal, continue */
+ }
+
+ /* Set the chain id */
+ old_chain_id = sss_chain_id_set(cctx->client_id_num);
+
+ if (flags & TEVENT_FD_READ) {
+ recv_fn(cctx);
+ return;
+ }
+
+ if (flags & TEVENT_FD_WRITE) {
+ send_fn(cctx);
+ return;
+ }
+ /* Restore the original chain id */
+ sss_chain_id_set(old_chain_id);
+}
+
+int sss_connection_setup(struct cli_ctx *cctx)
+{
+ cctx->protocol_ctx = talloc_zero(cctx, struct cli_protocol);
+ if (!cctx->protocol_ctx) {
+ return ENOMEM;
+ }
+
+ cctx->cfd_handler = client_fd_handler;
+
+ return EOK;
+}
+
+static int sss_responder_ctx_destructor(void *ptr)
+{
+ struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx);
+
+ /* mark that we are shutting down the responder, so it is propagated
+ * into underlying contexts that are freed right before rctx */
+ DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n");
+ rctx->shutting_down = true;
+
+ return 0;
+}
+
+static errno_t responder_init_ncache(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ struct sss_nc_ctx **ncache)
+{
+ uint32_t neg_timeout;
+ uint32_t locals_timeout;
+ int tmp_value;
+ int ret;
+
+ /* neg_timeout */
+ ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_ENTRY_NEG_TIMEOUT,
+ 15, &tmp_value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Fatal failure of setup negative cache timeout [%s].\n",
+ CONFDB_NSS_ENTRY_NEG_TIMEOUT);
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (tmp_value < 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ neg_timeout = tmp_value;
+
+ /* local_timeout */
+ ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT,
+ CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT_DEFAULT,
+ &tmp_value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Fatal failure of setup negative cache timeout [%s].\n",
+ CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT);
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (tmp_value < 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ locals_timeout = tmp_value;
+
+ /* negative cache init */
+ ret = sss_ncache_init(mem_ctx, neg_timeout, locals_timeout, ncache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Fatal failure of initializing negative cache.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t sss_get_etc_shells(TALLOC_CTX *mem_ctx, char ***_shells)
+{
+ int i = 0;
+ char *sh;
+ char **shells = NULL;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ int size;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ shells = talloc_array(tmp_ctx, char *, SHELL_REALLOC_INCREMENT);
+ if (!shells) {
+ ret = ENOMEM;
+ goto done;
+ }
+ size = SHELL_REALLOC_INCREMENT;
+
+ setusershell();
+ while ((sh = getusershell())) {
+ shells[i] = talloc_strdup(shells, sh);
+ if (!shells[i]) {
+ endusershell();
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Found shell %s in /etc/shells\n", shells[i]);
+ i++;
+
+ if (i == size) {
+ size += SHELL_REALLOC_INCREMENT;
+ if (size > SHELL_REALLOC_MAX) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Reached maximum number of shells [%d]. "
+ "Users may be denied access. "
+ "Please check /etc/shells for sanity\n",
+ SHELL_REALLOC_MAX);
+ break;
+ }
+ shells = talloc_realloc(NULL, shells, char *,
+ size);
+ if (!shells) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+ endusershell();
+
+ if (i + 1 < size) {
+ shells = talloc_realloc(NULL, shells, char *, i + 1);
+ if (!shells) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ shells[i] = NULL;
+
+ *_shells = talloc_move(mem_ctx, &shells);
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sss_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ struct sss_cmd_table sss_cmds[],
+ const char *sss_pipe_name,
+ int pipe_fd,
+ const char *sss_priv_pipe_name,
+ int priv_pipe_fd,
+ const char *confdb_service_path,
+ const char *conn_name,
+ const char *svc_name,
+ connection_setup_t conn_setup,
+ struct resp_ctx **responder_ctx)
+{
+ struct resp_ctx *rctx;
+ struct sss_domain_info *dom;
+ int ret;
+ char *tmp = NULL;
+
+ rctx = talloc_zero(mem_ctx, struct resp_ctx);
+ if (!rctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n");
+ return ENOMEM;
+ }
+ rctx->ev = ev;
+ rctx->cdb = cdb;
+ rctx->sss_cmds = sss_cmds;
+ rctx->sock_name = sss_pipe_name;
+ rctx->priv_sock_name = sss_priv_pipe_name;
+ rctx->lfd = pipe_fd;
+ rctx->priv_lfd = priv_pipe_fd;
+ rctx->confdb_service_path = confdb_service_path;
+ rctx->shutting_down = false;
+ rctx->socket_activated = is_socket_activated();
+ rctx->dbus_activated = is_dbus_activated();
+
+ talloc_set_destructor((TALLOC_CTX*)rctx, sss_responder_ctx_destructor);
+
+ ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path,
+ CONFDB_RESPONDER_CLI_IDLE_TIMEOUT,
+ CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT,
+ &rctx->client_idle_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the client idle timeout [%s] [%d]: %s\n",
+ CONFDB_RESPONDER_CLI_IDLE_TIMEOUT, ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Ensure that the client timeout is at least ten seconds */
+ if (rctx->client_idle_timeout < 10) {
+ rctx->client_idle_timeout = 10;
+ }
+
+ if (rctx->socket_activated || rctx->dbus_activated) {
+ ret = responder_setup_idle_timeout_config(rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+ }
+
+ ret = confdb_get_bool(rctx->cdb, rctx->confdb_service_path,
+ CONFDB_RESPONDER_CACHE_FIRST,
+ CONFDB_RESPONDER_CACHE_FIRST_DEFAILT,
+ &rctx->cache_first);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get \"cache_first_option\".\n"
+ "Querying the caches first before querying the "
+ "Data Providers will not be enforced [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ }
+
+ ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path,
+ CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT,
+ GET_DOMAINS_DEFAULT_TIMEOUT, &rctx->domains_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the default domain timeout [%s] [%d]: %s\n",
+ CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT, ret, strerror(ret));
+ goto fail;
+ }
+
+ if (rctx->domains_timeout < 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "timeout [%s] can't be set to negative value, "
+ "setting default [%d] seconds.\n",
+ CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT,
+ GET_DOMAINS_DEFAULT_TIMEOUT);
+ rctx->domains_timeout = GET_DOMAINS_DEFAULT_TIMEOUT;
+ }
+
+ ret = confdb_get_domains(rctx->cdb, &rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up domain map\n");
+ goto fail;
+ }
+
+ ret = confdb_get_string(rctx->cdb, rctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_DEFAULT_DOMAIN, NULL,
+ &rctx->default_domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the default domain [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = confdb_get_string(rctx->cdb, rctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_OVERRIDE_SPACE, NULL,
+ &tmp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the space substitution character [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ if (tmp != NULL) {
+ if (strlen(tmp) > 1) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Option %s is longer than 1 character "
+ "only the first character %c will be used\n",
+ CONFDB_MONITOR_OVERRIDE_SPACE, tmp[0]);
+ }
+
+ rctx->override_space = tmp[0];
+ }
+
+ ret = confdb_get_string(rctx->cdb, rctx,
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_DOMAIN_RESOLUTION_ORDER, NULL,
+ &tmp);
+ if (ret == EOK) {
+ rctx->domain_resolution_order = sss_replace_char(rctx, tmp, ',', ':');
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot get the \"domain_resolution_order\" option.\n"
+ "The set up lookup_order won't be followed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ }
+
+ /* Read shell settings */
+ ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_OVERRIDE_SHELL, NULL,
+ &rctx->override_shell);
+ if (ret != EOK && ret != ENOENT) goto fail;
+
+ ret = confdb_get_string_as_list(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_ALLOWED_SHELL,
+ &rctx->allowed_shells);
+ if (ret != EOK && ret != ENOENT) goto fail;
+
+ ret = confdb_get_string_as_list(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_VETOED_SHELL,
+ &rctx->vetoed_shells);
+ if (ret != EOK && ret != ENOENT) goto fail;
+
+ ret = sss_get_etc_shells(rctx, &rctx->etc_shells);
+ if (ret != EOK) goto fail;
+
+ ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_SHELL_FALLBACK,
+ CONFDB_DEFAULT_SHELL_FALLBACK,
+ &rctx->shell_fallback);
+ if (ret != EOK) goto fail;
+
+ ret = confdb_get_string(cdb, rctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_DEFAULT_SHELL,
+ NULL,
+ &rctx->default_shell);
+ if (ret != EOK) goto fail;
+
+ /* Read session_recording section */
+ ret = session_recording_conf_load(rctx, rctx->cdb, &rctx->sr_conf);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed loading session recording configuration: %s\n",
+ strerror(ret));
+ goto fail;
+ }
+
+ for (dom = rctx->domains; dom; dom = get_next_domain(dom, 0)) {
+ ret = sss_names_init(rctx->cdb, rctx->cdb, dom->name, &dom->names);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "fatal error initializing regex data for domain: %s\n",
+ dom->name);
+ goto fail;
+ }
+
+ ret = sss_dp_init(rctx, conn_name, svc_name, dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "fatal error setting up backend connector\n");
+ goto fail;
+ }
+ }
+
+ ret = sysdb_init(rctx, rctx->domains);
+ if (ret != EOK) {
+ SYSDB_VERSION_ERROR_DAEMON(ret);
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "fatal error initializing sysdb connection\n");
+ goto fail;
+ }
+
+ /* after all initializations we are ready to listen on our socket */
+ ret = activate_unix_sockets(rctx, conn_setup);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing socket\n");
+ goto fail;
+ }
+
+ ret = responder_init_ncache(rctx, rctx->cdb, &rctx->ncache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "fatal error initializing negcache\n");
+ goto fail;
+ }
+
+ ret = sss_ad_default_names_ctx(rctx, &rctx->global_names);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_ad_default_names_ctx failed.\n");
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Responder initialization complete (%s)\n",
+ rctx->socket_activated ? "socket-activated" :
+ rctx->dbus_activated ? "dbus-activated" :
+ "explicitly configured");
+
+ *responder_ctx = rctx;
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int sss_dp_get_domain_conn(struct resp_ctx *rctx, const char *domain,
+ struct be_conn **_conn)
+{
+ struct be_conn *iter;
+
+ if (!rctx->be_conns) return ENOENT;
+
+ for (iter = rctx->be_conns; iter; iter = iter->next) {
+ if (strcasecmp(domain, iter->domain->name) == 0) break;
+ }
+
+ if (!iter) return ENOENT;
+
+ *_conn = iter;
+
+ return EOK;
+}
+
+struct sss_domain_info *
+responder_get_domain(struct resp_ctx *rctx, const char *name)
+{
+ struct sss_domain_info *dom;
+ struct sss_domain_info *ret_dom = NULL;
+
+ for (dom = rctx->domains; dom;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ if (sss_domain_get_state(dom) == DOM_DISABLED) {
+ continue;
+ }
+
+ if (strcasecmp(dom->name, name) == 0 ||
+ (dom->flat_name != NULL &&
+ strcasecmp(dom->flat_name, name) == 0)) {
+ ret_dom = dom;
+ break;
+ }
+ }
+
+ if (!ret_dom) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown domain [%s]\n", name);
+ }
+
+ return ret_dom;
+}
+
+errno_t responder_get_domain_by_id(struct resp_ctx *rctx, const char *id,
+ struct sss_domain_info **_ret_dom)
+{
+ struct sss_domain_info *dom;
+ struct sss_domain_info *ret_dom = NULL;
+ size_t id_len;
+ size_t dom_id_len;
+ int ret;
+
+ if (id == NULL || _ret_dom == NULL) {
+ return EINVAL;
+ }
+
+ id_len = strlen(id);
+
+ for (dom = rctx->domains; dom;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ if (sss_domain_get_state(dom) == DOM_DISABLED ||
+ dom->domain_id == NULL) {
+ continue;
+ }
+
+ dom_id_len = strlen(dom->domain_id);
+ if ((id_len >= dom_id_len) &&
+ strncasecmp(dom->domain_id, id, dom_id_len) == 0) {
+ if (IS_SUBDOMAIN(dom) &&
+ ((time(NULL) - dom->parent->subdomains_last_checked.tv_sec) >
+ rctx->domains_timeout)) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Domain entry with id [%s] " \
+ "is expired.\n", id);
+ ret = EAGAIN;
+ goto done;
+ }
+ ret_dom = dom;
+ break;
+ }
+ }
+
+ if (ret_dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown domain id [%s], checking for "
+ "possible subdomains!\n", id);
+ ret = ENOENT;
+ } else {
+ *_ret_dom = ret_dom;
+ ret = EOK;
+ }
+
+done:
+ return ret;
+}
+
+errno_t
+responder_logrotate(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct resp_ctx *rctx)
+{
+ return server_common_rotate_logs(rctx->cdb, rctx->confdb_service_path);
+
+ return EOK;
+}
+
+void responder_set_fd_limit(rlim_t fd_limit)
+{
+ struct rlimit current_limit, new_limit;
+ int limret;
+
+ /* First, let's see if we have permission to just set
+ * the value as-is.
+ */
+ new_limit.rlim_cur = fd_limit;
+ new_limit.rlim_max = fd_limit;
+ limret = setrlimit(RLIMIT_NOFILE, &new_limit);
+ if (limret == 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Maximum file descriptors set to [%"SPRIrlim"]\n",
+ new_limit.rlim_cur);
+ return;
+ }
+
+ /* We couldn't set the soft and hard limits to this
+ * value. Let's see how high we CAN set it.
+ */
+
+ /* Determine the maximum hard limit */
+ limret = getrlimit(RLIMIT_NOFILE, &current_limit);
+ if (limret == 0) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Current fd limit: [%"SPRIrlim"]\n",
+ current_limit.rlim_cur);
+ /* Choose the lesser of the requested and the hard limit */
+ if (current_limit.rlim_max < fd_limit) {
+ new_limit.rlim_cur = current_limit.rlim_max;
+ } else {
+ new_limit.rlim_cur = fd_limit;
+ }
+ new_limit.rlim_max = current_limit.rlim_max;
+
+ limret = setrlimit(RLIMIT_NOFILE, &new_limit);
+ if (limret == 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Maximum file descriptors set to [%"SPRIrlim"]\n",
+ new_limit.rlim_cur);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not set new fd limits. Proceeding with "
+ "[%"SPRIrlim"]\n", current_limit.rlim_cur);
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not determine fd limits. "
+ "Proceeding with system values\n");
+ }
+}
+
+errno_t responder_setup_idle_timeout_config(struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ ret = confdb_get_int(rctx->cdb, rctx->confdb_service_path,
+ CONFDB_RESPONDER_IDLE_TIMEOUT,
+ CONFDB_RESPONDER_IDLE_DEFAULT_TIMEOUT,
+ &rctx->idle_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the responder idle timeout [%s] [%d]: %s\n",
+ CONFDB_RESPONDER_IDLE_TIMEOUT, ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ /* Idle timeout set to 0 means that no timeout will be set up to
+ * the responder */
+ if (rctx->idle_timeout == 0) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Responder idle timeout won't be set up as the "
+ "responder_idle_timeout is set to 0\n");
+ } else {
+ /* Ensure that the responder timeout is at least sixty seconds */
+ if (rctx->idle_timeout < 60) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "responder_idle_timeout is set to a value lower than "
+ "the minimum allowed (60s). "
+ "The minimum allowed value will be used.\n");
+
+ rctx->idle_timeout = 60;
+ }
+
+ ret = setup_responder_idle_timer(rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "An error occurred when setting up the responder's idle "
+ "timeout [%s] for the responder [%p]: %s [%d].\n"
+ "The responder won't be automatically shutdown after %d "
+ "seconds inactive.\n",
+ CONFDB_RESPONDER_IDLE_TIMEOUT,
+ rctx, sss_strerror(ret), ret,
+ rctx->idle_timeout);
+ }
+ }
+
+ ret = EOK;
+
+fail:
+ return ret;
+
+}
+
+/* ====== Helper functions for the domain resolution order ======= */
+static errno_t
+sss_resp_new_cr_domains_from_ipa_id_view(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ struct sysdb_ctx *sysdb,
+ struct cache_req_domain **_cr_domains)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct cache_req_domain *cr_domains = NULL;
+ const char *domain_resolution_order = NULL;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_get_view_domain_resolution_order(tmp_ctx, sysdb,
+ &domain_resolution_order);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sysdb_get_view_cache_req_domain() failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ goto done;
+ }
+
+ ret = cache_req_domain_new_list_from_domain_resolution_order(
+ mem_ctx, domains, domain_resolution_order, &cr_domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_DEFAULT,
+ "cache_req_domain_new_list_from_domain_resolution_order() "
+ "failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_cr_domains = cr_domains;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sss_resp_new_cr_domains_from_ipa_config(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ struct sysdb_ctx *sysdb,
+ const char *domain,
+ struct cache_req_domain **_cr_domains)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *domain_resolution_order = NULL;
+ errno_t ret;
+
+ *_cr_domains = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_domain_get_domain_resolution_order(tmp_ctx, sysdb, domain,
+ &domain_resolution_order);
+
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sysdb_domain_get_cache_req_domain() failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ goto done;
+ }
+
+ ret = cache_req_domain_new_list_from_domain_resolution_order(
+ mem_ctx, domains, domain_resolution_order, _cr_domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_DEFAULT,
+ "cache_req_domain_new_list_from_domain_resolution_order() "
+ "failed [%d]: [%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sss_resp_populate_cr_domains(struct resp_ctx *rctx)
+{
+ struct cache_req_domain *cr_domains = NULL;
+ struct sss_domain_info *dom;
+ errno_t ret;
+
+ if (rctx->domain_resolution_order != NULL) {
+ ret = cache_req_domain_new_list_from_domain_resolution_order(
+ rctx, rctx->domains,
+ rctx->domain_resolution_order, &cr_domains);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Using domain_resolution_order from sssd.conf\n");
+ goto done;
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to use domain_resolution_order set in the config file.\n"
+ "Trying to fallback to use ipaDomainOrderResolution setup by "
+ "IPA.\n");
+ }
+ }
+
+ for (dom = rctx->domains; dom != NULL; dom = dom->next) {
+ if (dom->provider != NULL && strcmp(dom->provider, "ipa") == 0) {
+ break;
+ }
+ }
+
+ if (dom == NULL) {
+ ret = cache_req_domain_new_list_from_domain_resolution_order(
+ rctx, rctx->domains, NULL, &cr_domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to flatten the list of domains.\n");
+ }
+ goto done;
+ }
+
+ if (dom->has_views) {
+ ret = sss_resp_new_cr_domains_from_ipa_id_view(rctx, rctx->domains,
+ dom->sysdb,
+ &cr_domains);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Using domain_resolution_order from IPA ID View\n");
+ goto done;
+ }
+
+ if (ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to use ipaDomainResolutionOrder set for the "
+ "view \"%s\".\n"
+ "Trying to fallback to use ipaDomainOrderResolution "
+ "set in ipaConfig for the domain: %s.\n",
+ dom->view_name, dom->name);
+ }
+ }
+
+ ret = sss_resp_new_cr_domains_from_ipa_config(rctx, rctx->domains,
+ dom->sysdb, dom->name,
+ &cr_domains);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Using domain_resolution_order from IPA Config\n");
+ goto done;
+ }
+
+ if (ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to use ipaDomainResolutionOrder set in ipaConfig "
+ "for the domain: \"%s\".\n"
+ "No ipaDomainResolutionOrder will be followed.\n",
+ dom->name);
+ }
+
+ ret = cache_req_domain_new_list_from_domain_resolution_order(
+ rctx, rctx->domains, NULL, &cr_domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to flatten the list of domains.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ cache_req_domain_list_zfree(&rctx->cr_domains);
+ rctx->cr_domains = cr_domains;
+
+ return ret;
+}
+
+/**
+ * Helper functions to format output names
+ */
+int sized_output_name(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ const char *orig_name,
+ struct sss_domain_info *name_dom,
+ struct sized_string **_name)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ char *name_str;
+ struct sized_string *name;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ name = talloc_zero(tmp_ctx, struct sized_string);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_output_fqname(name, name_dom, orig_name,
+ rctx->override_space, &name_str);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ to_sized_string(name, name_str);
+ *_name = talloc_steal(mem_ctx, name);
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+int sized_domain_name(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ const char *member_name,
+ struct sized_string **_name)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ char *domname;
+ struct sss_domain_info *member_dom;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_parse_internal_fqname(tmp_ctx, member_name, NULL, &domname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_internal_fqname failed\n");
+ goto done;
+ }
+
+ if (domname == NULL) {
+ ret = ERR_WRONG_NAME_FORMAT;
+ goto done;
+ }
+
+ member_dom = find_domain_by_name(get_domains_head(rctx->domains),
+ domname, true);
+ if (member_dom == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ ret = sized_output_name(mem_ctx, rctx, member_name,
+ member_dom, _name);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c
new file mode 100644
index 0000000..227a229
--- /dev/null
+++ b/src/responder/common/responder_dp.c
@@ -0,0 +1,460 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2009 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 <sys/time.h>
+#include <time.h>
+#include "util/util.h"
+#include "util/sss_chain_id.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "providers/data_provider.h"
+
+#ifdef BUILD_FILES_PROVIDER
+static errno_t
+sss_dp_account_files_params(struct sss_domain_info *dom,
+ enum sss_dp_acct_type type_in,
+ const char *opt_name_in,
+ enum sss_dp_acct_type *_type_out,
+ const char **_opt_name_out)
+{
+ if (type_in != SSS_DP_CERT) {
+ if (sss_domain_get_state(dom) != DOM_INCONSISTENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "The entries in the files domain are up-to-date\n");
+ return EOK;
+ }
+
+ if (sss_domain_fallback_to_nss(dom)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Domain files is not consistent, falling back to nss.\n");
+ return ENOENT;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Domain files is not consistent, issuing update\n");
+ }
+
+ switch(type_in) {
+ case SSS_DP_USER:
+ case SSS_DP_GROUP:
+ *_type_out = type_in;
+ *_opt_name_out = NULL;
+ return EAGAIN;
+ case SSS_DP_INITGROUPS:
+ /* There is no initgroups enumeration so let's use a dummy
+ * name to let the DP chain the requests
+ */
+ *_type_out = type_in;
+ *_opt_name_out = DP_REQ_OPT_FILES_INITGR;
+ return EAGAIN;
+ case SSS_DP_CERT:
+ /* Let the backend handle certificate mapping for local users */
+ *_type_out = type_in;
+ *_opt_name_out = opt_name_in;
+ return EAGAIN;
+ /* These are not handled by the files provider, just fall back */
+ case SSS_DP_SUBID_RANGES:
+ case SSS_DP_NETGR:
+ case SSS_DP_SERVICES:
+ case SSS_DP_SECID:
+ case SSS_DP_USER_AND_GROUP:
+ case SSS_DP_WILDCARD_USER:
+ case SSS_DP_WILDCARD_GROUP:
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unhandled type %d\n", type_in);
+ return EINVAL;
+}
+#endif
+
+static errno_t
+sss_dp_get_account_filter(TALLOC_CTX *mem_ctx,
+ enum sss_dp_acct_type type,
+ bool fast_reply,
+ const char *opt_name,
+ uint32_t opt_id,
+ uint32_t *_dp_flags,
+ uint32_t *_entry_type,
+ char **_filter)
+{
+ uint32_t entry_type = 0;
+ uint32_t dp_flags;
+ char *filter;
+
+ switch (type) {
+ case SSS_DP_USER:
+ case SSS_DP_WILDCARD_USER:
+ entry_type = BE_REQ_USER;
+ break;
+ case SSS_DP_GROUP:
+ case SSS_DP_WILDCARD_GROUP:
+ entry_type = BE_REQ_GROUP;
+ break;
+ case SSS_DP_INITGROUPS:
+ entry_type = BE_REQ_INITGROUPS;
+ break;
+ case SSS_DP_SUBID_RANGES:
+ entry_type = BE_REQ_SUBID_RANGES;
+ break;
+ case SSS_DP_NETGR:
+ entry_type = BE_REQ_NETGROUP;
+ break;
+ case SSS_DP_SERVICES:
+ entry_type = BE_REQ_SERVICES;
+ break;
+ case SSS_DP_SECID:
+ entry_type = BE_REQ_BY_SECID;
+ break;
+ case SSS_DP_USER_AND_GROUP:
+ entry_type = BE_REQ_USER_AND_GROUP;
+ break;
+ case SSS_DP_CERT:
+ entry_type = BE_REQ_BY_CERT;
+ break;
+ }
+
+ dp_flags = fast_reply ? DP_FAST_REPLY : 0;
+
+ if (opt_name != NULL) {
+ switch(type) {
+ case SSS_DP_SECID:
+ filter = talloc_asprintf(mem_ctx, "%s=%s", DP_SEC_ID,
+ opt_name);
+ break;
+ case SSS_DP_CERT:
+ filter = talloc_asprintf(mem_ctx, "%s=%s", DP_CERT,
+ opt_name);
+ break;
+ case SSS_DP_WILDCARD_USER:
+ case SSS_DP_WILDCARD_GROUP:
+ filter = talloc_asprintf(mem_ctx, "%s=%s", DP_WILDCARD,
+ opt_name);
+ break;
+ default:
+ filter = talloc_asprintf(mem_ctx, "name=%s", opt_name);
+ break;
+ }
+ } else if (opt_id != 0) {
+ filter = talloc_asprintf(mem_ctx, "idnumber=%u", opt_id);
+ } else {
+ filter = talloc_strdup(mem_ctx, ENUM_INDICATOR);
+ }
+
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
+ return ENOMEM;
+ }
+
+ *_dp_flags = dp_flags;
+ *_entry_type = entry_type;
+ *_filter = filter;
+
+ return EOK;
+}
+
+struct sss_dp_get_account_state {
+ uint16_t dp_error;
+ uint32_t error;
+ const char *error_message;
+};
+
+static void sss_dp_get_account_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_acct_type type,
+ const char *opt_name,
+ uint32_t opt_id,
+ const char *extra)
+{
+ struct sss_dp_get_account_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ uint32_t entry_type;
+ uint32_t dp_flags;
+ char *filter;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_account_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ /* either, or, not both */
+ if (opt_name != NULL && opt_id != 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (dom == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+#ifdef BUILD_FILES_PROVIDER
+ if (is_files_provider(dom)) {
+ /* This is a special case. If the files provider is just being updated,
+ * we issue an enumeration request. We always use the same request type
+ * (user enumeration) to make sure concurrent requests are just chained
+ * in the Data Provider */
+ ret = sss_dp_account_files_params(dom, type, opt_name,
+ &type, &opt_name);
+ if (ret == EOK) {
+ state->dp_error = DP_ERR_OK;
+ state->error = EOK;
+ state->error_message = talloc_strdup(state, "Success");
+ if (state->error_message == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ goto done;
+ } else if (ret != EAGAIN) {
+ DEBUG((ret == ENOENT) ? SSSDBG_MINOR_FAILURE : SSSDBG_OP_FAILURE,
+ "Failed to set files provider update [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ /* EAGAIN, fall through to issuing the request */
+ }
+#endif
+
+ ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ dom->name);
+ ret = EIO;
+ goto done;
+ }
+
+ /* Build filter. */
+ ret = sss_dp_get_account_filter(state, type, fast_reply, opt_name, opt_id,
+ &dp_flags, &entry_type, &filter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Creating request for [%s][%#x][%s][%s:%s]\n",
+ dom->name, entry_type, be_req2str(entry_type),
+ filter, extra == NULL ? "-" : extra);
+
+ subreq = sbus_call_dp_dp_getAccountInfo_send(state, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH, dp_flags,
+ entry_type, filter, dom->name, extra,
+ sss_chain_id_get());
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_dp_get_account_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, rctx->ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, rctx->ev);
+ }
+
+ return req;
+}
+
+static void sss_dp_get_account_done(struct tevent_req *subreq)
+{
+ struct sss_dp_get_account_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_dp_get_account_state);
+
+ ret = sbus_call_dp_dp_getAccountInfo_recv(state, subreq, &state->dp_error,
+ &state->error,
+ &state->error_message);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+sss_dp_get_account_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char **_error_message)
+{
+ struct sss_dp_get_account_state *state;
+ state = tevent_req_data(req, struct sss_dp_get_account_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_dp_error = state->dp_error;
+ *_error = state->error;
+ *_error_message = talloc_steal(mem_ctx, state->error_message);
+
+ return EOK;
+}
+
+struct sss_dp_resolver_get_state {
+ uint16_t dp_error;
+ uint32_t error;
+ const char *error_message;
+};
+
+static void sss_dp_resolver_get_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sss_dp_resolver_get_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ uint32_t entry_type,
+ uint32_t filter_type,
+ const char *filter_value)
+{
+ struct sss_dp_resolver_get_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct be_conn *be_conn;
+ uint32_t dp_flags;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_dp_resolver_get_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ /* Validate filter_type */
+ switch (filter_type) {
+ case BE_FILTER_NAME:
+ case BE_FILTER_ADDR:
+ case BE_FILTER_ENUM:
+ break;
+ default:
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (dom == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ dom->name);
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Creating request for [%s][%#x][%s][%#x:%s]\n",
+ dom->name, entry_type, be_req2str(entry_type),
+ filter_type, filter_value ? filter_value : "-");
+
+ dp_flags = fast_reply ? DP_FAST_REPLY : 0;
+ subreq = sbus_call_dp_dp_resolverHandler_send(state, be_conn->conn,
+ be_conn->bus_name,
+ SSS_BUS_PATH,
+ dp_flags, entry_type,
+ filter_type, filter_value,
+ sss_chain_id_get());
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_dp_resolver_get_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, rctx->ev);
+ }
+
+ return req;
+}
+
+static void sss_dp_resolver_get_done(struct tevent_req *subreq)
+{
+ struct sss_dp_resolver_get_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_dp_resolver_get_state);
+
+ ret = sbus_call_dp_dp_resolverHandler_recv(state, subreq,
+ &state->dp_error,
+ &state->error,
+ &state->error_message);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+sss_dp_resolver_get_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char **_error_message)
+{
+ struct sss_dp_resolver_get_state *state;
+ state = tevent_req_data(req, struct sss_dp_resolver_get_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_dp_error = state->dp_error;
+ *_error = state->error;
+ *_error_message = talloc_steal(mem_ctx, state->error_message);
+
+ return EOK;
+}
diff --git a/src/responder/common/responder_get_domains.c b/src/responder/common/responder_get_domains.c
new file mode 100644
index 0000000..1a388e7
--- /dev/null
+++ b/src/responder/common/responder_get_domains.c
@@ -0,0 +1,841 @@
+/*
+ Authors:
+ Jan Zeleny <jzeleny@redhat.com>
+
+ Copyright (C) 2011 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 "util/util.h"
+#include "util/sss_chain_id.h"
+#include "responder/common/responder.h"
+#include "providers/data_provider.h"
+#include "db/sysdb.h"
+#include "sss_iface/sss_iface_async.h"
+
+/* ========== Get subdomains for a domain ================= */
+struct get_subdomains_state {
+ uint16_t dp_error;
+ uint32_t error;
+ const char *error_message;
+};
+
+static void get_subdomains_done(struct tevent_req *subreq);
+
+struct tevent_req *
+get_subdomains_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ const char *hint)
+{
+ struct get_subdomains_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct get_subdomains_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ if (is_files_provider(dom)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Domain %s does not check DP\n",
+ dom->name);
+ state->dp_error = DP_ERR_OK;
+ state->error = EOK;
+ state->error_message = talloc_strdup(state, "Success");
+ if (state->error_message == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ dom->name);
+ ret = EIO;
+ goto done;
+ }
+
+ subreq = sbus_call_dp_dp_getDomains_send(state, be_conn->conn,
+ be_conn->bus_name,
+ SSS_BUS_PATH, hint);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, get_subdomains_done, req);
+
+ ret = EAGAIN;
+
+done:
+#ifdef BUILD_FILES_PROVIDER
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, rctx->ev);
+ } else
+#endif
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, rctx->ev);
+ }
+
+ return req;
+}
+
+static void get_subdomains_done(struct tevent_req *subreq)
+{
+ struct get_subdomains_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct get_subdomains_state);
+
+ ret = sbus_call_dp_dp_getDomains_recv(state, subreq, &state->dp_error,
+ &state->error, &state->error_message);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ state->dp_error = DP_ERR_FATAL;
+ state->error = ret;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+get_subdomains_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char **_error_message)
+{
+ struct get_subdomains_state *state;
+ state = tevent_req_data(req, struct get_subdomains_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_dp_error = state->dp_error;
+ *_error = state->error;
+ *_error_message = talloc_steal(mem_ctx, state->error_message);
+
+ return EOK;
+}
+
+/* ====== Iterate over all domains, searching for their subdomains ======= */
+static errno_t process_subdomains(struct sss_domain_info *dom,
+ struct confdb_ctx *confdb);
+static void set_time_of_last_request(struct resp_ctx *rctx);
+static errno_t check_last_request(struct resp_ctx *rctx, const char *hint);
+
+struct sss_dp_get_domains_state {
+ struct resp_ctx *rctx;
+ struct sss_domain_info *dom;
+ const char *hint;
+};
+
+static void
+sss_dp_get_domains_process(struct tevent_req *subreq);
+
+struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ bool force,
+ const char *hint)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct sss_dp_get_domains_state *state;
+ bool refresh_timeout = false;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_domains_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (rctx->domains == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No domains configured.\n");
+ ret = EINVAL;
+ goto immediately;
+ }
+
+ if (!force) {
+ ret = check_last_request(rctx, hint);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Last call was too recent, nothing to do!\n");
+ goto immediately;
+ } else if (ret != EAGAIN) {
+ DEBUG(SSSDBG_TRACE_FUNC, "check_domain_request failed with [%d][%s]\n",
+ ret, strerror(ret));
+ goto immediately;
+ }
+ }
+ refresh_timeout = true;
+
+ state->rctx = rctx;
+ if (hint != NULL) {
+ state->hint = hint;
+ } else {
+ state->hint = talloc_strdup(state, "");
+ if (state->hint == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+ }
+
+ state->dom = rctx->domains;
+ while(is_files_provider(state->dom)) {
+ state->dom = get_next_domain(state->dom, 0);
+ }
+
+ if (state->dom == NULL) {
+ /* All domains were local */
+ ret = sss_resp_populate_cr_domains(state->rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_resp_populate_cr_domains() failed [%d]: [%s]\n",
+ ret, sss_strerror(ret));
+ goto immediately;
+ }
+ ret = EOK;
+ goto immediately;
+ }
+
+ subreq = get_subdomains_send(req, rctx, state->dom, state->hint);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+ tevent_req_set_callback(subreq, sss_dp_get_domains_process, req);
+
+ return req;
+
+immediately:
+ if (ret == EOK) {
+ if (refresh_timeout) {
+ set_time_of_last_request(rctx);
+ }
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, rctx->ev);
+
+ return req;
+}
+
+static void sss_resp_update_certmaps(struct resp_ctx *rctx)
+{
+ int ret;
+ struct certmap_info **certmaps;
+ bool user_name_hint;
+ struct sss_domain_info *dom;
+
+ for (dom = rctx->domains; dom != NULL; dom = dom->next) {
+ ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint);
+ if (ret == EOK) {
+ dom->user_name_hint = user_name_hint;
+ talloc_free(dom->certmaps);
+ dom->certmaps = certmaps;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sysdb_get_certmap failed for domain [%s].\n", dom->name);
+ }
+ }
+}
+
+static void
+sss_dp_get_domains_process(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sss_dp_get_domains_state *state = tevent_req_data(req,
+ struct sss_dp_get_domains_state);
+ uint16_t dp_err;
+ uint32_t dp_ret;
+ const char *err_msg;
+
+ ret = get_subdomains_recv(subreq, subreq, &dp_err, &dp_ret, &err_msg);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = process_subdomains(state->dom, state->rctx->cdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "process_subdomains failed, "
+ "trying next domain.\n");
+ goto fail;
+ }
+
+ /* Advance to the next domain */
+ state->dom = get_next_domain(state->dom, 0);
+
+ /* Skip "files provider" */
+ while(is_files_provider(state->dom)) {
+ state->dom = get_next_domain(state->dom, 0);
+ }
+
+ if (state->dom == NULL) {
+ /* No more domains to check, refreshing the active configuration */
+ set_time_of_last_request(state->rctx);
+ ret = sss_resp_populate_cr_domains(state->rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_resp_populate_cr_domains() failed [%d]: [%s]\n",
+ ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ sss_resp_update_certmaps(state->rctx);
+
+ ret = sss_ncache_reset_repopulate_permanent(state->rctx,
+ state->rctx->ncache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ncache_reset_repopulate_permanent failed, ignored.\n");
+ }
+
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = get_subdomains_send(req, state->rctx, state->dom, state->hint);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, sss_dp_get_domains_process, req);
+ return;
+
+fail:
+ tevent_req_error(req, ret);
+ return;
+}
+
+static errno_t
+process_subdomains(struct sss_domain_info *domain, struct confdb_ctx *confdb)
+{
+ int ret;
+
+ if (domain->realm == NULL ||
+ domain->flat_name == NULL ||
+ domain->domain_id == NULL) {
+ ret = sysdb_master_domain_update(domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FUNC_DATA, "sysdb_master_domain_get_info " \
+ "failed.\n");
+ goto done;
+ }
+ }
+
+ /* Retrieve all subdomains of this domain from sysdb
+ * and create their struct sss_domain_info representations
+ */
+ ret = sysdb_update_subdomains(domain, confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FUNC_DATA, "sysdb_update_subdomains failed.\n");
+ goto done;
+ }
+
+ errno = 0;
+ ret = gettimeofday(&domain->subdomains_last_checked, NULL);
+ if (ret == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to update sub-domains "
+ "of domain [%s].\n", domain->name);
+ }
+
+ return ret;
+}
+
+errno_t sss_dp_get_domains_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static void set_time_of_last_request(struct resp_ctx *rctx)
+{
+ int ret;
+
+ errno = 0;
+ ret = gettimeofday(&rctx->get_domains_last_call, NULL);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_TRACE_FUNC, "gettimeofday failed [%d][%s].\n",
+ ret, strerror(ret));
+ }
+}
+
+static errno_t check_last_request(struct resp_ctx *rctx, const char *hint)
+{
+ struct sss_domain_info *dom;
+ time_t now = time(NULL);
+ time_t diff;
+
+ diff = now - rctx->get_domains_last_call.tv_sec;
+ if (diff >= rctx->domains_timeout) {
+ /* Timeout, expired, fetch domains again */
+ return EAGAIN;
+ }
+
+ if (hint != NULL) {
+ for (dom = rctx->domains; dom;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ if (!IS_SUBDOMAIN(dom)) {
+ diff = now - dom->subdomains_last_checked.tv_sec;
+ /* not a subdomain */
+ continue;
+ }
+ if (strcasecmp(dom->name, hint) == 0) {
+ if (diff >= rctx->domains_timeout) {
+ /* Timeout, expired, fetch domains again */
+ return EAGAIN;
+ }
+ }
+ }
+ }
+
+ return EOK;
+}
+
+struct get_domains_state {
+ struct resp_ctx *rctx;
+ struct sss_nc_ctx *optional_ncache;
+ get_domains_callback_fn_t *callback;
+ void *callback_pvt;
+};
+
+static void get_domains_at_startup_done(struct tevent_req *req)
+{
+ int ret;
+ struct get_domains_state *state;
+
+ state = tevent_req_callback_data(req, struct get_domains_state);
+
+ ret = sss_dp_get_domains_recv(req);
+ talloc_free(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "sss_dp_get_domains request failed.\n");
+ }
+
+ if (state->optional_ncache != NULL) {
+ ret = sss_ncache_reset_repopulate_permanent(state->rctx,
+ state->optional_ncache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sss_ncache_reset_repopulate_permanent failed.\n");
+ }
+ }
+
+ if (is_files_provider(state->rctx->domains)) {
+ ret = sysdb_master_domain_update(state->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_master_domain_update failed, "
+ "ignored.\n");
+ }
+ }
+
+ if (state->callback != NULL) {
+ state->callback(state->callback_pvt);
+ }
+
+ talloc_free(state);
+ return;
+}
+
+static void get_domains_at_startup(struct tevent_context *ev,
+ struct tevent_immediate *imm,
+ void *pvt)
+{
+ struct tevent_req *req;
+ struct get_domains_state *state;
+
+ state = talloc_get_type(pvt, struct get_domains_state);
+
+ req = sss_dp_get_domains_send(state, state->rctx, true, NULL);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_dp_get_domains_send failed.\n");
+ talloc_free(state);
+ return;
+ }
+
+ tevent_req_set_callback(req, get_domains_at_startup_done, state);
+ return;
+}
+
+errno_t schedule_get_domains_task(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_nc_ctx *optional_ncache,
+ get_domains_callback_fn_t *callback,
+ void *callback_pvt)
+{
+ struct tevent_immediate *imm;
+ struct get_domains_state *state;
+
+ state = talloc(mem_ctx, struct get_domains_state);
+ if (state == NULL) {
+ return ENOMEM;
+ }
+ state->rctx = rctx;
+ state->optional_ncache = optional_ncache;
+ state->callback = callback;
+ state->callback_pvt = callback_pvt;
+
+ imm = tevent_create_immediate(mem_ctx);
+ if (imm == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n");
+ talloc_free(state);
+ return ENOMEM;
+ }
+
+ tevent_schedule_immediate(imm, ev, get_domains_at_startup, state);
+
+ return EOK;
+}
+
+struct sss_parse_inp_state {
+ struct resp_ctx *rctx;
+ const char *default_domain;
+ const char *rawinp;
+
+ char *name;
+ char *domname;
+ errno_t error;
+};
+
+static void sss_parse_inp_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sss_parse_inp_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ const char *default_domain,
+ const char *rawinp)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct sss_parse_inp_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_parse_inp_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (rawinp == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Empty input!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->rctx = rctx;
+
+ state->rawinp = talloc_strdup(state, rawinp);
+ if (state->rawinp == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ state->default_domain = talloc_strdup(state, default_domain);
+ if (default_domain != NULL && state->default_domain == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* If the subdomains haven't been checked yet, we need to always
+ * attach to the post-startup subdomain request and only then parse
+ * the input. Otherwise, we might not be able to parse input with a
+ * flat domain name specifier */
+ if (rctx->get_domains_last_call.tv_sec > 0) {
+ ret = sss_parse_name_for_domains(state, rctx->domains,
+ default_domain, rawinp,
+ &state->domname, &state->name);
+ if (ret == EOK) {
+ /* Was able to use cached domains */
+ goto done;
+ } else if (ret != EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawinp);
+ ret = ERR_INPUT_PARSE;
+ goto done;
+ }
+ }
+
+ /* EAGAIN - check the DP for subdomains */
+
+ DEBUG(SSSDBG_FUNC_DATA, "Requesting info for [%s] from [%s]\n",
+ state->name, state->domname ? state->domname : "<ALL>");
+
+ /* We explicitly use force=false here. This request should decide itself
+ * if it's time to re-use the cached subdomain list or refresh. If the
+ * caller needs to specify the 'force' parameter, they should use the
+ * sss_dp_get_domains_send() request itself
+ */
+ subreq = sss_dp_get_domains_send(state, rctx, false, state->domname);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, sss_parse_inp_done, req);
+ return req;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, rctx->ev);
+ return req;
+}
+
+static void sss_parse_inp_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sss_parse_inp_state *state = tevent_req_data(req,
+ struct sss_parse_inp_state);
+
+ ret = sss_dp_get_domains_recv(subreq);
+ talloc_free(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->error = ERR_OK;
+
+ ret = sss_parse_name_for_domains(state, state->rctx->domains,
+ state->default_domain,
+ state->rawinp,
+ &state->domname, &state->name);
+ if (ret == EAGAIN && state->domname != NULL && state->name == NULL) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Unknown domain in [%s]\n", state->rawinp);
+ state->error = ERR_DOMAIN_NOT_FOUND;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Invalid name received [%s]\n", state->rawinp);
+ state->error = ERR_INPUT_PARSE;
+ }
+
+ if (state->error != ERR_OK) {
+ tevent_req_error(req, state->error);
+ return;
+ }
+
+ /* Was able to parse the name now */
+ tevent_req_done(req);
+}
+
+errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char **_name, char **_domname)
+{
+ struct sss_parse_inp_state *state = tevent_req_data(req,
+ struct sss_parse_inp_state);
+
+ if (state->error != ERR_DOMAIN_NOT_FOUND) {
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ }
+
+ if (_name) {
+ *_name = talloc_steal(mem_ctx, state->name);
+ }
+
+ if (_domname) {
+ *_domname = talloc_steal(mem_ctx, state->domname);
+ }
+
+ return state->error;
+}
+
+/* ========== Get domain of an account ================= */
+
+
+struct sss_dp_get_account_domain_state {
+ uint16_t dp_error;
+ uint32_t error;
+ const char *domain_name;
+};
+
+static void sss_dp_get_account_domain_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_acct_type type,
+ uint32_t opt_id,
+ const char *opt_str)
+{
+ struct sss_dp_get_account_domain_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ uint32_t entry_type;
+ char *filter;
+ uint32_t dp_flags;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_account_domain_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ dom->name);
+ ret = EIO;
+ goto done;
+ }
+
+ switch (type) {
+ case SSS_DP_USER:
+ entry_type = BE_REQ_USER;
+ break;
+ case SSS_DP_GROUP:
+ entry_type = BE_REQ_GROUP;
+ break;
+ case SSS_DP_USER_AND_GROUP:
+ entry_type = BE_REQ_USER_AND_GROUP;
+ break;
+ case SSS_DP_SECID:
+ entry_type = BE_REQ_BY_SECID;
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unsupported lookup type %X for this request\n", type);
+ return NULL;
+ }
+
+ if (type == SSS_DP_SECID) {
+ if (opt_str == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: SID search called without SID parameter!\n");
+ ret = EINVAL;
+ goto done;
+ }
+ filter = talloc_asprintf(state, DP_SEC_ID"=%s", opt_str);
+ } else {
+ filter = talloc_asprintf(state, "idnumber=%u", opt_id);
+ }
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dp_flags = fast_reply ? DP_FAST_REPLY : 0;
+
+ subreq = sbus_call_dp_dp_getAccountDomain_send(state, be_conn->conn,
+ be_conn->bus_name,
+ SSS_BUS_PATH, dp_flags,
+ entry_type, filter,
+ sss_chain_id_get());
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_dp_get_account_domain_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, rctx->ev);
+ }
+
+ return req;
+}
+
+static void sss_dp_get_account_domain_done(struct tevent_req *subreq)
+{
+ struct sss_dp_get_account_domain_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_dp_get_account_domain_state);
+
+ ret = sbus_call_dp_dp_getAccountDomain_recv(state, subreq, &state->dp_error,
+ &state->error,
+ &state->domain_name);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not get account info [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->dp_error != DP_ERR_OK) {
+ DEBUG(state->error == ERR_GET_ACCT_DOM_NOT_SUPPORTED ? SSSDBG_TRACE_INTERNAL
+ : SSSDBG_IMPORTANT_INFO,
+ "Data Provider Error: %u, %u [%s]\n",
+ (unsigned int)state->dp_error, (unsigned int)state->error,
+ sss_strerror(state->error));
+ tevent_req_error(req, state->error ? state->error : EIO);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ char **_domain)
+{
+ struct sss_dp_get_account_domain_state *state;
+ state = tevent_req_data(req, struct sss_dp_get_account_domain_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_domain = talloc_strdup(mem_ctx, state->domain_name);
+ if (*_domain == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
diff --git a/src/responder/common/responder_iface.c b/src/responder/common/responder_iface.c
new file mode 100644
index 0000000..cf32fe0
--- /dev/null
+++ b/src/responder/common/responder_iface.c
@@ -0,0 +1,161 @@
+/*
+ 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 "util/util.h"
+#include "sss_iface/sss_iface_async.h"
+#include "responder/common/negcache.h"
+#include "responder/common/responder.h"
+
+static void set_domain_state_by_name(struct resp_ctx *rctx,
+ const char *domain_name,
+ enum sss_domain_state state)
+{
+ struct sss_domain_info *dom;
+
+ if (domain_name == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "BUG: NULL domain name\n");
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Setting state of domain %s\n", domain_name);
+
+ for (dom = rctx->domains;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+
+ if (strcasecmp(dom->name, domain_name) == 0) {
+ break;
+ }
+ }
+
+ if (dom != NULL) {
+ sss_domain_set_state(dom, state);
+ }
+}
+
+static errno_t
+sss_resp_domain_active(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct resp_ctx *rctx,
+ const char *domain_name)
+{
+ DEBUG(SSSDBG_TRACE_LIBS, "Enabling domain %s\n", domain_name);
+
+ set_domain_state_by_name(rctx, domain_name, DOM_ACTIVE);
+
+ return EOK;
+}
+
+static errno_t
+sss_resp_domain_inconsistent(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct resp_ctx *rctx,
+ const char *domain_name)
+{
+ DEBUG(SSSDBG_TRACE_LIBS, "Disabling domain %s\n", domain_name);
+
+ set_domain_state_by_name(rctx, domain_name, DOM_INCONSISTENT);
+
+ return EOK;
+}
+
+static errno_t
+sss_resp_reset_ncache_users(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct resp_ctx *rctx)
+{
+ sss_ncache_reset_users(rctx->ncache);
+
+ return EOK;
+}
+
+static errno_t
+sss_resp_reset_ncache_groups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct resp_ctx *rctx)
+{
+ sss_ncache_reset_groups(rctx->ncache);
+
+ return EOK;
+}
+
+errno_t
+sss_resp_register_sbus_iface(struct sbus_connection *conn,
+ struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface_resp_domain,
+ sssd_Responder_Domain,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_Responder_Domain, SetActive, sss_resp_domain_active, rctx),
+ SBUS_SYNC(METHOD, sssd_Responder_Domain, SetInconsistent, sss_resp_domain_inconsistent, rctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_resp_negcache,
+ sssd_Responder_NegativeCache,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_Responder_NegativeCache, ResetUsers, sss_resp_reset_ncache_users, rctx),
+ SBUS_SYNC(METHOD, sssd_Responder_NegativeCache, ResetGroups, sss_resp_reset_ncache_groups, rctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ struct sbus_path paths[] = {
+ {SSS_BUS_PATH, &iface_resp_domain},
+ {SSS_BUS_PATH, &iface_resp_negcache},
+ {NULL, NULL}
+ };
+
+ ret = sbus_connection_add_path_map(conn, paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+errno_t
+sss_resp_register_service_iface(struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface_svc,
+ sssd_service,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_service, rotateLogs, responder_logrotate, rctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, sssd_service, debug_level, generic_get_debug_level, NULL),
+ SBUS_SYNC(SETTER, sssd_service, debug_level, generic_set_debug_level, NULL)
+ )
+ );
+
+ ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
diff --git a/src/responder/common/responder_packet.c b/src/responder/common/responder_packet.c
new file mode 100644
index 0000000..db74cc1
--- /dev/null
+++ b/src/responder/common/responder_packet.c
@@ -0,0 +1,364 @@
+/*
+ SSSD
+
+ SSS Client Responder, command parser
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "responder/common/responder_packet.h"
+
+#define SSSSRV_PACKET_MEM_SIZE 512
+
+struct sss_packet {
+ size_t memsize;
+
+ /* Structure of the buffer:
+ * Bytes Content
+ * ---------------------------------
+ * 0-15 packet header
+ * 0-3 packet length (uint32_t)
+ * 4-7 command type (uint32_t)
+ * 8-11 status (uint32_t)
+ * 12-15 reserved
+ * 16+ packet body */
+ uint8_t *buffer;
+
+ /* io pointer */
+ size_t iop;
+};
+
+/* Offsets to data in sss_packet's buffer */
+#define SSS_PACKET_LEN_OFFSET 0
+#define SSS_PACKET_CMD_OFFSET sizeof(uint32_t)
+#define SSS_PACKET_ERR_OFFSET (2*(sizeof(uint32_t)))
+#define SSS_PACKET_BODY_OFFSET (4*(sizeof(uint32_t)))
+
+static void sss_packet_set_len(struct sss_packet *packet, uint32_t len);
+static void sss_packet_set_cmd(struct sss_packet *packet,
+ enum sss_cli_command cmd);
+static uint32_t sss_packet_get_len(struct sss_packet *packet);
+
+/*
+ * Allocate a new packet structure
+ *
+ * - if size is defined use it otherwise the default packet will be
+ * SSSSRV_PACKET_MEM_SIZE bytes.
+ */
+int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size,
+ enum sss_cli_command cmd,
+ struct sss_packet **rpacket)
+{
+ struct sss_packet *packet;
+
+ packet = talloc(mem_ctx, struct sss_packet);
+ if (!packet) return ENOMEM;
+
+ if (size) {
+ int n = (size + SSS_NSS_HEADER_SIZE) / SSSSRV_PACKET_MEM_SIZE;
+ packet->memsize = (n + 1) * SSSSRV_PACKET_MEM_SIZE;
+ } else {
+ packet->memsize = SSSSRV_PACKET_MEM_SIZE;
+ }
+
+ packet->buffer = talloc_size(packet, packet->memsize);
+ if (!packet->buffer) {
+ talloc_free(packet);
+ return ENOMEM;
+ }
+ memset(packet->buffer, 0, SSS_NSS_HEADER_SIZE);
+
+ sss_packet_set_len(packet, size + SSS_NSS_HEADER_SIZE);
+ sss_packet_set_cmd(packet, cmd);
+
+ packet->iop = 0;
+
+ *rpacket = packet;
+
+ return EOK;
+}
+
+/* grows a packet size only in SSSSRV_PACKET_MEM_SIZE chunks */
+int sss_packet_grow(struct sss_packet *packet, size_t size)
+{
+ size_t totlen, len;
+ uint8_t *newmem;
+ uint32_t packet_len;
+
+ if (size == 0) {
+ return EOK;
+ }
+
+ totlen = packet->memsize;
+ packet_len = sss_packet_get_len(packet);
+
+ len = packet_len + size;
+
+ /* make sure we do not overflow */
+ if (totlen < len) {
+ int n = len / SSSSRV_PACKET_MEM_SIZE + 1;
+ totlen += n * SSSSRV_PACKET_MEM_SIZE;
+ if (totlen < len) {
+ return EINVAL;
+ }
+ }
+
+ if (totlen > packet->memsize) {
+ newmem = talloc_realloc_size(packet, packet->buffer, totlen);
+ if (!newmem) {
+ return ENOMEM;
+ }
+
+ packet->memsize = totlen;
+
+ /* re-set pointers if realloc had to move memory */
+ if (newmem != packet->buffer) {
+ packet->buffer = newmem;
+ }
+ }
+
+ packet_len += size;
+ sss_packet_set_len(packet, packet_len);
+
+
+ return 0;
+}
+
+/* reclaim back previously reserved space in the packet
+ * usually done in function recovering from not fatal errors */
+int sss_packet_shrink(struct sss_packet *packet, size_t size)
+{
+ size_t newlen;
+ size_t oldlen = sss_packet_get_len(packet);
+
+ if (size > oldlen) return EINVAL;
+
+ newlen = oldlen - size;
+ if (newlen < SSS_NSS_HEADER_SIZE) return EINVAL;
+
+ sss_packet_set_len(packet, newlen);
+ return 0;
+}
+
+int sss_packet_set_size(struct sss_packet *packet, size_t size)
+{
+ size_t newlen;
+
+ newlen = SSS_NSS_HEADER_SIZE + size;
+
+ /* make sure we do not overflow */
+ if (packet->memsize < newlen) return EINVAL;
+
+ sss_packet_set_len(packet, newlen);
+
+ return 0;
+}
+
+int sss_packet_recv(struct sss_packet *packet, int fd)
+{
+ size_t rb;
+ size_t len;
+ void *buf;
+ size_t new_len;
+ int ret;
+
+ buf = (uint8_t *)packet->buffer + packet->iop;
+ if (packet->iop >= SSS_PACKET_CMD_OFFSET) {
+ len = sss_packet_get_len(packet) - packet->iop;
+ } else {
+ len = packet->memsize - packet->iop;
+ }
+
+ /* check for wrapping */
+ if (len > (packet->memsize - packet->iop)) {
+ return EINVAL;
+ }
+
+ errno = 0;
+ rb = recv(fd, buf, len, 0);
+
+ if (rb == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+ return EAGAIN;
+ } else {
+ return errno;
+ }
+ }
+
+ if (rb == 0) {
+ return ENODATA;
+ }
+
+ packet->iop += rb;
+ if (packet->iop < SSS_PACKET_CMD_OFFSET) {
+ return EAGAIN;
+ }
+
+ new_len = sss_packet_get_len(packet);
+ if (new_len > packet->memsize) {
+ enum sss_cli_command cmd = sss_packet_get_cmd(packet);
+ size_t max_recv_size;
+
+ /* Allow certain packet types to use a larger buffer. */
+ switch (cmd) {
+ case SSS_NSS_GETNAMEBYCERT:
+ case SSS_NSS_GETLISTBYCERT:
+ max_recv_size = SSS_CERT_PACKET_MAX_RECV_SIZE;
+ break;
+
+ case SSS_GSSAPI_SEC_CTX:
+ case SSS_PAC_ADD_PAC_USER:
+ max_recv_size = SSS_GSSAPI_PACKET_MAX_RECV_SIZE;
+ break;
+
+ default:
+ max_recv_size = 0;
+ }
+
+ /* Due to the way sss_packet_grow() works, the packet len must be set
+ * to 0 first, and then grown to the expected size. */
+ if (new_len <= max_recv_size) {
+ sss_packet_set_len(packet, 0);
+ ret = sss_packet_grow(packet, new_len);
+ if (ret != EOK) {
+ return ret;
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Refusing to read overlarge packet from fd %d (length %zu bytes, cmd %#04x)",
+ fd, new_len, cmd);
+ return EINVAL;
+ }
+ }
+
+ if (packet->iop < new_len) {
+ return EAGAIN;
+ }
+
+ return EOK;
+}
+
+int sss_packet_send(struct sss_packet *packet, int fd)
+{
+ size_t rb;
+ size_t len;
+ void *buf;
+
+ if (!packet) {
+ /* No packet object to write to? */
+ return EINVAL;
+ }
+
+ buf = packet->buffer + packet->iop;
+ len = sss_packet_get_len(packet) - packet->iop;
+
+ errno = 0;
+ rb = send(fd, buf, len, 0);
+
+ if (rb == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+ return EAGAIN;
+ } else {
+ return errno;
+ }
+ }
+
+ if (rb == 0) {
+ return EIO;
+ }
+
+ packet->iop += rb;
+
+ if (packet->iop < sss_packet_get_len(packet)) {
+ return EAGAIN;
+ }
+
+ return EOK;
+}
+
+enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet)
+{
+ uint32_t cmd;
+
+ SAFEALIGN_COPY_UINT32(&cmd, packet->buffer + SSS_PACKET_CMD_OFFSET, NULL);
+ return (enum sss_cli_command)cmd;
+}
+
+uint32_t sss_packet_get_status(struct sss_packet *packet)
+{
+ uint32_t status;
+
+ SAFEALIGN_COPY_UINT32(&status, packet->buffer + SSS_PACKET_ERR_OFFSET,
+ NULL);
+ return status;
+}
+
+void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen)
+{
+ *body = packet->buffer + SSS_PACKET_BODY_OFFSET;
+ *blen = sss_packet_get_len(packet) - SSS_NSS_HEADER_SIZE;
+}
+
+errno_t sss_packet_set_body(struct sss_packet *packet,
+ uint8_t *body,
+ size_t blen)
+{
+ uint8_t *pbody;
+ size_t plen;
+ errno_t ret;
+
+ ret = sss_packet_grow(packet, blen);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &pbody, &plen);
+ memcpy(pbody, body, blen);
+
+ return EOK;
+}
+
+void sss_packet_set_error(struct sss_packet *packet, int error)
+{
+ SAFEALIGN_SETMEM_UINT32(packet->buffer + SSS_PACKET_ERR_OFFSET, error,
+ NULL);
+}
+
+static void sss_packet_set_len(struct sss_packet *packet, uint32_t len)
+{
+ SAFEALIGN_SETMEM_UINT32(packet->buffer + SSS_PACKET_LEN_OFFSET, len, NULL);
+}
+
+static void sss_packet_set_cmd(struct sss_packet *packet,
+ enum sss_cli_command cmd)
+{
+ SAFEALIGN_SETMEM_UINT32(packet->buffer + SSS_PACKET_CMD_OFFSET, cmd, NULL);
+}
+
+static uint32_t sss_packet_get_len(struct sss_packet *packet)
+{
+ uint32_t len;
+
+ SAFEALIGN_COPY_UINT32(&len, packet->buffer + SSS_PACKET_LEN_OFFSET, NULL);
+ return len;
+}
diff --git a/src/responder/common/responder_packet.h b/src/responder/common/responder_packet.h
new file mode 100644
index 0000000..fd99196
--- /dev/null
+++ b/src/responder/common/responder_packet.h
@@ -0,0 +1,51 @@
+/*
+ SSSD
+
+ SSS Client Responder, header file
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SSSSRV_PACKET_H__
+#define __SSSSRV_PACKET_H__
+
+#include "sss_client/sss_cli.h"
+
+#define SSS_PACKET_MAX_RECV_SIZE 1024
+#define SSS_CERT_PACKET_MAX_RECV_SIZE ( 10 * SSS_PACKET_MAX_RECV_SIZE )
+#define SSS_GSSAPI_PACKET_MAX_RECV_SIZE ( 128 * 1024 )
+
+struct sss_packet;
+
+int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size,
+ enum sss_cli_command cmd,
+ struct sss_packet **rpacket);
+int sss_packet_grow(struct sss_packet *packet, size_t size);
+int sss_packet_shrink(struct sss_packet *packet, size_t size);
+int sss_packet_set_size(struct sss_packet *packet, size_t size);
+int sss_packet_recv(struct sss_packet *packet, int fd);
+int sss_packet_send(struct sss_packet *packet, int fd);
+enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet);
+uint32_t sss_packet_get_status(struct sss_packet *packet);
+void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen);
+void sss_packet_set_error(struct sss_packet *packet, int error);
+
+/* Grow packet and set its body. */
+errno_t sss_packet_set_body(struct sss_packet *packet,
+ uint8_t *body,
+ size_t blen);
+
+#endif /* __SSSSRV_PACKET_H__ */
diff --git a/src/responder/common/responder_utils.c b/src/responder/common/responder_utils.c
new file mode 100644
index 0000000..47aeace
--- /dev/null
+++ b/src/responder/common/responder_utils.c
@@ -0,0 +1,527 @@
+
+/*
+ SSSD
+
+ Common Responder utility functions
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2014
+
+ 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 "db/sysdb.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "util/util.h"
+
+static inline bool
+attr_in_list(const char **list, size_t nlist, const char *str)
+{
+ return string_in_list_size(str, list, nlist, false);
+}
+
+const char **parse_attr_list_ex(TALLOC_CTX *mem_ctx, const char *conf_str,
+ const char **defaults)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ const char **list = NULL;
+ const char **res = NULL;
+ int list_size;
+ char **conf_list = NULL;
+ int conf_list_size = 0;
+ const char **allow = NULL;
+ const char **deny = NULL;
+ int ai = 0, di = 0, li = 0;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ if (conf_str) {
+ ret = split_on_separator(tmp_ctx, conf_str, ',', true, true,
+ &conf_list, &conf_list_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot parse attribute ACL list %s: %d\n", conf_str, ret);
+ goto done;
+ }
+
+ allow = talloc_zero_array(tmp_ctx, const char *, conf_list_size);
+ deny = talloc_zero_array(tmp_ctx, const char *, conf_list_size);
+ if (allow == NULL || deny == NULL) {
+ goto done;
+ }
+ }
+
+ for (i = 0; i < conf_list_size; i++) {
+ switch (conf_list[i][0]) {
+ case '+':
+ allow[ai] = conf_list[i] + 1;
+ ai++;
+ continue;
+ case '-':
+ deny[di] = conf_list[i] + 1;
+ di++;
+ continue;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "ACL values must start with "
+ "either '+' (allow) or '-' (deny), got '%s'\n",
+ conf_list[i]);
+ goto done;
+ }
+ }
+
+ /* Assume the output will have to hold defaults and all the configured,
+ * values, resize later
+ */
+ list_size = 0;
+ if (defaults != NULL) {
+ while (defaults[list_size]) {
+ list_size++;
+ }
+ }
+ list_size += conf_list_size;
+
+ list = talloc_zero_array(tmp_ctx, const char *, list_size + 1);
+ if (list == NULL) {
+ goto done;
+ }
+
+ /* Start by copying explicitly allowed attributes */
+ for (i = 0; i < ai; i++) {
+ /* if the attribute is explicitly denied, skip it */
+ if (attr_in_list(deny, di, allow[i])) {
+ continue;
+ }
+
+ /* If the attribute is already in the list, skip it */
+ if (attr_in_list(list, li, allow[i])) {
+ continue;
+ }
+
+ list[li] = talloc_strdup(list, allow[i]);
+ if (list[li] == NULL) {
+ goto done;
+ }
+ li++;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Added allowed attr %s to whitelist\n", allow[i]);
+ }
+
+ /* Add defaults */
+ if (defaults != NULL) {
+ for (i = 0; defaults[i]; i++) {
+ /* if the attribute is explicitly denied, skip it */
+ if (attr_in_list(deny, di, defaults[i])) {
+ continue;
+ }
+
+ /* If the attribute is already in the list, skip it */
+ if (attr_in_list(list, li, defaults[i])) {
+ continue;
+ }
+
+ list[li] = talloc_strdup(list, defaults[i]);
+ if (list[li] == NULL) {
+ goto done;
+ }
+ li++;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Added default attr %s to whitelist\n", defaults[i]);
+ }
+ }
+
+ res = talloc_steal(mem_ctx, list);
+done:
+ talloc_free(tmp_ctx);
+ return res;
+}
+
+char *sss_resp_create_fqname(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool name_is_upn,
+ const char *orig_name)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *name;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_get_cased_name failed\n");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ name = sss_reverse_replace_space(tmp_ctx, name, rctx->override_space);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_reverse_replace_space failed\n");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+
+ if (name_is_upn == false) {
+ name = sss_create_internal_fqname(tmp_ctx, name, dom->name);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_create_internal_fqname failed\n");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ }
+
+ name = talloc_steal(mem_ctx, name);
+ talloc_free(tmp_ctx);
+ return name;
+}
+
+struct resp_resolve_group_names_state {
+ struct tevent_context *ev;
+ struct resp_ctx *rctx;
+ struct sss_domain_info *dom;
+ struct ldb_result *initgr_res;
+
+ bool needs_refresh;
+ unsigned int group_iter;
+ bool is_original_primary_group_request;
+
+ struct ldb_result *initgr_named_res;
+};
+
+static void resp_resolve_group_done(struct tevent_req *subreq);
+static errno_t resp_resolve_group_next(struct tevent_req *req);
+static errno_t resp_resolve_group_trigger_request(struct tevent_req *req, const char *attr_name);
+static errno_t resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state);
+
+struct tevent_req *resp_resolve_group_names_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ struct ldb_result *initgr_res)
+{
+ struct resp_resolve_group_names_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct resp_resolve_group_names_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+ state->ev = ev;
+ state->rctx = rctx;
+ state->dom = dom;
+ state->initgr_res = initgr_res;
+ state->is_original_primary_group_request = true;
+
+ ret = resp_resolve_group_next(req);
+ if (ret == EOK) {
+ goto immediate;
+ } else if (ret != EAGAIN) {
+ goto immediate;
+ }
+
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static bool
+resp_resolve_group_needs_refresh(struct resp_resolve_group_names_state *state)
+{
+ /* Refresh groups that have a non-zero GID,
+ * but are marked as non-POSIX
+ */
+ bool is_posix;
+ uint64_t gid;
+ struct ldb_message *group_msg;
+
+ group_msg = state->initgr_res->msgs[state->group_iter];
+
+ is_posix = ldb_msg_find_attr_as_bool(group_msg, SYSDB_POSIX, false);
+ gid = ldb_msg_find_attr_as_uint64(group_msg, SYSDB_GIDNUM, 0);
+
+ if (is_posix == false && gid != 0) {
+ return true;
+ }
+
+ return false;
+}
+
+static errno_t resp_resolve_group_next(struct tevent_req *req)
+{
+ struct resp_resolve_group_names_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct resp_resolve_group_names_state);
+
+ while (state->group_iter < state->initgr_res->count
+ && !resp_resolve_group_needs_refresh(state)) {
+ state->group_iter++;
+ }
+
+ if (state->group_iter >= state->initgr_res->count) {
+ /* All groups were refreshed */
+ return EOK;
+ }
+
+ if(state->group_iter == 0 &&
+ state->is_original_primary_group_request == true) {
+ ret = resp_resolve_group_trigger_request(req,
+ SYSDB_PRIMARY_GROUP_GIDNUM);
+
+ /* If auto_private_groups is disabled then
+ * resp_resolve_group_trigger_request will return EINVAL, but this
+ * doesn't mean a failure. Thus, the search should continue with the
+ * next element.
+ */
+ if(ret == EINVAL) {
+ state->is_original_primary_group_request = false;
+ return resp_resolve_group_trigger_request(req, SYSDB_GIDNUM);
+ } else {
+ return ret;
+ }
+ } else {
+ return resp_resolve_group_trigger_request(req, SYSDB_GIDNUM);
+ }
+}
+
+static errno_t resp_resolve_group_trigger_request(struct tevent_req *req,
+ const char *attr_name)
+{
+ struct cache_req_data *data;
+ uint64_t gid;
+ struct tevent_req *subreq;
+ struct resp_resolve_group_names_state *state;
+
+ state = tevent_req_data(req, struct resp_resolve_group_names_state);
+
+ gid = ldb_msg_find_attr_as_uint64(state->initgr_res->msgs[state->group_iter],
+ attr_name, 0);
+ if (gid == 0) {
+ return EINVAL;
+ }
+
+ data = cache_req_data_id_attrs(state, CACHE_REQ_GROUP_BY_ID, gid, NULL);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
+ return ENOMEM;
+ }
+
+ subreq = cache_req_send(state,
+ state->ev,
+ state->rctx,
+ state->rctx->ncache,
+ 0,
+ CACHE_REQ_ANY_DOM,
+ NULL,
+ data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, resp_resolve_group_done, req);
+ return EAGAIN;
+}
+
+static void resp_resolve_group_done(struct tevent_req *subreq)
+{
+ struct resp_resolve_group_names_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct resp_resolve_group_names_state);
+
+ ret = cache_req_single_domain_recv(state, subreq, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to refresh group\n");
+ /* Try to refresh the others on error */
+ }
+
+ if(state->group_iter == 0 &&
+ state->is_original_primary_group_request == true) {
+ state->is_original_primary_group_request = false;
+ } else {
+ state->group_iter++;
+ }
+ state->needs_refresh = true;
+
+ ret = resp_resolve_group_next(req);
+ if (ret == EOK) {
+ ret = resp_resolve_group_reread_names(state);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "All groups are refreshed, done\n");
+ tevent_req_done(req);
+ return;
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* Continue refreshing.. */
+}
+
+static errno_t
+resp_resolve_group_reread_names(struct resp_resolve_group_names_state *state)
+{
+ errno_t ret;
+ const char *username;
+
+ /* re-read reply in case any groups were renamed */
+ /* msgs[0] is the user entry */
+ username = sss_view_ldb_msg_find_attr_as_string(state->dom,
+ state->initgr_res->msgs[0],
+ SYSDB_NAME,
+ NULL);
+ if (username == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n");
+ return EINVAL;
+ }
+
+ ret = sysdb_initgroups_with_views(state,
+ state->dom,
+ username,
+ &state->initgr_named_res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot re-read the group names\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+int resp_resolve_group_names_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ldb_result **_initgr_named_res)
+{
+ struct resp_resolve_group_names_state *state = NULL;
+ state = tevent_req_data(req, struct resp_resolve_group_names_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_initgr_named_res = talloc_steal(mem_ctx, state->initgr_named_res);
+ return EOK;
+}
+
+const char *
+sss_resp_get_shell_override(struct ldb_message *msg,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain)
+{
+ const char *shell;
+ int i;
+
+ /* Here we skip the files provider as it should always return *only*
+ * what's in the files and nothing else. */
+ if (!is_files_provider(domain)) {
+ /* Check whether we are unconditionally overriding
+ * the server for the login shell. */
+ if (domain->override_shell) {
+ return domain->override_shell;
+ } else if (rctx->override_shell) {
+ return rctx->override_shell;
+ }
+ }
+
+ shell = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_SHELL,
+ NULL);
+ if (shell == NULL) {
+ /* Check whether there is a default shell specified */
+ if (domain->default_shell) {
+ return domain->default_shell;
+ } else if (rctx->default_shell) {
+ return rctx->default_shell;
+ }
+
+ return "";
+ }
+
+ if (rctx->allowed_shells == NULL && rctx->vetoed_shells == NULL) {
+ return shell;
+ }
+
+ if (rctx->vetoed_shells) {
+ for (i = 0; rctx->vetoed_shells[i]; i++) {
+ if (strcmp(rctx->vetoed_shells[i], shell) == 0) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "The shell '%s' is vetoed. Using fallback.\n",
+ shell);
+ return rctx->shell_fallback;
+ }
+ }
+ }
+
+ if (rctx->etc_shells) {
+ for (i = 0; rctx->etc_shells[i]; i++) {
+ if (strcmp(shell, rctx->etc_shells[i]) == 0) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Shell %s found in /etc/shells\n", shell);
+ break;
+ }
+ }
+
+ if (rctx->etc_shells[i]) {
+ DEBUG(SSSDBG_TRACE_ALL, "Using original shell '%s'\n", shell);
+ return shell;
+ }
+ }
+
+ if (rctx->allowed_shells) {
+ if (strcmp(rctx->allowed_shells[0], "*") == 0) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "The shell '%s' is allowed but does not exist. "
+ "Using fallback\n", shell);
+ return rctx->shell_fallback;
+ } else {
+ for (i = 0; rctx->allowed_shells[i]; i++) {
+ if (strcmp(rctx->allowed_shells[i], shell) == 0) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "The shell '%s' is allowed but does not exist. "
+ "Using fallback\n", shell);
+ return rctx->shell_fallback;
+ }
+ }
+ }
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "The shell '%s' is not allowed and does not exist.\n", shell);
+
+ return NOLOGIN_SHELL;
+}
diff --git a/src/responder/ifp/ifp_cache.c b/src/responder/ifp/ifp_cache.c
new file mode 100644
index 0000000..a4dd393
--- /dev/null
+++ b/src/responder/ifp/ifp_cache.c
@@ -0,0 +1,270 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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 "db/sysdb.h"
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/ifp/ifp_cache.h"
+#include "responder/ifp/ifp_users.h"
+#include "responder/ifp/ifp_groups.h"
+
+static struct ldb_dn *
+ifp_cache_build_base_dn(TALLOC_CTX *mem_ctx,
+ enum ifp_cache_type type,
+ struct sss_domain_info *domain)
+{
+ struct ldb_dn *base_dn = NULL;
+
+ switch (type) {
+ case IFP_CACHE_USER:
+ base_dn = sysdb_user_base_dn(mem_ctx, domain);
+ break;
+ case IFP_CACHE_GROUP:
+ base_dn = sysdb_group_base_dn(mem_ctx, domain);
+ break;
+ }
+
+ return base_dn;
+}
+
+static char *
+ifp_cache_build_path(TALLOC_CTX *mem_ctx,
+ enum ifp_cache_type type,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ char *path = NULL;
+
+ switch (type) {
+ case IFP_CACHE_USER:
+ path = ifp_users_build_path_from_msg(mem_ctx, domain, msg);
+ break;
+ case IFP_CACHE_GROUP:
+ path = ifp_groups_build_path_from_msg(mem_ctx, domain, msg);
+ break;
+ }
+
+ return path;
+}
+
+static const char *
+ifp_cache_object_class(enum ifp_cache_type type)
+{
+ const char *class = NULL;
+
+ switch (type) {
+ case IFP_CACHE_USER:
+ class = SYSDB_USER_CLASS;
+ break;
+ case IFP_CACHE_GROUP:
+ class = SYSDB_GROUP_CLASS;
+ break;
+ }
+
+ return class;
+}
+
+static errno_t
+ifp_cache_get_cached_objects(TALLOC_CTX *mem_ctx,
+ enum ifp_cache_type type,
+ struct sss_domain_info *domain,
+ const char ***_paths)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *base_dn;
+ struct ldb_result *result;
+ const char *class = ifp_cache_object_class(type);
+ const char **paths;
+ errno_t ret;
+ int ldb_ret;
+ int i;
+ const char *attrs[] = {SYSDB_OBJECTCATEGORY, SYSDB_UIDNUM,
+ SYSDB_GIDNUM, NULL};
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ base_dn = ifp_cache_build_base_dn(tmp_ctx, type, domain);
+ if (base_dn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create base dn\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ldb_ret = ldb_search(sysdb_ctx_get_ldb(domain->sysdb), tmp_ctx, &result,
+ base_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(%s=%s)(%s=TRUE))", SYSDB_OBJECTCATEGORY, class,
+ SYSDB_IFP_CACHED);
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search the cache\n");
+ ret = sss_ldb_error_to_errno(ldb_ret);
+ goto done;
+ }
+
+ paths = talloc_zero_array(tmp_ctx, const char *, result->count + 1);
+ if (paths == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < result->count; i++) {
+ paths[i] = ifp_cache_build_path(paths, type, domain, result->msgs[i]);
+ if (paths[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_paths = talloc_steal(mem_ctx, paths);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+ifp_cache_list_domains(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ enum ifp_cache_type type,
+ const char ***_paths)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *domain;
+ const char **tmp_paths = NULL;
+ const char **paths;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ domain = domains;
+ paths = NULL;
+ while (domain != NULL) {
+ ret = ifp_cache_get_cached_objects(tmp_ctx, type, domain, &tmp_paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build object list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = add_strings_lists(tmp_ctx, paths, tmp_paths, true, &paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build object list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ domain = get_next_domain(domain, SSS_GND_DESCEND);
+ }
+
+ if (_paths != NULL) {
+ *_paths = talloc_steal(mem_ctx, paths);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+ifp_cache_list(TALLOC_CTX *mem_ctx,
+ struct ifp_ctx *ifp_ctx,
+ enum ifp_cache_type type,
+ const char ***_paths)
+{
+ return ifp_cache_list_domains(mem_ctx, ifp_ctx->rctx->domains,
+ type, _paths);
+}
+
+errno_t
+ifp_cache_list_by_domain(TALLOC_CTX *mem_ctx,
+ struct ifp_ctx *ifp_ctx,
+ const char *domainname,
+ enum ifp_cache_type type,
+ const char ***_paths)
+{
+ struct sss_domain_info *domain;
+
+ domain = find_domain_by_name(ifp_ctx->rctx->domains, domainname, true);
+ if (domain == NULL) {
+ return ERR_DOMAIN_NOT_FOUND;
+ }
+
+ return ifp_cache_get_cached_objects(mem_ctx, type, domain, _paths);
+}
+
+static errno_t ifp_cache_object_set(struct sss_domain_info *domain,
+ struct ldb_dn *dn,
+ bool value)
+{
+ struct sysdb_attrs *attrs;
+ errno_t ret;
+
+ attrs = sysdb_new_attrs(NULL);
+ if (attrs == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_IFP_CACHED, value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to modify entry [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(attrs);
+
+ return ret;
+}
+
+errno_t
+ifp_cache_object_store(struct sss_domain_info *domain,
+ struct ldb_dn *dn)
+{
+ return ifp_cache_object_set(domain, dn, true);
+}
+
+errno_t
+ifp_cache_object_remove(struct sss_domain_info *domain,
+ struct ldb_dn *dn)
+{
+ return ifp_cache_object_set(domain, dn, false);
+}
diff --git a/src/responder/ifp/ifp_cache.h b/src/responder/ifp/ifp_cache.h
new file mode 100644
index 0000000..5c05a7f
--- /dev/null
+++ b/src/responder/ifp/ifp_cache.h
@@ -0,0 +1,63 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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 IFP_CACHE_H_
+#define IFP_CACHE_H_
+
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/ifp/ifp_private.h"
+
+enum ifp_cache_type {
+ IFP_CACHE_USER,
+ IFP_CACHE_GROUP
+};
+
+errno_t
+ifp_cache_list_domains(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ enum ifp_cache_type type,
+ const char ***_paths);
+
+/* org.freedesktop-sssd-infopipe.Cache */
+
+errno_t
+ifp_cache_list(TALLOC_CTX *mem_ctx,
+ struct ifp_ctx *ifp_ctx,
+ enum ifp_cache_type type,
+ const char ***_paths);
+
+errno_t
+ifp_cache_list_by_domain(TALLOC_CTX *mem_ctx,
+ struct ifp_ctx *ifp_ctx,
+ const char *domainname,
+ enum ifp_cache_type type,
+ const char ***_paths);
+
+/* org.freedesktop-sssd-infopipe.Cache.Object */
+
+errno_t
+ifp_cache_object_store(struct sss_domain_info *domain,
+ struct ldb_dn *dn);
+
+errno_t
+ifp_cache_object_remove(struct sss_domain_info *domain,
+ struct ldb_dn *dn);
+#endif /* IFP_CACHE_H_ */
diff --git a/src/responder/ifp/ifp_components.c b/src/responder/ifp/ifp_components.c
new file mode 100644
index 0000000..c6b9837
--- /dev/null
+++ b/src/responder/ifp/ifp_components.c
@@ -0,0 +1,662 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 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 <string.h>
+#include <talloc.h>
+#include <signal.h>
+#include <errno.h>
+#include <utime.h>
+
+#include "config.h"
+#include "confdb/confdb.h"
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/ifp/ifp_components.h"
+
+#define PATH_MONITOR IFP_PATH_COMPONENTS "/monitor"
+#define PATH_RESPONDERS IFP_PATH_COMPONENTS "/Responders"
+#define PATH_BACKENDS IFP_PATH_COMPONENTS "/Backends"
+
+enum component_type {
+ COMPONENT_MONITOR,
+ COMPONENT_RESPONDER,
+ COMPONENT_BACKEND
+};
+
+static bool responder_exists(const char *name)
+{
+ const char * const *svc = get_known_services();
+ int i;
+
+ for (i = 0; svc[i] != NULL; i++) {
+ if (strcmp(svc[i], name) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool backend_exists(struct confdb_ctx *confdb, const char *name)
+{
+ char **names = NULL;
+ errno_t ret;
+ int i;
+
+ ret = confdb_list_all_domain_names(NULL, confdb, &names);
+ if (ret != EOK) {
+ return false;
+ }
+
+ for (i = 0; names[i] != NULL; i++) {
+ if (strcmp(names[i], name) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static errno_t check_and_get_component_from_path(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *confdb,
+ const char *path,
+ enum component_type *_type,
+ char **_name)
+{
+ enum component_type type;
+ char *name = NULL;
+ errno_t ret;
+
+ if (confdb == NULL || path == NULL) {
+ return EINVAL;
+ }
+
+ if (strcmp(path, PATH_MONITOR) == 0) {
+ type = COMPONENT_MONITOR;
+ name = talloc_strdup(mem_ctx, "monitor");
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ name = sbus_opath_object_name(mem_ctx, path, PATH_RESPONDERS);
+ if (name != NULL) {
+ type = COMPONENT_RESPONDER;
+ } else {
+ name = sbus_opath_object_name(mem_ctx, path, PATH_BACKENDS);
+ if (name != NULL) {
+ type = COMPONENT_BACKEND;
+ } else {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+
+ if (strchr(name, '/') != NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ switch (type) {
+ case COMPONENT_MONITOR:
+ /* noop */
+ break;
+ case COMPONENT_RESPONDER:
+ if (!responder_exists(name)) {
+ ret = ENOENT;
+ goto done;
+ }
+ break;
+ case COMPONENT_BACKEND:
+ if (!backend_exists(confdb, name)) {
+ ret = ENOENT;
+ goto done;
+ }
+ break;
+ }
+
+ if (_type != NULL) {
+ *_type = type;
+ }
+
+ if (_name != NULL) {
+ *_name = name;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(name);
+ }
+
+ return ret;
+}
+
+static errno_t list_responders(TALLOC_CTX *mem_ctx,
+ const char ***_list,
+ int *_num)
+{
+ const char **list = NULL;
+ const char * const *svc = get_known_services();
+ errno_t ret;
+ int num;
+ int i;
+
+ for (num = 0; svc[num] != NULL; num++);
+
+ list = talloc_zero_array(mem_ctx, const char*, num + 1);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < num; i++) {
+ list[i] = sbus_opath_compose(list, PATH_RESPONDERS, svc[i]);
+ if (list[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_num = num;
+ *_list = list;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(list);
+ }
+
+ return ret;
+}
+
+static errno_t list_backends(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *confdb,
+ const char ***_list,
+ int *_num)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char **list = NULL;
+ char **names = NULL;
+ errno_t ret;
+ int num;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = confdb_list_all_domain_names(tmp_ctx, confdb, &names);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (num = 0; names[num] != NULL; num++);
+
+ list = talloc_zero_array(tmp_ctx, const char*, num + 1);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < num; i++) {
+ list[i] = sbus_opath_compose(list, PATH_BACKENDS, names[i]);
+ if (list[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_num = num;
+ *_list = talloc_steal(mem_ctx, list);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+ifp_list_components(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_paths)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char **responders;
+ const char **backends;
+ const char **result;
+ int num_responders;
+ int num_backends;
+ int num;
+ int i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = list_responders(tmp_ctx, &responders, &num_responders);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = list_backends(tmp_ctx, ctx->rctx->cdb, &backends, &num_backends);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ num = num_responders + num_backends + 1;
+ result = talloc_zero_array(mem_ctx, const char *, num + 1);
+ if (result == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ result[0] = PATH_MONITOR;
+
+ for (i = 0; i < num_responders; i++) {
+ result[i + 1] = talloc_steal(result, responders[i]);
+ }
+
+ for (i = 0; i < num_backends; i++) {
+ result[i + num_responders + 1] = talloc_steal(result, backends[i]);
+ }
+
+ *_paths = result;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+
+errno_t
+ifp_list_responders(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_paths)
+{
+ const char **result;
+ int num;
+ errno_t ret;
+
+ ret = list_responders(mem_ctx, &result, &num);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_paths = result;
+
+ return EOK;
+}
+
+errno_t
+ifp_list_backends(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_paths)
+{
+ const char **result;
+ int num;
+ errno_t ret;
+
+ ret = list_backends(mem_ctx, ctx->rctx->cdb, &result, &num);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_paths = result;
+
+ return EOK;
+}
+
+errno_t
+ifp_find_monitor(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_path)
+{
+ *_path = PATH_MONITOR;
+
+ return EOK;
+}
+
+
+errno_t
+ifp_find_responder_by_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char **_path)
+{
+ const char *result;
+
+ if (responder_exists(name)) {
+ result = sbus_opath_compose(mem_ctx, PATH_RESPONDERS, name);
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ *_path = result;
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_MINOR_FAILURE, "Responder \"%s\" does not exist", name);
+ return ENOENT;
+}
+
+errno_t
+ifp_find_backend_by_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char **_path)
+{
+ const char *result;
+
+ if (backend_exists(ctx->rctx->cdb, name)) {
+ result = sbus_opath_compose(mem_ctx, PATH_BACKENDS, name);
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ *_path = result;
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_MINOR_FAILURE, "Backend \"%s\" does not exist", name);
+ return ENOENT;
+}
+
+errno_t
+ifp_component_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ char *name;
+ errno_t ret;
+
+ ret = check_and_get_component_from_path(mem_ctx, ctx->rctx->cdb,
+ sbus_req->path, NULL, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown object [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ *_out = name;
+
+ return EOK;
+}
+
+errno_t
+ifp_component_get_debug_level(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *confdb_path = NULL;
+ enum component_type type;
+ char *name;
+ int level;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = check_and_get_component_from_path(tmp_ctx, ctx->rctx->cdb,
+ sbus_req->path, &type, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown object [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ switch (type) {
+ case COMPONENT_MONITOR:
+ confdb_path = CONFDB_MONITOR_CONF_ENTRY;
+ break;
+ case COMPONENT_RESPONDER:
+ confdb_path = talloc_asprintf(tmp_ctx, CONFDB_SERVICE_PATH_TMPL, name);
+ break;
+ case COMPONENT_BACKEND:
+ confdb_path = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL, name);
+ break;
+ }
+
+ if (confdb_path == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = confdb_get_int(ctx->rctx->cdb, confdb_path,
+ CONFDB_SERVICE_DEBUG_LEVEL, SSSDBG_DEFAULT, &level);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve configuration option"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_out = level;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+ifp_component_get_enabled(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out)
+{
+ TALLOC_CTX *tmp_ctx;
+ enum component_type type;
+ const char *param = NULL;
+ char **values;
+ char *name;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = check_and_get_component_from_path(tmp_ctx, ctx->rctx->cdb,
+ sbus_req->path, &type, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown object [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ switch (type) {
+ case COMPONENT_MONITOR:
+ *_out = true;
+ ret = EOK;
+ goto done;
+ case COMPONENT_RESPONDER:
+ param = CONFDB_MONITOR_ACTIVE_SERVICES;
+ break;
+ case COMPONENT_BACKEND:
+ param = CONFDB_MONITOR_ACTIVE_DOMAINS;
+ break;
+ }
+
+ ret = confdb_get_string_as_list(ctx->rctx->cdb, tmp_ctx,
+ CONFDB_MONITOR_CONF_ENTRY, param, &values);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve configuration option"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (i = 0; values[i] != NULL; i++) {
+ if (strcmp(values[i], name) == 0) {
+ *_out = true;
+ break;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+ifp_component_get_type(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ enum component_type type;
+ errno_t ret;
+
+ ret = check_and_get_component_from_path(mem_ctx, ctx->rctx->cdb,
+ sbus_req->path, &type, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown object [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ switch (type) {
+ case COMPONENT_MONITOR:
+ *_out = "monitor";
+ break;
+ case COMPONENT_RESPONDER:
+ *_out = "responder";
+ break;
+ case COMPONENT_BACKEND:
+ *_out = "backend";
+ break;
+ }
+
+ return EOK;
+}
+
+errno_t
+ifp_backend_get_providers(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *confdb_path;
+ char *name;
+ enum component_type type;
+ const char **out;
+ char *value;
+ static const char *providers[] = {CONFDB_DOMAIN_ID_PROVIDER,
+ CONFDB_DOMAIN_AUTH_PROVIDER,
+ CONFDB_DOMAIN_ACCESS_PROVIDER,
+ CONFDB_DOMAIN_CHPASS_PROVIDER,
+ CONFDB_DOMAIN_SUDO_PROVIDER,
+ CONFDB_DOMAIN_AUTOFS_PROVIDER,
+ CONFDB_DOMAIN_SELINUX_PROVIDER,
+ CONFDB_DOMAIN_HOSTID_PROVIDER,
+ CONFDB_DOMAIN_SUBDOMAINS_PROVIDER,
+ CONFDB_DOMAIN_SESSION_PROVIDER};
+ int num_providers = sizeof(providers) / sizeof(providers[0]);
+ errno_t ret;
+ int i;
+ int j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = check_and_get_component_from_path(tmp_ctx, ctx->rctx->cdb,
+ sbus_req->path, &type, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown object [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (type != COMPONENT_BACKEND) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ confdb_path = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL, name);
+ if (confdb_path == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ out = talloc_zero_array(tmp_ctx, const char *, num_providers + 1);
+ if (out == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ j = 0;
+ for (i = 0; i < num_providers; i++) {
+ ret = confdb_get_string(ctx->rctx->cdb, tmp_ctx, confdb_path,
+ providers[i], NULL, &value);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (value == NULL) {
+ continue;
+ }
+
+ out[j] = talloc_asprintf(out, "%s=%s", providers[i], value);
+ if (out[j] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ j++;
+ }
+
+ *_out = talloc_steal(mem_ctx, out);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/responder/ifp/ifp_components.h b/src/responder/ifp/ifp_components.h
new file mode 100644
index 0000000..50e8c44
--- /dev/null
+++ b/src/responder/ifp/ifp_components.h
@@ -0,0 +1,100 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 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 _IFP_COMPONENTS_H_
+#define _IFP_COMPONENTS_H_
+
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+#include "responder/ifp/ifp_private.h"
+
+/* org.freedesktop.sssd.infopipe */
+
+errno_t
+ifp_list_components(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_paths);
+
+errno_t
+ifp_list_responders(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_paths);
+errno_t
+ifp_list_backends(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_paths);
+
+errno_t
+ifp_find_monitor(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_path);
+
+errno_t
+ifp_find_responder_by_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char **_path);
+
+errno_t
+ifp_find_backend_by_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char **_path);
+
+/* org.freedesktop.sssd.infopipe.Components */
+
+errno_t
+ifp_component_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_component_get_debug_level(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out);
+
+errno_t
+ifp_component_get_enabled(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out);
+
+errno_t
+ifp_component_get_type(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+/* org.freedesktop.sssd.infopipe.Components.Backends */
+
+errno_t
+ifp_backend_get_providers(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out);
+
+#endif /* _IFP_COMPONENTS_H_ */
diff --git a/src/responder/ifp/ifp_domains.c b/src/responder/ifp/ifp_domains.c
new file mode 100644
index 0000000..16d80e6
--- /dev/null
+++ b/src/responder/ifp/ifp_domains.c
@@ -0,0 +1,1022 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 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 <string.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/ifp/ifp_domains.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+#include "sss_iface/sss_iface_async.h"
+
+#define RETURN_DOM_PROP(dbus_req, ctx, out, property) ({ \
+ struct sss_domain_info *__dom; \
+ errno_t __ret; \
+ \
+ __dom = get_domain_info_from_req((dbus_req), (ctx)); \
+ if (__dom == NULL) { \
+ __ret = ERR_DOMAIN_NOT_FOUND; \
+ } else { \
+ *(out) = __dom->property; \
+ __ret = EOK; \
+ } \
+ __ret; \
+})
+
+
+struct ifp_list_domains_state {
+ struct ifp_ctx *ifp_ctx;
+ const char **paths;
+};
+
+static void ifp_list_domains_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_list_domains_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx)
+{
+ struct ifp_list_domains_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_list_domains_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->ifp_ctx = ifp_ctx;
+
+ subreq = sss_dp_get_domains_send(state, ifp_ctx->rctx, false, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_list_domains_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_list_domains_done(struct tevent_req *subreq)
+{
+ struct ifp_list_domains_state *state;
+ struct sss_domain_info *dom;
+ struct tevent_req *req;
+ size_t num_domains;
+ size_t pi;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_list_domains_state);
+
+ ret = sss_dp_get_domains_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to refresh domain objects\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sysdb_master_domain_update(state->ifp_ctx->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to refresh subdomain list\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ num_domains = 0;
+ for (dom = state->ifp_ctx->rctx->domains;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ num_domains++;
+ }
+
+ state->paths = talloc_zero_array(state, const char *, num_domains + 1);
+ if (state->paths == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ pi = 0;
+ for (dom = state->ifp_ctx->rctx->domains;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ state->paths[pi] = sbus_opath_compose(state->paths, IFP_PATH_DOMAINS, dom->name);
+ if (state->paths[pi] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not create path for dom %s\n",
+ dom->name);
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ pi++;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t ifp_list_domains_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths)
+{
+ struct ifp_list_domains_state *state;
+ state = tevent_req_data(req, struct ifp_list_domains_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_paths = talloc_steal(mem_ctx, state->paths);
+
+ return EOK;
+}
+
+struct ifp_find_domain_by_name_state {
+ struct ifp_ctx *ifp_ctx;
+ const char *name;
+ const char *path;
+};
+
+static void ifp_find_domain_by_name_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_find_domain_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *name)
+{
+ struct ifp_find_domain_by_name_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_find_domain_by_name_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->ifp_ctx = ifp_ctx;
+ state->name = name;
+
+ subreq = sss_dp_get_domains_send(state, ifp_ctx->rctx, false, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_find_domain_by_name_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_find_domain_by_name_done(struct tevent_req *subreq)
+{
+ struct ifp_find_domain_by_name_state *state;
+ struct sss_domain_info *iter;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_find_domain_by_name_state);
+
+ ret = sss_dp_get_domains_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to refresh domain objects\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sysdb_master_domain_update(state->ifp_ctx->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to refresh subdomain list\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* Reply with the domain that was asked for */
+ for (iter = state->ifp_ctx->rctx->domains;
+ iter != NULL;
+ iter = get_next_domain(iter, SSS_GND_DESCEND)) {
+ if (strcasecmp(iter->name, state->name) == 0) {
+ break;
+ }
+ }
+
+ if (iter == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Domain not found: %s\n", state->name);
+ tevent_req_error(req, ERR_DOMAIN_NOT_FOUND);
+ return;
+ }
+
+ state->path = sbus_opath_compose(state, IFP_PATH_DOMAINS, iter->name);
+ if (state->path == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not create path for domain %s, skipping\n", iter->name);
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_find_domain_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_find_domain_by_name_state *state;
+ state = tevent_req_data(req, struct ifp_find_domain_by_name_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_path = talloc_steal(mem_ctx, state->path);
+
+ return EOK;
+}
+
+static struct sss_domain_info *
+get_domain_info_from_req(struct sbus_request *dbus_req,
+ struct ifp_ctx *ctx)
+{
+ struct sss_domain_info *domains = NULL;
+ struct sss_domain_info *iter = NULL;
+ char *name = NULL;
+
+ name = sbus_opath_object_name(NULL, dbus_req->path, IFP_PATH_DOMAINS);
+ if (name == NULL) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Looking for domain %s\n", name);
+
+ domains = ctx->rctx->domains;
+ for (iter = domains; iter != NULL;
+ iter = get_next_domain(iter, SSS_GND_DESCEND)) {
+ if (strcasecmp(iter->name, name) == 0) {
+ break;
+ }
+ }
+
+ talloc_free(name);
+ return iter;
+}
+
+static errno_t
+get_server_list(TALLOC_CTX *mem_ctx,
+ struct sbus_request *dbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out,
+ bool backup)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *srv[] = {"_srv_", NULL};
+ struct sss_domain_info *dom = NULL;
+ char *conf_path = NULL;
+ const char *option = NULL;
+ const char **out = NULL;
+ char **servers = NULL;
+ int num_servers;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ dom = get_domain_info_from_req(dbus_req, ctx);
+ if (dom == NULL) {
+ return ERR_DOMAIN_NOT_FOUND;
+ }
+
+ if (dom->parent != NULL) {
+ /* subdomains are not present in configuration */
+ ret = ENOENT;
+ goto done;
+ }
+
+ conf_path = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL, dom->name);
+ if (conf_path == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* TODO: replace hardcoded values with option names from the provider */
+ if (strcasecmp(dom->provider, "ldap") == 0) {
+ option = backup == false ? "ldap_uri" : "ldap_backup_uri";
+ } else if (strcasecmp(dom->provider, "ipa") == 0) {
+ option = backup == false ? "ipa_server" : "ipa_backup_server";
+ } else if (strcasecmp(dom->provider, "ad") == 0) {
+ option = backup == false ? "ad_server" : "ad_backup_server";
+ } else {
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = confdb_get_string_as_list(ctx->rctx->cdb, tmp_ctx, conf_path,
+ option, &servers);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (num_servers = 0; servers[num_servers] != NULL; num_servers++);
+
+ if (num_servers == 0) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ out = talloc_zero_array(mem_ctx, const char *, num_servers + 1);
+ if (out == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < num_servers; i++) {
+ out[i] = talloc_steal(out, servers[i]);
+ }
+
+ *_out = out;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret == ENOENT) {
+ *_out = srv;
+ }
+
+ return ret;
+}
+
+errno_t
+ifp_dom_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, name);
+}
+
+errno_t
+ifp_dom_get_provider(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, provider);
+}
+
+errno_t
+ifp_dom_get_primary_servers(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out)
+{
+ return get_server_list(mem_ctx, sbus_req, ctx, _out, false);
+}
+
+errno_t
+ifp_dom_get_backup_servers(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out)
+{
+ return get_server_list(mem_ctx, sbus_req, ctx, _out, true);
+}
+
+errno_t
+ifp_dom_get_min_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, id_min);
+}
+
+errno_t
+ifp_dom_get_max_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, id_max);
+}
+
+errno_t
+ifp_dom_get_realm(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, realm);
+}
+
+errno_t
+ifp_dom_get_forest(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, forest);
+}
+
+errno_t
+ifp_dom_get_login_format(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, names->re_pattern);
+}
+
+errno_t
+ifp_dom_get_fqdn_format(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, names->fq_fmt);
+}
+
+errno_t
+ifp_dom_get_enumerable(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, enumerate);
+}
+
+errno_t
+ifp_dom_get_use_fqdn(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out)
+{
+ return RETURN_DOM_PROP(sbus_req, ctx, _out, fqnames);
+}
+
+errno_t
+ifp_dom_get_subdomain(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out)
+{
+ struct sss_domain_info *dom;
+
+ dom = get_domain_info_from_req(sbus_req, ctx);
+ if (dom == NULL) {
+ return ERR_DOMAIN_NOT_FOUND;
+ }
+
+ *_out = dom->parent != NULL ? true : false;
+
+ return EOK;
+}
+
+errno_t
+ifp_dom_get_parent_domain(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ struct sss_domain_info *dom;
+ const char *path;
+
+ dom = get_domain_info_from_req(sbus_req, ctx);
+ if (dom == NULL) {
+ return ERR_DOMAIN_NOT_FOUND;
+ }
+
+ if (dom->parent == NULL) {
+ *_out = "/";
+ return EOK;
+ }
+
+ path = sbus_opath_compose(mem_ctx, IFP_PATH_DOMAINS, dom->parent->name);
+ if (path == NULL) {
+ return ENOMEM;
+ }
+
+ *_out = path;
+
+ return EOK;
+}
+
+struct ifp_domains_domain_is_online_state {
+ bool is_online;
+};
+
+static void ifp_domains_domain_is_online_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_domains_domain_is_online_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx)
+{
+ struct ifp_domains_domain_is_online_state *state;
+ struct sss_domain_info *dom;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ifp_domains_domain_is_online_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ dom = get_domain_info_from_req(sbus_req, ifp_ctx);
+ if (dom == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(ifp_ctx->rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: The Data Provider connection for "
+ "%s is not available!\n", dom->name);
+ goto done;
+ }
+
+ subreq = sbus_call_dp_backend_IsOnline_send(state, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH, dom->name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_domains_domain_is_online_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_domains_domain_is_online_done(struct tevent_req *subreq)
+{
+ struct ifp_domains_domain_is_online_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_domains_domain_is_online_state);
+
+ ret = sbus_call_dp_backend_IsOnline_recv(subreq, &state->is_online);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_domains_domain_is_online_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ bool *_is_online)
+{
+ struct ifp_domains_domain_is_online_state *state;
+ state = tevent_req_data(req, struct ifp_domains_domain_is_online_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_is_online = state->is_online;
+
+ return EOK;
+}
+
+struct ifp_domains_domain_list_services_state {
+ const char **services;
+};
+
+static void ifp_domains_domain_list_services_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_domains_domain_list_services_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx)
+{
+ struct ifp_domains_domain_list_services_state *state;
+ struct sss_domain_info *dom;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ifp_domains_domain_list_services_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ dom = get_domain_info_from_req(sbus_req, ifp_ctx);
+ if (dom == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(ifp_ctx->rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: The Data Provider connection for "
+ "%s is not available!\n", dom->name);
+ goto done;
+ }
+
+ subreq = sbus_call_dp_failover_ListServices_send(state, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH, dom->name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_domains_domain_list_services_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_domains_domain_list_services_done(struct tevent_req *subreq)
+{
+ struct ifp_domains_domain_list_services_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_domains_domain_list_services_state);
+
+ ret = sbus_call_dp_failover_ListServices_recv(state, subreq, &state->services);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_domains_domain_list_services_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_services)
+{
+ struct ifp_domains_domain_list_services_state *state;
+ state = tevent_req_data(req, struct ifp_domains_domain_list_services_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_services = talloc_steal(mem_ctx, state->services);
+
+ return EOK;
+}
+
+struct ifp_domains_domain_active_server_state {
+ const char *server;
+};
+
+static void ifp_domains_domain_active_server_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_domains_domain_active_server_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *service)
+{
+ struct ifp_domains_domain_active_server_state *state;
+ struct sss_domain_info *dom;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ifp_domains_domain_active_server_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ dom = get_domain_info_from_req(sbus_req, ifp_ctx);
+ if (dom == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(ifp_ctx->rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: The Data Provider connection for "
+ "%s is not available!\n", dom->name);
+ goto done;
+ }
+
+ subreq = sbus_call_dp_failover_ActiveServer_send(state, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH, service);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_domains_domain_active_server_done, req);
+
+ ret = EAGAIN;
+
+done:
+if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+}
+
+ return req;
+}
+
+static void ifp_domains_domain_active_server_done(struct tevent_req *subreq)
+{
+ struct ifp_domains_domain_active_server_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_domains_domain_active_server_state);
+
+ ret = sbus_call_dp_failover_ActiveServer_recv(state, subreq, &state->server);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_domains_domain_active_server_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_server)
+{
+ struct ifp_domains_domain_active_server_state *state;
+ state = tevent_req_data(req, struct ifp_domains_domain_active_server_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_server = talloc_steal(mem_ctx, state->server);
+
+ return EOK;
+}
+
+struct ifp_domains_domain_list_servers_state {
+ const char **servers;
+};
+
+static void ifp_domains_domain_list_servers_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_domains_domain_list_servers_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *service)
+{
+ struct ifp_domains_domain_list_servers_state *state;
+ struct sss_domain_info *dom;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ifp_domains_domain_list_servers_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ dom = get_domain_info_from_req(sbus_req, ifp_ctx);
+ if (dom == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(ifp_ctx->rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: The Data Provider connection for "
+ "%s is not available!\n", dom->name);
+ goto done;
+ }
+
+ subreq = sbus_call_dp_failover_ListServers_send(state, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH, service);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_domains_domain_list_servers_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_domains_domain_list_servers_done(struct tevent_req *subreq)
+{
+ struct ifp_domains_domain_list_servers_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_domains_domain_list_servers_state);
+
+ ret = sbus_call_dp_failover_ListServers_recv(state, subreq, &state->servers);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_domains_domain_list_servers_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_servers)
+{
+ struct ifp_domains_domain_list_servers_state *state;
+ state = tevent_req_data(req, struct ifp_domains_domain_list_servers_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_servers = talloc_steal(mem_ctx, state->servers);
+
+ return EOK;
+}
+
+struct ifp_domains_domain_refresh_access_rules_state {
+ int dummy;
+};
+
+static void ifp_domains_domain_refresh_access_rules_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_domains_domain_refresh_access_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx)
+{
+ struct ifp_domains_domain_refresh_access_rules_state *state;
+ struct sss_domain_info *dom;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ifp_domains_domain_refresh_access_rules_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ dom = get_domain_info_from_req(sbus_req, ifp_ctx);
+ if (dom == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(ifp_ctx->rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: The Data Provider connection for "
+ "%s is not available!\n", dom->name);
+ goto done;
+ }
+
+ subreq = sbus_call_dp_access_RefreshRules_send(state, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_domains_domain_refresh_access_rules_done, req);
+
+ ret = EAGAIN;
+
+done:
+if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+}
+
+ return req;
+}
+
+static void ifp_domains_domain_refresh_access_rules_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = sbus_call_dp_access_RefreshRules_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_domains_domain_refresh_access_rules_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
diff --git a/src/responder/ifp/ifp_domains.h b/src/responder/ifp/ifp_domains.h
new file mode 100644
index 0000000..1c84163
--- /dev/null
+++ b/src/responder/ifp/ifp_domains.h
@@ -0,0 +1,193 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2014 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 IFP_DOMAINS_H_
+#define IFP_DOMAINS_H_
+
+#include "responder/ifp/ifp_private.h"
+
+/* org.freedesktop.sssd.infopipe */
+
+struct tevent_req *
+ifp_list_domains_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx);
+
+errno_t ifp_list_domains_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths);
+
+struct tevent_req *
+ifp_find_domain_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *name);
+
+errno_t
+ifp_find_domain_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+/* org.freedesktop.sssd.infopipe.Domains */
+
+errno_t
+ifp_dom_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_dom_get_provider(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_dom_get_primary_servers(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out);
+
+errno_t
+ifp_dom_get_backup_servers(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out);
+
+errno_t
+ifp_dom_get_min_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out);
+
+errno_t
+ifp_dom_get_max_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out);
+
+errno_t
+ifp_dom_get_realm(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_dom_get_forest(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_dom_get_login_format(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_dom_get_fqdn_format(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_dom_get_enumerable(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out);
+
+errno_t
+ifp_dom_get_use_fqdn(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out);
+
+errno_t
+ifp_dom_get_subdomain(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_out);
+
+errno_t
+ifp_dom_get_parent_domain(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+struct tevent_req *
+ifp_domains_domain_is_online_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx);
+
+errno_t
+ifp_domains_domain_is_online_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ bool *_is_online);
+
+struct tevent_req *
+ifp_domains_domain_list_services_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx);
+
+errno_t
+ifp_domains_domain_list_services_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_services);
+
+struct tevent_req *
+ifp_domains_domain_active_server_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *service);
+
+errno_t
+ifp_domains_domain_active_server_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_server);
+
+struct tevent_req *
+ifp_domains_domain_list_servers_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *service);
+
+errno_t
+ifp_domains_domain_list_servers_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_servers);
+
+struct tevent_req *
+ifp_domains_domain_refresh_access_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx);
+
+errno_t
+ifp_domains_domain_refresh_access_rules_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req);
+
+#endif /* IFP_DOMAINS_H_ */
diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c
new file mode 100644
index 0000000..e65716d
--- /dev/null
+++ b/src/responder/ifp/ifp_groups.c
@@ -0,0 +1,1169 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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 "util/util.h"
+#include "db/sysdb.h"
+#include "util/strtonum.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ifp/ifp_groups.h"
+#include "responder/ifp/ifp_users.h"
+#include "responder/ifp/ifp_cache.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+
+char * ifp_groups_build_path_from_msg(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ const char *key = NULL;
+
+ switch (domain->type) {
+ case DOM_TYPE_APPLICATION:
+ key = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ break;
+ case DOM_TYPE_POSIX:
+ key = ldb_msg_find_attr_as_string(msg, SYSDB_GIDNUM, NULL);
+ break;
+ }
+
+
+ if (key == NULL) {
+ return NULL;
+ }
+
+ return sbus_opath_compose(mem_ctx, IFP_PATH_GROUPS, domain->name, key);
+}
+
+static errno_t ifp_groups_decompose_path(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ const char *path,
+ struct sss_domain_info **_domain,
+ char **_key)
+{
+ char **parts = NULL;
+ struct sss_domain_info *domain;
+ errno_t ret;
+
+ ret = sbus_opath_decompose_expected(NULL, path, IFP_PATH_GROUPS, 2, &parts);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ domain = find_domain_by_name(domains, parts[0], false);
+ if (domain == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ *_domain = domain;
+ *_key = talloc_steal(mem_ctx, parts[1]);
+
+done:
+ talloc_free(parts);
+ return ret;
+}
+
+static int ifp_groups_list_copy(struct ifp_list_ctx *list_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ size_t copy_count, i;
+ errno_t ret;
+
+ ret = ifp_list_ctx_remaining_capacity(list_ctx, result->count, &copy_count);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (i = 0; i < copy_count; i++) {
+ list_ctx->paths[list_ctx->path_count + i] = \
+ ifp_groups_build_path_from_msg(list_ctx->paths, domain,
+ result->msgs[i]);
+ if (list_ctx->paths[list_ctx->path_count + i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ list_ctx->path_count += copy_count;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+struct ifp_groups_find_by_name_state {
+ const char *path;
+};
+
+static void ifp_groups_find_by_name_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_groups_find_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name)
+{
+ struct ifp_groups_find_by_name_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ifp_groups_find_by_name_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ subreq = cache_req_group_by_name_send(state, ctx->rctx->ev, ctx->rctx,
+ ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM, NULL,
+ name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_groups_find_by_name_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_groups_find_by_name_done(struct tevent_req *subreq)
+{
+ struct ifp_groups_find_by_name_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_groups_find_by_name_state);
+
+ ret = cache_req_group_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find group [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->path = ifp_groups_build_path_from_msg(state, result->domain,
+ result->msgs[0]);
+ if (state->path == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_groups_find_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_groups_find_by_name_state *state;
+ state = tevent_req_data(req, struct ifp_groups_find_by_name_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_path = talloc_steal(mem_ctx, state->path);
+
+ return EOK;
+}
+
+struct ifp_groups_find_by_id_state {
+ const char *path;
+};
+
+static void ifp_groups_find_by_id_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_groups_find_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t id)
+{
+ struct ifp_groups_find_by_id_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_groups_find_by_id_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ subreq = cache_req_group_by_id_send(state, ctx->rctx->ev, ctx->rctx,
+ ctx->rctx->ncache, 0, NULL, id);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_groups_find_by_id_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_groups_find_by_id_done(struct tevent_req *subreq)
+{
+ struct ifp_groups_find_by_id_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_groups_find_by_id_state);
+
+ ret = cache_req_group_by_id_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find group [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->path = ifp_groups_build_path_from_msg(state, result->domain,
+ result->msgs[0]);
+ if (state->path == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_groups_find_by_id_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_groups_find_by_id_state *state;
+ state = tevent_req_data(req, struct ifp_groups_find_by_id_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_path = talloc_steal(mem_ctx, state->path);
+
+ return EOK;
+}
+
+struct ifp_groups_list_by_name_state {
+ struct ifp_ctx *ifp_ctx;
+ struct ifp_list_ctx *list_ctx;
+};
+
+static errno_t ifp_groups_list_by_name_step(struct tevent_req *req);
+static void ifp_groups_list_by_name_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_groups_list_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *filter,
+ uint32_t limit)
+{
+ struct ifp_groups_list_by_name_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_groups_list_by_name_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->ifp_ctx = ctx;
+ state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, filter, limit);
+ if (state->list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ifp_groups_list_by_name_step(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t
+ifp_groups_list_by_name_step(struct tevent_req *req)
+{
+ struct ifp_groups_list_by_name_state *state;
+ struct tevent_req *subreq;
+
+ state = tevent_req_data(req, struct ifp_groups_list_by_name_state);
+
+ if (state->list_ctx->dom == NULL) {
+ return EOK;
+ }
+
+ subreq = cache_req_group_by_filter_send(state->list_ctx,
+ state->ifp_ctx->rctx->ev,
+ state->ifp_ctx->rctx,
+ CACHE_REQ_ANY_DOM,
+ state->list_ctx->dom->name,
+ state->list_ctx->filter);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, ifp_groups_list_by_name_done, req);
+
+ state->list_ctx->dom = get_next_domain(state->list_ctx->dom,
+ SSS_GND_DESCEND);
+
+ return EAGAIN;
+}
+
+static void ifp_groups_list_by_name_done(struct tevent_req *subreq)
+{
+ struct ifp_groups_list_by_name_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_groups_list_by_name_state);
+
+ ret = cache_req_group_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret == EOK) {
+ ret = ifp_groups_list_copy(state->list_ctx, result->domain,
+ result->ldb_result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to list groups [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ifp_groups_list_by_name_step(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+errno_t
+ifp_groups_list_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths)
+{
+ struct ifp_groups_list_by_name_state *state;
+ state = tevent_req_data(req, struct ifp_groups_list_by_name_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_paths = talloc_steal(mem_ctx, state->list_ctx->paths);
+
+ return EOK;
+}
+
+struct ifp_groups_list_by_domain_and_name_state {
+ struct ifp_list_ctx *list_ctx;
+};
+
+static void ifp_groups_list_by_domain_and_name_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_groups_list_by_domain_and_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char *filter,
+ uint32_t limit)
+{
+ struct ifp_groups_list_by_domain_and_name_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_groups_list_by_domain_and_name_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, filter, limit);
+ if (state->list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = cache_req_group_by_filter_send(state->list_ctx, ctx->rctx->ev,
+ ctx->rctx, CACHE_REQ_ANY_DOM,
+ domain, filter);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_groups_list_by_domain_and_name_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_groups_list_by_domain_and_name_done(struct tevent_req *subreq)
+{
+ struct ifp_groups_list_by_domain_and_name_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_groups_list_by_domain_and_name_state);
+
+ ret = cache_req_group_by_filter_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ifp_groups_list_copy(state->list_ctx, result->domain,
+ result->ldb_result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_groups_list_by_domain_and_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths)
+{
+ struct ifp_groups_list_by_domain_and_name_state *state;
+ state = tevent_req_data(req, struct ifp_groups_list_by_domain_and_name_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_paths = talloc_steal(mem_ctx, state->list_ctx->paths);
+
+ return EOK;
+}
+
+static errno_t
+ifp_groups_get_from_cache(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *key,
+ struct ldb_message **_group)
+{
+ struct ldb_result *group_res = NULL;
+ errno_t ret;
+ gid_t gid;
+ char *endptr;
+
+ switch (domain->type) {
+ case DOM_TYPE_POSIX:
+ gid = strtouint32(key, &endptr, 10);
+ if ((errno != 0) || *endptr || (key == endptr)) {
+ ret = errno ? errno : EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid GID value\n");
+ return ret;
+ }
+
+ ret = sysdb_getgrgid_with_views(NULL, domain, gid, &group_res);
+ if (ret == EOK && group_res->count == 0) {
+ *_group = NULL;
+ ret = ENOENT;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %u@%s [%d]: %s\n",
+ gid, domain->name, ret, sss_strerror(ret));
+ goto done;
+ }
+ break;
+ case DOM_TYPE_APPLICATION:
+ ret = sysdb_getgrnam_with_views(NULL, domain, key, &group_res);
+ if (ret == EOK && group_res->count == 0) {
+ *_group = NULL;
+ ret = ENOENT;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %s@%s [%d]: %s\n",
+ key, domain->name, ret, sss_strerror(ret));
+ goto done;
+ }
+ break;
+ }
+
+ if (group_res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More groups matched by the single key\n");
+ return EIO;
+ }
+
+ *_group = talloc_steal(mem_ctx, group_res->msgs[0]);
+
+ ret = EOK;
+
+done:
+ talloc_free(group_res);
+
+ return ret;
+}
+
+static errno_t
+ifp_groups_group_get(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ struct sss_domain_info **_domain,
+ struct ldb_message **_group)
+{
+ struct sss_domain_info *domain;
+ char *key;
+ errno_t ret;
+
+ ret = ifp_groups_decompose_path(NULL, ctx->rctx->domains, sbus_req->path,
+ &domain, &key);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path"
+ "[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (_group != NULL) {
+ ret = ifp_groups_get_from_cache(mem_ctx, domain, key, _group);
+ }
+
+ talloc_free(key);
+
+ if (ret == EOK || ret == ENOENT) {
+ if (_domain != NULL) {
+ *_domain = domain;
+ }
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve group from cache\n");
+ }
+
+ return ret;
+}
+
+struct resolv_ghosts_state {
+ struct tevent_context *ev;
+ struct sbus_request *sbus_req;
+ struct ifp_ctx *ctx;
+
+ struct sss_domain_info *domain;
+ const char **ghosts;
+ int index;
+};
+
+static void resolv_ghosts_group_done(struct tevent_req *subreq);
+static errno_t resolv_ghosts_step(struct tevent_req *req);
+static void resolv_ghosts_done(struct tevent_req *subreq);
+
+static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx)
+{
+ struct resolv_ghosts_state *state;
+ struct sss_domain_info *domain;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct ldb_message *group;
+ const char *name;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct resolv_ghosts_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->sbus_req = sbus_req;
+ state->ctx = ctx;
+
+ ret = ifp_groups_group_get(state, sbus_req, ctx, &domain, &group);
+ if (ret != EOK) {
+ goto immediately;
+ }
+
+ name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Group name is empty!\n");
+ ret = ERR_INTERNAL;
+ goto immediately;
+ }
+
+ subreq = cache_req_group_by_name_send(state, ev, ctx->rctx,
+ ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM,
+ domain->name,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+
+ tevent_req_set_callback(subreq, resolv_ghosts_group_done, req);
+
+ return req;
+
+immediately:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static void resolv_ghosts_group_done(struct tevent_req *subreq)
+{
+ struct resolv_ghosts_state *state;
+ struct ldb_message *group = NULL;
+ struct ldb_message_element *el;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct resolv_ghosts_state);
+
+ ret = ifp_groups_group_get(state, state->sbus_req, state->ctx,
+ &state->domain, &group);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ el = ldb_msg_find_element(group, SYSDB_GHOST);
+ if (el == NULL || el->num_values == 0) {
+ ret = EOK;
+ goto done;
+ }
+
+ state->ghosts = sss_ldb_el_to_string_list(state, el);
+ if (state->ghosts == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->index = 0;
+ ret = resolv_ghosts_step(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+errno_t resolv_ghosts_step(struct tevent_req *req)
+{
+ struct resolv_ghosts_state *state;
+ struct tevent_req *subreq;
+
+ state = tevent_req_data(req, struct resolv_ghosts_state);
+
+ if (state->ghosts[state->index] == NULL) {
+ return EOK;
+ }
+
+ subreq = cache_req_user_by_name_send(state, state->ev, state->ctx->rctx,
+ state->ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM,
+ state->domain->name,
+ state->ghosts[state->index]);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, resolv_ghosts_done, req);
+
+ state->index++;
+
+ return EAGAIN;
+}
+
+static void resolv_ghosts_done(struct tevent_req *subreq)
+{
+ struct resolv_ghosts_state *state = NULL;
+ struct tevent_req *req = NULL;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct resolv_ghosts_state);
+
+ ret = cache_req_user_by_name_recv(state, subreq, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = resolv_ghosts_step(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static errno_t resolv_ghosts_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct ifp_groups_group_update_member_list_state {
+ int dummy;
+};
+
+static void ifp_groups_group_update_member_list_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_groups_group_update_member_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx)
+{
+ struct ifp_groups_group_update_member_list_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_groups_group_update_member_list_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ subreq = resolv_ghosts_send(state, ev, sbus_req, ctx);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_groups_group_update_member_list_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_groups_group_update_member_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = resolv_ghosts_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to resolve ghost members [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_groups_group_update_member_list_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+errno_t
+ifp_groups_group_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ struct sss_domain_info *domain;
+ struct ldb_message *msg;
+ const char *in_name;
+ const char *out;
+ errno_t ret;
+
+ ret = ifp_groups_group_get(mem_ctx, sbus_req, ctx, &domain, &msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ in_name = sss_view_ldb_msg_find_attr_as_string(domain, msg,
+ SYSDB_NAME, NULL);
+ if (in_name == NULL) {
+ talloc_zfree(msg);
+ DEBUG(SSSDBG_OP_FAILURE, "No name?\n");
+ return ERR_INTERNAL;
+ }
+
+ out = ifp_format_name_attr(mem_ctx, ctx, in_name, domain);
+ talloc_zfree(msg);
+ if (out == NULL) {
+ return ENOMEM;
+ }
+
+ *_out = out;
+
+ return EOK;
+}
+
+errno_t
+ifp_groups_group_get_gid_number(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out)
+{
+ struct ldb_message *msg;
+ struct sss_domain_info *domain;
+ errno_t ret;
+
+ ret = ifp_groups_group_get(mem_ctx, sbus_req, ctx, &domain, &msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_out = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0);
+ talloc_zfree(msg);
+
+ return EOK;
+}
+
+errno_t
+ifp_groups_group_get_unique_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ struct ldb_message *msg;
+ struct sss_domain_info *domain;
+ const char *uuid;
+ errno_t ret;
+
+ ret = ifp_groups_group_get(mem_ctx, sbus_req, ctx, &domain, &msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ uuid = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_UUID, NULL);
+ if (uuid == NULL) {
+ talloc_zfree(msg);
+ return ENOENT;
+ }
+
+ uuid = talloc_strdup(mem_ctx, uuid);
+ talloc_zfree(msg);
+ if (uuid == NULL) {
+ return ENOMEM;
+ }
+
+ *_out = uuid;
+
+ return EOK;
+}
+
+static errno_t
+ifp_groups_group_get_members(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_users,
+ const char ***_groups)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *domain;
+ struct ldb_message *group;
+ struct ldb_message **members;
+ size_t num_members;
+ const char *class;
+ const char **users;
+ const char **groups;
+ int num_users;
+ int num_groups;
+ int i;
+ errno_t ret;
+ const char *attrs[] = {SYSDB_OBJECTCATEGORY, SYSDB_UIDNUM,
+ SYSDB_GIDNUM, NULL};
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ifp_groups_group_get(tmp_ctx, sbus_req, ctx, &domain, &group);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_asq_search(tmp_ctx, domain, group->dn, NULL, SYSDB_MEMBER,
+ attrs, &num_members, &members);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform ASQ search [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (num_members == 0) {
+ users = NULL;
+ groups = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ users = talloc_zero_array(tmp_ctx, const char *, num_members + 1);
+ if (users == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ groups = talloc_zero_array(tmp_ctx, const char *, num_members + 1);
+ if (groups == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_users = 0;
+ num_groups = 0;
+ for (i = 0; i < num_members; i++) {
+ class = ldb_msg_find_attr_as_string(members[i], SYSDB_OBJECTCATEGORY,
+ NULL);
+ if (class == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (strcmp(class, SYSDB_USER_CLASS) == 0) {
+ users[num_users] = ifp_users_build_path_from_msg(users, domain,
+ members[i]);
+ if (users[num_users] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_users++;
+ } else if (strcmp(class, SYSDB_GROUP_CLASS) == 0) {
+ groups[num_groups] = ifp_groups_build_path_from_msg(groups,
+ domain, members[i]);
+ if (groups[num_groups] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_groups++;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected object class %s\n", class);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ if (_users != NULL) {
+ *_users = talloc_steal(mem_ctx, users);
+ }
+
+ if (_groups != NULL) {
+ *_groups = talloc_steal(mem_ctx, groups);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+ifp_groups_group_get_users(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out)
+{
+ errno_t ret;
+
+ ret = ifp_groups_group_get_members(mem_ctx, sbus_req, ctx, _out, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire groups members\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t
+ifp_groups_group_get_groups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out)
+{
+ errno_t ret;
+
+ ret = ifp_groups_group_get_members(mem_ctx, sbus_req, ctx, NULL, _out);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire groups members\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t
+ifp_cache_list_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out)
+{
+ return ifp_cache_list(mem_ctx, ctx, IFP_CACHE_GROUP, _out);
+}
+
+errno_t
+ifp_cache_list_by_domain_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char ***_out)
+{
+ return ifp_cache_list_by_domain(mem_ctx, ctx, domain, IFP_CACHE_GROUP, _out);
+}
+
+errno_t
+ifp_cache_object_store_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result)
+{
+ struct sss_domain_info *domain;
+ struct ldb_message *group;
+ errno_t ret;
+
+ ret = ifp_groups_group_get(NULL, sbus_req, ctx, &domain, &group);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = ifp_cache_object_store(domain, group->dn);
+ talloc_free(group);
+
+ if (ret == EOK) {
+ *_result = true;
+ }
+
+ return ret;
+}
+
+errno_t
+ifp_cache_object_remove_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result)
+{
+ struct sss_domain_info *domain;
+ struct ldb_message *group;
+ errno_t ret;
+
+ ret = ifp_groups_group_get(NULL, sbus_req, ctx, &domain, &group);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = ifp_cache_object_remove(domain, group->dn);
+ talloc_free(group);
+
+ if (ret == EOK) {
+ *_result = true;
+ }
+
+ return ret;
+}
diff --git a/src/responder/ifp/ifp_groups.h b/src/responder/ifp/ifp_groups.h
new file mode 100644
index 0000000..114962f
--- /dev/null
+++ b/src/responder/ifp/ifp_groups.h
@@ -0,0 +1,156 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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 IFP_GROUPS_H_
+#define IFP_GROUPS_H_
+
+#include "responder/ifp/ifp_private.h"
+
+/* Utility functions */
+
+char * ifp_groups_build_path_from_msg(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg);
+
+/* org.freedesktop.sssd.infopipe.Groups */
+
+struct tevent_req *
+ifp_groups_find_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name);
+
+errno_t
+ifp_groups_find_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+struct tevent_req *
+ifp_groups_find_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t id);
+
+errno_t
+ifp_groups_find_by_id_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+struct tevent_req *
+ifp_groups_list_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *filter,
+ uint32_t limit);
+
+errno_t
+ifp_groups_list_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths);
+
+struct tevent_req *
+ifp_groups_list_by_domain_and_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char *filter,
+ uint32_t limit);
+
+errno_t
+ifp_groups_list_by_domain_and_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths);
+
+/* org.freedesktop.sssd.infopipe.Groups.Group */
+
+struct tevent_req *
+ifp_groups_group_update_member_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx);
+
+errno_t
+ifp_groups_group_update_member_list_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req);
+
+errno_t
+ifp_groups_group_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_groups_group_get_gid_number(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out);
+
+errno_t
+ifp_groups_group_get_unique_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_groups_group_get_users(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out);
+
+errno_t
+ifp_groups_group_get_groups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out);
+
+/* org.freedesktop.sssd.infopipe.Cache */
+
+errno_t
+ifp_cache_list_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out);
+
+errno_t
+ifp_cache_list_by_domain_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char ***_out);
+
+/* org.freedesktop.sssd.infopipe.Cache.Object */
+
+errno_t
+ifp_cache_object_store_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result);
+
+errno_t
+ifp_cache_object_remove_group(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result);
+
+#endif /* IFP_GROUPS_H_ */
diff --git a/src/responder/ifp/ifp_iface/ifp_iface.c b/src/responder/ifp/ifp_iface/ifp_iface.c
new file mode 100644
index 0000000..cbab470
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/ifp_iface.c
@@ -0,0 +1,274 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2018 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 "responder/common/responder.h"
+#include "responder/ifp/ifp_private.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+#include "responder/ifp/ifp_cache.h"
+#include "responder/ifp/ifp_components.h"
+#include "responder/ifp/ifp_domains.h"
+#include "responder/ifp/ifp_groups.h"
+#include "responder/ifp/ifp_users.h"
+
+errno_t
+ifp_access_check(struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx)
+{
+ uid_t uid;
+ errno_t ret;
+
+ /* We allow those special cases to access infopipe. */
+ if (sbus_req->sender->uid < 0) {
+ return EOK;
+ }
+
+ uid = (uid_t)sbus_req->sender->uid;
+
+ ret = check_allowed_uids(uid,
+ ifp_ctx->rctx->allowed_uids_count,
+ ifp_ctx->rctx->allowed_uids);
+ if (ret == EACCES) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "User %"PRIi64" not in ACL\n",
+ sbus_req->sender->uid);
+ return ret;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot check if user %"PRIi64
+ "is present in ACL\n", sbus_req->sender->uid);
+ return ret;
+ }
+
+ switch (sbus_req->type) {
+ case SBUS_REQUEST_PROPERTY_GET:
+ if (strcmp(sbus_req->interface, "org.freedesktop.sssd.infopipe.Users.User") == 0) {
+ if (!ifp_is_user_attr_allowed(ifp_ctx, sbus_req->property)) {
+ DEBUG(SSSDBG_TRACE_ALL, "Attribute %s is not allowed\n",
+ sbus_req->property);
+ return EACCES;
+ }
+ }
+ break;
+ default:
+ return EOK;
+ }
+
+ return EOK;
+}
+
+errno_t
+ifp_register_sbus_interface(struct sbus_connection *conn,
+ struct ifp_ctx *ctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface_ifp,
+ org_freedesktop_sssd_infopipe,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe, Ping, ifp_ping, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe, ListComponents, ifp_list_components, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe, ListResponders, ifp_list_responders, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe, ListBackends, ifp_list_backends, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe, FindMonitor, ifp_find_monitor, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe, FindResponderByName, ifp_find_responder_by_name, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe, FindBackendByName, ifp_find_backend_by_name, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe, GetUserAttr, ifp_get_user_attr_send, ifp_get_user_attr_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe, GetUserGroups, ifp_user_get_groups_send, ifp_user_get_groups_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe, FindDomainByName, ifp_find_domain_by_name_send, ifp_find_domain_by_name_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe, ListDomains, ifp_list_domains_send, ifp_list_domains_recv, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_ifp_components,
+ org_freedesktop_sssd_infopipe_Components,
+ SBUS_METHODS(SBUS_NO_METHODS),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Components, name, ifp_component_get_name, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Components, debug_level, ifp_component_get_debug_level, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Components, enabled, ifp_component_get_enabled, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Components, type, ifp_component_get_type, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Components, providers, ifp_backend_get_providers, ctx)
+ )
+ );
+
+ SBUS_INTERFACE(iface_ifp_domains,
+ org_freedesktop_sssd_infopipe_Domains,
+ SBUS_METHODS(SBUS_NO_METHODS),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, name, ifp_dom_get_name, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, provider, ifp_dom_get_provider, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, primary_servers, ifp_dom_get_primary_servers, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, backup_servers, ifp_dom_get_backup_servers, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, min_id, ifp_dom_get_min_id, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, max_id, ifp_dom_get_max_id, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, realm, ifp_dom_get_realm, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, forest, ifp_dom_get_forest, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, login_format, ifp_dom_get_login_format, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, fully_qualified_name_format, ifp_dom_get_fqdn_format, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, enumerable, ifp_dom_get_enumerable, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, use_fully_qualified_names, ifp_dom_get_use_fqdn, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, subdomain, ifp_dom_get_subdomain, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Domains, parent_domain, ifp_dom_get_parent_domain, ctx)
+ )
+ );
+
+ SBUS_INTERFACE(iface_ifp_domains_domain,
+ org_freedesktop_sssd_infopipe_Domains_Domain,
+ SBUS_METHODS(
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Domains_Domain, IsOnline, ifp_domains_domain_is_online_send, ifp_domains_domain_is_online_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Domains_Domain, ListServices, ifp_domains_domain_list_services_send, ifp_domains_domain_list_services_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Domains_Domain, ActiveServer, ifp_domains_domain_active_server_send, ifp_domains_domain_active_server_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Domains_Domain, ListServers, ifp_domains_domain_list_servers_send, ifp_domains_domain_list_servers_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Domains_Domain, RefreshAccessRules, ifp_domains_domain_refresh_access_rules_send, ifp_domains_domain_refresh_access_rules_recv, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_ifp_users,
+ org_freedesktop_sssd_infopipe_Users,
+ SBUS_METHODS(
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, FindByName, ifp_users_find_by_name_send, ifp_users_find_by_name_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, FindByID, ifp_users_find_by_id_send, ifp_users_find_by_id_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, FindByCertificate, ifp_users_find_by_cert_send, ifp_users_find_by_cert_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByCertificate, ifp_users_list_by_cert_send, ifp_users_list_by_cert_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, FindByNameAndCertificate, ifp_users_find_by_name_and_cert_send, ifp_users_find_by_name_and_cert_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByName, ifp_users_list_by_name_send, ifp_users_list_by_attr_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByDomainAndName, ifp_users_list_by_domain_and_name_send, ifp_users_list_by_domain_and_name_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, FindByValidCertificate, ifp_users_find_by_valid_cert_send, ifp_users_find_by_valid_cert_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByAttr, ifp_users_list_by_attr_send, ifp_users_list_by_attr_recv, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_ifp_users_user,
+ org_freedesktop_sssd_infopipe_Users_User,
+ SBUS_METHODS(SBUS_NO_METHODS),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, name, ifp_users_user_get_name, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, uidNumber, ifp_users_user_get_uid_number, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, gidNumber, ifp_users_user_get_gid_number, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, gecos, ifp_users_user_get_gecos, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, homeDirectory, ifp_users_user_get_home_directory, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, loginShell, ifp_users_user_get_login_shell, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, uniqueID, ifp_users_user_get_unique_id, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, groups, ifp_users_user_get_groups, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, domain, ifp_users_user_get_domain, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, domainname, ifp_users_user_get_domainname, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Users_User, extraAttributes, ifp_users_user_get_extra_attributes, ctx)
+ )
+ );
+
+ SBUS_INTERFACE(iface_ifp_cache_user,
+ org_freedesktop_sssd_infopipe_Cache,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache, List, ifp_cache_list_user, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache, ListByDomain, ifp_cache_list_by_domain_user, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_ifp_cache_object_user,
+ org_freedesktop_sssd_infopipe_Cache_Object,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache_Object, Store, ifp_cache_object_store_user, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache_Object, Remove, ifp_cache_object_remove_user, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_ifp_groups,
+ org_freedesktop_sssd_infopipe_Groups,
+ SBUS_METHODS(
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Groups, FindByName, ifp_groups_find_by_name_send, ifp_groups_find_by_name_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Groups, FindByID, ifp_groups_find_by_id_send, ifp_groups_find_by_id_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Groups, ListByName, ifp_groups_list_by_name_send, ifp_groups_list_by_name_recv, ctx),
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Groups, ListByDomainAndName, ifp_groups_list_by_domain_and_name_send, ifp_groups_list_by_domain_and_name_recv, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_ifp_groups_group,
+ org_freedesktop_sssd_infopipe_Groups_Group,
+ SBUS_METHODS(
+ SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Groups_Group, UpdateMemberList, ifp_groups_group_update_member_list_send, ifp_groups_group_update_member_list_recv, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Groups_Group, name, ifp_groups_group_get_name, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Groups_Group, gidNumber, ifp_groups_group_get_gid_number, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Groups_Group, uniqueID, ifp_groups_group_get_unique_id, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Groups_Group, users, ifp_groups_group_get_users, ctx),
+ SBUS_SYNC(GETTER, org_freedesktop_sssd_infopipe_Groups_Group, groups, ifp_groups_group_get_groups, ctx)
+ )
+ );
+
+ SBUS_INTERFACE(iface_ifp_cache_group,
+ org_freedesktop_sssd_infopipe_Cache,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache, List, ifp_cache_list_group, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache, ListByDomain, ifp_cache_list_by_domain_group, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ SBUS_INTERFACE(iface_ifp_cache_object_group,
+ org_freedesktop_sssd_infopipe_Cache_Object,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache_Object, Store, ifp_cache_object_store_group, ctx),
+ SBUS_SYNC(METHOD, org_freedesktop_sssd_infopipe_Cache_Object, Remove, ifp_cache_object_remove_group, ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ struct sbus_path paths[] = {
+ { IFP_PATH, &iface_ifp },
+ { IFP_PATH_DOMAINS, &iface_ifp_domains },
+ { IFP_PATH_DOMAINS_TREE, &iface_ifp_domains },
+ { IFP_PATH_DOMAINS_TREE, &iface_ifp_domains_domain },
+ { IFP_PATH_COMPONENTS_TREE, &iface_ifp_components },
+ { IFP_PATH_USERS, &iface_ifp_users },
+ { IFP_PATH_USERS, &iface_ifp_cache_user },
+ { IFP_PATH_USERS_TREE, &iface_ifp_users_user },
+ { IFP_PATH_USERS_TREE, &iface_ifp_cache_object_user },
+ { IFP_PATH_GROUPS, &iface_ifp_groups },
+ { IFP_PATH_GROUPS, &iface_ifp_cache_group },
+ { IFP_PATH_GROUPS_TREE, &iface_ifp_groups_group },
+ { IFP_PATH_GROUPS_TREE, &iface_ifp_cache_object_group },
+ {NULL, NULL}
+ };
+
+ ret = sbus_connection_add_path_map(conn, paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to add paths [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
diff --git a/src/responder/ifp/ifp_iface/ifp_iface.h b/src/responder/ifp/ifp_iface/ifp_iface.h
new file mode 100644
index 0000000..0bf6510
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/ifp_iface.h
@@ -0,0 +1,40 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2018 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 _IFP_IFACE_H_
+#define _IFP_IFACE_H_
+
+#define IFP_BUS "org.freedesktop.sssd.infopipe"
+
+#define IFP_PATH "/org/freedesktop/sssd/infopipe"
+
+#define IFP_PATH_DOMAINS IFP_PATH "/Domains"
+#define IFP_PATH_DOMAINS_TREE IFP_PATH_DOMAINS "/*"
+
+#define IFP_PATH_COMPONENTS IFP_PATH "/Components"
+#define IFP_PATH_COMPONENTS_TREE IFP_PATH_COMPONENTS "/*"
+
+#define IFP_PATH_GROUPS IFP_PATH "/Groups"
+#define IFP_PATH_GROUPS_TREE IFP_PATH_GROUPS "/*"
+
+#define IFP_PATH_USERS IFP_PATH "/Users"
+#define IFP_PATH_USERS_TREE IFP_PATH_USERS "/*"
+
+#endif /* _IFP_IFACE_H_ */
diff --git a/src/responder/ifp/ifp_iface/ifp_iface.xml b/src/responder/ifp/ifp_iface/ifp_iface.xml
new file mode 100644
index 0000000..75b4891
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/ifp_iface.xml
@@ -0,0 +1,253 @@
+<!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.infopipe">
+ <annotation name="codegen.Name" value="ifp" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="Ping">
+ <arg name="ping" type="s" direction="in" key="1" />
+ <arg name="pong" type="s" direction="out" />
+ </method>
+
+ <!-- SSSD components -->
+
+ <method name="ListComponents" key="True">
+ <arg name="components" type="ao" direction="out"/>
+ </method>
+
+ <method name="ListResponders" key="True">
+ <arg name="responders" type="ao" direction="out"/>
+ </method>
+
+ <method name="ListBackends" key="True">
+ <arg name="backends" type="ao" direction="out"/>
+ </method>
+
+ <method name="FindMonitor" key="True">
+ <arg name="monitor" type="o" direction="out"/>
+ </method>
+
+ <method name="FindResponderByName">
+ <arg name="name" type="s" direction="in" key="1" />
+ <arg name="responder" type="o" direction="out"/>
+ </method>
+
+ <method name="FindBackendByName">
+ <arg name="name" type="s" direction="in" key="1" />
+ <arg name="backend" type="o" direction="out"/>
+ </method>
+
+ <method name="GetUserAttr">
+ <annotation name="codegen.CustomOutputHandler" value="true"/>
+ <arg name="user" type="s" direction="in" />
+ <arg name="attr" type="as" direction="in" />
+ <arg name="values" type="a{sv}" direction="out"/>
+ </method>
+
+ <method name="GetUserGroups">
+ <arg name="user" type="s" direction="in" key="1" />
+ <arg name="values" type="as" direction="out"/>
+ </method>
+
+ <method name="FindDomainByName">
+ <arg name="name" type="s" direction="in" key="1" />
+ <arg name="domain" type="o" direction="out"/>
+ </method>
+
+ <method name="ListDomains" key="True">
+ <arg name="domain" type="ao" direction="out"/>
+ </method>
+
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Components">
+ <annotation name="codegen.Name" value="ifp_components" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <property name="name" type="s" access="read" />
+ <property name="debug_level" type="u" access="read" />
+ <property name="enabled" type="b" access="read" />
+ <property name="type" type="s" access="read" />
+
+ <!-- FIXME: This should be part of Components.Backends interface, onece
+ SSSD supports multiple interfaces per object path. -->
+ <property name="providers" type="as" access="read" />
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Domains">
+ <annotation name="codegen.Name" value="ifp_domains" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <property name="name" type="s" access="read"/>
+ <property name="provider" type="s" access="read"/>
+ <property name="primary_servers" type="as" access="read"/>
+ <property name="backup_servers" type="as" access="read"/>
+ <property name="min_id" type="u" access="read"/>
+ <property name="max_id" type="u" access="read"/>
+ <property name="realm" type="s" access="read"/>
+ <property name="forest" type="s" access="read"/>
+ <property name="login_format" type="s" access="read"/>
+ <property name="fully_qualified_name_format" type="s" access="read"/>
+ <property name="enumerable" type="b" access="read"/>
+ <property name="use_fully_qualified_names" type="b" access="read"/>
+ <property name="subdomain" type="b" access="read"/>
+ <property name="parent_domain" type="o" access="read"/>
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Domains.Domain">
+ <annotation name="codegen.Name" value="ifp_domain" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="IsOnline" key="True">
+ <arg name="status" type="b" direction="out" />
+ </method>
+
+ <method name="ListServices" key="True">
+ <arg name="services" type="as" direction="out" />
+ </method>
+
+ <method name="ActiveServer">
+ <arg name="service" type="s" direction="in" key="1" />
+ <arg name="server" type="s" direction="out" />
+ </method>
+
+ <method name="ListServers">
+ <arg name="service_name" type="s" direction="in" key="1" />
+ <arg name="servers" type="as" direction="out" />
+ </method>
+
+ <method name="RefreshAccessRules" key="True" />
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Cache">
+ <annotation name="codegen.Name" value="ifp_cache" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="List" key="True">
+ <arg name="result" type="ao" direction="out" />
+ </method>
+ <method name="ListByDomain">
+ <arg name="domain_name" type="s" direction="in" key="1" />
+ <arg name="result" type="ao" direction="out"/>
+ </method>
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Cache.Object">
+ <annotation name="codegen.Name" value="ifp_cache_object" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="Store" key="True">
+ <arg name="result" type="b" direction="out" />
+ </method>
+ <method name="Remove" key="True">
+ <arg name="result" type="b" direction="out" />
+ </method>
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Users">
+ <annotation name="codegen.Name" value="ifp_users" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="FindByName">
+ <arg name="name" type="s" direction="in" key="1" />
+ <arg name="result" type="o" direction="out" />
+ </method>
+ <method name="FindByID">
+ <arg name="id" type="u" direction="in" key="1" />
+ <arg name="result" type="o" direction="out" />
+ </method>
+ <method name="FindByCertificate">
+ <arg name="pem_cert" type="s" direction="in" />
+ <arg name="result" type="o" direction="out" />
+ </method>
+ <method name="ListByCertificate">
+ <arg name="pem_cert" type="s" direction="in" />
+ <arg name="limit" type="u" direction="in" />
+ <arg name="result" type="ao" direction="out" />
+ </method>
+ <method name="FindByNameAndCertificate">
+ <arg name="name" type="s" direction="in" />
+ <arg name="pem_cert" type="s" direction="in" />
+ <arg name="result" type="o" direction="out" />
+ </method>
+ <method name="ListByName">
+ <arg name="name_filter" type="s" direction="in" key="1" />
+ <arg name="limit" type="u" direction="in" key="2" />
+ <arg name="result" type="ao" direction="out" />
+ </method>
+ <method name="ListByDomainAndName">
+ <arg name="domain_name" type="s" direction="in" key="1" />
+ <arg name="name_filter" type="s" direction="in" key="2" />
+ <arg name="limit" type="u" direction="in" key="3" />
+ <arg name="result" type="ao" direction="out"/>
+ </method>
+ <method name="FindByValidCertificate">
+ <arg name="pem_cert" type="s" direction="in" />
+ <arg name="result" type="o" direction="out" />
+ </method>
+ <method name="ListByAttr">
+ <arg name="attribute" type="s" direction="in" key="1" />
+ <arg name="attr_filter" type="s" direction="in" key="2" />
+ <arg name="limit" type="u" direction="in" key="3" />
+ <arg name="result" type="ao" direction="out" />
+ </method>
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Users.User">
+ <annotation name="codegen.Name" value="ifp_user" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="UpdateGroupsList" key="True" />
+
+ <property name="name" type="s" access="read" />
+ <property name="uidNumber" type="u" access="read" />
+ <property name="gidNumber" type="u" access="read" />
+ <property name="gecos" type="s" access="read" />
+ <property name="homeDirectory" type="s" access="read" />
+ <property name="loginShell" type="s" access="read" />
+ <property name="uniqueID" type="s" access="read" />
+ <property name="groups" type="ao" access="read" />
+ <property name="domain" type="o" access="read" />
+ <property name="domainname" type="s" access="read" />
+ <property name="extraAttributes" type="ifp_extra" access="read" />
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Groups">
+ <annotation name="codegen.Name" value="ifp_groups" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="FindByName">
+ <arg name="name" type="s" direction="in" key="1" />
+ <arg name="result" type="o" direction="out" />
+ </method>
+ <method name="FindByID">
+ <arg name="id" type="u" direction="in" key="1" />
+ <arg name="result" type="o" direction="out" />
+ </method>
+ <method name="ListByName">
+ <arg name="name_filter" type="s" direction="in" key="1" />
+ <arg name="limit" type="u" direction="in" key="2" />
+ <arg name="result" type="ao" direction="out" />
+ </method>
+ <method name="ListByDomainAndName">
+ <arg name="domain_name" type="s" direction="in" key="1" />
+ <arg name="name_filter" type="s" direction="in" key="2" />
+ <arg name="limit" type="u" direction="in" key="3" />
+ <arg name="result" type="ao" direction="out"/>
+ </method>
+ </interface>
+
+ <interface name="org.freedesktop.sssd.infopipe.Groups.Group">
+ <annotation name="codegen.Name" value="ifp_group" />
+ <annotation name="codegen.AsyncCaller" value="false" />
+
+ <method name="UpdateMemberList" key="True" />
+
+ <property name="name" type="s" access="read" />
+ <property name="gidNumber" type="u" access="read" />
+ <property name="uniqueID" type="s" access="read" />
+ <property name="users" type="ao" access="read" />
+ <property name="groups" type="ao" access="read" />
+ </interface>
+</node>
diff --git a/src/responder/ifp/ifp_iface/ifp_iface_async.h b/src/responder/ifp/ifp_iface/ifp_iface_async.h
new file mode 100644
index 0000000..bdf737d
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/ifp_iface_async.h
@@ -0,0 +1,28 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2018 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 _IFP_IFACE_ASYNC_H_
+#define _IFP_IFACE_ASYNC_H_
+
+#include "responder/ifp/ifp_iface/sbus_ifp_server.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_client_async.h"
+#include "responder/ifp/ifp_iface/ifp_iface.h"
+
+#endif /* _IFP_IFACE_ASYNC_H_ */
diff --git a/src/responder/ifp/ifp_iface/ifp_iface_sync.h b/src/responder/ifp/ifp_iface/ifp_iface_sync.h
new file mode 100644
index 0000000..804e0dd
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/ifp_iface_sync.h
@@ -0,0 +1,27 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2018 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 _IFP_IFACE_SYNC_H_
+#define _IFP_IFACE_SYNC_H_
+
+#include "responder/ifp/ifp_iface/sbus_ifp_client_sync.h"
+#include "responder/ifp/ifp_iface/ifp_iface.h"
+
+#endif /* _IFP_IFACE_SYNC_H_ */
diff --git a/src/responder/ifp/ifp_iface/ifp_iface_types.c b/src/responder/ifp/ifp_iface/ifp_iface_types.c
new file mode 100644
index 0000000..ad18304
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/ifp_iface_types.c
@@ -0,0 +1,225 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 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 <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "responder/ifp/ifp_iface/ifp_iface_types.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+
+/**
+ * D-Bus signature: a{sas}
+ */
+errno_t sbus_iterator_read_ifp_extra(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ hash_table_t **_table)
+{
+ DBusMessageIter iter_array;
+ DBusMessageIter iter_dict;
+ hash_table_t *table;
+ hash_key_t hkey;
+ hash_value_t hvalue;
+ char **values;
+ char *key;
+ int arg_type;
+ errno_t ret;
+ int count;
+ int hret;
+ int i;
+
+ ret = sss_hash_create(mem_ctx, 0, &table);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ arg_type = dbus_message_iter_get_arg_type(iterator);
+ if (arg_type != DBUS_TYPE_ARRAY) {
+ ret = ERR_SBUS_INVALID_TYPE;
+ goto done;
+ }
+
+ count = dbus_message_iter_get_element_count(iterator);
+ dbus_message_iter_recurse(iterator, &iter_array);
+
+ for (i = 0; i < count; i++) {
+ arg_type = dbus_message_iter_get_arg_type(&iter_array);
+ if (arg_type != DBUS_TYPE_DICT_ENTRY) {
+ ret = ERR_SBUS_INVALID_TYPE;
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter_array, &iter_dict);
+
+ ret = sbus_iterator_read_S(table, &iter_dict, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_iterator_read_aS(table, &iter_dict, &values);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ hkey.type = HASH_KEY_STRING;
+ hkey.str = key;
+
+ hvalue.type = HASH_VALUE_PTR;
+ hvalue.ptr = values;
+
+ hret = hash_enter(table, &hkey, &hvalue);
+ if (hret != HASH_SUCCESS) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* dhash will duplicate the key internally */
+ talloc_free(key);
+
+ dbus_message_iter_next(&iter_array);
+ }
+
+ *_table = table;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(table);
+ }
+
+ return ret;
+}
+
+/**
+ * D-Bus signature: a{sas}
+ */
+errno_t sbus_iterator_write_ifp_extra(DBusMessageIter *iterator,
+ hash_table_t *table)
+{
+ DBusMessageIter it_array;
+ DBusMessageIter it_dict;
+ struct hash_iter_context_t *table_iter = NULL;
+ bool in_array = false;
+ bool in_dict = false;
+ hash_entry_t *entry;
+ const char **values;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ dbret = dbus_message_iter_open_container(iterator, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &it_array);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ in_array = true;
+
+ if (table == NULL) {
+ dbret = dbus_message_iter_close_container(iterator, &it_array);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ in_array = false;
+ ret = EOK;
+ goto done;
+ }
+
+ table_iter = new_hash_iter_context(table);
+ if (table_iter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ while ((entry = table_iter->next(table_iter)) != NULL) {
+ if (entry->key.type != HASH_KEY_STRING || entry->key.str == NULL
+ || entry->value.type != HASH_VALUE_PTR
+ || entry->value.ptr == NULL) {
+ continue;
+ }
+
+ dbret = dbus_message_iter_open_container(&it_array,
+ DBUS_TYPE_DICT_ENTRY, NULL,
+ &it_dict);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ in_dict = true;
+
+ ret = sbus_iterator_write_s(&it_dict, entry->key.str);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ values = entry->value.ptr;
+ ret = sbus_iterator_write_as(&it_dict, values);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ dbret = dbus_message_iter_close_container(&it_array, &it_dict);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ in_dict = false;
+ }
+
+ dbret = dbus_message_iter_close_container(iterator, &it_array);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ in_array = false;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ if (in_dict) {
+ dbus_message_iter_abandon_container(&it_array, &it_dict);
+ }
+
+ if (in_array) {
+ dbus_message_iter_abandon_container(iterator, &it_array);
+ }
+ }
+
+ if (table_iter != NULL) {
+ talloc_free(table_iter);
+ }
+
+ return ret;
+}
diff --git a/src/responder/ifp/ifp_iface/ifp_iface_types.h b/src/responder/ifp/ifp_iface/ifp_iface_types.h
new file mode 100644
index 0000000..0a3cbd0
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/ifp_iface_types.h
@@ -0,0 +1,35 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 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 _IFP_IFACE_CUSTOM_TYPES_H_
+#define _IFP_IFACE_CUSTOM_TYPES_H_
+
+#include <talloc.h>
+#include <dhash.h>
+#include <dbus/dbus.h>
+
+errno_t sbus_iterator_read_ifp_extra(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ hash_table_t **_table);
+
+errno_t sbus_iterator_write_ifp_extra(DBusMessageIter *iterator,
+ hash_table_t *table);
+
+#endif /* _IFP_IFACE_CUSTOM_TYPES_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_arguments.c b/src/responder/ifp/ifp_iface/sbus_ifp_arguments.c
new file mode 100644
index 0000000..031db83
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_arguments.c
@@ -0,0 +1,397 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 <errno.h>
+#include <stdint.h>
+#include <talloc.h>
+#include <stdbool.h>
+#include <dbus/dbus.h>
+
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_arguments.h"
+
+errno_t _sbus_ifp_invoker_read_ao
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ao *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_ao(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_ao
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ao *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_ao(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_as
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_as *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_as(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_as
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_as *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_as(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_b
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_b *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_b(iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_b
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_b *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_b(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_ifp_extra
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ifp_extra *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_ifp_extra(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_ifp_extra
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ifp_extra *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_ifp_extra(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_o
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_o *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_o(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_o
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_o *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_o(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_s
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_s *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_s
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_s *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_sas
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_sas *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_as(mem_ctx, iter, &args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_sas
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_sas *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_as(iter, args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_ss
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ss *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_ss
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ss *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_s(iter, args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_ssu
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ssu *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_u(iter, &args->arg2);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_ssu
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ssu *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_s(iter, args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_u(iter, args->arg2);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_su
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_su *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_u(iter, &args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_su
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_su *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_u(iter, args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_read_u
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_u *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_u(iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_ifp_invoker_write_u
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_u *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_u(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_arguments.h b/src/responder/ifp/ifp_iface/sbus_ifp_arguments.h
new file mode 100644
index 0000000..4b88b78
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_arguments.h
@@ -0,0 +1,201 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_ARGUMENTS_H_
+#define _SBUS_IFP_ARGUMENTS_H_
+
+#include <errno.h>
+#include <stdint.h>
+#include <talloc.h>
+#include <stdbool.h>
+#include <dbus/dbus.h>
+
+#include "responder/ifp/ifp_iface/ifp_iface_types.h"
+
+struct _sbus_ifp_invoker_args_ao {
+ const char ** arg0;
+};
+
+errno_t
+_sbus_ifp_invoker_read_ao
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ao *args);
+
+errno_t
+_sbus_ifp_invoker_write_ao
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ao *args);
+
+struct _sbus_ifp_invoker_args_as {
+ const char ** arg0;
+};
+
+errno_t
+_sbus_ifp_invoker_read_as
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_as *args);
+
+errno_t
+_sbus_ifp_invoker_write_as
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_as *args);
+
+struct _sbus_ifp_invoker_args_b {
+ bool arg0;
+};
+
+errno_t
+_sbus_ifp_invoker_read_b
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_b *args);
+
+errno_t
+_sbus_ifp_invoker_write_b
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_b *args);
+
+struct _sbus_ifp_invoker_args_ifp_extra {
+ hash_table_t * arg0;
+};
+
+errno_t
+_sbus_ifp_invoker_read_ifp_extra
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ifp_extra *args);
+
+errno_t
+_sbus_ifp_invoker_write_ifp_extra
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ifp_extra *args);
+
+struct _sbus_ifp_invoker_args_o {
+ const char * arg0;
+};
+
+errno_t
+_sbus_ifp_invoker_read_o
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_o *args);
+
+errno_t
+_sbus_ifp_invoker_write_o
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_o *args);
+
+struct _sbus_ifp_invoker_args_s {
+ const char * arg0;
+};
+
+errno_t
+_sbus_ifp_invoker_read_s
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_s *args);
+
+errno_t
+_sbus_ifp_invoker_write_s
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_s *args);
+
+struct _sbus_ifp_invoker_args_sas {
+ const char * arg0;
+ const char ** arg1;
+};
+
+errno_t
+_sbus_ifp_invoker_read_sas
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_sas *args);
+
+errno_t
+_sbus_ifp_invoker_write_sas
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_sas *args);
+
+struct _sbus_ifp_invoker_args_ss {
+ const char * arg0;
+ const char * arg1;
+};
+
+errno_t
+_sbus_ifp_invoker_read_ss
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ss *args);
+
+errno_t
+_sbus_ifp_invoker_write_ss
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ss *args);
+
+struct _sbus_ifp_invoker_args_ssu {
+ const char * arg0;
+ const char * arg1;
+ uint32_t arg2;
+};
+
+errno_t
+_sbus_ifp_invoker_read_ssu
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ssu *args);
+
+errno_t
+_sbus_ifp_invoker_write_ssu
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_ssu *args);
+
+struct _sbus_ifp_invoker_args_su {
+ const char * arg0;
+ uint32_t arg1;
+};
+
+errno_t
+_sbus_ifp_invoker_read_su
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_su *args);
+
+errno_t
+_sbus_ifp_invoker_write_su
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_su *args);
+
+struct _sbus_ifp_invoker_args_u {
+ uint32_t arg0;
+};
+
+errno_t
+_sbus_ifp_invoker_read_u
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_u *args);
+
+errno_t
+_sbus_ifp_invoker_write_u
+ (DBusMessageIter *iter,
+ struct _sbus_ifp_invoker_args_u *args);
+
+#endif /* _SBUS_IFP_ARGUMENTS_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_client_async.c b/src/responder/ifp/ifp_iface/sbus_ifp_client_async.c
new file mode 100644
index 0000000..792061c
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_client_async.c
@@ -0,0 +1,30 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "sbus/sbus_private.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface_dbus/sbus_dbus_client_async.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_arguments.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_keygens.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_client_properties.h"
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_client_async.h b/src/responder/ifp/ifp_iface/sbus_ifp_client_async.h
new file mode 100644
index 0000000..eb7844b
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_client_async.h
@@ -0,0 +1,31 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_CLIENT_ASYNC_H_
+#define _SBUS_IFP_CLIENT_ASYNC_H_
+
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "sbus/sbus.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_client_properties.h"
+#include "responder/ifp/ifp_iface/ifp_iface_types.h"
+
+#endif /* _SBUS_IFP_CLIENT_ASYNC_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_client_properties.h b/src/responder/ifp/ifp_iface/sbus_ifp_client_properties.h
new file mode 100644
index 0000000..deb3ade
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_client_properties.h
@@ -0,0 +1,180 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_CLIENT_PROPERTIES_H_
+#define _SBUS_IFP_CLIENT_PROPERTIES_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "responder/ifp/ifp_iface/ifp_iface_types.h"
+
+struct sbus_all_ifp_components {
+ struct {
+ bool is_set;
+ uint32_t value;
+ } debug_level;
+ struct {
+ bool is_set;
+ bool value;
+ } enabled;
+ struct {
+ bool is_set;
+ const char * value;
+ } name;
+ struct {
+ bool is_set;
+ const char ** value;
+ } providers;
+ struct {
+ bool is_set;
+ const char * value;
+ } type;
+};
+
+struct sbus_all_ifp_domains {
+ struct {
+ bool is_set;
+ const char ** value;
+ } backup_servers;
+ struct {
+ bool is_set;
+ bool value;
+ } enumerable;
+ struct {
+ bool is_set;
+ const char * value;
+ } forest;
+ struct {
+ bool is_set;
+ const char * value;
+ } fully_qualified_name_format;
+ struct {
+ bool is_set;
+ const char * value;
+ } login_format;
+ struct {
+ bool is_set;
+ uint32_t value;
+ } max_id;
+ struct {
+ bool is_set;
+ uint32_t value;
+ } min_id;
+ struct {
+ bool is_set;
+ const char * value;
+ } name;
+ struct {
+ bool is_set;
+ const char * value;
+ } parent_domain;
+ struct {
+ bool is_set;
+ const char ** value;
+ } primary_servers;
+ struct {
+ bool is_set;
+ const char * value;
+ } provider;
+ struct {
+ bool is_set;
+ const char * value;
+ } realm;
+ struct {
+ bool is_set;
+ bool value;
+ } subdomain;
+ struct {
+ bool is_set;
+ bool value;
+ } use_fully_qualified_names;
+};
+
+struct sbus_all_ifp_group {
+ struct {
+ bool is_set;
+ uint32_t value;
+ } gidNumber;
+ struct {
+ bool is_set;
+ const char ** value;
+ } groups;
+ struct {
+ bool is_set;
+ const char * value;
+ } name;
+ struct {
+ bool is_set;
+ const char * value;
+ } uniqueID;
+ struct {
+ bool is_set;
+ const char ** value;
+ } users;
+};
+
+struct sbus_all_ifp_user {
+ struct {
+ bool is_set;
+ const char * value;
+ } domain;
+ struct {
+ bool is_set;
+ const char * value;
+ } domainname;
+ struct {
+ bool is_set;
+ hash_table_t * value;
+ } extraAttributes;
+ struct {
+ bool is_set;
+ const char * value;
+ } gecos;
+ struct {
+ bool is_set;
+ uint32_t value;
+ } gidNumber;
+ struct {
+ bool is_set;
+ const char ** value;
+ } groups;
+ struct {
+ bool is_set;
+ const char * value;
+ } homeDirectory;
+ struct {
+ bool is_set;
+ const char * value;
+ } loginShell;
+ struct {
+ bool is_set;
+ const char * value;
+ } name;
+ struct {
+ bool is_set;
+ uint32_t value;
+ } uidNumber;
+ struct {
+ bool is_set;
+ const char * value;
+ } uniqueID;
+};
+
+#endif /* _SBUS_IFP_CLIENT_PROPERTIES_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c
new file mode 100644
index 0000000..e450233
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c
@@ -0,0 +1,2304 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 <errno.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "sbus/sbus_sync.h"
+#include "sbus/sbus_sync_private.h"
+#include "sbus/sbus_message.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface_dbus/sbus_dbus_client_sync.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_arguments.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_client_properties.h"
+
+static errno_t
+sbus_method_in__out_
+ (struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method)
+{
+ TALLOC_CTX *tmp_ctx;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL, NULL,
+ bus, path, iface, method, NULL, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in__out_ao
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char *** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_ao *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_ao);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL, NULL,
+ bus, path, iface, method, NULL, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_ao, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in__out_as
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char *** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_as *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_as);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL, NULL,
+ bus, path, iface, method, NULL, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_as, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in__out_b
+ (struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ bool* _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_b *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_b);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL, NULL,
+ bus, path, iface, method, NULL, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_b, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = out->arg0;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in__out_o
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char ** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_o *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_o);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL, NULL,
+ bus, path, iface, method, NULL, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_o, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_s_out_ao
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char *** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_s in;
+ struct _sbus_ifp_invoker_args_ao *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_ao);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_s,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_ao, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_s_out_as
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char *** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_s in;
+ struct _sbus_ifp_invoker_args_as *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_as);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_s,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_as, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_s_out_o
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char ** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_s in;
+ struct _sbus_ifp_invoker_args_o *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_o);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_s,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_o, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_s_out_s
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char ** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_s in;
+ struct _sbus_ifp_invoker_args_s *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_s);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_s,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_s, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_sas_out_raw
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char ** arg1,
+ DBusMessage **_reply)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_sas in;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ in.arg0 = arg0;
+ in.arg1 = arg1;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_sas,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Bounded reference cannot be unreferenced with dbus_message_unref.
+ * For that reason we do not allow NULL memory context as it would
+ * result in leaking the message memory. */
+ if (mem_ctx == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sbus_message_bound_steal(mem_ctx, reply);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to steal message [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_reply = reply;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_ss_out_o
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char * arg1,
+ const char ** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_ss in;
+ struct _sbus_ifp_invoker_args_o *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_o);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+ in.arg1 = arg1;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_ss,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_o, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_ssu_out_ao
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char * arg1,
+ uint32_t arg2,
+ const char *** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_ssu in;
+ struct _sbus_ifp_invoker_args_ao *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_ao);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+ in.arg1 = arg1;
+ in.arg2 = arg2;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_ssu,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_ao, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_su_out_ao
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ uint32_t arg1,
+ const char *** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_su in;
+ struct _sbus_ifp_invoker_args_ao *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_ao);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+ in.arg1 = arg1;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_su,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_ao, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_u_out_o
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ uint32_t arg0,
+ const char ** _arg0)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_u in;
+ struct _sbus_ifp_invoker_args_o *out;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_o);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ in.arg0 = arg0;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_ifp_invoker_write_u,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_ifp_invoker_read_o, out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_arg0 = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sbus_call_ifp_FindBackendByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_backend)
+{
+ return sbus_method_in_s_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "FindBackendByName", arg_name,
+ _arg_backend);
+}
+
+errno_t
+sbus_call_ifp_FindDomainByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_domain)
+{
+ return sbus_method_in_s_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "FindDomainByName", arg_name,
+ _arg_domain);
+}
+
+errno_t
+sbus_call_ifp_FindMonitor
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _arg_monitor)
+{
+ return sbus_method_in__out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "FindMonitor",
+ _arg_monitor);
+}
+
+errno_t
+sbus_call_ifp_FindResponderByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_responder)
+{
+ return sbus_method_in_s_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "FindResponderByName", arg_name,
+ _arg_responder);
+}
+
+errno_t
+sbus_call_ifp_GetUserAttr
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_user,
+ const char ** arg_attr,
+ DBusMessage **_reply)
+{
+ return sbus_method_in_sas_out_raw(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "GetUserAttr", arg_user, arg_attr,
+ _reply);
+}
+
+errno_t
+sbus_call_ifp_GetUserGroups
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_user,
+ const char *** _arg_values)
+{
+ return sbus_method_in_s_out_as(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "GetUserGroups", arg_user,
+ _arg_values);
+}
+
+errno_t
+sbus_call_ifp_ListBackends
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_backends)
+{
+ return sbus_method_in__out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "ListBackends",
+ _arg_backends);
+}
+
+errno_t
+sbus_call_ifp_ListComponents
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_components)
+{
+ return sbus_method_in__out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "ListComponents",
+ _arg_components);
+}
+
+errno_t
+sbus_call_ifp_ListDomains
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_domain)
+{
+ return sbus_method_in__out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "ListDomains",
+ _arg_domain);
+}
+
+errno_t
+sbus_call_ifp_ListResponders
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_responders)
+{
+ return sbus_method_in__out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "ListResponders",
+ _arg_responders);
+}
+
+errno_t
+sbus_call_ifp_Ping
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_ping,
+ const char ** _arg_pong)
+{
+ return sbus_method_in_s_out_s(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe", "Ping", arg_ping,
+ _arg_pong);
+}
+
+errno_t
+sbus_call_ifp_cache_List
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_result)
+{
+ return sbus_method_in__out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Cache", "List",
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_cache_ListByDomain
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_domain_name,
+ const char *** _arg_result)
+{
+ return sbus_method_in_s_out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Cache", "ListByDomain", arg_domain_name,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_cache_object_Remove
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _arg_result)
+{
+ return sbus_method_in__out_b(conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Cache.Object", "Remove",
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_cache_object_Store
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _arg_result)
+{
+ return sbus_method_in__out_b(conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Cache.Object", "Store",
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_domain_ActiveServer
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_service,
+ const char ** _arg_server)
+{
+ return sbus_method_in_s_out_s(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Domains.Domain", "ActiveServer", arg_service,
+ _arg_server);
+}
+
+errno_t
+sbus_call_ifp_domain_IsOnline
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _arg_status)
+{
+ return sbus_method_in__out_b(conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Domains.Domain", "IsOnline",
+ _arg_status);
+}
+
+errno_t
+sbus_call_ifp_domain_ListServers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_service_name,
+ const char *** _arg_servers)
+{
+ return sbus_method_in_s_out_as(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Domains.Domain", "ListServers", arg_service_name,
+ _arg_servers);
+}
+
+errno_t
+sbus_call_ifp_domain_ListServices
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_services)
+{
+ return sbus_method_in__out_as(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Domains.Domain", "ListServices",
+ _arg_services);
+}
+
+errno_t
+sbus_call_ifp_domain_RefreshAccessRules
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path)
+{
+ return sbus_method_in__out_(conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Domains.Domain", "RefreshAccessRules");
+}
+
+errno_t
+sbus_call_ifp_groups_FindByID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t arg_id,
+ const char ** _arg_result)
+{
+ return sbus_method_in_u_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Groups", "FindByID", arg_id,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_groups_FindByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_result)
+{
+ return sbus_method_in_s_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Groups", "FindByName", arg_name,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_groups_ListByDomainAndName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_domain_name,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result)
+{
+ return sbus_method_in_ssu_out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Groups", "ListByDomainAndName", arg_domain_name, arg_name_filter, arg_limit,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_groups_ListByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result)
+{
+ return sbus_method_in_su_out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Groups", "ListByName", arg_name_filter, arg_limit,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_group_UpdateMemberList
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path)
+{
+ return sbus_method_in__out_(conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Groups.Group", "UpdateMemberList");
+}
+
+errno_t
+sbus_call_ifp_users_FindByCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_pem_cert,
+ const char ** _arg_result)
+{
+ return sbus_method_in_s_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "FindByCertificate", arg_pem_cert,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_FindByID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t arg_id,
+ const char ** _arg_result)
+{
+ return sbus_method_in_u_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "FindByID", arg_id,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_FindByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_result)
+{
+ return sbus_method_in_s_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "FindByName", arg_name,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_FindByNameAndCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char * arg_pem_cert,
+ const char ** _arg_result)
+{
+ return sbus_method_in_ss_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "FindByNameAndCertificate", arg_name, arg_pem_cert,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_FindByValidCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_pem_cert,
+ const char ** _arg_result)
+{
+ return sbus_method_in_s_out_o(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "FindByValidCertificate", arg_pem_cert,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_ListByAttr
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_attribute,
+ const char * arg_attr_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result)
+{
+ return sbus_method_in_ssu_out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "ListByAttr", arg_attribute, arg_attr_filter, arg_limit,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_ListByCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_pem_cert,
+ uint32_t arg_limit,
+ const char *** _arg_result)
+{
+ return sbus_method_in_su_out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "ListByCertificate", arg_pem_cert, arg_limit,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_ListByDomainAndName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_domain_name,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result)
+{
+ return sbus_method_in_ssu_out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "ListByDomainAndName", arg_domain_name, arg_name_filter, arg_limit,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_users_ListByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result)
+{
+ return sbus_method_in_su_out_ao(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users", "ListByName", arg_name_filter, arg_limit,
+ _arg_result);
+}
+
+errno_t
+sbus_call_ifp_user_UpdateGroupsList
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path)
+{
+ return sbus_method_in__out_(conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users.User", "UpdateGroupsList");
+}
+
+static errno_t
+sbus_get_ao
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ const char *** _value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_ao *out;
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_ao);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_call_DBusProperties_Get(tmp_ctx, conn,
+ bus, path, iface, property, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ reader = NULL;
+ reader_talloc = (sbus_value_reader_talloc_fn)sbus_iterator_read_ao;
+ ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_value = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_get_as
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ const char *** _value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_as *out;
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_as);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_call_DBusProperties_Get(tmp_ctx, conn,
+ bus, path, iface, property, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ reader = NULL;
+ reader_talloc = (sbus_value_reader_talloc_fn)sbus_iterator_read_as;
+ ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_value = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_get_b
+ (struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ bool* _value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_b *out;
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_b);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_call_DBusProperties_Get(tmp_ctx, conn,
+ bus, path, iface, property, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ reader = (sbus_value_reader_fn)sbus_iterator_read_b;
+ reader_talloc = NULL;
+ ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_value = out->arg0;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_get_ifp_extra
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ hash_table_t ** _value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_ifp_extra *out;
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_ifp_extra);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_call_DBusProperties_Get(tmp_ctx, conn,
+ bus, path, iface, property, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ reader = NULL;
+ reader_talloc = (sbus_value_reader_talloc_fn)sbus_iterator_read_ifp_extra;
+ ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_value = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_get_o
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ const char ** _value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_o *out;
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_o);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_call_DBusProperties_Get(tmp_ctx, conn,
+ bus, path, iface, property, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ reader = NULL;
+ reader_talloc = (sbus_value_reader_talloc_fn)sbus_iterator_read_o;
+ ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_value = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_get_s
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ const char ** _value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_s *out;
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_s);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_call_DBusProperties_Get(tmp_ctx, conn,
+ bus, path, iface, property, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ reader = NULL;
+ reader_talloc = (sbus_value_reader_talloc_fn)sbus_iterator_read_s;
+ ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_value = talloc_steal(mem_ctx, out->arg0);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_get_u
+ (struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ uint32_t* _value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_ifp_invoker_args_u *out;
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ out = talloc_zero(tmp_ctx, struct _sbus_ifp_invoker_args_u);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_call_DBusProperties_Get(tmp_ctx, conn,
+ bus, path, iface, property, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ reader = (sbus_value_reader_fn)sbus_iterator_read_u;
+ reader_talloc = NULL;
+ ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_value = out->arg0;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sbus_get_ifp_components_debug_level
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value)
+{
+ return sbus_get_u(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Components", "debug_level", _value);
+}
+
+errno_t
+sbus_get_ifp_components_enabled
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value)
+{
+ return sbus_get_b(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Components", "enabled", _value);
+}
+
+errno_t
+sbus_get_ifp_components_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Components", "name", _value);
+}
+
+errno_t
+sbus_get_ifp_components_providers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value)
+{
+ return sbus_get_as(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Components", "providers", _value);
+}
+
+errno_t
+sbus_get_ifp_components_type
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Components", "type", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_backup_servers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value)
+{
+ return sbus_get_as(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "backup_servers", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_enumerable
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value)
+{
+ return sbus_get_b(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "enumerable", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_forest
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "forest", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_fully_qualified_name_format
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "fully_qualified_name_format", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_login_format
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "login_format", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_max_id
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value)
+{
+ return sbus_get_u(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "max_id", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_min_id
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value)
+{
+ return sbus_get_u(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "min_id", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "name", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_parent_domain
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_o(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "parent_domain", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_primary_servers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value)
+{
+ return sbus_get_as(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "primary_servers", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_provider
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "provider", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_realm
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "realm", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_subdomain
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value)
+{
+ return sbus_get_b(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "subdomain", _value);
+}
+
+errno_t
+sbus_get_ifp_domains_use_fully_qualified_names
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value)
+{
+ return sbus_get_b(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Domains", "use_fully_qualified_names", _value);
+}
+
+errno_t
+sbus_get_ifp_group_gidNumber
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value)
+{
+ return sbus_get_u(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Groups.Group", "gidNumber", _value);
+}
+
+errno_t
+sbus_get_ifp_group_groups
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value)
+{
+ return sbus_get_ao(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Groups.Group", "groups", _value);
+}
+
+errno_t
+sbus_get_ifp_group_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Groups.Group", "name", _value);
+}
+
+errno_t
+sbus_get_ifp_group_uniqueID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Groups.Group", "uniqueID", _value);
+}
+
+errno_t
+sbus_get_ifp_group_users
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value)
+{
+ return sbus_get_ao(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Groups.Group", "users", _value);
+}
+
+errno_t
+sbus_get_ifp_user_domain
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_o(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "domain", _value);
+}
+
+errno_t
+sbus_get_ifp_user_domainname
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "domainname", _value);
+}
+
+errno_t
+sbus_get_ifp_user_extraAttributes
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ hash_table_t ** _value)
+{
+ return sbus_get_ifp_extra(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "extraAttributes", _value);
+}
+
+errno_t
+sbus_get_ifp_user_gecos
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "gecos", _value);
+}
+
+errno_t
+sbus_get_ifp_user_gidNumber
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value)
+{
+ return sbus_get_u(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "gidNumber", _value);
+}
+
+errno_t
+sbus_get_ifp_user_groups
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value)
+{
+ return sbus_get_ao(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "groups", _value);
+}
+
+errno_t
+sbus_get_ifp_user_homeDirectory
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "homeDirectory", _value);
+}
+
+errno_t
+sbus_get_ifp_user_loginShell
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "loginShell", _value);
+}
+
+errno_t
+sbus_get_ifp_user_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "name", _value);
+}
+
+errno_t
+sbus_get_ifp_user_uidNumber
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value)
+{
+ return sbus_get_u(conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "uidNumber", _value);
+}
+
+errno_t
+sbus_get_ifp_user_uniqueID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value)
+{
+ return sbus_get_s(mem_ctx, conn, busname, object_path,
+ "org.freedesktop.sssd.infopipe.Users.User", "uniqueID", _value);
+}
+
+errno_t
+sbus_getall_ifp_components
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_components **_properties)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_all_ifp_components *properties;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ properties = talloc_zero(tmp_ctx, struct sbus_all_ifp_components);
+ if (properties == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ struct sbus_parse_getall_table table[] = {
+ {"debug_level", (sbus_value_reader_fn)sbus_iterator_read_u, NULL,
+ &properties->debug_level.value, &properties->debug_level.is_set},
+ {"enabled", (sbus_value_reader_fn)sbus_iterator_read_b, NULL,
+ &properties->enabled.value, &properties->enabled.is_set},
+ {"name", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->name.value, &properties->name.is_set},
+ {"providers", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_as,
+ &properties->providers.value, &properties->providers.is_set},
+ {"type", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->type.value, &properties->type.is_set},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+ ret = sbus_call_DBusProperties_GetAll(tmp_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Components", &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_parse_getall_message(properties, table, reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_properties = talloc_steal(mem_ctx, properties);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sbus_getall_ifp_domains
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_domains **_properties)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_all_ifp_domains *properties;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ properties = talloc_zero(tmp_ctx, struct sbus_all_ifp_domains);
+ if (properties == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ struct sbus_parse_getall_table table[] = {
+ {"enumerable", (sbus_value_reader_fn)sbus_iterator_read_b, NULL,
+ &properties->enumerable.value, &properties->enumerable.is_set},
+ {"max_id", (sbus_value_reader_fn)sbus_iterator_read_u, NULL,
+ &properties->max_id.value, &properties->max_id.is_set},
+ {"min_id", (sbus_value_reader_fn)sbus_iterator_read_u, NULL,
+ &properties->min_id.value, &properties->min_id.is_set},
+ {"subdomain", (sbus_value_reader_fn)sbus_iterator_read_b, NULL,
+ &properties->subdomain.value, &properties->subdomain.is_set},
+ {"use_fully_qualified_names", (sbus_value_reader_fn)sbus_iterator_read_b, NULL,
+ &properties->use_fully_qualified_names.value, &properties->use_fully_qualified_names.is_set},
+ {"backup_servers", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_as,
+ &properties->backup_servers.value, &properties->backup_servers.is_set},
+ {"forest", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->forest.value, &properties->forest.is_set},
+ {"fully_qualified_name_format", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->fully_qualified_name_format.value, &properties->fully_qualified_name_format.is_set},
+ {"login_format", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->login_format.value, &properties->login_format.is_set},
+ {"name", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->name.value, &properties->name.is_set},
+ {"parent_domain", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_o,
+ &properties->parent_domain.value, &properties->parent_domain.is_set},
+ {"primary_servers", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_as,
+ &properties->primary_servers.value, &properties->primary_servers.is_set},
+ {"provider", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->provider.value, &properties->provider.is_set},
+ {"realm", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->realm.value, &properties->realm.is_set},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+ ret = sbus_call_DBusProperties_GetAll(tmp_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Domains", &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_parse_getall_message(properties, table, reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_properties = talloc_steal(mem_ctx, properties);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sbus_getall_ifp_group
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_group **_properties)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_all_ifp_group *properties;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ properties = talloc_zero(tmp_ctx, struct sbus_all_ifp_group);
+ if (properties == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ struct sbus_parse_getall_table table[] = {
+ {"gidNumber", (sbus_value_reader_fn)sbus_iterator_read_u, NULL,
+ &properties->gidNumber.value, &properties->gidNumber.is_set},
+ {"groups", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_ao,
+ &properties->groups.value, &properties->groups.is_set},
+ {"name", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->name.value, &properties->name.is_set},
+ {"uniqueID", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->uniqueID.value, &properties->uniqueID.is_set},
+ {"users", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_ao,
+ &properties->users.value, &properties->users.is_set},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+ ret = sbus_call_DBusProperties_GetAll(tmp_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Groups.Group", &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_parse_getall_message(properties, table, reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_properties = talloc_steal(mem_ctx, properties);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sbus_getall_ifp_user
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_user **_properties)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_all_ifp_user *properties;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ properties = talloc_zero(tmp_ctx, struct sbus_all_ifp_user);
+ if (properties == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ struct sbus_parse_getall_table table[] = {
+ {"gidNumber", (sbus_value_reader_fn)sbus_iterator_read_u, NULL,
+ &properties->gidNumber.value, &properties->gidNumber.is_set},
+ {"uidNumber", (sbus_value_reader_fn)sbus_iterator_read_u, NULL,
+ &properties->uidNumber.value, &properties->uidNumber.is_set},
+ {"domain", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_o,
+ &properties->domain.value, &properties->domain.is_set},
+ {"domainname", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->domainname.value, &properties->domainname.is_set},
+ {"extraAttributes", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_ifp_extra,
+ &properties->extraAttributes.value, &properties->extraAttributes.is_set},
+ {"gecos", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->gecos.value, &properties->gecos.is_set},
+ {"groups", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_ao,
+ &properties->groups.value, &properties->groups.is_set},
+ {"homeDirectory", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->homeDirectory.value, &properties->homeDirectory.is_set},
+ {"loginShell", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->loginShell.value, &properties->loginShell.is_set},
+ {"name", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->name.value, &properties->name.is_set},
+ {"uniqueID", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_s,
+ &properties->uniqueID.value, &properties->uniqueID.is_set},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+ ret = sbus_call_DBusProperties_GetAll(tmp_ctx, conn,
+ busname, object_path, "org.freedesktop.sssd.infopipe.Users.User", &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_parse_getall_message(properties, table, reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_properties = talloc_steal(mem_ctx, properties);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h
new file mode 100644
index 0000000..b5455e9
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h
@@ -0,0 +1,637 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_CLIENT_SYNC_H_
+#define _SBUS_IFP_CLIENT_SYNC_H_
+
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "sbus/sbus_sync.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_client_properties.h"
+#include "responder/ifp/ifp_iface/ifp_iface_types.h"
+
+errno_t
+sbus_call_ifp_FindBackendByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_backend);
+
+errno_t
+sbus_call_ifp_FindDomainByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_domain);
+
+errno_t
+sbus_call_ifp_FindMonitor
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _arg_monitor);
+
+errno_t
+sbus_call_ifp_FindResponderByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_responder);
+
+errno_t
+sbus_call_ifp_GetUserAttr
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_user,
+ const char ** arg_attr,
+ DBusMessage **_reply);
+
+errno_t
+sbus_call_ifp_GetUserGroups
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_user,
+ const char *** _arg_values);
+
+errno_t
+sbus_call_ifp_ListBackends
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_backends);
+
+errno_t
+sbus_call_ifp_ListComponents
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_components);
+
+errno_t
+sbus_call_ifp_ListDomains
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_domain);
+
+errno_t
+sbus_call_ifp_ListResponders
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_responders);
+
+errno_t
+sbus_call_ifp_Ping
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_ping,
+ const char ** _arg_pong);
+
+errno_t
+sbus_call_ifp_cache_List
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_cache_ListByDomain
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_domain_name,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_cache_object_Remove
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _arg_result);
+
+errno_t
+sbus_call_ifp_cache_object_Store
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _arg_result);
+
+errno_t
+sbus_call_ifp_domain_ActiveServer
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_service,
+ const char ** _arg_server);
+
+errno_t
+sbus_call_ifp_domain_IsOnline
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _arg_status);
+
+errno_t
+sbus_call_ifp_domain_ListServers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_service_name,
+ const char *** _arg_servers);
+
+errno_t
+sbus_call_ifp_domain_ListServices
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _arg_services);
+
+errno_t
+sbus_call_ifp_domain_RefreshAccessRules
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path);
+
+errno_t
+sbus_call_ifp_groups_FindByID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t arg_id,
+ const char ** _arg_result);
+
+errno_t
+sbus_call_ifp_groups_FindByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_result);
+
+errno_t
+sbus_call_ifp_groups_ListByDomainAndName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_domain_name,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_groups_ListByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_group_UpdateMemberList
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path);
+
+errno_t
+sbus_call_ifp_users_FindByCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_pem_cert,
+ const char ** _arg_result);
+
+errno_t
+sbus_call_ifp_users_FindByID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t arg_id,
+ const char ** _arg_result);
+
+errno_t
+sbus_call_ifp_users_FindByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char ** _arg_result);
+
+errno_t
+sbus_call_ifp_users_FindByNameAndCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ const char * arg_pem_cert,
+ const char ** _arg_result);
+
+errno_t
+sbus_call_ifp_users_FindByValidCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_pem_cert,
+ const char ** _arg_result);
+
+errno_t
+sbus_call_ifp_users_ListByAttr
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_attribute,
+ const char * arg_attr_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_users_ListByCertificate
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_pem_cert,
+ uint32_t arg_limit,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_users_ListByDomainAndName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_domain_name,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_users_ListByName
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name_filter,
+ uint32_t arg_limit,
+ const char *** _arg_result);
+
+errno_t
+sbus_call_ifp_user_UpdateGroupsList
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path);
+
+errno_t
+sbus_get_ifp_components_debug_level
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value);
+
+errno_t
+sbus_get_ifp_components_enabled
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value);
+
+errno_t
+sbus_get_ifp_components_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_components_providers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value);
+
+errno_t
+sbus_get_ifp_components_type
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_backup_servers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value);
+
+errno_t
+sbus_get_ifp_domains_enumerable
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value);
+
+errno_t
+sbus_get_ifp_domains_forest
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_fully_qualified_name_format
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_login_format
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_max_id
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value);
+
+errno_t
+sbus_get_ifp_domains_min_id
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value);
+
+errno_t
+sbus_get_ifp_domains_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_parent_domain
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_primary_servers
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value);
+
+errno_t
+sbus_get_ifp_domains_provider
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_realm
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_domains_subdomain
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value);
+
+errno_t
+sbus_get_ifp_domains_use_fully_qualified_names
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ bool* _value);
+
+errno_t
+sbus_get_ifp_group_gidNumber
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value);
+
+errno_t
+sbus_get_ifp_group_groups
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value);
+
+errno_t
+sbus_get_ifp_group_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_group_uniqueID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_group_users
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value);
+
+errno_t
+sbus_get_ifp_user_domain
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_user_domainname
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_user_extraAttributes
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ hash_table_t ** _value);
+
+errno_t
+sbus_get_ifp_user_gecos
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_user_gidNumber
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value);
+
+errno_t
+sbus_get_ifp_user_groups
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char *** _value);
+
+errno_t
+sbus_get_ifp_user_homeDirectory
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_user_loginShell
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_user_name
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_get_ifp_user_uidNumber
+ (struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ uint32_t* _value);
+
+errno_t
+sbus_get_ifp_user_uniqueID
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char ** _value);
+
+errno_t
+sbus_getall_ifp_components
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_components **_properties);
+
+errno_t
+sbus_getall_ifp_domains
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_domains **_properties);
+
+errno_t
+sbus_getall_ifp_group
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_group **_properties);
+
+errno_t
+sbus_getall_ifp_user
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ struct sbus_all_ifp_user **_properties);
+
+#endif /* _SBUS_IFP_CLIENT_SYNC_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_interface.h b/src/responder/ifp/ifp_iface/sbus_ifp_interface.h
new file mode 100644
index 0000000..0faac69
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_interface.h
@@ -0,0 +1,2083 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_INTERFACE_H_
+#define _SBUS_IFP_INTERFACE_H_
+
+#include "sbus/sbus_interface_declarations.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_invokers.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_symbols.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_keygens.h"
+
+/* Interface: org.freedesktop.sssd.infopipe */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.FindBackendByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_FindBackendByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("FindBackendByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindBackendByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_FindBackendByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindBackendByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindBackendByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.FindDomainByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_FindDomainByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("FindDomainByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindDomainByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_FindDomainByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindDomainByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindDomainByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.FindMonitor */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_FindMonitor(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_method_sync("FindMonitor", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindMonitor, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_o_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_FindMonitor(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindMonitor", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindMonitor, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_o_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.FindResponderByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_FindResponderByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("FindResponderByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindResponderByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_FindResponderByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindResponderByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindResponderByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.GetUserAttr */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_GetUserAttr(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **, DBusMessageIter *); \
+ sbus_method_sync("GetUserAttr", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserAttr, \
+ NULL, \
+ _sbus_ifp_invoke_in_sas_out_raw_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_GetUserAttr(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, const char **, DBusMessageIter *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("GetUserAttr", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserAttr, \
+ NULL, \
+ _sbus_ifp_invoke_in_sas_out_raw_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.GetUserGroups */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_GetUserGroups(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char ***); \
+ sbus_method_sync("GetUserGroups", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserGroups, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_as_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_GetUserGroups(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("GetUserGroups", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserGroups, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_as_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.ListBackends */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_ListBackends(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("ListBackends", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListBackends, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_ListBackends(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListBackends", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListBackends, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.ListComponents */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_ListComponents(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("ListComponents", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListComponents, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_ListComponents(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListComponents", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListComponents, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.ListDomains */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_ListDomains(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("ListDomains", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListDomains, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_ListDomains(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListDomains", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListDomains, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.ListResponders */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_ListResponders(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("ListResponders", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListResponders, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_ListResponders(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListResponders", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListResponders, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Ping */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Ping(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("Ping", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Ping, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_s_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Ping(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("Ping", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Ping, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_s_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Cache */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Cache(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Cache", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Cache.List */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Cache_List(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("List", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_List, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Cache_List(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("List", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_List, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Cache.ListByDomain */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Cache_ListByDomain(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char ***); \
+ sbus_method_sync("ListByDomain", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_ListByDomain, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_ao_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Cache_ListByDomain(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListByDomain", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_ListByDomain, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_ao_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Cache.Object */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Cache_Object(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Cache.Object", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Cache.Object.Remove */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Cache_Object_Remove(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), bool*); \
+ sbus_method_sync("Remove", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Remove, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Cache_Object_Remove(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_method_async("Remove", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Remove, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Cache.Object.Store */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Cache_Object_Store(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), bool*); \
+ sbus_method_sync("Store", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Store, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Cache_Object_Store(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_method_async("Store", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Store, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Components */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Components(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Components", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Components.debug_level */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Components_debug_level(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t*); \
+ sbus_property_sync("debug_level", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_debug_level(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_property_async("debug_level", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Components_debug_level(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("debug_level", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_debug_level(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("debug_level", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Components.enabled */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Components_enabled(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), bool*); \
+ sbus_property_sync("enabled", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_enabled(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_property_async("enabled", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Components_enabled(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("enabled", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_enabled(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("enabled", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Components.name */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Components_name(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Components_name(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Components.providers */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Components_providers(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_property_sync("providers", "as", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_providers(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_property_async("providers", "as", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Components_providers(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("providers", "as", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_providers(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("providers", "as", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Components.type */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Components_type(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("type", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_type(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("type", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Components_type(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("type", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Components_type(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("type", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Domains */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Domains(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Domains", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.backup_servers */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_backup_servers(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_property_sync("backup_servers", "as", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_backup_servers(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_property_async("backup_servers", "as", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_backup_servers(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("backup_servers", "as", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_backup_servers(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("backup_servers", "as", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.enumerable */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_enumerable(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), bool*); \
+ sbus_property_sync("enumerable", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_enumerable(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_property_async("enumerable", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_enumerable(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("enumerable", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_enumerable(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("enumerable", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.forest */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_forest(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("forest", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_forest(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("forest", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_forest(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("forest", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_forest(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("forest", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.fully_qualified_name_format */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_fully_qualified_name_format(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("fully_qualified_name_format", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_fully_qualified_name_format(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("fully_qualified_name_format", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_fully_qualified_name_format(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("fully_qualified_name_format", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_fully_qualified_name_format(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("fully_qualified_name_format", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.login_format */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_login_format(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("login_format", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_login_format(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("login_format", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_login_format(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("login_format", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_login_format(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("login_format", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.max_id */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_max_id(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t*); \
+ sbus_property_sync("max_id", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_max_id(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_property_async("max_id", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_max_id(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("max_id", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_max_id(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("max_id", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.min_id */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_min_id(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t*); \
+ sbus_property_sync("min_id", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_min_id(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_property_async("min_id", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_min_id(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("min_id", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_min_id(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("min_id", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.name */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_name(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_name(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.parent_domain */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_parent_domain(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("parent_domain", "o", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_o_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_parent_domain(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("parent_domain", "o", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_o_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_parent_domain(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("parent_domain", "o", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_parent_domain(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("parent_domain", "o", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.primary_servers */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_primary_servers(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_property_sync("primary_servers", "as", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_primary_servers(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_property_async("primary_servers", "as", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_primary_servers(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("primary_servers", "as", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_primary_servers(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("primary_servers", "as", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.provider */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_provider(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("provider", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_provider(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("provider", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_provider(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("provider", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_provider(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("provider", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.realm */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_realm(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("realm", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_realm(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("realm", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_realm(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("realm", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_realm(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("realm", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.subdomain */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_subdomain(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), bool*); \
+ sbus_property_sync("subdomain", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_subdomain(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_property_async("subdomain", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_subdomain(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("subdomain", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_subdomain(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("subdomain", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Domains.use_fully_qualified_names */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_use_fully_qualified_names(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), bool*); \
+ sbus_property_sync("use_fully_qualified_names", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_use_fully_qualified_names(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_property_async("use_fully_qualified_names", "b", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Domains_use_fully_qualified_names(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("use_fully_qualified_names", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Domains_use_fully_qualified_names(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("use_fully_qualified_names", "b", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Domains.Domain */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Domains_Domain(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Domains.Domain", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Domains.Domain.ActiveServer */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Domains_Domain_ActiveServer(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("ActiveServer", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ActiveServer, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_s_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Domains_Domain_ActiveServer(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("ActiveServer", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ActiveServer, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_s_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Domains.Domain.IsOnline */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Domains_Domain_IsOnline(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), bool*); \
+ sbus_method_sync("IsOnline", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_IsOnline, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Domains_Domain_IsOnline(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_method_async("IsOnline", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_IsOnline, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_b_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Domains.Domain.ListServers */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Domains_Domain_ListServers(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char ***); \
+ sbus_method_sync("ListServers", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServers, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_as_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Domains_Domain_ListServers(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListServers", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServers, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_as_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Domains.Domain.ListServices */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Domains_Domain_ListServices(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("ListServices", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServices, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Domains_Domain_ListServices(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListServices", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServices, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_as_send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Domains.Domain.RefreshAccessRules */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Domains_Domain_RefreshAccessRules(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_method_sync("RefreshAccessRules", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_RefreshAccessRules, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Domains_Domain_RefreshAccessRules(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("RefreshAccessRules", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_RefreshAccessRules, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Groups */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Groups(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Groups", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Groups.FindByID */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Groups_FindByID(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t, const char **); \
+ sbus_method_sync("FindByID", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByID, \
+ NULL, \
+ _sbus_ifp_invoke_in_u_out_o_send, \
+ _sbus_ifp_key_u_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Groups_FindByID(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindByID", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByID, \
+ NULL, \
+ _sbus_ifp_invoke_in_u_out_o_send, \
+ _sbus_ifp_key_u_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Groups.FindByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Groups_FindByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("FindByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Groups_FindByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Groups.ListByDomainAndName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Groups_ListByDomainAndName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char *, uint32_t, const char ***); \
+ sbus_method_sync("ListByDomainAndName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByDomainAndName, \
+ NULL, \
+ _sbus_ifp_invoke_in_ssu_out_ao_send, \
+ _sbus_ifp_key_ssu_0_1_2, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Groups_ListByDomainAndName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListByDomainAndName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByDomainAndName, \
+ NULL, \
+ _sbus_ifp_invoke_in_ssu_out_ao_send, \
+ _sbus_ifp_key_ssu_0_1_2, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Groups.ListByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Groups_ListByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t, const char ***); \
+ sbus_method_sync("ListByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_su_out_ao_send, \
+ _sbus_ifp_key_su_0_1, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Groups_ListByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_su_out_ao_send, \
+ _sbus_ifp_key_su_0_1, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Groups.Group */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Groups_Group(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Groups.Group", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Groups.Group.UpdateMemberList */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_UpdateMemberList(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_method_sync("UpdateMemberList", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_Group_UpdateMemberList, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_UpdateMemberList(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("UpdateMemberList", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_Group_UpdateMemberList, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Groups.Group.gidNumber */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_gidNumber(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t*); \
+ sbus_property_sync("gidNumber", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_gidNumber(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_property_async("gidNumber", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_gidNumber(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("gidNumber", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_gidNumber(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("gidNumber", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Groups.Group.groups */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_groups(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_property_sync("groups", "ao", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_groups(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_property_async("groups", "ao", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_groups(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("groups", "ao", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_groups(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("groups", "ao", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Groups.Group.name */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_name(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_name(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Groups.Group.uniqueID */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_uniqueID(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("uniqueID", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_uniqueID(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("uniqueID", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_uniqueID(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("uniqueID", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_uniqueID(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("uniqueID", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Groups.Group.users */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_users(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_property_sync("users", "ao", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_users(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_property_async("users", "ao", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Groups_Group_users(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("users", "ao", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Groups_Group_users(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("users", "ao", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Users */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Users(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Users", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.FindByCertificate */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_FindByCertificate(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("FindByCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_FindByCertificate(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindByCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.FindByID */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_FindByID(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t, const char **); \
+ sbus_method_sync("FindByID", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByID, \
+ NULL, \
+ _sbus_ifp_invoke_in_u_out_o_send, \
+ _sbus_ifp_key_u_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_FindByID(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindByID", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByID, \
+ NULL, \
+ _sbus_ifp_invoke_in_u_out_o_send, \
+ _sbus_ifp_key_u_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.FindByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_FindByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("FindByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_FindByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ _sbus_ifp_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.FindByNameAndCertificate */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char *, const char **); \
+ sbus_method_sync("FindByNameAndCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_ss_out_o_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindByNameAndCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_ss_out_o_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.FindByValidCertificate */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("FindByValidCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("FindByValidCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_s_out_o_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.ListByAttr */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_ListByAttr(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char *, uint32_t, const char ***); \
+ sbus_method_sync("ListByAttr", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByAttr, \
+ NULL, \
+ _sbus_ifp_invoke_in_ssu_out_ao_send, \
+ _sbus_ifp_key_ssu_0_1_2, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_ListByAttr(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListByAttr", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByAttr, \
+ NULL, \
+ _sbus_ifp_invoke_in_ssu_out_ao_send, \
+ _sbus_ifp_key_ssu_0_1_2, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.ListByCertificate */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_ListByCertificate(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t, const char ***); \
+ sbus_method_sync("ListByCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_su_out_ao_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_ListByCertificate(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListByCertificate", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByCertificate, \
+ NULL, \
+ _sbus_ifp_invoke_in_su_out_ao_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.ListByDomainAndName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_ListByDomainAndName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char *, uint32_t, const char ***); \
+ sbus_method_sync("ListByDomainAndName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByDomainAndName, \
+ NULL, \
+ _sbus_ifp_invoke_in_ssu_out_ao_send, \
+ _sbus_ifp_key_ssu_0_1_2, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_ListByDomainAndName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListByDomainAndName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByDomainAndName, \
+ NULL, \
+ _sbus_ifp_invoke_in_ssu_out_ao_send, \
+ _sbus_ifp_key_ssu_0_1_2, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.ListByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_ListByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t, const char ***); \
+ sbus_method_sync("ListByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_su_out_ao_send, \
+ _sbus_ifp_key_su_0_1, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_ListByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListByName", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByName, \
+ NULL, \
+ _sbus_ifp_invoke_in_su_out_ao_send, \
+ _sbus_ifp_key_su_0_1, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.sssd.infopipe.Users.User */
+#define SBUS_IFACE_org_freedesktop_sssd_infopipe_Users_User(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.sssd.infopipe.Users.User", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.sssd.infopipe.Users.User.UpdateGroupsList */
+#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_User_UpdateGroupsList(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_method_sync("UpdateGroupsList", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_User_UpdateGroupsList, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ _sbus_ifp_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_User_UpdateGroupsList(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("UpdateGroupsList", \
+ &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_User_UpdateGroupsList, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ _sbus_ifp_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.domain */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_domain(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("domain", "o", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_o_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_domain(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("domain", "o", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_o_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_domain(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("domain", "o", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_domain(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("domain", "o", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.domainname */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_domainname(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("domainname", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_domainname(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("domainname", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_domainname(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("domainname", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_domainname(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("domainname", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.extraAttributes */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_extraAttributes(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), hash_table_t **); \
+ sbus_property_sync("extraAttributes", "a{sas}", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ifp_extra_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_extraAttributes(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), hash_table_t **); \
+ sbus_property_async("extraAttributes", "a{sas}", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ifp_extra_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_extraAttributes(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("extraAttributes", "a{sas}", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_extraAttributes(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("extraAttributes", "a{sas}", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.gecos */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_gecos(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("gecos", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_gecos(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("gecos", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_gecos(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("gecos", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_gecos(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("gecos", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.gidNumber */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_gidNumber(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t*); \
+ sbus_property_sync("gidNumber", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_gidNumber(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_property_async("gidNumber", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_gidNumber(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("gidNumber", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_gidNumber(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("gidNumber", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.groups */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_groups(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_property_sync("groups", "ao", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_groups(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_property_async("groups", "ao", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_ao_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_groups(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("groups", "ao", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_groups(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("groups", "ao", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.homeDirectory */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_homeDirectory(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("homeDirectory", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_homeDirectory(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("homeDirectory", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_homeDirectory(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("homeDirectory", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_homeDirectory(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("homeDirectory", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.loginShell */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_loginShell(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("loginShell", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_loginShell(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("loginShell", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_loginShell(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("loginShell", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_loginShell(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("loginShell", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.name */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_name(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_name(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_name(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("name", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.uidNumber */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_uidNumber(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), uint32_t*); \
+ sbus_property_sync("uidNumber", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_uidNumber(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_property_async("uidNumber", "u", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_u_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_uidNumber(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("uidNumber", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_uidNumber(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("uidNumber", "u", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Property: org.freedesktop.sssd.infopipe.Users.User.uniqueID */
+#define SBUS_GETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_uniqueID(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_property_sync("uniqueID", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler), (data)); \
+})
+
+#define SBUS_GETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_uniqueID(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_property_async("uniqueID", "s", SBUS_PROPERTY_READABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out_s_send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#define SBUS_SETTER_SYNC_org_freedesktop_sssd_infopipe_Users_User_uniqueID(handler, data) ({\
+ SBUS_CHECK_SYNC((handler), (data)); \
+ sbus_property_sync("uniqueID", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler), (data)); \
+})
+
+#define SBUS_SETTER_ASYNC_org_freedesktop_sssd_infopipe_Users_User_uniqueID(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_property_async("uniqueID", "s", SBUS_PROPERTY_WRITABLE, \
+ NULL, \
+ _sbus_ifp_invoke_in__out__send, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#endif /* _SBUS_IFP_INTERFACE_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_invokers.c b/src/responder/ifp/ifp_iface/sbus_ifp_invokers.c
new file mode 100644
index 0000000..a2db826
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_invokers.c
@@ -0,0 +1,3004 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "sbus/sbus_private.h"
+#include "sbus/sbus_interface_declarations.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_arguments.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_invokers.h"
+
+static errno_t
+sbus_invoker_schedule(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ void *handler,
+ void *private_data)
+{
+ /* Schedule invoker as a timed event so it is processed after other
+ * event types. This will give dispatcher a chance to catch more
+ * messages before this invoker is triggered and therefore it will
+ * allow to potentially chain more request into one, especially for
+ * synchronous handlers. */
+
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ tv = tevent_timeval_current_ofs(0, 5);
+ te = tevent_add_timer(ev, mem_ctx, tv, handler, private_data);
+ if (te == NULL) {
+ /* There is not enough memory to create a timer. We can't do
+ * anything about it. */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not add invoker event!\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+struct _sbus_ifp_invoke_in__out__state {
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out__done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out__send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out__state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out__state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out__step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out__state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out__state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out__done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out__done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out__state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out__state);
+
+ ret = state->handler.recv(state, subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in__out_ao_state {
+ struct _sbus_ifp_invoker_args_ao out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out_ao_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out_ao_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out_ao_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_ao_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_ao_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out_ao_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_ao_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_ao_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out_ao_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out_ao_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_ao_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in__out_as_state {
+ struct _sbus_ifp_invoker_args_as out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out_as_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out_as_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out_as_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_as_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_as_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out_as_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_as_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_as_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out_as_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out_as_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_as_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in__out_b_state {
+ struct _sbus_ifp_invoker_args_b out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, bool*);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, bool*);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out_b_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out_b_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out_b_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out_b_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_b_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_b_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out_b_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out_b_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_b_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_b(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_b_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out_b_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out_b_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_b_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_b(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in__out_ifp_extra_state {
+ struct _sbus_ifp_invoker_args_ifp_extra out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, hash_table_t **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, hash_table_t **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out_ifp_extra_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out_ifp_extra_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out_ifp_extra_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out_ifp_extra_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_ifp_extra_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_ifp_extra_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out_ifp_extra_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out_ifp_extra_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_ifp_extra_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_ifp_extra(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_ifp_extra_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out_ifp_extra_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out_ifp_extra_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_ifp_extra_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_ifp_extra(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in__out_o_state {
+ struct _sbus_ifp_invoker_args_o out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out_o_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out_o_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out_o_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_o_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_o_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out_o_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_o_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_o_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out_o_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out_o_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_o_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in__out_s_state {
+ struct _sbus_ifp_invoker_args_s out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out_s_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out_s_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out_s_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_s_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_s_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out_s_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_s_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_s_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out_s_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out_s_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_s_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in__out_u_state {
+ struct _sbus_ifp_invoker_args_u out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, uint32_t*);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, uint32_t*);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in__out_u_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in__out_u_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in__out_u_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in__out_u_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_u_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_u_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in__out_u_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in__out_u_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_u_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_u(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_u_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in__out_u_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in__out_u_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in__out_u_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_u(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_s_out_ao_state {
+ struct _sbus_ifp_invoker_args_s *in;
+ struct _sbus_ifp_invoker_args_ao out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_s_out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_s_out_ao_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_s_out_ao_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_s_out_ao_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_ao_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_ao_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_s_out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_s_out_ao_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_ao_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_ao_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_s_out_ao_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_s_out_ao_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_ao_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_s_out_as_state {
+ struct _sbus_ifp_invoker_args_s *in;
+ struct _sbus_ifp_invoker_args_as out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_s_out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_s_out_as_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_s_out_as_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_s_out_as_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_as_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_as_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_s_out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_s_out_as_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_as_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_as_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_s_out_as_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_s_out_as_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_as_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_s_out_o_state {
+ struct _sbus_ifp_invoker_args_s *in;
+ struct _sbus_ifp_invoker_args_o out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_s_out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_s_out_o_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_s_out_o_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_s_out_o_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_o_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_o_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_s_out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_s_out_o_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_o_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_o_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_s_out_o_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_s_out_o_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_o_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_s_out_s_state {
+ struct _sbus_ifp_invoker_args_s *in;
+ struct _sbus_ifp_invoker_args_s out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_s_out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_s_out_s_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_s_out_s_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_s_out_s_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_s_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_s_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_s_out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_s_out_s_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_s_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_s_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_s_out_s_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_s_out_s_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_s_out_s_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_sas_out_raw_state {
+ struct _sbus_ifp_invoker_args_sas *in;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char **, DBusMessageIter *);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char **, DBusMessageIter *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_sas_out_raw_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_sas_out_raw_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_sas_out_raw_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_sas_out_raw_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_sas_out_raw_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_sas);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_sas(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_sas_out_raw_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_sas_out_raw_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_sas_out_raw_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_sas_out_raw_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1, state->write_iterator);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1, state->write_iterator);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_sas_out_raw_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_sas_out_raw_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_sas_out_raw_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_sas_out_raw_state);
+
+ ret = state->handler.recv(state, subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_ss_out_o_state {
+ struct _sbus_ifp_invoker_args_ss *in;
+ struct _sbus_ifp_invoker_args_o out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char *, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_ss_out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_ss_out_o_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_ss_out_o_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_ss_out_o_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_ss_out_o_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_ss);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_ss(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_ss_out_o_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_ss_out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_ss_out_o_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_ss_out_o_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_ss_out_o_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_ss_out_o_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_ss_out_o_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_ss_out_o_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_ssu_out_ao_state {
+ struct _sbus_ifp_invoker_args_ssu *in;
+ struct _sbus_ifp_invoker_args_ao out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char *, uint32_t, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char *, uint32_t);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_ssu_out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_ssu_out_ao_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_ssu_out_ao_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_ssu_out_ao_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_ssu_out_ao_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_ssu);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_ssu(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_ssu_out_ao_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_ssu_out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_ssu_out_ao_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_ssu_out_ao_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1, state->in->arg2, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1, state->in->arg2);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_ssu_out_ao_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_ssu_out_ao_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_ssu_out_ao_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_ssu_out_ao_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_su_out_ao_state {
+ struct _sbus_ifp_invoker_args_su *in;
+ struct _sbus_ifp_invoker_args_ao out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, uint32_t, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, uint32_t);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_su_out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_su_out_ao_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_su_out_ao_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_su_out_ao_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_su_out_ao_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_su);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_su(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_su_out_ao_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_su_out_ao_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_su_out_ao_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_su_out_ao_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0, state->in->arg1);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_su_out_ao_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_su_out_ao_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_su_out_ao_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_su_out_ao_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_ifp_invoke_in_u_out_o_state {
+ struct _sbus_ifp_invoker_args_u *in;
+ struct _sbus_ifp_invoker_args_o out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, uint32_t, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, uint32_t);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_ifp_invoke_in_u_out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_ifp_invoke_in_u_out_o_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_ifp_invoke_in_u_out_o_send
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ sbus_invoker_keygen keygen,
+ const struct sbus_handler *handler,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key)
+{
+ struct _sbus_ifp_invoke_in_u_out_o_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_u_out_o_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->handler.type = handler->type;
+ state->handler.data = handler->data;
+ state->handler.sync = handler->sync;
+ state->handler.send = handler->async_send;
+ state->handler.recv = handler->async_recv;
+
+ state->sbus_req = sbus_req;
+ state->read_iterator = read_iterator;
+ state->write_iterator = write_iterator;
+
+ state->in = talloc_zero(state, struct _sbus_ifp_invoker_args_u);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_read_u(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_u_out_o_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (_key != NULL) {
+ *_key = talloc_steal(mem_ctx, key);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void _sbus_ifp_invoke_in_u_out_o_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_ifp_invoke_in_u_out_o_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(private_data, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_u_out_o_state);
+
+ switch (state->handler.type) {
+ case SBUS_HANDLER_SYNC:
+ if (state->handler.sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = state->handler.sync(state, state->sbus_req, state->handler.data, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ if (state->handler.send == NULL || state->handler.recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_u_out_o_done, req);
+ ret = EAGAIN;
+ goto done;
+ }
+
+ ret = ERR_INTERNAL;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void _sbus_ifp_invoke_in_u_out_o_done(struct tevent_req *subreq)
+{
+ struct _sbus_ifp_invoke_in_u_out_o_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct _sbus_ifp_invoke_in_u_out_o_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_invokers.h b/src/responder/ifp/ifp_iface/sbus_ifp_invokers.h
new file mode 100644
index 0000000..12d8f22
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_invokers.h
@@ -0,0 +1,60 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_INVOKERS_H_
+#define _SBUS_IFP_INVOKERS_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "sbus/sbus_interface_declarations.h"
+#include "sbus/sbus_request.h"
+
+#define _sbus_ifp_declare_invoker(input, output) \
+ struct tevent_req * \
+ _sbus_ifp_invoke_in_ ## input ## _out_ ## output ## _send \
+ (TALLOC_CTX *mem_ctx, \
+ struct tevent_context *ev, \
+ struct sbus_request *sbus_req, \
+ sbus_invoker_keygen keygen, \
+ const struct sbus_handler *handler, \
+ DBusMessageIter *read_iterator, \
+ DBusMessageIter *write_iterator, \
+ const char **_key)
+
+_sbus_ifp_declare_invoker(, );
+_sbus_ifp_declare_invoker(, ao);
+_sbus_ifp_declare_invoker(, as);
+_sbus_ifp_declare_invoker(, b);
+_sbus_ifp_declare_invoker(, ifp_extra);
+_sbus_ifp_declare_invoker(, o);
+_sbus_ifp_declare_invoker(, s);
+_sbus_ifp_declare_invoker(, u);
+_sbus_ifp_declare_invoker(s, ao);
+_sbus_ifp_declare_invoker(s, as);
+_sbus_ifp_declare_invoker(s, o);
+_sbus_ifp_declare_invoker(s, s);
+_sbus_ifp_declare_invoker(sas, raw);
+_sbus_ifp_declare_invoker(ss, o);
+_sbus_ifp_declare_invoker(ssu, ao);
+_sbus_ifp_declare_invoker(su, ao);
+_sbus_ifp_declare_invoker(u, o);
+
+#endif /* _SBUS_IFP_INVOKERS_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_keygens.c b/src/responder/ifp/ifp_iface/sbus_ifp_keygens.c
new file mode 100644
index 0000000..0f2f4ae
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_keygens.c
@@ -0,0 +1,107 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 <inttypes.h>
+#include <talloc.h>
+
+#include "sbus/sbus_request.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_arguments.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_keygens.h"
+
+const char *
+_sbus_ifp_key_
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s",
+ sbus_req->type, sbus_req->interface, sbus_req->member, sbus_req->path);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member, sbus_req->path);
+}
+
+const char *
+_sbus_ifp_key_s_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_s *args)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s:%s",
+ sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s:%s",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+}
+
+const char *
+_sbus_ifp_key_ssu_0_1_2
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_ssu *args)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s:%s:%s:%" PRIu32 "",
+ sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0, args->arg1, args->arg2);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s:%s:%s:%" PRIu32 "",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0, args->arg1, args->arg2);
+}
+
+const char *
+_sbus_ifp_key_su_0_1
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_su *args)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s:%s:%" PRIu32 "",
+ sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0, args->arg1);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s:%s:%" PRIu32 "",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0, args->arg1);
+}
+
+const char *
+_sbus_ifp_key_u_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_u *args)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s:%" PRIu32 "",
+ sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s:%" PRIu32 "",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+}
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_keygens.h b/src/responder/ifp/ifp_iface/sbus_ifp_keygens.h
new file mode 100644
index 0000000..a0e5fb2
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_keygens.h
@@ -0,0 +1,57 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_KEYGENS_H_
+#define _SBUS_IFP_KEYGENS_H_
+
+#include <talloc.h>
+
+#include "sbus/sbus_request.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_arguments.h"
+
+const char *
+_sbus_ifp_key_
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req);
+
+const char *
+_sbus_ifp_key_s_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_s *args);
+
+const char *
+_sbus_ifp_key_ssu_0_1_2
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_ssu *args);
+
+const char *
+_sbus_ifp_key_su_0_1
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_su *args);
+
+const char *
+_sbus_ifp_key_u_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_ifp_invoker_args_u *args);
+
+#endif /* _SBUS_IFP_KEYGENS_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_server.h b/src/responder/ifp/ifp_iface/sbus_ifp_server.h
new file mode 100644
index 0000000..4f65ea6
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_server.h
@@ -0,0 +1,27 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_SERVER_H_
+#define _SBUS_IFP_SERVER_H_
+
+#include "sbus/sbus.h"
+#include "sbus/sbus_interface.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_interface.h"
+
+#endif /* _SBUS_IFP_SERVER_H_ */
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_symbols.c b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.c
new file mode 100644
index 0000000..a861fbc
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.c
@@ -0,0 +1,436 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 "sbus/sbus_interface_declarations.h"
+#include "responder/ifp/ifp_iface/sbus_ifp_symbols.h"
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindBackendByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "backend"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindDomainByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "domain"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindMonitor = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "monitor"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindResponderByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "responder"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserAttr = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "user"},
+ {.type = "as", .name = "attr"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "a{sv}", .name = "values"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserGroups = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "user"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "as", .name = "values"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListBackends = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "backends"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListComponents = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "components"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListDomains = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "domain"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListResponders = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "responders"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Ping = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "ping"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "s", .name = "pong"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_List = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_ListByDomain = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "domain_name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Remove = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "b", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Store = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "b", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ActiveServer = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "service"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "s", .name = "server"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_IsOnline = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "b", .name = "status"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServers = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "service_name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "as", .name = "servers"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServices = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "as", .name = "services"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_RefreshAccessRules = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByID = {
+ .input = (const struct sbus_argument[]){
+ {.type = "u", .name = "id"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByDomainAndName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "domain_name"},
+ {.type = "s", .name = "name_filter"},
+ {.type = "u", .name = "limit"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name_filter"},
+ {.type = "u", .name = "limit"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_Group_UpdateMemberList = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByCertificate = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "pem_cert"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByID = {
+ .input = (const struct sbus_argument[]){
+ {.type = "u", .name = "id"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {.type = "s", .name = "pem_cert"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "pem_cert"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "o", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByAttr = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "attribute"},
+ {.type = "s", .name = "attr_filter"},
+ {.type = "u", .name = "limit"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByCertificate = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "pem_cert"},
+ {.type = "u", .name = "limit"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByDomainAndName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "domain_name"},
+ {.type = "s", .name = "name_filter"},
+ {.type = "u", .name = "limit"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name_filter"},
+ {.type = "u", .name = "limit"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "ao", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_User_UpdateGroupsList = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {NULL}
+ }
+};
diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_symbols.h b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.h
new file mode 100644
index 0000000..f433fa7
--- /dev/null
+++ b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.h
@@ -0,0 +1,130 @@
+/*
+ Generated by sbus code generator
+
+ Copyright (C) 2017 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 _SBUS_IFP_SYMBOLS_H_
+#define _SBUS_IFP_SYMBOLS_H_
+
+#include "sbus/sbus_interface_declarations.h"
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindBackendByName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindDomainByName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindMonitor;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_FindResponderByName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserAttr;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_GetUserGroups;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListBackends;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListComponents;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListDomains;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_ListResponders;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Ping;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_List;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_ListByDomain;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Remove;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Cache_Object_Store;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ActiveServer;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_IsOnline;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServers;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_ListServices;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Domains_Domain_RefreshAccessRules;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByID;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_FindByName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByDomainAndName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_ListByName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Groups_Group_UpdateMemberList;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByCertificate;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByID;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByAttr;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByCertificate;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByDomainAndName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByName;
+
+extern const struct sbus_method_arguments
+_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_User_UpdateGroupsList;
+
+#endif /* _SBUS_IFP_SYMBOLS_H_ */
diff --git a/src/responder/ifp/ifp_iface_nodes.c b/src/responder/ifp/ifp_iface_nodes.c
new file mode 100644
index 0000000..18e5374
--- /dev/null
+++ b/src/responder/ifp/ifp_iface_nodes.c
@@ -0,0 +1,166 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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 "responder/ifp/ifp_users.h"
+#include "responder/ifp/ifp_groups.h"
+#include "responder/ifp/ifp_cache.h"
+#include "responder/ifp/ifp_domains.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+
+static errno_t
+nodes_ifp(TALLOC_CTX *mem_ctx,
+ const char *path,
+ struct ifp_ctx *ctx,
+ const char ***_nodes)
+{
+ static const char *nodes[] = {"Users", "Groups", "Domains", NULL};
+
+ *_nodes = nodes;
+
+ return EOK;
+}
+
+static errno_t
+nodes_cached_objects(TALLOC_CTX *mem_ctx,
+ struct ifp_ctx *ifp_ctx,
+ enum ifp_cache_type type,
+ const char *prefix,
+ const char ***_nodes)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char **paths;
+ const char **nodes;
+ const char *node;
+ int num_paths;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = ifp_cache_list_domains(tmp_ctx, ifp_ctx->rctx->domains, type, &paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain cache objects list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ num_paths = talloc_array_length(paths) - 1;
+ nodes = talloc_zero_array(tmp_ctx, const char *, num_paths + 1);
+ if (nodes == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < num_paths; i++) {
+ node = sbus_opath_strip_prefix(paths[i], prefix);
+ nodes[i] = talloc_strdup(nodes, node);
+ if (nodes[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_nodes = talloc_steal(mem_ctx, nodes);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+nodes_users(TALLOC_CTX *mem_ctx,
+ const char *path,
+ struct ifp_ctx *ctx,
+ const char ***_nodes)
+{
+ return nodes_cached_objects(mem_ctx, ctx, IFP_CACHE_USER,
+ IFP_PATH_USERS "/", _nodes);
+}
+
+static errno_t
+nodes_groups(TALLOC_CTX *mem_ctx,
+ const char *path,
+ struct ifp_ctx *ctx,
+ const char ***_nodes)
+{
+ return nodes_cached_objects(mem_ctx, ctx, IFP_CACHE_GROUP,
+ IFP_PATH_GROUPS "/", _nodes);
+}
+
+static errno_t
+nodes_domains(TALLOC_CTX *mem_ctx,
+ const char *path,
+ struct ifp_ctx *ctx,
+ const char ***_nodes)
+{
+ struct sss_domain_info *domain;
+ const char **nodes;
+ size_t count;
+
+ count = 0;
+ domain = ctx->rctx->domains;
+ do {
+ count++;
+ } while ((domain = get_next_domain(domain, SSS_GND_ALL_DOMAINS)) != NULL);
+
+ nodes = talloc_zero_array(mem_ctx, const char *, count + 1);
+ if (nodes == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+ return ENOMEM;
+ }
+
+ count = 0;
+ domain = ctx->rctx->domains;
+ do {
+ nodes[count] = sbus_opath_escape(nodes, domain->name);
+ if (nodes[count] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sbus_opath_escape_part() failed\n");
+ talloc_free(nodes);
+ return ENOMEM;
+ }
+
+ count++;
+ } while ((domain = get_next_domain(domain, SSS_GND_ALL_DOMAINS)) != NULL);
+
+ *_nodes = nodes;
+
+ return EOK;
+}
+
+errno_t
+ifp_register_nodes(struct ifp_ctx *ctx, struct sbus_connection *conn)
+{
+ struct sbus_node nodes[] = SBUS_NODES(
+ SBUS_NODE_SYNC(IFP_PATH, nodes_ifp, ctx),
+ SBUS_NODE_SYNC(IFP_PATH_USERS, nodes_users, ctx),
+ SBUS_NODE_SYNC(IFP_PATH_GROUPS, nodes_groups, ctx),
+ SBUS_NODE_SYNC(IFP_PATH_DOMAINS, nodes_domains, ctx)
+ );
+
+ return sbus_router_add_node_map(conn, nodes);
+}
diff --git a/src/responder/ifp/ifp_private.h b/src/responder/ifp/ifp_private.h
new file mode 100644
index 0000000..e9a9d3a
--- /dev/null
+++ b/src/responder/ifp/ifp_private.h
@@ -0,0 +1,137 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2013 Red Hat
+
+ InfoPipe responder: A private header
+
+ 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 _IFPSRV_PRIVATE_H_
+#define _IFPSRV_PRIVATE_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include <stdint.h>
+#include <ldb.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/common/negcache.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+
+struct ifp_ctx {
+ struct resp_ctx *rctx;
+ struct sss_names_ctx *snctx;
+
+ struct sbus_connection *sysbus;
+ const char **user_whitelist;
+ uint32_t wildcard_limit;
+};
+
+errno_t
+ifp_access_check(struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx);
+
+errno_t ifp_register_sbus_interface(struct sbus_connection *conn,
+ struct ifp_ctx *ifp_ctx);
+
+errno_t
+ifp_register_nodes(struct ifp_ctx *ctx, struct sbus_connection *conn);
+
+errno_t
+ifp_ping(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *ping,
+ const char **_pong);
+
+struct tevent_req *
+ifp_get_user_attr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char **attrs,
+ DBusMessageIter *write_iter);
+
+errno_t
+ifp_get_user_attr_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req);
+
+struct tevent_req *
+ifp_user_get_groups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name);
+
+errno_t
+ifp_user_get_groups_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_groupnames);
+
+/* == Utility functions == */
+
+errno_t ifp_add_value_to_dict(DBusMessageIter *iter_dict,
+ const char *key,
+ const char *value);
+
+errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict,
+ struct ldb_message_element *el);
+const char **
+ifp_parse_user_attr_list(TALLOC_CTX *mem_ctx, const char *conf_str);
+
+const char **
+ifp_get_user_extra_attributes(TALLOC_CTX *mem_ctx, struct ifp_ctx *ifp_ctx);
+
+bool ifp_attr_allowed(const char *whitelist[], const char *attr);
+bool ifp_is_user_attr_allowed(struct ifp_ctx *ifp_ctx, const char *attr);
+
+/* Used for list calls */
+struct ifp_list_ctx {
+ const char *attr;
+ const char *filter;
+ uint32_t limit;
+
+ struct sss_domain_info *dom;
+ struct ifp_ctx *ctx;
+
+ const char **paths;
+ size_t paths_max;
+ size_t path_count;
+};
+
+struct ifp_list_ctx *ifp_list_ctx_new(TALLOC_CTX *mem_ctx,
+ struct ifp_ctx *ctx,
+ const char *attr,
+ const char *filter,
+ uint32_t limit);
+
+errno_t ifp_list_ctx_remaining_capacity(struct ifp_list_ctx *list_ctx,
+ size_t entries,
+ size_t *_capacity);
+
+errno_t ifp_ldb_el_output_name(struct resp_ctx *rctx,
+ struct ldb_message *msg,
+ const char *el_name,
+ struct sss_domain_info *dom);
+
+char *ifp_format_name_attr(TALLOC_CTX *mem_ctx, struct ifp_ctx *ifp_ctx,
+ const char *in_name, struct sss_domain_info *dom);
+
+#endif /* _IFPSRV_PRIVATE_H_ */
diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
new file mode 100644
index 0000000..7acd46e
--- /dev/null
+++ b/src/responder/ifp/ifp_users.c
@@ -0,0 +1,2040 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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 <string.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "util/cert.h"
+#include "util/child_common.h"
+#include "util/crypto/sss_crypto.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ifp/ifp_users.h"
+#include "responder/ifp/ifp_groups.h"
+#include "responder/ifp/ifp_cache.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+
+char * ifp_users_build_path_from_msg(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ const char *key = NULL;
+
+ switch (domain->type) {
+ case DOM_TYPE_APPLICATION:
+ key = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ break;
+ case DOM_TYPE_POSIX:
+ key = ldb_msg_find_attr_as_string(msg, SYSDB_UIDNUM, NULL);
+ break;
+ }
+
+
+ if (key == NULL) {
+ return NULL;
+ }
+
+ return sbus_opath_compose(mem_ctx, IFP_PATH_USERS, domain->name, key);
+}
+
+static errno_t ifp_users_decompose_path(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ const char *path,
+ struct sss_domain_info **_domain,
+ char **_key)
+{
+ char **parts = NULL;
+ struct sss_domain_info *domain;
+ errno_t ret;
+
+ ret = sbus_opath_decompose_expected(NULL, path, IFP_PATH_USERS, 2, &parts);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ domain = find_domain_by_name(domains, parts[0], false);
+ if (domain == NULL) {
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ *_domain = domain;
+ *_key = talloc_steal(mem_ctx, parts[1]);
+
+done:
+ talloc_free(parts);
+ return ret;
+}
+
+static int ifp_users_list_copy(struct ifp_list_ctx *list_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_result *result)
+{
+ size_t copy_count, i;
+ errno_t ret;
+
+ ret = ifp_list_ctx_remaining_capacity(list_ctx, result->count, &copy_count);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (i = 0; i < copy_count; i++) {
+ list_ctx->paths[list_ctx->path_count + i] = \
+ ifp_users_build_path_from_msg(list_ctx->paths,
+ domain,
+ result->msgs[i]);
+ if (list_ctx->paths[list_ctx->path_count + i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ list_ctx->path_count += copy_count;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+struct ifp_users_find_by_name_state {
+ const char *path;
+};
+
+static void ifp_users_find_by_name_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_find_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name)
+{
+ struct ifp_users_find_by_name_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ifp_users_find_by_name_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ subreq = cache_req_user_by_name_send(state, ctx->rctx->ev, ctx->rctx,
+ ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM, NULL,
+ name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_find_by_name_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_users_find_by_name_done(struct tevent_req *subreq)
+{
+ struct ifp_users_find_by_name_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_find_by_name_state);
+
+ ret = cache_req_user_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->path = ifp_users_build_path_from_msg(state, result->domain,
+ result->msgs[0]);
+ if (state->path == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_users_find_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_users_find_by_name_state *state;
+ state = tevent_req_data(req, struct ifp_users_find_by_name_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_path = talloc_steal(mem_ctx, state->path);
+
+ return EOK;
+}
+
+struct ifp_users_find_by_id_state {
+ const char *path;
+};
+
+static void ifp_users_find_by_id_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_find_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t id)
+{
+ struct ifp_users_find_by_id_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_id_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ subreq = cache_req_user_by_id_send(state, ctx->rctx->ev, ctx->rctx,
+ ctx->rctx->ncache, 0, NULL, id);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_find_by_id_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_users_find_by_id_done(struct tevent_req *subreq)
+{
+ struct ifp_users_find_by_id_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_find_by_id_state);
+
+ ret = cache_req_user_by_id_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->path = ifp_users_build_path_from_msg(state, result->domain,
+ result->msgs[0]);
+ if (state->path == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_users_find_by_id_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_users_find_by_id_state *state;
+ state = tevent_req_data(req, struct ifp_users_find_by_id_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_path = talloc_steal(mem_ctx, state->path);
+
+ return EOK;
+}
+
+struct ifp_users_find_by_cert_state {
+ const char *path;
+};
+
+static void ifp_users_find_by_cert_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_find_by_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *pem_cert)
+{
+ struct ifp_users_find_by_cert_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ char *derb64;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_cert_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ ret = sss_cert_pem_to_derb64(state, pem_cert, &derb64);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n");
+ goto done;
+ }
+
+ subreq = cache_req_user_by_cert_send(state, ctx->rctx->ev, ctx->rctx,
+ ctx->rctx->ncache, 0, CACHE_REQ_ANY_DOM,
+ NULL, derb64);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_find_by_cert_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_users_find_by_cert_done(struct tevent_req *subreq)
+{
+ struct ifp_users_find_by_cert_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_find_by_cert_state);
+
+ ret = cache_req_user_by_cert_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (result->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More than one user found. "
+ "Use ListByCertificate to get all.\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ state->path = ifp_users_build_path_from_msg(state, result->domain,
+ result->msgs[0]);
+ if (state->path == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_users_find_by_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_users_find_by_cert_state *state;
+ state = tevent_req_data(req, struct ifp_users_find_by_cert_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_path = talloc_steal(mem_ctx, state->path);
+
+ return EOK;
+}
+
+struct ifp_users_list_by_cert_state {
+ struct ifp_ctx *ifp_ctx;
+ struct ifp_list_ctx *list_ctx;
+ char *derb64;
+};
+
+static errno_t ifp_users_list_by_cert_step(struct tevent_req *req);
+static void ifp_users_list_by_cert_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_list_by_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *pem_cert,
+ uint32_t limit)
+{
+ struct ifp_users_list_by_cert_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_list_by_cert_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ ret = sss_cert_pem_to_derb64(state, pem_cert, &state->derb64);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n");
+ goto done;
+ }
+
+ state->ifp_ctx = ctx;
+ state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, state->derb64, limit);
+ if (state->list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ifp_users_list_by_cert_step(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t
+ifp_users_list_by_cert_step(struct tevent_req *req)
+{
+ struct ifp_users_list_by_cert_state *state;
+ struct tevent_req *subreq;
+
+ state = tevent_req_data(req, struct ifp_users_list_by_cert_state);
+
+ if (state->list_ctx->dom == NULL) {
+ return EOK;
+ }
+
+ subreq = cache_req_user_by_cert_send(state->list_ctx,
+ state->ifp_ctx->rctx->ev,
+ state->ifp_ctx->rctx,
+ state->ifp_ctx->rctx->ncache,
+ 0,
+ CACHE_REQ_ANY_DOM,
+ state->list_ctx->dom->name,
+ state->list_ctx->filter);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_list_by_cert_done, req);
+
+ state->list_ctx->dom = get_next_domain(state->list_ctx->dom,
+ SSS_GND_DESCEND);
+
+ return EAGAIN;
+}
+
+static void ifp_users_list_by_cert_done(struct tevent_req *subreq)
+{
+ struct ifp_users_list_by_cert_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_list_by_cert_state);
+
+ ret = cache_req_user_by_cert_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret == EOK) {
+ ret = ifp_users_list_copy(state->list_ctx, result->domain,
+ result->ldb_result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to list groups [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ifp_users_list_by_cert_step(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+errno_t
+ifp_users_list_by_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths)
+{
+ struct ifp_users_list_by_cert_state *state;
+ state = tevent_req_data(req, struct ifp_users_list_by_cert_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_paths = talloc_steal(mem_ctx, state->list_ctx->paths);
+
+ return EOK;
+}
+
+struct ifp_users_find_by_name_and_cert_state {
+ struct ifp_ctx *ifp_ctx;
+ struct ifp_list_ctx *list_ctx;
+ const char *name;
+ const char *pem_cert;
+ char *derb64;
+
+ const char *user_opath;
+};
+
+static void ifp_users_find_by_name_and_cert_name_done(struct tevent_req *subreq);
+static errno_t ifp_users_find_by_name_and_cert_step(struct tevent_req *req);
+static void ifp_users_find_by_name_and_cert_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_find_by_name_and_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char *pem_cert)
+{
+ struct ifp_users_find_by_name_and_cert_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_name_and_cert_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->ifp_ctx = ctx;
+
+ if (!SBUS_REQ_STRING_IS_EMPTY(name)) {
+ state->name = talloc_strdup(state, name);
+ if (state->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (!SBUS_REQ_STRING_IS_EMPTY(pem_cert)) {
+ state->pem_cert = talloc_strdup(state, pem_cert);
+ if (state->pem_cert == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_cert_pem_to_derb64(state, pem_cert, &state->derb64);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n");
+ goto done;
+ }
+
+ /* FIXME: if unlimted searches with limit=0 will work please replace
+ * 100 with 0. */
+ state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, state->derb64, 100);
+ if (state->list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (state->name == NULL && state->pem_cert == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Empty arguments!\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (state->name != NULL) {
+ subreq = cache_req_user_by_name_send(state, ctx->rctx->ev, ctx->rctx,
+ ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM,
+ NULL, state->name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_find_by_name_and_cert_name_done, req);
+ } else {
+ ret = ifp_users_find_by_name_and_cert_step(req);
+ goto done;
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_users_find_by_name_and_cert_name_done(struct tevent_req *subreq)
+{
+ struct ifp_users_find_by_name_and_cert_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state);
+
+ ret = cache_req_user_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->user_opath = ifp_users_build_path_from_msg(state,
+ result->domain,
+ result->msgs[0]);
+ if (state->user_opath == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ ret = ifp_users_find_by_name_and_cert_step(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ return;
+}
+
+static errno_t
+ifp_users_find_by_name_and_cert_step(struct tevent_req *req)
+{
+ struct ifp_users_find_by_name_and_cert_state *state;
+ struct tevent_req *subreq;
+
+ state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state);
+
+ if (state->list_ctx == NULL) {
+ if (state->name == NULL) {
+ return EINVAL;
+ }
+
+ /* Nothing to search for. */
+ return EOK;
+ }
+
+ /* No more domains to try. */
+ if (state->list_ctx->dom == NULL) {
+ return EOK;
+ }
+
+ subreq = cache_req_user_by_cert_send(state->list_ctx,
+ state->ifp_ctx->rctx->ev,
+ state->ifp_ctx->rctx,
+ state->ifp_ctx->rctx->ncache,
+ 0,
+ CACHE_REQ_ANY_DOM,
+ state->list_ctx->dom->name,
+ state->list_ctx->filter);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_find_by_name_and_cert_done, req);
+
+ state->list_ctx->dom = get_next_domain(state->list_ctx->dom,
+ SSS_GND_DESCEND);
+
+ return EAGAIN;
+}
+
+static void ifp_users_find_by_name_and_cert_done(struct tevent_req *subreq)
+{
+ struct ifp_users_find_by_name_and_cert_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state);
+
+ ret = cache_req_user_by_cert_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret == EOK) {
+ ret = ifp_users_list_copy(state->list_ctx, result->domain,
+ result->ldb_result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to list groups [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ifp_users_find_by_name_and_cert_step(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ return;
+}
+
+errno_t
+ifp_users_find_by_name_and_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_users_find_by_name_and_cert_state *state;
+ size_t c;
+
+ state = tevent_req_data(req, struct ifp_users_find_by_name_and_cert_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* If no name was given check if there is only one user mapped to the
+ * certificate and return its object path. Either no or more than one
+ * mapped users are errors in this case.
+ * The case where a given name could not be found is already handled in
+ * ifp_users_find_by_name_and_cert_name_done(). */
+ if (state->user_opath == NULL) {
+ if (state->list_ctx == NULL || state->list_ctx->path_count == 0) {
+ return ENOENT;
+ } else if (state->list_ctx->path_count == 1) {
+ *_path = talloc_steal(mem_ctx, state->list_ctx->paths[0]);
+ return EOK;
+ } else {
+ return EEXIST;
+ }
+ }
+
+ /* If there was no certificate given just return the object path of the
+ * user found by name. If a certificate was given an no mapped user was
+ * found return an error. */
+ if (state->pem_cert == NULL) {
+ *_path = talloc_steal(mem_ctx, state->user_opath);
+ return EOK;
+ } else {
+ for (c = 0; c < state->list_ctx->path_count; c++) {
+ if (strcmp(state->user_opath, state->list_ctx->paths[c]) == 0) {
+ *_path = talloc_steal(mem_ctx, state->user_opath);
+ return EOK;
+ }
+ }
+ }
+
+ return ENOENT;
+}
+
+struct ifp_users_list_by_attr_state {
+ struct ifp_ctx *ifp_ctx;
+ struct ifp_list_ctx *list_ctx;
+};
+
+static errno_t ifp_users_list_by_attr_step(struct tevent_req *req);
+static void ifp_users_list_by_attr_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_list_by_attr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *attr,
+ const char *filter,
+ uint32_t limit)
+{
+ struct ifp_users_list_by_attr_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_list_by_attr_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->ifp_ctx = ctx;
+ state->list_ctx = ifp_list_ctx_new(state, ctx, attr, filter, limit);
+ if (state->list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ifp_users_list_by_attr_step(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t
+ifp_users_list_by_attr_step(struct tevent_req *req)
+{
+ struct ifp_users_list_by_attr_state *state;
+ struct tevent_req *subreq;
+
+ state = tevent_req_data(req, struct ifp_users_list_by_attr_state);
+
+ if (state->list_ctx->dom == NULL) {
+ return EOK;
+ }
+
+ subreq = cache_req_user_by_filter_send(state->list_ctx,
+ state->ifp_ctx->rctx->ev,
+ state->ifp_ctx->rctx,
+ CACHE_REQ_ANY_DOM,
+ state->list_ctx->dom->name,
+ state->list_ctx->attr,
+ state->list_ctx->filter);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_list_by_attr_done, req);
+
+ state->list_ctx->dom = get_next_domain(state->list_ctx->dom,
+ SSS_GND_DESCEND);
+
+ return EAGAIN;
+}
+
+static void ifp_users_list_by_attr_done(struct tevent_req *subreq)
+{
+ struct ifp_users_list_by_attr_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_list_by_attr_state);
+
+ ret = cache_req_user_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret == EOK) {
+ ret = ifp_users_list_copy(state->list_ctx, result->domain,
+ result->ldb_result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to list users [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ifp_users_list_by_attr_step(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+}
+
+errno_t
+ifp_users_list_by_attr_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths)
+{
+ struct ifp_users_list_by_attr_state *state;
+ state = tevent_req_data(req, struct ifp_users_list_by_attr_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_paths = talloc_steal(mem_ctx, state->list_ctx->paths);
+
+ return EOK;
+}
+
+struct ifp_users_list_by_domain_and_name_state {
+ struct ifp_list_ctx *list_ctx;
+};
+
+static void ifp_users_list_by_domain_and_name_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_list_by_domain_and_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char *filter,
+ uint32_t limit)
+{
+ struct ifp_users_list_by_domain_and_name_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_list_by_domain_and_name_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->list_ctx = ifp_list_ctx_new(state, ctx, NULL, filter, limit);
+ if (state->list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = cache_req_user_by_filter_send(state->list_ctx, ctx->rctx->ev,
+ ctx->rctx, CACHE_REQ_ANY_DOM,
+ domain, NULL, filter);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_list_by_domain_and_name_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_users_list_by_domain_and_name_done(struct tevent_req *subreq)
+{
+ struct ifp_users_list_by_domain_and_name_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_list_by_domain_and_name_state);
+
+ ret = cache_req_user_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ifp_users_list_copy(state->list_ctx, result->domain,
+ result->ldb_result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to copy domain result\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_users_list_by_domain_and_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths)
+{
+ struct ifp_users_list_by_domain_and_name_state *state;
+ state = tevent_req_data(req, struct ifp_users_list_by_domain_and_name_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_paths = talloc_steal(mem_ctx, state->list_ctx->paths);
+
+ return EOK;
+}
+
+struct ifp_users_find_by_valid_cert_state {
+ struct ifp_ctx *ifp_ctx;
+ struct tevent_context *ev;
+ const char *logfile;
+ int timeout;
+ char *ca_db;
+ char *verify_opts;
+ char *derb64;
+ const char **extra_args;
+ const char *path;
+
+ struct sss_child_ctx_old *child_ctx;
+ struct child_io_fds *io;
+};
+
+static errno_t p11_child_exec(struct tevent_req *req);
+static void
+ifp_users_find_by_valid_cert_step(int child_status,
+ struct tevent_signal *sige,
+ void *pvt);
+static void ifp_users_find_by_valid_cert_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_find_by_valid_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *pem_cert)
+{
+ struct tevent_req *req;
+ struct ifp_users_find_by_valid_cert_state *state;
+ size_t arg_c = 0;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_find_by_valid_cert_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ifp_ctx = ctx;
+
+ ret = confdb_get_string(ctx->rctx->cdb, state,
+ CONFDB_IFP_CONF_ENTRY, CONFDB_SSH_CA_DB,
+ CONFDB_DEFAULT_SSH_CA_DB, &state->ca_db);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading CA DB from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_int(ctx->rctx->cdb, CONFDB_IFP_CONF_ENTRY,
+ CONFDB_PAM_P11_CHILD_TIMEOUT, -1,
+ &state->timeout);
+ if (ret != EOK || state->timeout == -1) {
+ /* check pam configuration as well or use default */
+ ret = confdb_get_int(ctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_P11_CHILD_TIMEOUT,
+ P11_CHILD_TIMEOUT_DEFAULT,
+ &state->timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read p11_child_timeout from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = confdb_get_string(ctx->rctx->cdb, state, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_CERT_VERIFICATION, NULL,
+ &state->verify_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read '"CONFDB_MONITOR_CERT_VERIFICATION"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ state->ev = ev;
+ state->logfile = P11_CHILD_LOG_FILE;
+ state->io = talloc(state, struct child_io_fds);
+ if (state->io == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->io->write_to_child_fd = -1;
+ state->io->read_from_child_fd = -1;
+ talloc_set_destructor((void *) state->io, child_io_destructor);
+
+ ret = sss_cert_pem_to_derb64(state, pem_cert, &state->derb64);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n");
+ goto done;
+ }
+
+ state->extra_args = talloc_zero_array(state, const char *, 8);
+ if (state->extra_args == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->extra_args[arg_c++] = state->derb64;
+ state->extra_args[arg_c++] = "--certificate";
+ state->extra_args[arg_c++] = state->ca_db;
+ state->extra_args[arg_c++] = "--ca_db";
+ if (state->verify_opts != NULL) {
+ state->extra_args[arg_c++] = state->verify_opts;
+ state->extra_args[arg_c++] = "--verify";
+ }
+ state->extra_args[arg_c++] = "--verification";
+
+ ret = p11_child_exec(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t p11_child_exec(struct tevent_req *req)
+{
+ struct ifp_users_find_by_valid_cert_state *state;
+ int pipefd_from_child[2] = PIPE_INIT;
+ int pipefd_to_child[2] = PIPE_INIT;
+ pid_t child_pid;
+ struct timeval tv;
+ bool endtime;
+ int ret;
+
+ state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state);
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ child_pid = fork();
+ if (child_pid == 0) { /* child */
+ exec_child_ex(state, pipefd_to_child, pipefd_from_child,
+ P11_CHILD_PATH, state->logfile, state->extra_args,
+ false, STDIN_FILENO, STDOUT_FILENO);
+ /* We should never get here */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec p11 child\n");
+ return ret;
+ } else if (child_pid > 0) { /* parent */
+ state->io->read_from_child_fd = pipefd_from_child[0];
+ PIPE_FD_CLOSE(pipefd_from_child[1]);
+ sss_fd_nonblocking(state->io->read_from_child_fd);
+
+ state->io->write_to_child_fd = pipefd_to_child[1];
+ PIPE_FD_CLOSE(pipefd_to_child[0]);
+ sss_fd_nonblocking(state->io->write_to_child_fd);
+
+ /* Set up SIGCHLD handler */
+ ret = child_handler_setup(state->ev, child_pid,
+ ifp_users_find_by_valid_cert_step,
+ req, &state->child_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+
+ /* Set up timeout handler */
+ tv = tevent_timeval_current_ofs(state->timeout, 0);
+ endtime = tevent_req_set_endtime(req, state->ev, tv);
+ if (endtime == false) {
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+ /* Now either wait for the timeout to fire or the child to finish */
+ } else { /* error */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ return EAGAIN;
+
+done:
+ if (ret != EOK) {
+ PIPE_CLOSE(pipefd_from_child);
+ PIPE_CLOSE(pipefd_to_child);
+ }
+
+ return ret;
+}
+
+static void
+ifp_users_find_by_valid_cert_step(int child_status,
+ struct tevent_signal *sige,
+ void *pvt)
+{
+ struct tevent_req *subreq;
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct ifp_users_find_by_valid_cert_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state);
+
+ PIPE_FD_CLOSE(state->io->read_from_child_fd);
+ PIPE_FD_CLOSE(state->io->write_to_child_fd);
+
+ if (WIFEXITED(child_status)) {
+ if (WEXITSTATUS(child_status) == CA_DB_NOT_FOUND_EXIT_CODE) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ P11_CHILD_PATH " failed [%d]: [%s].\n",
+ ERR_CA_DB_NOT_FOUND, sss_strerror(ERR_CA_DB_NOT_FOUND));
+ tevent_req_error(req, ERR_CA_DB_NOT_FOUND);
+ return;
+ } else if (WEXITSTATUS(child_status) != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ P11_CHILD_PATH " failed with status [%d]. Check p11_child"
+ " logs for more information.\n",
+ WEXITSTATUS(child_status));
+ tevent_req_error(req, ERR_INVALID_CERT);
+ return;
+ }
+ } else if (WIFSIGNALED(child_status)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ P11_CHILD_PATH " was terminated by signal [%d]. Check p11_child"
+ " logs for more information.\n",
+ WTERMSIG(child_status));
+ tevent_req_error(req, ECHILD);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Certificate [%s] is valid.\n",
+ state->extra_args[0]);
+
+ subreq = cache_req_user_by_cert_send(state, state->ifp_ctx->rctx->ev,
+ state->ifp_ctx->rctx,
+ state->ifp_ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM, NULL,
+ state->derb64);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_find_by_valid_cert_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, state->ifp_ctx->rctx->ev);
+ }
+
+ return;
+}
+
+static void ifp_users_find_by_valid_cert_done(struct tevent_req *subreq)
+{
+ struct ifp_users_find_by_valid_cert_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state);
+
+ ret = cache_req_user_by_cert_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (result->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More than one user found. "
+ "Use ListByCertificate to get all.\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ state->path = ifp_users_build_path_from_msg(state, result->domain,
+ result->msgs[0]);
+ if (state->path == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_users_find_by_valid_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path)
+{
+ struct ifp_users_find_by_valid_cert_state *state;
+
+ state = tevent_req_data(req, struct ifp_users_find_by_valid_cert_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_path = talloc_steal(mem_ctx, state->path);
+
+ return EOK;
+}
+
+static errno_t
+ifp_users_get_from_cache(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *key,
+ struct ldb_message **_user)
+{
+ struct ldb_result *user_res = NULL;
+ errno_t ret;
+ uid_t uid;
+ char *endptr;
+
+ switch (domain->type) {
+ case DOM_TYPE_POSIX:
+ uid = strtouint32(key, &endptr, 10);
+ if ((errno != 0) || *endptr || (key == endptr)) {
+ ret = errno ? errno : EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID value\n");
+ goto done;
+ }
+
+ ret = sysdb_getpwuid_with_views(mem_ctx, domain, uid, &user_res);
+ if (ret == EOK && user_res->count == 0) {
+ *_user = NULL;
+ ret = ENOENT;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %u@%s [%d]: %s\n",
+ uid, domain->name, ret, sss_strerror(ret));
+ goto done;
+ }
+ break;
+ case DOM_TYPE_APPLICATION:
+ ret = sysdb_getpwnam_with_views(mem_ctx, domain, key, &user_res);
+ if (ret == EOK && user_res->count == 0) {
+ *_user = NULL;
+ ret = ENOENT;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user %s@%s [%d]: %s\n",
+ key, domain->name, ret, sss_strerror(ret));
+ goto done;
+ }
+ break;
+ }
+
+ if (user_res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More users matched by the single key\n");
+ ret = EIO;
+ goto done;
+ }
+
+ *_user = talloc_steal(mem_ctx, user_res->msgs[0]);
+
+ ret = EOK;
+
+done:
+ talloc_free(user_res);
+
+ return ret;
+}
+
+static errno_t
+ifp_users_user_get(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ struct sss_domain_info **_domain,
+ struct ldb_message **_user)
+{
+ struct sss_domain_info *domain;
+ char *key;
+ errno_t ret;
+
+ ret = ifp_users_decompose_path(NULL,
+ ifp_ctx->rctx->domains, sbus_req->path,
+ &domain, &key);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path"
+ "[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (_user != NULL) {
+ ret = ifp_users_get_from_cache(mem_ctx, domain, key, _user);
+ }
+
+ talloc_free(key);
+
+ if (ret == EOK || ret == ENOENT) {
+ if (_domain != NULL) {
+ *_domain = domain;
+ }
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve user from cache\n");
+ }
+
+ return ret;
+}
+
+static errno_t
+ifp_users_get_as_string(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *attr,
+ const char **_out,
+ struct sss_domain_info **_domain)
+{
+ struct ldb_message *msg;
+ struct sss_domain_info *domain;
+ const char *out;
+ errno_t ret;
+
+ ret = ifp_users_user_get(NULL, sbus_req, ifp_ctx, &domain, &msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ out = sss_view_ldb_msg_find_attr_as_string(domain, msg, attr, NULL);
+ if (out == NULL) {
+ talloc_free(msg);
+ return ENOENT;
+ }
+
+ *_out = talloc_steal(mem_ctx, out);
+ talloc_free(msg);
+
+ if (_domain != NULL) {
+ *_domain = domain;
+ }
+
+ return EOK;
+}
+
+static errno_t
+ifp_users_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *attr,
+ const char **_out)
+{
+ struct sss_domain_info *domain;
+ const char *in_name;
+ const char *out;
+ errno_t ret;
+
+ ret = ifp_users_get_as_string(NULL, sbus_req, ifp_ctx, attr,
+ &in_name, &domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ out = ifp_format_name_attr(mem_ctx, ifp_ctx, in_name, domain);
+ talloc_free(discard_const(in_name));
+ if (out == NULL) {
+ return ENOMEM;
+ }
+
+ *_out = out;
+
+ return EOK;
+}
+
+static errno_t
+ifp_users_get_as_uint32(struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char *attr,
+ uint32_t *_out)
+{
+ struct ldb_message *msg;
+ struct sss_domain_info *domain;
+ errno_t ret;
+
+ ret = ifp_users_user_get(NULL, sbus_req, ifp_ctx, &domain, &msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_out = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, attr, 0);
+ talloc_free(msg);
+
+ return EOK;
+}
+
+struct ifp_users_user_update_groups_list_state {
+ int dummy;
+};
+
+static void ifp_users_user_update_groups_list_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_users_user_update_groups_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx)
+{
+ struct ifp_users_user_update_groups_list_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct sss_domain_info *domain;
+ struct ldb_message *user;
+ const char *username;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_users_user_update_groups_list_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ ret = ifp_users_user_get(state, sbus_req, ctx, &domain, &user);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ username = ldb_msg_find_attr_as_string(user, SYSDB_NAME, NULL);
+ if (username == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "User name is empty!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = cache_req_initgr_by_name_send(state, ctx->rctx->ev, ctx->rctx,
+ ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM, domain->name,
+ username);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_users_user_update_groups_list_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_users_user_update_groups_list_done(struct tevent_req *subreq)
+{
+ struct ifp_users_user_update_groups_list_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_users_user_update_groups_list_state);
+
+ ret = cache_req_initgr_by_name_recv(state, subreq, NULL);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_users_user_update_groups_list_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+errno_t
+ifp_users_user_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return ifp_users_get_name(mem_ctx, sbus_req, ctx, SYSDB_NAME, _out);
+}
+
+errno_t
+ifp_users_user_get_uid_number(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out)
+{
+ return ifp_users_get_as_uint32(sbus_req, ctx, SYSDB_UIDNUM, _out);
+}
+
+errno_t
+ifp_users_user_get_gid_number(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out)
+{
+ return ifp_users_get_as_uint32(sbus_req, ctx, SYSDB_GIDNUM, _out);
+}
+
+errno_t
+ifp_users_user_get_gecos(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_GECOS, _out, NULL);
+}
+
+errno_t
+ifp_users_user_get_home_directory(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_HOMEDIR, _out, NULL);
+}
+
+errno_t
+ifp_users_user_get_login_shell(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_SHELL, _out, NULL);
+}
+
+errno_t
+ifp_users_user_get_unique_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ return ifp_users_get_as_string(mem_ctx, sbus_req, ctx, SYSDB_UUID, _out, NULL);
+}
+
+errno_t
+ifp_users_user_get_groups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char ***_out)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *domain;
+ const char *username;
+ struct ldb_message *user;
+ struct ldb_result *res;
+ const char **out;
+ int num_groups;
+ gid_t gid;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = ifp_users_user_get(tmp_ctx, sbus_req, ifp_ctx, &domain, &user);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ username = ldb_msg_find_attr_as_string(user, SYSDB_NAME, NULL);
+ if (username == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "User name is empty!\n");
+ return ERR_INTERNAL;
+ }
+
+ /* Run initgroups. */
+ ret = sysdb_initgroups_with_views(tmp_ctx, domain, username, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get groups for %s@%s [%d]: %s\n",
+ username, domain->name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (res->count == 0) {
+ *_out = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ out = talloc_zero_array(tmp_ctx, const char *, res->count + 1);
+ if (out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_groups = 0;
+ for (i = 0; i < res->count; i++) {
+ gid = sss_view_ldb_msg_find_attr_as_uint64(domain, res->msgs[i],
+ SYSDB_GIDNUM, 0);
+ if (gid == 0 && domain->type == DOM_TYPE_POSIX) {
+ continue;
+ }
+
+ out[num_groups] = ifp_groups_build_path_from_msg(out,
+ domain,
+ res->msgs[i]);
+ if (out[num_groups] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "ifp_groups_build_path() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_groups++;
+ }
+
+ *_out = talloc_steal(mem_ctx, out);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+ifp_users_user_get_domainname(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char **_out)
+{
+ struct sss_domain_info *domain;
+ errno_t ret;
+
+ ret = ifp_users_user_get(mem_ctx, sbus_req, ifp_ctx, &domain, NULL);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_out = domain->name;
+
+ return EOK;
+}
+
+errno_t
+ifp_users_user_get_domain(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out)
+{
+ const char *name;
+ const char *out;
+ errno_t ret;
+
+ ret = ifp_users_user_get_domainname(NULL, sbus_req, ctx, &name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ out = sbus_opath_compose(mem_ctx, IFP_PATH_DOMAINS, name);
+ if (out == NULL) {
+ return ENOMEM;
+ }
+
+ *_out = out;
+
+ return EOK;
+}
+
+errno_t
+ifp_users_user_get_extra_attributes(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ hash_table_t **_out)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *domain;
+ struct ldb_message *base_user;
+ const char *name;
+ struct ldb_message **user;
+ struct ldb_message_element *el;
+ struct ldb_dn *basedn;
+ size_t count;
+ const char *filter;
+ const char **extra;
+ hash_table_t *table;
+ hash_key_t key;
+ hash_value_t value;
+ const char **values;
+ errno_t ret;
+ int hret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ extra = ifp_get_user_extra_attributes(tmp_ctx, ifp_ctx);
+ if (extra == NULL || extra[0] == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "No extra attributes to return\n");
+ *_out = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ ret = ifp_users_user_get(tmp_ctx, sbus_req, ifp_ctx, &domain, &base_user);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ basedn = sysdb_user_base_dn(tmp_ctx, domain);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_user_base_dn() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name = ldb_msg_find_attr_as_string(base_user, SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))",
+ SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS,
+ SYSDB_NAME, name);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, basedn,
+ LDB_SCOPE_SUBTREE, filter,
+ extra, &count, &user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (count == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "User %s not found!\n", name);
+ ret = ENOENT;
+ goto done;
+ } else if (count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More than one entry found!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = sss_hash_create(tmp_ctx, 0, &table);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table!\n");
+ goto done;
+ }
+
+ /* Read each extra attribute. */
+ for (i = 0; extra[i] != NULL; i++) {
+ el = ldb_msg_find_element(user[0], extra[i]);
+ if (el == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Attribute %s not found, skipping...\n",
+ extra[i]);
+ continue;
+ }
+
+ values = sss_ldb_el_to_string_list(table, el);
+ if (values == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldb_el_to_string_list() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ key.type = HASH_KEY_STRING;
+ key.str = talloc_strdup(table, extra[i]);
+ if (key.str == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = values;
+
+ hret = hash_enter(table, &key, &value);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to insert entry "
+ "into hash table: %d\n", hret);
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ *_out = talloc_steal(mem_ctx, table);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+ifp_cache_list_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out)
+{
+ return ifp_cache_list(mem_ctx, ctx, IFP_CACHE_USER, _out);
+}
+
+errno_t
+ifp_cache_list_by_domain_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char ***_out)
+{
+ return ifp_cache_list_by_domain(mem_ctx, ctx, domain, IFP_CACHE_USER, _out);
+}
+
+errno_t
+ifp_cache_object_store_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result)
+{
+ struct sss_domain_info *domain;
+ struct ldb_message *user;
+ errno_t ret;
+
+ ret = ifp_users_user_get(NULL, sbus_req, ctx, &domain, &user);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = ifp_cache_object_store(domain, user->dn);
+ talloc_free(user);
+
+ if (ret == EOK) {
+ *_result = true;
+ }
+
+ return ret;
+}
+
+errno_t
+ifp_cache_object_remove_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result)
+{
+ struct sss_domain_info *domain;
+ struct ldb_message *user;
+ errno_t ret;
+
+ ret = ifp_users_user_get(NULL, sbus_req, ctx, &domain, &user);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = ifp_cache_object_remove(domain, user->dn);
+ talloc_free(user);
+
+ if (ret == EOK) {
+ *_result = true;
+ }
+
+ return ret;
+}
+
+struct tevent_req *
+ifp_users_list_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *filter,
+ uint32_t limit)
+{
+ return ifp_users_list_by_attr_send(mem_ctx, ev, sbus_req, ctx, NULL,
+ filter, limit);
+}
diff --git a/src/responder/ifp/ifp_users.h b/src/responder/ifp/ifp_users.h
new file mode 100644
index 0000000..f0f725c
--- /dev/null
+++ b/src/responder/ifp/ifp_users.h
@@ -0,0 +1,254 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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 IFP_USERS_H_
+#define IFP_USERS_H_
+
+#include "responder/ifp/ifp_private.h"
+
+/* Utility functions */
+
+char * ifp_users_build_path_from_msg(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg);
+
+/* org.freedesktop.sssd.infopipe.Users */
+
+struct tevent_req *
+ifp_users_find_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name);
+
+errno_t
+ifp_users_find_by_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+struct tevent_req *
+ifp_users_find_by_id_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t id);
+
+errno_t
+ifp_users_find_by_id_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+struct tevent_req *
+ifp_users_find_by_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *pem_cert);
+
+errno_t
+ifp_users_find_by_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+struct tevent_req *
+ifp_users_list_by_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *pem_cert,
+ uint32_t limit);
+
+errno_t
+ifp_users_list_by_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths);
+
+struct tevent_req *
+ifp_users_find_by_name_and_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char *pem_cert);
+
+errno_t
+ifp_users_find_by_name_and_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+struct tevent_req *
+ifp_users_list_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *filter,
+ uint32_t limit);
+
+errno_t
+ifp_users_list_by_attr_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths);
+
+struct tevent_req *
+ifp_users_list_by_domain_and_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char *filter,
+ uint32_t limit);
+
+errno_t
+ifp_users_list_by_domain_and_name_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths);
+
+struct tevent_req *
+ifp_users_find_by_valid_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *pem_cert);
+
+errno_t
+ifp_users_find_by_valid_cert_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_path);
+
+/* org.freedesktop.sssd.infopipe.Users.User */
+
+struct tevent_req *
+ifp_users_user_update_groups_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx);
+
+errno_t
+ifp_users_user_update_groups_list_recv(struct tevent_req *req);
+
+errno_t
+ifp_users_user_get_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_users_user_get_uid_number(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out);
+
+errno_t
+ifp_users_user_get_gid_number(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ uint32_t *_out);
+
+errno_t
+ifp_users_user_get_gecos(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_users_user_get_home_directory(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_users_user_get_login_shell(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_users_user_get_unique_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_users_user_get_groups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char ***_out);
+
+errno_t
+ifp_users_user_get_domain(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char **_out);
+
+errno_t
+ifp_users_user_get_domainname(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ const char **_out);
+
+errno_t
+ifp_users_user_get_extra_attributes(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx,
+ hash_table_t **_out);
+
+/* org.freedesktop.sssd.infopipe.Cache */
+
+errno_t
+ifp_cache_list_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char ***_out);
+
+errno_t
+ifp_cache_list_by_domain_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *domain,
+ const char ***_out);
+
+/* org.freedesktop.sssd.infopipe.Cache.Object */
+
+errno_t
+ifp_cache_object_store_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result);
+
+errno_t
+ifp_cache_object_remove_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ bool *_result);
+
+struct tevent_req *
+ifp_users_list_by_attr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *attr,
+ const char *filter,
+ uint32_t limit);
+
+errno_t
+ifp_users_list_by_attr_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_paths);
+#endif /* IFP_USERS_H_ */
diff --git a/src/responder/ifp/ifpsrv.c b/src/responder/ifp/ifpsrv.c
new file mode 100644
index 0000000..aaf8325
--- /dev/null
+++ b/src/responder/ifp/ifpsrv.c
@@ -0,0 +1,363 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2013 Red Hat
+
+ InfoPipe responder: the responder server
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <popt.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "confdb/confdb.h"
+#include "responder/ifp/ifp_private.h"
+#include "responder/ifp/ifp_domains.h"
+#include "responder/ifp/ifp_components.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+#include "sss_iface/sss_iface_async.h"
+
+#define DEFAULT_ALLOWED_UIDS "0"
+
+struct sss_cmd_table *get_ifp_cmds(void)
+{
+ static struct sss_cmd_table ifp_cmds[] = {
+ { SSS_GET_VERSION, sss_cmd_get_version },
+ { SSS_CLI_NULL, NULL}
+ };
+
+ return ifp_cmds;
+}
+
+static errno_t
+sysbus_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *dbus_name,
+ struct ifp_ctx *ifp_ctx,
+ struct sbus_connection **_sysbus)
+{
+ struct sbus_connection *sysbus;
+ errno_t ret;
+
+ sysbus = sbus_connect_system(mem_ctx, ev, dbus_name,
+ &ifp_ctx->rctx->last_request_time);
+ if (sysbus == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to connect to system bus!\n");
+ return ERR_NO_SYSBUS;
+ }
+
+ sbus_connection_set_access_check(sysbus, ifp_access_check, ifp_ctx);
+
+ ret = ifp_register_sbus_interface(sysbus, ifp_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not register interfaces\n");
+ goto done;
+ }
+
+ ret = ifp_register_nodes(ifp_ctx, sysbus);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not register nodes factories\n");
+ goto done;
+ }
+
+ *_sysbus = sysbus;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(sysbus);
+ }
+
+ return ret;
+}
+
+static errno_t
+ifp_sysbus_reconnect(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ifp_ctx)
+{
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Attempting to reconnect to the system bus\n");
+
+ if (ifp_ctx->sysbus != NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Already connected to sysbus\n");
+ return EOK;
+ }
+
+ /* Connect to the D-BUS system bus and set up methods */
+ ret = sysbus_init(ifp_ctx, ifp_ctx->rctx->ev, IFP_BUS,
+ ifp_ctx, &ifp_ctx->sysbus);
+ if (ret == ERR_NO_SYSBUS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The system bus is not available..\n");
+ return ret;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to connect to the system message bus\n");
+ return ret;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Reconnected to the system bus!\n");
+
+ return EOK;
+}
+
+static errno_t
+ifp_register_service_iface(struct ifp_ctx *ifp_ctx,
+ struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface_svc,
+ sssd_service,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_service, rotateLogs, responder_logrotate, rctx),
+ SBUS_SYNC(METHOD, sssd_service, sysbusReconnect, ifp_sysbus_reconnect, ifp_ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, sssd_service, debug_level, generic_get_debug_level, NULL),
+ SBUS_SYNC(SETTER, sssd_service, debug_level, generic_set_debug_level, NULL)
+ )
+ );
+
+ ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+int ifp_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *ifp_cmds;
+ struct ifp_ctx *ifp_ctx;
+ int ret;
+ char *uid_str;
+ char *attr_list_str;
+ char *wildcard_limit_str;
+ char *endptr;
+
+ ifp_cmds = get_ifp_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ ifp_cmds,
+ NULL, -1, NULL, -1,
+ CONFDB_IFP_CONF_ENTRY,
+ SSS_BUS_IFP, SSS_IFP_SBUS_SERVICE_NAME,
+ sss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ ifp_ctx = talloc_zero(rctx, struct ifp_ctx);
+ if (ifp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing ifp_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ifp_ctx->rctx = rctx;
+ ifp_ctx->rctx->pvt_ctx = ifp_ctx;
+
+ ret = sss_names_init_from_args(ifp_ctx,
+ SSS_DEFAULT_RE,
+ "%1$s@%2$s", &ifp_ctx->snctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing regex data\n");
+ goto fail;
+ }
+
+ ret = confdb_get_string(ifp_ctx->rctx->cdb, ifp_ctx->rctx,
+ CONFDB_IFP_CONF_ENTRY, CONFDB_SERVICE_ALLOWED_UIDS,
+ DEFAULT_ALLOWED_UIDS, &uid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get allowed UIDs.\n");
+ goto fail;
+ }
+
+ ret = csv_string_to_uid_array(ifp_ctx->rctx, uid_str,
+ &ifp_ctx->rctx->allowed_uids_count,
+ &ifp_ctx->rctx->allowed_uids);
+ talloc_free(uid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set allowed UIDs.\n");
+ goto fail;
+ }
+
+ ret = confdb_get_string(ifp_ctx->rctx->cdb, ifp_ctx->rctx,
+ CONFDB_IFP_CONF_ENTRY, CONFDB_IFP_USER_ATTR_LIST,
+ NULL, &attr_list_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get user attribute list.\n");
+ goto fail;
+ }
+
+ ifp_ctx->user_whitelist = ifp_parse_user_attr_list(ifp_ctx, attr_list_str);
+ talloc_free(attr_list_str);
+ if (ifp_ctx->user_whitelist == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to parse the allowed attribute list\n");
+ goto fail;
+ }
+
+ /* A bit convoluted way until we have a confdb_get_uint32 */
+ ret = confdb_get_string(ifp_ctx->rctx->cdb,
+ ifp_ctx->rctx,
+ CONFDB_IFP_CONF_ENTRY,
+ CONFDB_IFP_WILDCARD_LIMIT,
+ NULL, /* no limit by default */
+ &wildcard_limit_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to retrieve limit for a wildcard search\n");
+ goto fail;
+ }
+
+ if (wildcard_limit_str) {
+ ifp_ctx->wildcard_limit = strtouint32(wildcard_limit_str, &endptr, 10);
+ if ((errno != 0) || *endptr || (wildcard_limit_str == endptr)) {
+ ret = errno ? errno : EINVAL;
+ goto fail;
+ }
+ }
+
+ /* Connect to the D-BUS system bus and set up methods */
+ ret = sysbus_init(ifp_ctx, ifp_ctx->rctx->ev, IFP_BUS,
+ ifp_ctx, &ifp_ctx->sysbus);
+ if (ret == ERR_NO_SYSBUS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The system bus is not available..\n");
+ /* Explicitly ignore, the D-Bus daemon will start us */
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to connect to the system message bus\n");
+ talloc_free(ifp_ctx);
+ return EIO;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_IFP,
+ SSS_IFP_SBUS_SERVICE_NAME,
+ SSS_IFP_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = ifp_register_service_iface(ifp_ctx, rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "InfoPipe Initialization complete\n");
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_ifp";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ ret = server_setup("ifp", true, 0, 0, 0,
+ CONFDB_IFP_CONF_ENTRY, &main_ctx, true);
+ if (ret != EOK) return 2;
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not set up to exit when parent process does\n");
+ }
+
+ ret = ifp_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) return 3;
+
+ /* loop on main */
+ server_loop(main_ctx);
+ return 0;
+}
diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
new file mode 100644
index 0000000..8cf1ec8
--- /dev/null
+++ b/src/responder/ifp/ifpsrv_cmd.c
@@ -0,0 +1,631 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2013 Red Hat
+
+ InfoPipe responder: the responder commands
+
+ 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 "db/sysdb.h"
+
+#include "responder/ifp/ifp_private.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ifp/ifp_iface/ifp_iface_async.h"
+
+struct ifp_user_get_attr_state {
+ const char **attrs;
+ struct ldb_result *res;
+
+ enum sss_dp_acct_type search_type;
+
+ struct sss_domain_info *dom;
+
+ struct resp_ctx *rctx;
+ struct sss_nc_ctx *ncache;
+};
+
+static void ifp_user_get_attr_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ifp_user_get_attr_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache,
+ enum sss_dp_acct_type search_type,
+ const char *input, const char **attrs)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct ifp_user_get_attr_state *state;
+ struct cache_req_data *data;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_user_get_attr_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->attrs = attrs;
+ state->rctx = rctx;
+ state->ncache = ncache;
+ state->search_type = search_type;
+
+ switch (state->search_type) {
+ case SSS_DP_USER:
+ data = cache_req_data_name(state, CACHE_REQ_USER_BY_NAME, input);
+ break;
+ case SSS_DP_INITGROUPS:
+ data = cache_req_data_name(state, CACHE_REQ_INITGROUPS, input);
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported search type [%d]!\n",
+ state->search_type);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* IFP serves both POSIX and application domains. Requests that need
+ * to differentiate between the two must be qualified
+ */
+ subreq = cache_req_send(state, state->rctx->ev, state->rctx, state->ncache,
+ 0, CACHE_REQ_ANY_DOM, NULL, data);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_user_get_attr_done, req);
+
+ ret = EOK;
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, rctx->ev);
+ }
+ return req;
+}
+
+static void ifp_user_get_attr_done(struct tevent_req *subreq)
+{
+ struct ifp_user_get_attr_state *state = NULL;
+ struct tevent_req *req = NULL;
+ struct cache_req_result *result;
+ errno_t ret;
+ const char *fqdn;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_user_get_attr_state);
+
+ ret = cache_req_single_domain_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->res = talloc_steal(state, result->ldb_result);
+ state->dom = result->domain;
+ talloc_zfree(result);
+
+ fqdn = ldb_msg_find_attr_as_string(state->res->msgs[0], SYSDB_NAME, NULL);
+ if (fqdn == NULL) {
+ tevent_req_error(req, ERR_INTERNAL);
+ return;
+ }
+
+ if (state->search_type == SSS_DP_USER) {
+ /* throw away the result but keep the fqdn and perform attr search */
+ fqdn = talloc_steal(state, fqdn);
+ talloc_zfree(state->res);
+
+ ret = sysdb_get_user_attr_with_views(state, state->dom, fqdn,
+ state->attrs, &state->res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_get_user_attr_with_views() "
+ "failed [%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ } else if (state->res->count == 0) {
+ tevent_req_error(req, ENOENT);
+ return;
+ } else if (state->res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_get_user_attr_with_views() "
+ "returned more than one result!\n");
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t
+ifp_user_get_attr_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ldb_result **_res,
+ struct sss_domain_info **_domain)
+{
+ struct ifp_user_get_attr_state *state = tevent_req_data(req,
+ struct ifp_user_get_attr_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (state->res == NULL) {
+ /* Did the request end with success but with no data? */
+ return ENOENT;
+ }
+
+ if (_res) {
+ *_res = talloc_steal(mem_ctx, state->res);
+ }
+
+ if (_domain) {
+ *_domain = state->dom;
+ }
+
+ return EOK;
+}
+
+static errno_t
+ifp_get_user_attr_write_reply(DBusMessageIter *iter,
+ const char **attrs,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct ldb_result *res)
+{
+ struct ldb_message_element *el;
+ DBusMessageIter iter_dict;
+ dbus_bool_t dbret;
+ errno_t ret;
+ int ai;
+
+ dbret = dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &iter_dict);
+ if (!dbret) {
+ return EIO;
+ }
+
+ if (res->count > 0) {
+ ret = ifp_ldb_el_output_name(rctx, res->msgs[0], SYSDB_NAME, domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert SYSDB_NAME to output format [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = ifp_ldb_el_output_name(rctx, res->msgs[0], SYSDB_NAME_ALIAS, domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert SYSDB_NAME_ALIAS to output format [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (ai = 0; attrs != NULL && attrs[ai] != NULL; ai++) {
+ if (strcmp(attrs[ai], "domainname") == 0) {
+ ret = ifp_add_value_to_dict(&iter_dict, "domainname",
+ domain->name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot add attribute domainname to message\n");
+ continue;
+ }
+ }
+
+ el = sss_view_ldb_msg_find_element(domain, res->msgs[0], attrs[ai]);
+ if (el == NULL || el->num_values == 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Attribute %s not present or has no values\n",
+ attrs[ai]);
+ continue;
+ }
+
+ ret = ifp_add_ldb_el_to_dict(&iter_dict, el);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot add attribute %s to message\n",
+ attrs[ai]);
+ continue;
+ }
+ }
+ }
+
+ dbret = dbus_message_iter_close_container(iter, &iter_dict);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ dbus_message_iter_abandon_container(iter, &iter_dict);
+ }
+
+ return ret;
+}
+
+struct ifp_get_user_attr_state {
+ const char *name;
+ const char **attrs;
+ struct resp_ctx *rctx;
+
+ DBusMessageIter *write_iter;
+};
+
+static void ifp_get_user_attr_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_get_user_attr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name,
+ const char **attrs,
+ DBusMessageIter *write_iter)
+{
+ struct ifp_get_user_attr_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ DEBUG(SSSDBG_IMPORTANT_INFO, "GetUserAttr is deprecated, please consider "
+ "switching to org.freedesktop.sssd.infopipe.Users.User interface\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_get_user_attr_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->name = name;
+ state->attrs = attrs;
+ state->rctx = ctx->rctx;
+ state->write_iter = write_iter;
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Looking up attributes of user [%s] on behalf of %"PRIi64"\n",
+ state->name, sbus_req->sender->uid);
+
+ subreq = ifp_user_get_attr_send(state, ctx->rctx, ctx->rctx->ncache,
+ SSS_DP_USER, state->name, state->attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_get_user_attr_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_get_user_attr_done(struct tevent_req *subreq)
+{
+ struct ifp_get_user_attr_state *state;
+ struct sss_domain_info *dom;
+ struct ldb_result *res;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_get_user_attr_state);
+
+ ret = ifp_user_get_attr_recv(state, subreq, &res, &dom);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to get user attributes [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ifp_get_user_attr_write_reply(state->write_iter, state->attrs,
+ state->rctx, dom, res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to construct reply [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+ifp_get_user_attr_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static errno_t
+ifp_user_get_groups_build_group_list(struct resp_ctx *rctx,
+ const char *name,
+ const char **groupnames,
+ int *gri)
+{
+ struct sized_string *group_name;
+ errno_t ret;
+
+ ret = sized_domain_name(NULL, rctx, name, &group_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get sized name for %s [%d]: %s\n",
+ name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ groupnames[*gri] = talloc_strndup(groupnames,
+ group_name->str,
+ group_name->len);
+ talloc_free(group_name);
+ if (groupnames[*gri] == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "talloc_strndup failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ (*gri)++;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding group %s\n", groupnames[*gri]);
+
+done:
+ return ret;
+}
+
+static errno_t
+ifp_user_get_groups_build_reply(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ struct ldb_result *res,
+ const char ***_groupnames)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ int i, gri, num;
+ const char *name;
+ const char **groupnames;
+ gid_t orig_gid;
+ struct ldb_message *msg = NULL;
+ const char *attrs[] = {SYSDB_NAME, NULL};
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ num = res->count;
+ groupnames = talloc_zero_array(mem_ctx, const char *, num + 1);
+ if (groupnames == NULL) {
+ return ENOMEM;
+ }
+
+ gri = 0;
+ orig_gid = sss_view_ldb_msg_find_attr_as_uint64(domain,
+ res->msgs[0],
+ SYSDB_PRIMARY_GROUP_GIDNUM, 0);
+ ret = sysdb_search_group_by_gid(tmp_ctx, domain, orig_gid, attrs, &msg);
+
+ /* If origPrimaryGroupGidNumber exists add it to group list */
+ if(ret == EOK) {
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+
+ if (name != NULL) {
+ ifp_user_get_groups_build_group_list(rctx, name, groupnames, &gri);
+ }
+ }
+
+ /* Start counting from 1 to exclude the user entry */
+ for (i = 1; i < num; i++) {
+ name = sss_view_ldb_msg_find_attr_as_string(domain,
+ res->msgs[i],
+ SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Skipping a group with no name\n");
+ continue;
+ }
+
+ ifp_user_get_groups_build_group_list(rctx, name, groupnames, &gri);
+ }
+
+ *_groupnames = groupnames;
+
+ talloc_free(tmp_ctx);
+ return EOK;
+}
+
+struct ifp_user_get_groups_state {
+ struct tevent_context *ev;
+ struct resp_ctx *rctx;
+ struct ldb_result *res;
+ struct sss_domain_info *domain;
+ const char **groupnames;
+};
+
+static void ifp_user_get_groups_attr_done(struct tevent_req *subreq);
+static void ifp_user_get_groups_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ifp_user_get_groups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *name)
+{
+ const char *attrs[] = {SYSDB_MEMBEROF, NULL};
+ struct ifp_user_get_groups_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ifp_user_get_groups_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->rctx = ctx->rctx;
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Looking up groups of user [%s] on behalf of %"PRIi64"\n",
+ name, sbus_req->sender->uid);
+
+ subreq = ifp_user_get_attr_send(state, ctx->rctx, ctx->rctx->ncache,
+ SSS_DP_INITGROUPS, name, attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ifp_user_get_groups_attr_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ifp_user_get_groups_attr_done(struct tevent_req *subreq)
+{
+ struct ifp_user_get_groups_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_user_get_groups_state);
+
+ ret = ifp_user_get_attr_recv(state, subreq, &state->res, &state->domain);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to get group members [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = resp_resolve_group_names_send(state, state->ev, state->rctx,
+ state->domain, state->res);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, ifp_user_get_groups_done, req);
+}
+
+static void ifp_user_get_groups_done(struct tevent_req *subreq)
+{
+ struct ifp_user_get_groups_state *state;
+ struct ldb_result *res;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct ifp_user_get_groups_state);
+
+ ret = resp_resolve_group_names_recv(state, subreq, &res);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to resolve group names [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ res = res == NULL ? state->res : res;
+
+ ret = ifp_user_get_groups_build_reply(state, state->rctx, state->domain,
+ res, &state->groupnames);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to construct reply [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t
+ifp_user_get_groups_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ***_groupnames)
+{
+ struct ifp_user_get_groups_state *state;
+ state = tevent_req_data(req, struct ifp_user_get_groups_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_groupnames = talloc_steal(mem_ctx, state->groupnames);
+
+ return EOK;
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version ssh_cli_protocol_version[] = {
+ {0, NULL, NULL}
+ };
+
+ return ssh_cli_protocol_version;
+}
+
+errno_t
+ifp_ping(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct ifp_ctx *ctx,
+ const char *ping,
+ const char **_pong)
+{
+ DEBUG(SSSDBG_CONF_SETTINGS, "Got request for [%s]\n", ping);
+
+ if (strcasecmp(ping, "ping") != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Ping() only accepts \"ping\" as a param\n");
+ return EINVAL;
+ }
+
+ *_pong = "PONG";
+
+ return EOK;
+}
diff --git a/src/responder/ifp/ifpsrv_util.c b/src/responder/ifp/ifpsrv_util.c
new file mode 100644
index 0000000..c21ceef
--- /dev/null
+++ b/src/responder/ifp/ifpsrv_util.c
@@ -0,0 +1,455 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2013 Red Hat
+
+ InfoPipe responder: Utility functions
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/param.h>
+
+#include "db/sysdb.h"
+#include "responder/ifp/ifp_private.h"
+
+#define IFP_USER_DEFAULT_ATTRS {SYSDB_NAME, SYSDB_UIDNUM, \
+ SYSDB_GIDNUM, SYSDB_GECOS, \
+ SYSDB_HOMEDIR, SYSDB_SHELL, \
+ "groups", "domain", "domainname", \
+ "extraAttributes", NULL}
+
+errno_t ifp_add_value_to_dict(DBusMessageIter *iter_dict,
+ const char *key,
+ const char *value)
+{
+ DBusMessageIter iter_dict_entry;
+ DBusMessageIter iter_dict_val;
+ DBusMessageIter iter_array;
+ dbus_bool_t dbret;
+
+ if (value == NULL || key == NULL) {
+ return EINVAL;
+ }
+
+ dbret = dbus_message_iter_open_container(iter_dict,
+ DBUS_TYPE_DICT_ENTRY, NULL,
+ &iter_dict_entry);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ /* Start by appending the key */
+ dbret = dbus_message_iter_append_basic(&iter_dict_entry,
+ DBUS_TYPE_STRING, &key);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_open_container(&iter_dict_entry,
+ DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING,
+ &iter_dict_val);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ /* Open container for values */
+ dbret = dbus_message_iter_open_container(&iter_dict_val,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING,
+ &iter_array);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_append_basic(&iter_array,
+ DBUS_TYPE_STRING,
+ &value);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_close_container(&iter_dict_val,
+ &iter_array);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_close_container(&iter_dict_entry,
+ &iter_dict_val);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_close_container(iter_dict,
+ &iter_dict_entry);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict,
+ struct ldb_message_element *el)
+{
+ DBusMessageIter iter_dict_entry;
+ DBusMessageIter iter_dict_val;
+ DBusMessageIter iter_array;
+ dbus_bool_t dbret;
+ unsigned int i;
+
+ if (el == NULL) {
+ return EINVAL;
+ }
+
+ dbret = dbus_message_iter_open_container(iter_dict,
+ DBUS_TYPE_DICT_ENTRY, NULL,
+ &iter_dict_entry);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ /* Start by appending the key */
+ dbret = dbus_message_iter_append_basic(&iter_dict_entry,
+ DBUS_TYPE_STRING, &(el->name));
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_open_container(&iter_dict_entry,
+ DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING,
+ &iter_dict_val);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ /* Open container for values */
+ dbret = dbus_message_iter_open_container(&iter_dict_val,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING,
+ &iter_array);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ /* Now add all the values */
+ for (i = 0; i < el->num_values; i++) {
+ DEBUG(SSSDBG_TRACE_FUNC, "element [%s] has value [%s]\n",
+ el->name, (const char *) el->values[i].data);
+
+ dbret = dbus_message_iter_append_basic(&iter_array,
+ DBUS_TYPE_STRING,
+ &(el->values[i].data));
+ if (!dbret) {
+ return ENOMEM;
+ }
+ }
+
+ dbret = dbus_message_iter_close_container(&iter_dict_val,
+ &iter_array);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_close_container(&iter_dict_entry,
+ &iter_dict_val);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ dbret = dbus_message_iter_close_container(iter_dict,
+ &iter_dict_entry);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+
+bool
+ifp_attr_allowed(const char *whitelist[], const char *attr)
+{
+ size_t i;
+
+ if (whitelist == NULL) {
+ return false;
+ }
+
+ for (i = 0; whitelist[i]; i++) {
+ if (strcasecmp(whitelist[i], attr) == 0) {
+ break;
+ }
+ }
+
+ return (whitelist[i]) ? true : false;
+}
+
+const char **
+ifp_parse_user_attr_list(TALLOC_CTX *mem_ctx, const char *csv)
+{
+ static const char *defaults[] = IFP_USER_DEFAULT_ATTRS;
+
+ return parse_attr_list_ex(mem_ctx, csv, defaults);
+}
+
+const char **
+ifp_get_user_extra_attributes(TALLOC_CTX *mem_ctx, struct ifp_ctx *ifp_ctx)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *std[] = IFP_USER_DEFAULT_ATTRS;
+ const char **whitelist = ifp_ctx->user_whitelist;
+ const char **extra;
+ bool found;
+ int extra_num;
+ int i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return NULL;
+ }
+
+ for (i = 0; whitelist[i] != NULL; i++) {
+ /* Just count number of attributes in whitelist. */
+ }
+
+ extra = talloc_zero_array(tmp_ctx, const char *, i + 1);
+ if (extra == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+ goto fail;
+ }
+
+ extra_num = 0;
+ for (i = 0; whitelist[i] != NULL; i++) {
+ found = false;
+ for (j = 0; std[j] != NULL; j++) {
+ if (strcmp(whitelist[i], std[j]) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ extra[extra_num] = talloc_strdup(extra, whitelist[i]);
+ if (extra[extra_num] == NULL) {
+ goto fail;
+ }
+
+ extra_num++;
+ }
+ }
+
+ extra = talloc_realloc(tmp_ctx, extra, const char *, extra_num + 1);
+ if (extra == NULL) {
+ goto fail;
+ }
+
+ talloc_steal(mem_ctx, extra);
+ talloc_free(tmp_ctx);
+ return extra;
+
+fail:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+bool
+ifp_is_user_attr_allowed(struct ifp_ctx *ifp_ctx, const char *attr)
+{
+ return ifp_attr_allowed(ifp_ctx->user_whitelist, attr);
+}
+
+static uint32_t ifp_list_limit(struct ifp_ctx *ctx, uint32_t limit)
+{
+ if (limit == 0) {
+ return ctx->wildcard_limit;
+ } else if (ctx->wildcard_limit) {
+ return MIN(ctx->wildcard_limit, limit);
+ } else {
+ return limit;
+ }
+}
+
+struct ifp_list_ctx *ifp_list_ctx_new(TALLOC_CTX *mem_ctx,
+ struct ifp_ctx *ctx,
+ const char *attr,
+ const char *filter,
+ uint32_t limit)
+{
+ struct ifp_list_ctx *list_ctx;
+
+ list_ctx = talloc_zero(mem_ctx, struct ifp_list_ctx);
+ if (list_ctx == NULL) {
+ return NULL;
+ }
+
+ list_ctx->limit = ifp_list_limit(ctx, limit);
+ list_ctx->ctx = ctx;
+ list_ctx->dom = ctx->rctx->domains;
+ list_ctx->attr = attr;
+ list_ctx->filter = filter;
+ list_ctx->paths_max = 1;
+ list_ctx->paths = talloc_zero_array(list_ctx, const char *,
+ list_ctx->paths_max + 1);
+ if (list_ctx->paths == NULL) {
+ talloc_free(list_ctx);
+ return NULL;
+ }
+
+ return list_ctx;
+}
+
+errno_t ifp_list_ctx_remaining_capacity(struct ifp_list_ctx *list_ctx,
+ size_t entries,
+ size_t *_capacity)
+{
+ size_t capacity = list_ctx->limit - list_ctx->path_count;
+ errno_t ret;
+ size_t c;
+
+ if (list_ctx->limit == 0) {
+ capacity = entries;
+ goto immediately;
+ }
+
+ if (capacity < entries) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "IFP list request has limit of %"PRIu32" entries but back end "
+ "returned %zu entries\n", list_ctx->limit,
+ list_ctx->path_count + entries);
+ } else {
+ capacity = entries;
+ }
+
+immediately:
+ list_ctx->paths_max = list_ctx->path_count + capacity;
+ list_ctx->paths = talloc_realloc(list_ctx, list_ctx->paths, const char *,
+ list_ctx->paths_max + 1);
+ if (list_ctx->paths == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ for (c = list_ctx->path_count; c <= list_ctx->paths_max; c++) {
+ list_ctx->paths[c] = NULL;
+ }
+
+ *_capacity = capacity;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+errno_t ifp_ldb_el_output_name(struct resp_ctx *rctx,
+ struct ldb_message *msg,
+ const char *el_name,
+ struct sss_domain_info *dom)
+{
+ struct ldb_message_element *el;
+ char *in_name;
+ char *out_name;
+ errno_t ret;
+ char *name;
+ TALLOC_CTX *tmp_ctx;
+
+ el = ldb_msg_find_element(msg, el_name);
+ if (el == NULL) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ for (size_t c = 0; c < el->num_values; c++) {
+ in_name = (char *) el->values[c].data;
+ ret = sss_parse_internal_fqname(tmp_ctx, in_name, &name, NULL);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ out_name = sss_output_name(tmp_ctx, in_name, dom->case_preserve,
+ rctx->override_space);
+ if (out_name == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ if (dom->fqnames) {
+ out_name = sss_tc_fqname(tmp_ctx, dom->names, dom, out_name);
+ if (out_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ talloc_free(el->values[c].data);
+ el->values[c].data = (uint8_t *) talloc_steal(el->values, out_name);
+ el->values[c].length = strlen(out_name);
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+char *ifp_format_name_attr(TALLOC_CTX *mem_ctx, struct ifp_ctx *ifp_ctx,
+ const char *in_name, struct sss_domain_info *dom)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *out_name;
+ char *ret_name = NULL;
+ char *shortname;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ ret = sss_parse_internal_fqname(tmp_ctx, in_name, &shortname, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unparseable name %s\n", in_name);
+ goto done;
+ }
+
+ out_name = sss_output_name(tmp_ctx, in_name, dom->case_preserve,
+ ifp_ctx->rctx->override_space);
+ if (out_name == NULL) {
+ goto done;
+ }
+
+ if (dom->fqnames) {
+ out_name = sss_tc_fqname(tmp_ctx, dom->names, dom, out_name);
+ if (out_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname failed\n");
+ goto done;
+ }
+ }
+
+ ret_name = talloc_steal(mem_ctx, out_name);
+done:
+ talloc_free(tmp_ctx);
+ return ret_name;
+}
diff --git a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
new file mode 100644
index 0000000..4437fb3
--- /dev/null
+++ b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
@@ -0,0 +1,47 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+ <!-- This configuration file specifies the required security policies
+ for the SSSD InfoPipe to work. -->
+
+ <!-- Only root can own (provide) the SSSD service -->
+ <policy user="root">
+ <allow own="org.freedesktop.sssd.infopipe"/>
+ </policy>
+
+ <!-- Allow all methods on the interface -->
+ <!-- Right now, this will be handled by a limited ACL
+ within the InfoPipe Daemon. -->
+ <policy context="default">
+ <allow send_destination="org.freedesktop.sssd.infopipe"
+ send_interface="org.freedesktop.DBus.Introspectable"/>
+
+ <allow send_destination="org.freedesktop.sssd.infopipe"
+ send_interface="org.freedesktop.DBus.Properties"
+ send_member="GetAll"/>
+ <allow send_destination="org.freedesktop.sssd.infopipe"
+ send_interface="org.freedesktop.DBus.Properties"
+ send_member="Get"/>
+ <allow send_destination="org.freedesktop.sssd.infopipe"
+ send_interface="org.freedesktop.DBus.Properties"
+ send_member="Set"/>
+
+ <allow send_interface="org.freedesktop.sssd.infopipe"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Domains"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Domains.Domain"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Users"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Users.User"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Groups"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Groups.Group"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Cache"/>
+ <allow send_interface="org.freedesktop.sssd.infopipe.Cache.Object"/>
+ </policy>
+
+ <policy user="root">
+ <allow send_interface="org.freedesktop.sssd.infopipe.Components"/>
+ </policy>
+
+</busconfig>
diff --git a/src/responder/ifp/org.freedesktop.sssd.infopipe.service b/src/responder/ifp/org.freedesktop.sssd.infopipe.service
new file mode 100644
index 0000000..b5d5435
--- /dev/null
+++ b/src/responder/ifp/org.freedesktop.sssd.infopipe.service
@@ -0,0 +1,6 @@
+[D-BUS Service]
+Name=org.freedesktop.sssd.infopipe
+# "sss_signal" is used to force SSSD monitor to trigger "sssd_ifp" reconnection to dbus
+Exec=/usr/local/libexec/sssd/sss_signal
+User=root
+
diff --git a/src/responder/ifp/org.freedesktop.sssd.infopipe.service.in b/src/responder/ifp/org.freedesktop.sssd.infopipe.service.in
new file mode 100644
index 0000000..ca06cb1
--- /dev/null
+++ b/src/responder/ifp/org.freedesktop.sssd.infopipe.service.in
@@ -0,0 +1,6 @@
+[D-BUS Service]
+Name=org.freedesktop.sssd.infopipe
+@ifp_dbus_exec_comment@
+Exec=@ifp_exec_cmd@
+User=root
+@ifp_systemdservice@
diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
new file mode 100644
index 0000000..f61d478
--- /dev/null
+++ b/src/responder/kcm/kcm.c
@@ -0,0 +1,374 @@
+/*
+ SSSD
+
+ KCM Server - the mainloop and server setup
+
+ Copyright (C) Red Hat, 2016
+
+ 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 "config.h"
+
+#include <popt.h>
+
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcm_renew.h"
+#include "responder/common/responder.h"
+#include "providers/krb5/krb5_common.h"
+#include "util/util.h"
+#include "util/sss_krb5.h"
+
+#define DEFAULT_KCM_FD_LIMIT 2048
+#define DEFAULT_KCM_CLI_IDLE_TIMEOUT 300
+
+#ifndef SSS_KCM_SOCKET_NAME
+#define SSS_KCM_SOCKET_NAME DEFAULT_KCM_SOCKET_PATH
+#endif
+
+static int kcm_responder_ctx_destructor(void *ptr)
+{
+ struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx);
+
+ /* mark that we are shutting down the responder, so it is propagated
+ * into underlying contexts that are freed right before rctx */
+ DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n");
+ rctx->shutting_down = true;
+
+ return 0;
+}
+
+static errno_t kcm_renewals_init(struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct kcm_ctx *kctx,
+ struct krb5_ctx *krb5_ctx,
+ time_t renew_intv,
+ bool *_renewal_enabled)
+{
+#ifndef HAVE_KCM_RENEWAL
+ return EOK;
+#else
+ errno_t ret;
+
+ ret = kcm_get_renewal_config(kctx, &krb5_ctx, &renew_intv);
+ if (ret == ENOTSUP) {
+ DEBUG(SSSDBG_TRACE_FUNC, "TGT Renewals support disabled\n");
+ return EOK;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read TGT renewal configuration [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (renew_intv > 0) {
+ *_renewal_enabled = true;
+
+ ret = kcm_renewal_setup(rctx, krb5_ctx, ev, kctx->kcm_data->db, renew_intv);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to setup TGT renewals [%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ return EOK;
+#endif
+}
+
+static errno_t kcm_get_ccdb_be(struct kcm_ctx *kctx)
+{
+ errno_t ret;
+ char *str_db;
+
+ ret = confdb_get_string(kctx->rctx->cdb,
+ kctx->rctx,
+ kctx->rctx->confdb_service_path,
+ CONFDB_KCM_DB,
+ "secdb",
+ &str_db);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the KCM database type [%d]: %s\n",
+ ret, strerror(ret));
+ return ret;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "KCM database type: %s\n", str_db);
+ if (strcasecmp(str_db, "memory") == 0) {
+ kctx->cc_be = CCDB_BE_MEMORY;
+ } else if (strcasecmp(str_db, "secdb") == 0) {
+ kctx->cc_be = CCDB_BE_SECDB;
+ } else {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected KCM database type %s\n", str_db);
+ }
+
+ return EOK;
+}
+
+static int kcm_get_config(struct kcm_ctx *kctx)
+{
+ int ret;
+ char *sock_name;
+
+ ret = confdb_get_int(kctx->rctx->cdb,
+ CONFDB_KCM_CONF_ENTRY,
+ CONFDB_SERVICE_FD_LIMIT,
+ DEFAULT_KCM_FD_LIMIT,
+ &kctx->fd_limit);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get file descriptors limit\n");
+ goto done;
+ }
+
+ ret = confdb_get_int(kctx->rctx->cdb,
+ kctx->rctx->confdb_service_path,
+ CONFDB_RESPONDER_CLI_IDLE_TIMEOUT,
+ DEFAULT_KCM_CLI_IDLE_TIMEOUT,
+ &kctx->rctx->client_idle_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the client idle timeout [%s] [%d]: %s\n",
+ CONFDB_RESPONDER_CLI_IDLE_TIMEOUT, ret, strerror(ret));
+ goto done;
+ }
+
+ /* Ensure that the client timeout is at least ten seconds */
+ if (kctx->rctx->client_idle_timeout < 10) {
+ kctx->rctx->client_idle_timeout = 10;
+ }
+
+ ret = confdb_get_string(kctx->rctx->cdb,
+ kctx->rctx,
+ kctx->rctx->confdb_service_path,
+ CONFDB_KCM_SOCKET,
+ SSS_KCM_SOCKET_NAME,
+ &sock_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get KCM socket path [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ kctx->rctx->sock_name = sock_name;
+
+ ret = kcm_get_ccdb_be(kctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get KCM ccache DB [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ kctx->qctx = kcm_ops_queue_create(kctx, kctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot create KCM request queue [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ ret = EOK;
+done:
+ return ret;
+}
+
+static int kcm_data_destructor(void *ptr)
+{
+ struct kcm_resp_ctx *kcm_data = talloc_get_type(ptr, struct kcm_resp_ctx);
+
+ if (kcm_data != NULL) {
+ krb5_free_context(kcm_data->k5c);
+ }
+ return 0;
+}
+
+static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ const char *confdb_service_path,
+ enum kcm_ccdb_be cc_be)
+{
+ struct kcm_resp_ctx *kcm_data;
+ krb5_error_code kret;
+
+ kcm_data = talloc_zero(mem_ctx, struct kcm_resp_ctx);
+ if (kcm_data == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm data\n");
+ return NULL;
+ }
+
+ kcm_data->db = kcm_ccdb_init(kcm_data,
+ ev,
+ cdb,
+ confdb_service_path,
+ cc_be);
+ if (kcm_data->db == NULL) {
+ talloc_free(kcm_data);
+ return NULL;
+ }
+
+ kret = sss_krb5_init_context(&kcm_data->k5c);
+ if (kret != EOK) {
+ talloc_free(kcm_data);
+ return NULL;
+ }
+ talloc_set_destructor((TALLOC_CTX*)kcm_data, kcm_data_destructor);
+
+ return kcm_data;
+}
+
+static int kcm_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct kcm_ctx *kctx;
+ bool renewal_enabled = false;
+ struct krb5_ctx *krb5_ctx = NULL;
+ time_t renew_intv = 0;
+ int ret;
+
+ rctx = talloc_zero(mem_ctx, struct resp_ctx);
+ if (rctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n");
+ return ENOMEM;
+ }
+ rctx->ev = ev;
+ rctx->cdb = cdb;
+ rctx->confdb_service_path = CONFDB_KCM_CONF_ENTRY;
+ rctx->shutting_down = false;
+ rctx->lfd = -1;
+ rctx->priv_lfd = -1;
+
+ talloc_set_destructor((TALLOC_CTX*)rctx, kcm_responder_ctx_destructor);
+
+ kctx = talloc_zero(rctx, struct kcm_ctx);
+ if (kctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ kctx->rctx = rctx;
+ kctx->rctx->pvt_ctx = kctx;
+
+ ret = kcm_get_config(kctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM config\n");
+ goto fail;
+ }
+
+ kctx->kcm_data = kcm_data_setup(kctx,
+ ev,
+ kctx->rctx->cdb,
+ kctx->rctx->confdb_service_path,
+ kctx->cc_be);
+ if (kctx->kcm_data == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "fatal error initializing responder data\n");
+ ret = EIO;
+ goto fail;
+ }
+
+ ret = kcm_renewals_init(ev, rctx, kctx, krb5_ctx, renew_intv, &renewal_enabled);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize TGT Renewals [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ if (renewal_enabled) {
+ /* Disable resp idle timeout to allow renewals */
+ rctx->idle_timeout = 0;
+ } else {
+ responder_setup_idle_timeout_config(kctx->rctx);
+ }
+
+ /* Set up file descriptor limits */
+ responder_set_fd_limit(kctx->fd_limit);
+
+ ret = activate_unix_sockets(rctx, kcm_connection_setup);
+ if (ret != EOK) goto fail;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "KCM Initialization complete\n");
+
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_kcm";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ ret = server_setup("kcm", true, 0, uid, gid, CONFDB_KCM_CONF_ENTRY,
+ &main_ctx, true);
+ if (ret != EOK) return 2;
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set up to exit when parent process does\n");
+ }
+
+ ret = kcm_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) return 3;
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
diff --git a/src/responder/kcm/kcm_renew.c b/src/responder/kcm/kcm_renew.c
new file mode 100644
index 0000000..39e9470
--- /dev/null
+++ b/src/responder/kcm/kcm_renew.c
@@ -0,0 +1,775 @@
+/*
+ SSSD
+
+ KCM Kerberos renewals -- Renew a TGT automatically
+
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Copyright (C) 2020 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 "util/util.h"
+#include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_auth.h"
+#include "providers/krb5/krb5_utils.h"
+#include "providers/krb5/krb5_ccache.h"
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcm_renew.h"
+
+extern struct dp_option default_krb5_opts[];
+
+struct kcm_renew_auth_ctx {
+ struct tevent_context *ev;
+ struct krb5child_req *kr;
+
+ struct krb5_ctx *krb5_ctx;
+ struct kcm_auth_data *auth_data;
+
+ uint8_t *buf;
+ ssize_t len;
+};
+
+struct kcm_auth_data {
+ struct kcm_renew_auth_ctx *auth_ctx;
+ struct krb5_ctx *krb5_ctx;
+ uid_t uid;
+ gid_t gid;
+ const char *ccname;
+ const char *upn;
+};
+
+static void kcm_renew_tgt_done(struct tevent_req *req);
+
+static errno_t kcm_set_options(struct krb5_ctx *krb5_ctx,
+ char *lifetime,
+ char *rtime,
+ bool validate,
+ bool canonicalize,
+ int timeout,
+ char *renew_intv,
+ time_t *_renew_intv_tm)
+{
+ errno_t ret;
+ krb5_error_code kerr;
+ krb5_deltat renew_interval_delta;
+
+ if (renew_intv != NULL) {
+ kerr = krb5_string_to_deltat(renew_intv, &renew_interval_delta);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "krb5_string_to_deltat failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_renew_intv_tm = renew_interval_delta;
+ } else {
+ *_renew_intv_tm = 0;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n",
+ CONFDB_KCM_KRB5_RENEW_INTERVAL,
+ renew_intv == NULL ? "none" : renew_intv);
+
+ if (lifetime != NULL) {
+ ret = krb5_string_to_deltat(lifetime, &krb5_ctx->lifetime);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to convert lifetime string [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ krb5_ctx->lifetime_str = lifetime;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n",
+ CONFDB_KCM_KRB5_LIFETIME,
+ krb5_ctx->lifetime_str == NULL ? "none" : krb5_ctx->lifetime_str);
+
+ if (rtime != 0) {
+ ret = krb5_string_to_deltat(rtime, &krb5_ctx->rlife);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to convert renewable lifetime "
+ "string [%d]: %s.\n", ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n",
+ CONFDB_KCM_KRB5_RENEWABLE_LIFETIME,
+ rtime == NULL ? "none" : rtime);
+
+ ret = dp_opt_set_bool(krb5_ctx->opts, KRB5_VALIDATE, validate);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot set krb5 child timeout [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n",
+ CONFDB_KCM_KRB5_VALIDATE,
+ validate ? "true" : "false");
+
+ krb5_ctx->canonicalize = canonicalize;
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n",
+ CONFDB_KCM_KRB5_CANONICALIZE,
+ canonicalize ? "true" : "false");
+
+ if (timeout > 0) {
+ ret = dp_opt_set_int(krb5_ctx->opts, KRB5_AUTH_TIMEOUT, timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot set krb5 child timeout [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%d]\n",
+ CONFDB_KCM_KRB5_AUTH_TIMEOUT,
+ timeout);
+
+ ret = EOK;
+done:
+ return ret;
+}
+
+static errno_t kcm_read_options(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *cdb,
+ const char *cpath,
+ char **_lifetime,
+ char **_rtime,
+ bool *_validate,
+ bool *_canonicalize,
+ int *_timeout,
+ char **_renew_intv)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *lifetime;
+ char *rtime;
+ bool validate;
+ bool canonicalize;
+ int timeout;
+ char *renew_intv;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = confdb_get_string(cdb, tmp_ctx, cpath,
+ CONFDB_KCM_KRB5_LIFETIME, NULL,
+ &lifetime);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+ CONFDB_KCM_KRB5_LIFETIME, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_string(cdb, tmp_ctx, cpath,
+ CONFDB_KCM_KRB5_RENEWABLE_LIFETIME, NULL,
+ &rtime);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+ CONFDB_KCM_KRB5_RENEWABLE_LIFETIME, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_bool(cdb, cpath,
+ CONFDB_KCM_KRB5_VALIDATE, false,
+ &validate);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+ CONFDB_KCM_KRB5_VALIDATE, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_bool(cdb, cpath,
+ CONFDB_KCM_KRB5_CANONICALIZE, false,
+ &canonicalize);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+ CONFDB_KCM_KRB5_CANONICALIZE, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_int(cdb, cpath,
+ CONFDB_KCM_KRB5_AUTH_TIMEOUT, 0,
+ &timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+ CONFDB_KCM_KRB5_AUTH_TIMEOUT, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_string(cdb, tmp_ctx, cpath,
+ CONFDB_KCM_KRB5_RENEW_INTERVAL, NULL,
+ &renew_intv);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read %s/%s [%d]: %s\n", cpath,
+ CONFDB_KCM_KRB5_AUTH_TIMEOUT, ret, sss_strerror(ret));
+ goto done;
+ }
+
+
+ *_lifetime = talloc_steal(mem_ctx, lifetime);
+ *_rtime = talloc_steal(mem_ctx, rtime);
+ *_validate = validate;
+ *_canonicalize = canonicalize;
+ *_timeout = timeout;
+ *_renew_intv = renew_intv;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+int kcm_get_renewal_config(struct kcm_ctx *kctx,
+ struct krb5_ctx **_krb5_ctx,
+ time_t *_renew_intv)
+{
+ int ret;
+ struct krb5_ctx *krb5_ctx;
+ char *lifetime;
+ char *rtime;
+ bool validate;
+ bool canonicalize;
+ int timeout;
+ char *renew_intv;
+ time_t renew_intv_tm;
+ bool tgt_renewal;
+ char *tgt_renewal_inherit;
+ const char *conf_path;
+ int i;
+
+ krb5_ctx = talloc_zero(kctx->rctx, struct krb5_ctx);
+ if (krb5_ctx == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error allocating krb5_ctx\n");
+ goto done;
+ }
+
+ /* Set default Kerberos options */
+ krb5_ctx->opts = talloc_zero_array(krb5_ctx, struct dp_option, KRB5_OPTS);
+ if (krb5_ctx->opts == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error allocating krb5_ctx opts\n");
+ goto done;
+ }
+
+ for (i = 0; i < KRB5_OPTS; i++) {
+ krb5_ctx->opts[i].opt_name = default_krb5_opts[i].opt_name;
+ krb5_ctx->opts[i].type = default_krb5_opts[i].type;
+ krb5_ctx->opts[i].def_val = default_krb5_opts[i].def_val;
+ switch (krb5_ctx->opts[i].type) {
+ case DP_OPT_STRING:
+ ret = dp_opt_set_string(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.string);
+ break;
+ case DP_OPT_BLOB:
+ ret = dp_opt_set_blob(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.blob);
+ break;
+ case DP_OPT_NUMBER:
+ ret = dp_opt_set_int(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.number);
+ break;
+ case DP_OPT_BOOL:
+ ret = dp_opt_set_bool(krb5_ctx->opts, i,
+ default_krb5_opts[i].def_val.boolean);
+ break;
+ default:
+ ret = EINVAL;
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed setting default renewal kerberos "
+ "options [%d]: %s\n", ret, sss_strerror(ret));
+ talloc_free(krb5_ctx->opts);
+ goto done;
+ }
+ }
+
+ ret = confdb_get_bool(kctx->rctx->cdb,
+ kctx->rctx->confdb_service_path,
+ CONFDB_KCM_TGT_RENEWAL, false,
+ &tgt_renewal);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve TGT Renewal confdb value "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n",
+ CONFDB_KCM_TGT_RENEWAL,
+ tgt_renewal ? "true" : "false");
+ if (tgt_renewal == false) {
+ ret = ENOTSUP;
+ goto done;
+ }
+
+ ret = confdb_get_string(kctx->rctx->cdb,
+ kctx->rctx,
+ kctx->rctx->confdb_service_path,
+ CONFDB_KCM_TGT_RENEWAL_INHERIT,
+ NULL,
+ &tgt_renewal_inherit);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve TGT Renewal inherit confdb "
+ "valule [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Option [%s] set to [%s]\n",
+ CONFDB_KCM_TGT_RENEWAL_INHERIT,
+ tgt_renewal_inherit == NULL ? "none" : tgt_renewal_inherit);
+
+ /* Override with config options */
+ if (tgt_renewal_inherit == NULL) {
+ ret = kcm_read_options(kctx, kctx->rctx->cdb, kctx->rctx->confdb_service_path,
+ &lifetime, &rtime, &validate, &canonicalize,
+ &timeout, &renew_intv);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to read krb5 options from "
+ "[kcm] section [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+ } else {
+ conf_path = talloc_asprintf(kctx->rctx, CONFDB_DOMAIN_PATH_TMPL,
+ tgt_renewal_inherit);
+ if (conf_path == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error allocating conf_path\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Inherit krb5 options for domain [%s] for renewals\n",
+ conf_path);
+ ret = kcm_read_options(kctx, kctx->rctx->cdb, conf_path,
+ &lifetime, &rtime, &validate, &canonicalize,
+ &timeout, &renew_intv);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed reading domain [%s] inherit krb5 options "
+ "[%d]: %s\n", conf_path, ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = kcm_set_options(krb5_ctx, lifetime, rtime, validate, canonicalize,
+ timeout, renew_intv, &renew_intv_tm);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed setting krb5 options for renewal "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_renew_intv = renew_intv_tm;
+ *_krb5_ctx = krb5_ctx;
+ ret = EOK;
+done:
+ if (ret != EOK) {
+ talloc_free(krb5_ctx);
+ }
+ return ret;
+}
+
+static errno_t kcm_child_req_setup(TALLOC_CTX *mem_ctx,
+ struct kcm_auth_data *auth_data,
+ struct krb5_ctx *krb5_ctx,
+ struct krb5child_req **_req)
+{
+ struct krb5child_req *krreq;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Setup for renewal of [%s] " \
+ "for principal name [%s]\n",
+ auth_data->upn,
+ auth_data->ccname);
+
+ krreq = talloc_zero(mem_ctx, struct krb5child_req);
+ if (krreq == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to alloc krreq [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ krreq->krb5_ctx = krb5_ctx;
+
+ /* Set uid and gid */
+ krreq->uid = auth_data->uid;
+ krreq->gid = auth_data->gid;
+
+ krreq->upn = talloc_strdup(krreq, auth_data->upn);
+ if (krreq->upn == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to strdup krreq->upn [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ krreq->ccname = talloc_asprintf(krreq, "KCM:%s", auth_data->ccname);
+ if (krreq->ccname == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to strdup krreq->ccname [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Set PAM Data */
+ krreq->pd = create_pam_data(krreq);
+ if (krreq->pd == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed creating pam data on krreq->pd "
+ "[%d]: %s\n", ret, strerror(ret));
+ goto fail;
+ }
+
+ krreq->pd->cmd = SSS_CMD_RENEW;
+ krreq->pd->user = talloc_strdup(krreq->pd, auth_data->upn);
+ if (krreq->pd->user == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to strdup krreq->pd->user "
+ "[%d]: %s\n", ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Set authtok values */
+ sss_authtok_set_empty(krreq->pd->newauthtok);
+
+ ret = sss_authtok_set_ccfile(krreq->pd->authtok, krreq->ccname, 0);
+ if (ret != EOK) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed setting authtok krreq->ccname"
+ "[%d]: %s\n", ret, strerror(ret));
+ goto fail;
+ }
+
+ krreq->old_ccname = krreq->ccname;
+
+ *_req = krreq;
+
+ return EOK;
+fail:
+ talloc_zfree(krreq);
+ return ret;
+}
+
+static void kcm_renew_tgt(struct tevent_context *ev,
+ struct tevent_immediate *imm,
+ void *private_data)
+{
+ struct kcm_auth_data *auth_data;
+ struct tevent_req *req;
+ struct kcm_renew_auth_ctx *ctx;
+ errno_t ret;
+
+ auth_data = talloc_get_type(private_data, struct kcm_auth_data);
+
+ ctx = talloc_zero(auth_data, struct kcm_renew_auth_ctx);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to allocate renew auth ctx\n");
+ return;
+ }
+ auth_data->auth_ctx = ctx;
+
+ ret = kcm_child_req_setup(ctx, auth_data, auth_data->krb5_ctx, &ctx->kr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to setup krb5 child for renewal [%d]: %s\n",
+ ret, sss_strerror(ret));
+ talloc_free(auth_data);
+ return;
+ }
+
+ req = handle_child_send(ctx, ev, ctx->kr);
+ if (req == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to trigger krb5 child process request"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ talloc_free(auth_data);
+ return;
+ }
+
+ tevent_req_set_callback(req, kcm_renew_tgt_done, auth_data);
+
+ return;
+}
+
+static void kcm_renew_tgt_done(struct tevent_req *req)
+{
+ struct kcm_auth_data *auth_data;
+ struct kcm_renew_auth_ctx *ctx;
+ int ret;
+ struct krb5_child_response *res;
+
+ auth_data = tevent_req_callback_data(req, struct kcm_auth_data);
+ ctx = auth_data->auth_ctx;
+
+ ret = handle_child_recv(req, ctx, &ctx->buf, &ctx->len);
+ talloc_free(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to receive krb5 child process request"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+ ret = parse_krb5_child_response(ctx, ctx->buf, ctx->len, ctx->kr->pd,
+ 0, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Krb5 child returned error! Please " \
+ "inspect the krb5_child.log file. "
+ " Error [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+ if (res->msg_status != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Renewal failed - krb5_child [%d]\n",
+ res->msg_status);
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Successfully renewed [%s]\n", res->ccname);
+done:
+ talloc_zfree(ctx);
+ talloc_zfree(auth_data);
+ return;
+}
+
+static errno_t kcm_creds_check_times(TALLOC_CTX *mem_ctx,
+ struct kcm_renew_tgt_ctx *renew_tgt_ctx,
+ krb5_creds *creds,
+ struct kcm_ccache *cc,
+ const char *client_name)
+{
+ struct tgt_times tgtt;
+ time_t now;
+ time_t start_renew;
+ struct kcm_auth_data *auth_data;
+ struct tevent_immediate *imm;
+ int ret;
+
+ memset(&tgtt, 0, sizeof(tgtt));
+ tgtt.authtime = creds->times.authtime;
+ tgtt.starttime = creds->times.starttime;
+ tgtt.endtime = creds->times.endtime;
+ tgtt.renew_till = creds->times.renew_till;
+
+ now = time(NULL);
+ /* Attempt renewal only after half of the ticket lifetime has exceeded */
+ start_renew = (time_t) (tgtt.starttime + 0.5 * (tgtt.endtime - tgtt.starttime));
+ if (tgtt.renew_till >= tgtt.endtime && tgtt.renew_till >= now
+ && tgtt.endtime >= now && start_renew <= now) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Renewal cred ready!\n");
+ auth_data = talloc_zero(renew_tgt_ctx, struct kcm_auth_data);
+ if (auth_data == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate auth_data for renewals\n");
+ goto done;
+ }
+
+ auth_data->krb5_ctx = renew_tgt_ctx->krb5_ctx;
+ auth_data->upn = talloc_strdup(auth_data, client_name);
+ auth_data->uid = cc->owner.uid;
+ auth_data->gid = cc->owner.gid;
+ auth_data->ccname = cc->name;
+ if (auth_data->upn == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate auth_data->upn for renewals\n");
+ goto done;
+ }
+
+ imm = tevent_create_immediate(auth_data);
+ if (imm == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed\n");
+ goto done;
+ }
+
+ tevent_schedule_immediate(imm, renew_tgt_ctx->ev, kcm_renew_tgt,
+ auth_data);
+ } else {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Time not applicable\n");
+ }
+
+ ret = EOK;
+done:
+ return ret;
+}
+
+errno_t kcm_renew_all_tgts(TALLOC_CTX *mem_ctx,
+ struct kcm_renew_tgt_ctx *renew_tgt_ctx,
+ struct kcm_ccache **cc_list)
+{
+ TALLOC_CTX *tmp_ctx;
+ size_t count = 0;
+ int ret;
+ struct kcm_ccache *cc;
+ char *client_name;
+ krb5_context krb_context;
+ krb5_creds **extracted_creds;
+ krb5_error_code kerr;
+
+ if (cc_list == NULL) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create tmp talloc_ctx\n");
+ return ENOMEM;
+ }
+
+ kerr = krb5_init_context(&krb_context);
+ if (kerr != 0) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init krb5 context\n");
+ goto done;
+ }
+
+ count = talloc_array_length(cc_list);
+ if (count <= 1) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No renewal entries found.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Found [%zu] renewal entries.\n", count - 1);
+ for (int i = 0; i < count - 1; i++) {
+ cc = cc_list[i];
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Checking ccache [%s] for creds to renew\n", cc->name);
+
+ extracted_creds = kcm_cc_unmarshal(tmp_ctx, krb_context, cc);
+ if (extracted_creds == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed unmarshaling creds\n");
+ goto done;
+ }
+
+ for (int j = 0; extracted_creds[j] != NULL; j++) {
+ kerr = krb5_unparse_name(tmp_ctx, extracted_creds[j]->client,
+ &client_name);
+ if (kerr != 0) {
+ ret = EIO;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed unparsing name\n");
+ goto done;
+ }
+
+ kcm_creds_check_times(tmp_ctx, renew_tgt_ctx, extracted_creds[j],
+ cc, client_name);
+ }
+ }
+
+ ret = EOK;
+done:
+ if (tmp_ctx != NULL) {
+ talloc_free(tmp_ctx);
+ }
+ krb5_free_context(krb_context);
+ return ret;
+}
+
+static void kcm_renew_tgt_timer_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *data)
+{
+ struct kcm_renew_tgt_ctx *renew_tgt_ctx;
+ errno_t ret;
+ struct timeval next;
+ struct kcm_ccache **cc_list;
+ TALLOC_CTX *tmp_ctx;
+
+ renew_tgt_ctx = talloc_get_type(data, struct kcm_renew_tgt_ctx);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure in tmp_ctx talloc_new\n");
+ return;
+ }
+
+ /* forget the timer event, it will be freed by the tevent timer loop */
+ renew_tgt_ctx->te = NULL;
+
+ /* Prepare KCM ccache list for renewals */
+ ret = kcm_ccdb_renew_tgts(tmp_ctx, renew_tgt_ctx->krb5_ctx,
+ ev, renew_tgt_ctx->db, &cc_list);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_ALL, "No ccache renewal entries to prepare.\n");
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve list of TGTs for renewal "
+ "preparation [%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ if (ret == EOK) {
+ ret = kcm_renew_all_tgts(tmp_ctx, renew_tgt_ctx, cc_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to successfully execute renewal of TGT list"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+ }
+
+ /* Reschedule timer */
+ next = sss_tevent_timeval_current_ofs_time_t(renew_tgt_ctx->timer_interval);
+ renew_tgt_ctx->te = tevent_add_timer(ev, renew_tgt_ctx,
+ next, kcm_renew_tgt_timer_handler,
+ renew_tgt_ctx);
+ if (renew_tgt_ctx->te == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup timer, renewals will be "
+ "disabled until the next interval triggers\n");
+ talloc_zfree(renew_tgt_ctx);
+ }
+
+ talloc_free(tmp_ctx);
+ return;
+}
+
+errno_t kcm_renewal_setup(struct resp_ctx *rctx,
+ struct krb5_ctx *krb5_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ time_t renew_intv)
+{
+ int ret;
+ struct timeval next;
+
+ krb5_ctx->kcm_renew_tgt_ctx = talloc_zero(krb5_ctx, struct kcm_renew_tgt_ctx);
+ if (krb5_ctx->kcm_renew_tgt_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+ return ENOMEM;
+ }
+
+ krb5_ctx->kcm_renew_tgt_ctx->rctx = rctx;
+ krb5_ctx->kcm_renew_tgt_ctx->krb5_ctx = krb5_ctx;
+ krb5_ctx->kcm_renew_tgt_ctx->db = db,
+ krb5_ctx->kcm_renew_tgt_ctx->ev = ev;
+ krb5_ctx->kcm_renew_tgt_ctx->timer_interval = renew_intv;
+
+ /* Check KCM for tickets to renew */
+ next = sss_tevent_timeval_current_ofs_time_t(
+ krb5_ctx->kcm_renew_tgt_ctx->timer_interval);
+ krb5_ctx->kcm_renew_tgt_ctx->te = tevent_add_timer(ev, krb5_ctx->kcm_renew_tgt_ctx,
+ next,
+ kcm_renew_tgt_timer_handler,
+ krb5_ctx->kcm_renew_tgt_ctx);
+ if (krb5_ctx->kcm_renew_tgt_ctx->te == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup renewal timer\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ return EOK;
+
+fail:
+ talloc_zfree(krb5_ctx->renew_tgt_ctx);
+ return ret;
+}
diff --git a/src/responder/kcm/kcm_renew.h b/src/responder/kcm/kcm_renew.h
new file mode 100644
index 0000000..0d69f42
--- /dev/null
+++ b/src/responder/kcm/kcm_renew.h
@@ -0,0 +1,53 @@
+/*
+ SSSD
+
+ KCM Renewal, private header file
+
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Copyright (C) 2020 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 __KCM_RENEW_H__
+#define __KCM_RENEW_H__
+
+#include "providers/krb5/krb5_common.h"
+#include "src/providers/krb5/krb5_ccache.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "util/sss_ptr_hash.h"
+
+struct kcm_renew_tgt_ctx {
+ struct kcm_ccache **cc_list;
+ struct tevent_context *ev;
+ struct krb5_ctx *krb5_ctx;
+ struct resp_ctx *rctx;
+ struct kcm_ccdb *db;
+ time_t timer_interval;
+ struct tevent_timer *te;
+};
+
+
+int kcm_get_renewal_config(struct kcm_ctx *kctx,
+ struct krb5_ctx **_krb5_ctx,
+ time_t *_renew_intv);
+
+errno_t kcm_renewal_setup(struct resp_ctx *rctx, struct krb5_ctx *kctx,
+ struct tevent_context *ev, struct kcm_ccdb *db,
+ time_t renew_intv);
+
+#endif /* __KCM_RENEW_H__ */
diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c
new file mode 100644
index 0000000..6e4ea64
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache.c
@@ -0,0 +1,1724 @@
+/*
+ SSSD
+
+ KCM Server - the KCM ccache operations
+
+ Copyright (C) Red Hat, 2016
+
+ 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 "config.h"
+
+#include "util/crypto/sss_crypto.h"
+#include "util/util.h"
+#include "util/sss_krb5.h"
+#include "src/providers/krb5/krb5_ccache.h"
+#include "responder/kcm/kcm_renew.h"
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+
+
+static struct kcm_cred *kcm_cred_dup(TALLOC_CTX *mem_ctx,
+ struct kcm_cred *crd);
+
+static int kcm_cc_destructor(struct kcm_ccache *cc)
+{
+ if (cc == NULL) {
+ return 0;
+ }
+
+ if (cc->client != NULL) {
+ krb5_free_principal(NULL, cc->client);
+ }
+ return 0;
+}
+
+errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
+ krb5_context k5c,
+ struct cli_creds *owner,
+ const char *name,
+ krb5_principal princ,
+ struct kcm_ccache **_cc)
+{
+ struct kcm_ccache *cc = NULL;
+ krb5_error_code kret;
+ errno_t ret;
+
+ cc = talloc_zero(mem_ctx, struct kcm_ccache);
+ if (cc == NULL) {
+ return ENOMEM;
+ }
+
+ ret = kcm_check_name(name, owner);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Name %s is malformed\n", name);
+ goto done;
+ }
+
+ cc->name = talloc_strdup(cc, name);
+ if (cc->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ uuid_generate(cc->uuid);
+
+ if (princ) {
+ kret = krb5_copy_principal(k5c, princ, &cc->client);
+ if (kret != 0) {
+ const char *err_msg = sss_krb5_get_error_message(k5c, kret);
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_copy_principal failed: [%d][%s]\n", kret, err_msg);
+ sss_krb5_free_error_message(k5c, err_msg);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ }
+
+ cc->owner.uid = cli_creds_get_uid(owner);
+ cc->owner.gid = cli_creds_get_gid(owner);
+ cc->kdc_offset = 0;
+
+ talloc_set_destructor(cc, kcm_cc_destructor);
+ *_cc = cc;
+ ret = EOK;
+done:
+ if (ret != EOK) {
+ talloc_free(cc);
+ }
+ return ret;
+}
+
+struct kcm_ccache *kcm_cc_dup(TALLOC_CTX *mem_ctx,
+ const struct kcm_ccache *cc)
+{
+ struct kcm_ccache *dup;
+ struct kcm_cred *crd_dup;
+ struct kcm_cred *crd;
+
+ dup = talloc_zero(mem_ctx, struct kcm_ccache);
+ if (dup == NULL) {
+ return NULL;
+ }
+ memcpy(dup, cc, sizeof(struct kcm_ccache));
+
+ dup->creds = NULL;
+ DLIST_FOR_EACH(crd, cc->creds) {
+ crd_dup = kcm_cred_dup(dup, crd);
+ if (crd_dup == NULL) {
+ talloc_free(dup);
+ return NULL;
+ }
+
+ DLIST_ADD(dup->creds, crd_dup);
+ }
+
+ return dup;
+}
+
+const char *kcm_cc_get_name(struct kcm_ccache *cc)
+{
+ return cc ? cc->name : NULL;
+}
+
+errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid)
+{
+ if (cc == NULL) {
+ return EINVAL;
+ }
+ uuid_copy(_uuid, cc->uuid);
+ return EOK;
+}
+
+krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc)
+{
+ return cc ? cc->client : NULL;
+}
+
+bool kcm_cc_access(struct kcm_ccache *cc,
+ struct cli_creds *client)
+{
+ bool ok;
+ uid_t uid = cli_creds_get_uid(client);
+ gid_t gid = cli_creds_get_gid(client);
+
+ if (cc == NULL) {
+ return false;
+ }
+
+ if (uid == 0 && gid == 0) {
+ /* root can access any ccache */
+ return true;
+ }
+
+ ok = ((cc->owner.uid == uid) && (cc->owner.gid == gid));
+ if (!ok) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Client %"SPRIuid":%"SPRIgid" has no access to ccache %s\n",
+ cli_creds_get_uid(client),
+ cli_creds_get_gid(client),
+ cc->name);
+ }
+ return ok;
+}
+
+int32_t kcm_cc_get_offset(struct kcm_ccache *cc)
+{
+ return cc ? cc->kdc_offset : INT32_MAX;
+}
+
+errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc,
+ struct sss_iobuf *cred_blob)
+{
+ struct kcm_cred *kcreds;
+ uuid_t uuid;
+ errno_t ret;
+
+ if (cc == NULL || cred_blob == NULL) {
+ return EINVAL;
+ }
+
+ uuid_generate(uuid);
+ kcreds = kcm_cred_new(cc, uuid, cred_blob);
+ if (kcreds == NULL) {
+ return ENOMEM;
+ }
+
+ ret = kcm_cc_store_creds(cc, kcreds);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc)
+{
+ if (cc == NULL) {
+ return NULL;
+ }
+
+ return cc->creds;
+}
+
+struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd)
+{
+ if (crd == NULL) {
+ return NULL;
+ }
+
+ return crd->next;
+}
+
+struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob)
+{
+ struct kcm_cred *kcreds;
+
+ kcreds = talloc_zero(mem_ctx, struct kcm_cred);
+ if (kcreds == NULL) {
+ return NULL;
+ }
+ uuid_copy(kcreds->uuid, uuid);
+ kcreds->cred_blob = talloc_steal(kcreds, cred_blob);
+ return kcreds;
+}
+
+static struct kcm_cred *kcm_cred_dup(TALLOC_CTX *mem_ctx,
+ struct kcm_cred *crd)
+{
+ struct kcm_cred *dup;
+
+ dup = talloc_zero(mem_ctx, struct kcm_cred);
+ if (dup == NULL) {
+ return NULL;
+ }
+
+ uuid_copy(dup->uuid, crd->uuid);
+ dup->cred_blob = crd->cred_blob;
+
+ return dup;
+}
+
+#ifdef HAVE_KRB5_UNMARSHAL_CREDENTIALS
+static krb5_creds *kcm_cred_to_krb5(krb5_context kctx, struct kcm_cred *kcm_crd)
+{
+ krb5_error_code kerr;
+ krb5_creds *kcrd;
+ krb5_data data;
+
+ get_krb5_data_from_cred(kcm_crd->cred_blob, &data);
+
+ kerr = krb5_unmarshal_credentials(kctx, &data, &kcrd);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unmarshal credentials\n");
+ return NULL;
+ }
+
+ return kcrd;
+}
+#endif
+
+static errno_t
+kcm_cc_remove_duplicates(struct kcm_ccache *cc,
+ struct kcm_cred *kcm_crd)
+{
+#ifdef HAVE_KRB5_UNMARSHAL_CREDENTIALS
+ struct kcm_cred *p, *q;
+ krb5_error_code kerr;
+ krb5_context kctx;
+ krb5_creds *kcrd_cc;
+ krb5_creds *kcrd;
+ errno_t ret;
+ bool bret;
+
+ kerr = krb5_init_context(&kctx);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init krb5 context\n");
+ return EIO;
+ }
+
+ kcrd = kcm_cred_to_krb5(kctx, kcm_crd);
+ if (kcrd == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to convert kcm cred to krb5\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ DLIST_FOR_EACH_SAFE(p, q, cc->creds) {
+ kcrd_cc = kcm_cred_to_krb5(kctx, p);
+ if (kcrd_cc == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to convert kcm cred to krb5\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ bret = sss_krb5_creds_compare(kctx, kcrd, kcrd_cc);
+ krb5_free_creds(kctx, kcrd_cc);
+ if (!bret) {
+ continue;
+ }
+
+ /* This cred is the same ticket. We will replace it with the new one. */
+ DLIST_REMOVE(cc->creds, p);
+ }
+
+ ret = EOK;
+
+done:
+ krb5_free_creds(kctx, kcrd);
+ krb5_free_context(kctx);
+
+ return ret;
+#else
+ return EOK;
+#endif
+}
+
+/* Add a cred to ccache */
+errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
+ struct kcm_cred *crd)
+{
+ errno_t ret;
+
+ ret = kcm_cc_remove_duplicates(cc, crd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to remove duplicate credentials "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ DLIST_ADD(cc->creds, crd);
+ talloc_steal(cc, crd);
+ return EOK;
+}
+
+errno_t kcm_cc_set_header(struct kcm_ccache *cc,
+ const char *sec_key,
+ struct cli_creds *client)
+{
+ errno_t ret;
+
+ ret = sec_key_parse(cc, sec_key, &cc->name, cc->uuid);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* We rely on sssd-secrets only searching the user's subtree so we
+ * set the ownership to the client
+ */
+ cc->owner.uid = cli_creds_get_uid(client);
+ cc->owner.gid = cli_creds_get_gid(client);
+
+ return EOK;
+}
+
+#ifdef HAVE_KCM_RENEWAL
+static int kcm_cc_unmarshal_destructor(krb5_creds **creds)
+{
+ krb5_error_code kerr;
+ krb5_context krb_ctx;
+ int i;
+
+ kerr = krb5_init_context(&krb_ctx);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init krb5 context\n");
+ return 1;
+ }
+
+ for (i = 0; creds[i] != NULL; i++) {
+ krb5_free_creds(krb_ctx, creds[i]);
+ }
+
+ krb5_free_context(krb_ctx);
+
+ return 0;
+}
+#endif
+
+krb5_creds **kcm_cc_unmarshal(TALLOC_CTX *mem_ctx,
+ krb5_context krb_context,
+ struct kcm_ccache *cc)
+{
+#ifndef HAVE_KCM_RENEWAL
+ return NULL;
+#else
+ TALLOC_CTX *tmp_ctx;
+ struct kcm_cred *cred;
+ krb5_creds **cred_list;
+ int i = 0;
+ int count = 0;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ goto done;
+ }
+
+ for (cred = kcm_cc_get_cred(cc); cred != NULL; cred = kcm_cc_next_cred(cred)) {
+ count++;
+ }
+
+ cred_list = talloc_array(tmp_ctx, krb5_creds *, count + 1);
+
+ for (cred = kcm_cc_get_cred(cc); cred != NULL; cred = kcm_cc_next_cred(cred), i++) {
+ cred_list[i] = kcm_cred_to_krb5(krb_context, cred);
+ if (cred_list[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to convert kcm cred to krb5\n");
+ goto done;
+ }
+ }
+
+ cred_list[count] = NULL;
+ talloc_set_destructor(cred_list, kcm_cc_unmarshal_destructor);
+
+ talloc_steal(mem_ctx, cred_list);
+
+ return cred_list;
+done:
+ talloc_free(tmp_ctx);
+ return NULL;
+#endif
+}
+
+errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid)
+{
+ if (crd == NULL) {
+ return EINVAL;
+ }
+ uuid_copy(_uuid, crd->uuid);
+ return EOK;
+}
+
+struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd)
+{
+ return crd ? crd->cred_blob : NULL;
+}
+
+errno_t kcm_ccdb_renew_tgts(TALLOC_CTX *mem_ctx,
+ struct krb5_ctx *krb5_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *ccdb,
+ struct kcm_ccache ***_cc_list)
+{
+ struct kcm_ccache **cc;
+ errno_t ret;
+
+ if (krb5_ctx == NULL || ev == NULL || ccdb == NULL) {
+ ret = EINVAL;
+ return ret;
+ }
+
+ if (ccdb->ops->list_all_cc == NULL) {
+ ret = EINVAL;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "List all cc function not available\n");
+ goto done;
+ }
+
+ ret = ccdb->ops->list_all_cc(mem_ctx, krb5_ctx, ev, ccdb, &cc);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to retrieve list of ccaches"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ } else if (ret == ENOENT) {
+ goto done;
+ }
+
+ *_cc_list = talloc_steal(mem_ctx, cc);
+
+ ret = EOK;
+done:
+
+ return ret;
+}
+
+struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ const char *confdb_service_path,
+ enum kcm_ccdb_be cc_be)
+{
+ errno_t ret;
+ struct kcm_ccdb *ccdb = NULL;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ ccdb = talloc_zero(mem_ctx, struct kcm_ccdb);
+ if (ccdb == NULL) {
+ return NULL;
+ }
+ ccdb->ev = ev;
+
+ switch (cc_be) {
+ case CCDB_BE_MEMORY:
+ DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n");
+ ccdb->ops = &ccdb_mem_ops;
+ break;
+ case CCDB_BE_SECDB:
+ DEBUG(SSSDBG_FUNC_DATA, "KCM back end: libsss_secrets\n");
+ ccdb->ops = &ccdb_secdb_ops;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown ccache database backend\n");
+ break;
+ }
+
+ if (ccdb->ops == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Ccache database not initialized\n");
+ talloc_free(ccdb);
+ return NULL;
+ }
+
+ ret = ccdb->ops->init(ccdb, cdb, confdb_service_path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize ccache database\n");
+ talloc_free(ccdb);
+ return NULL;
+ }
+
+ return ccdb;
+}
+
+struct kcm_ccdb_nextid_state {
+ char *next_cc;
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+};
+
+static void kcm_ccdb_nextid_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_nextid_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_nextid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->client = client;
+
+ if (ev == NULL || db == NULL || client == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = state->db->ops->nextid_send(state, ev, state->db, client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_nextid_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_nextid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_nextid_state *state = tevent_req_data(req,
+ struct kcm_ccdb_nextid_state);
+ errno_t ret;
+ unsigned int nextid;
+
+ ret = state->db->ops->nextid_recv(subreq, &nextid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to generate next UID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->next_cc = talloc_asprintf(state, "%"SPRIuid":%u",
+ cli_creds_get_uid(state->client),
+ nextid);
+ if (state->next_cc == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n");
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "generated %s\n", state->next_cc);
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_nextid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **_next_cc)
+{
+ struct kcm_ccdb_nextid_state *state = tevent_req_data(req,
+ struct kcm_ccdb_nextid_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_next_cc = talloc_steal(mem_ctx, state->next_cc);
+ return EOK;
+}
+
+struct kcm_ccdb_list_state {
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+
+ uuid_t *uuid_list;
+};
+
+static void kcm_ccdb_list_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_list_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->client = client;
+
+ if (ev == NULL || db == NULL || client == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = state->db->ops->list_send(state,
+ ev,
+ state->db,
+ client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_list_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_list_state *state = tevent_req_data(req,
+ struct kcm_ccdb_list_state);
+ errno_t ret;
+
+ ret = state->db->ops->list_recv(subreq, state, &state->uuid_list);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to list all ccaches [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_list_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t **_uuid_list)
+{
+ struct kcm_ccdb_list_state *state = tevent_req_data(req,
+ struct kcm_ccdb_list_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
+ return EOK;
+}
+
+struct kcm_ccdb_get_default_state {
+ struct kcm_ccdb *db;
+ uuid_t uuid;
+};
+
+static void kcm_ccdb_get_default_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_get_default_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_get_default_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+
+ if (ev == NULL || db == NULL || client == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = db->ops->get_default_send(state, ev, db, client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_get_default_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_get_default_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_get_default_state *state = tevent_req_data(req,
+ struct kcm_ccdb_get_default_state);
+ errno_t ret;
+
+ ret = state->db->ops->get_default_recv(subreq, state->uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get the default ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_get_default_recv(struct tevent_req *req,
+ uuid_t *uuid)
+{
+ struct kcm_ccdb_get_default_state *state = tevent_req_data(req,
+ struct kcm_ccdb_get_default_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (uuid != NULL) {
+ /* The caller might supply a NULL dfl to just check if there is
+ * some default ccache
+ */
+ uuid_copy(*uuid, state->uuid);
+ }
+
+ return EOK;
+}
+
+struct kcm_ccdb_set_default_state {
+ struct tevent_context *ev;
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+ uuid_t uuid;
+};
+
+static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq);
+static void kcm_ccdb_set_default_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_set_default_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_set_default_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->ev = ev;
+ state->client = client;
+ uuid_copy(state->uuid, uuid);
+
+ if (ev == NULL || db == NULL || client == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ if (uuid_is_null(uuid)) {
+ /* NULL UUID means to just reset the default to 'no default' */
+ subreq = state->db->ops->set_default_send(state,
+ state->ev,
+ state->db,
+ state->client,
+ state->uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req);
+ } else {
+ /* Otherwise we need to check if the client can access the UUID
+ * about to be set as default
+ */
+ subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_set_default_uuid_resolved, req);
+ }
+
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_set_default_state *state = tevent_req_data(req,
+ struct kcm_ccdb_set_default_state);
+ errno_t ret;
+ bool ok;
+ struct kcm_ccache *cc;
+
+ ret = state->db->ops->getbyuuid_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get cache by UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (cc == NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n");
+ tevent_req_error(req, ERR_KCM_CC_END);
+ return;
+ }
+
+ ok = kcm_cc_access(cc, state->client);
+ if (!ok) {
+ tevent_req_error(req, EACCES);
+ return;
+ }
+
+ subreq = state->db->ops->set_default_send(state,
+ state->ev,
+ state->db,
+ state->client,
+ state->uuid);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req);
+}
+
+static void kcm_ccdb_set_default_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_set_default_state *state = tevent_req_data(req,
+ struct kcm_ccdb_set_default_state);
+ errno_t ret;
+
+ ret = state->db->ops->set_default_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to set the default ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_set_default_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+struct kcm_ccdb_getbyname_state {
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+
+ struct kcm_ccache *cc;
+};
+
+static void kcm_ccdb_getbyname_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_getbyname_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->client = client;
+
+ if (ev == NULL || db == NULL || client == NULL || name == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = db->ops->getbyname_send(state, ev, db, client, name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_getbyname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_getbyname_state *state = tevent_req_data(req,
+ struct kcm_ccdb_getbyname_state);
+ errno_t ret;
+ bool ok;
+
+ ret = state->db->ops->getbyname_recv(subreq, state, &state->cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get cache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->cc == NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No cache found by name\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ ok = kcm_cc_access(state->cc, state->client);
+ if (!ok) {
+ tevent_req_error(req, EACCES);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct kcm_ccdb_getbyname_state *state = tevent_req_data(req,
+ struct kcm_ccdb_getbyname_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+struct kcm_ccdb_getbyuuid_state {
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+
+ struct kcm_ccache *cc;
+};
+
+static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_getbyuuid_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyuuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->client = client;
+
+ if (ev == NULL || db == NULL || client == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_getbyuuid_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req,
+ struct kcm_ccdb_getbyuuid_state);
+ errno_t ret;
+ bool ok;
+
+ ret = state->db->ops->getbyuuid_recv(subreq, state, &state->cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get cache by UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->cc == NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ ok = kcm_cc_access(state->cc, state->client);
+ if (!ok) {
+ tevent_req_error(req, EACCES);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req,
+ struct kcm_ccdb_getbyuuid_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+struct kcm_ccdb_name_by_uuid_state {
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+
+ const char *name;
+};
+
+static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_name_by_uuid_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct kcm_ccdb_name_by_uuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->client = client;
+
+ if (ev == NULL || db == NULL || client == NULL || uuid_is_null(uuid)) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = db->ops->name_by_uuid_send(state, ev, db, client, uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_name_by_uuid_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req,
+ struct kcm_ccdb_name_by_uuid_state);
+ errno_t ret;
+
+ ret = state->db->ops->name_by_uuid_recv(subreq, state, &state->name);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to resolve cache by UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ const char **_name)
+{
+ struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req,
+ struct kcm_ccdb_name_by_uuid_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_name = talloc_steal(mem_ctx, state->name);
+ return EOK;
+}
+
+struct kcm_ccdb_uuid_by_name_state {
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+
+ uuid_t uuid;
+};
+
+static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_uuid_by_name_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct kcm_ccdb_uuid_by_name_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->client = client;
+
+ if (ev == NULL || db == NULL || client == NULL || name == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = db->ops->uuid_by_name_send(state, ev, db, client, name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_uuid_by_name_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req,
+ struct kcm_ccdb_uuid_by_name_state);
+ errno_t ret;
+
+ ret = state->db->ops->uuid_by_name_recv(subreq, state, state->uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to resolve cache by UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t _uuid)
+{
+ struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req,
+ struct kcm_ccdb_uuid_by_name_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ uuid_copy(_uuid, state->uuid);
+ return EOK;
+}
+
+struct kcm_ccdb_create_cc_state {
+ struct kcm_ccdb *db;
+};
+
+static void kcm_ccdb_create_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ struct kcm_ccache *cc)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_create_cc_state *state = NULL;
+ errno_t ret;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_create_cc_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+
+ if (ev == NULL || db == NULL || client == NULL || cc == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ ok = kcm_cc_access(cc, client);
+ if (!ok) {
+ ret = EACCES;
+ goto immediate;
+ }
+
+ subreq = state->db->ops->create_send(state,
+ ev,
+ state->db,
+ client,
+ cc);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_create_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_create_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_create_cc_state *state = tevent_req_data(req,
+ struct kcm_ccdb_create_cc_state);
+ errno_t ret;
+
+ ret = state->db->ops->create_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to create ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx)
+{
+ if (mod_ctx == NULL) {
+ return;
+ }
+
+ mod_ctx->kdc_offset = INT32_MAX;
+ if (mod_ctx->client != NULL) {
+ krb5_free_principal(NULL, mod_ctx->client);
+ mod_ctx->client = NULL;
+ }
+
+ return;
+}
+
+struct kcm_mod_ctx *kcm_mod_ctx_new(TALLOC_CTX *mem_ctx)
+{
+ struct kcm_mod_ctx *mod_ctx;
+
+ mod_ctx = talloc_zero(mem_ctx, struct kcm_mod_ctx);
+ if (mod_ctx == NULL) {
+ return NULL;
+ }
+
+ kcm_mod_ctx_clear(mod_ctx);
+ return mod_ctx;
+}
+
+errno_t kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx)
+{
+ if (cc == NULL || mod_ctx == NULL) {
+ return EINVAL;
+ }
+
+ if (mod_ctx->kdc_offset != INT32_MAX) {
+ cc->kdc_offset = mod_ctx->kdc_offset;
+ }
+
+ if (mod_ctx->client != NULL) {
+ krb5_error_code kret;
+
+ kret = krb5_copy_principal(NULL, mod_ctx->client, &cc->client);
+ if (kret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_copy_principal failed: %d\n", kret);
+ return ERR_INTERNAL;
+ }
+ }
+
+ return EOK;
+}
+
+struct kcm_ccdb_mod_cc_state {
+ struct kcm_ccdb *db;
+};
+
+static void kcm_ccdb_mod_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct kcm_mod_ctx *mod_cc)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_mod_cc_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_mod_cc_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+
+ if (ev == NULL || db == NULL || client == NULL || mod_cc == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = state->db->ops->mod_send(state,
+ ev,
+ state->db,
+ client,
+ uuid,
+ mod_cc);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_mod_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_mod_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_mod_cc_state *state = tevent_req_data(req,
+ struct kcm_ccdb_mod_cc_state);
+ errno_t ret;
+
+ ret = state->db->ops->mod_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to create ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+struct kcm_ccdb_store_cred_blob_state {
+ struct kcm_ccdb *db;
+};
+
+static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_store_cred_blob_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_store_cred_blob_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+
+ if (ev == NULL || db == NULL || client == NULL || cred_blob == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = state->db->ops->store_cred_send(state,
+ ev,
+ state->db,
+ client,
+ uuid,
+ cred_blob);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_store_cred_blob_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_store_cred_blob_state *state = tevent_req_data(req,
+ struct kcm_ccdb_store_cred_blob_state);
+ errno_t ret;
+
+ ret = state->db->ops->store_cred_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to create ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+struct kcm_ccdb_delete_cc_state {
+ struct tevent_context *ev;
+ struct kcm_ccdb *db;
+ struct cli_creds *client;
+ uuid_t uuid;
+};
+
+static void kcm_ccdb_delete_done(struct tevent_req *subreq);
+static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq);
+static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_ccdb_delete_cc_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_delete_cc_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->db = db;
+ state->ev = ev;
+ state->client = client;
+ uuid_copy(state->uuid, uuid);
+
+ if (ev == NULL || db == NULL || client == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ subreq = state->db->ops->delete_send(state,
+ state->ev,
+ state->db,
+ state->client,
+ state->uuid);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_delete_done, req);
+
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_ccdb_delete_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
+ struct kcm_ccdb_delete_cc_state);
+ errno_t ret;
+
+ ret = state->db->ops->delete_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to delete ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* The delete operation must also check if the deleted ccache was
+ * the default and reset the default if it was
+ */
+ subreq = state->db->ops->get_default_send(state,
+ state->ev,
+ state->db,
+ state->client);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_delete_get_default_done, req);
+}
+
+static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
+ struct kcm_ccdb_delete_cc_state);
+ errno_t ret;
+ uuid_t dfl_uuid;
+ uuid_t null_uuid;
+
+ ret = state->db->ops->get_default_recv(subreq, dfl_uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get the default ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (uuid_compare(dfl_uuid, state->uuid) != 0) {
+ /* The ccache about to be deleted was not the default, quit */
+ tevent_req_done(req);
+ return;
+ }
+
+ /* If we deleted the default ccache, reset the default ccache to 'none' */
+ uuid_clear(null_uuid);
+
+ subreq = state->db->ops->set_default_send(state,
+ state->ev,
+ state->db,
+ state->client,
+ null_uuid);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_ccdb_delete_default_reset_done, req);
+}
+
+static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req,
+ struct kcm_ccdb_delete_cc_state);
+ errno_t ret;
+
+ ret = state->db->ops->set_default_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to NULL the default ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+void kcm_debug_uuid(uuid_t uuid)
+{
+ char dbgbuf[UUID_STR_SIZE];
+
+ if (!(debug_level & SSSDBG_TRACE_ALL) || uuid == NULL) {
+ return;
+ }
+
+ uuid_unparse(uuid, dbgbuf);
+ DEBUG(SSSDBG_TRACE_ALL, "UUID: %s\n", dbgbuf);
+}
+
+errno_t kcm_check_name(const char *name, struct cli_creds *client)
+{
+ char prefix[64];
+ size_t prefix_len;
+
+ prefix_len = snprintf(prefix, sizeof(prefix),
+ "%"SPRIuid, cli_creds_get_uid(client));
+
+ if (strncmp(name, prefix, prefix_len) != 0) {
+ return ERR_KCM_WRONG_CCNAME_FORMAT;
+ }
+ return EOK;
+}
diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h
new file mode 100644
index 0000000..1061ee2
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache.h
@@ -0,0 +1,380 @@
+/*
+ SSSD
+
+ KCM Server - the KCM ccache operations
+
+ Copyright (C) Red Hat, 2016
+
+ 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 _KCMSRV_CCACHE_H_
+#define _KCMSRV_CCACHE_H_
+
+#include "config.h"
+
+#include <krb5/krb5.h>
+#include <uuid/uuid.h>
+
+#include "util/util.h"
+#include "util/sss_iobuf.h"
+#include "util/util_creds.h"
+#include "providers/krb5/krb5_common.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+
+#define UUID_BYTES 16
+#define UUID_STR_SIZE 37
+
+/* Just to keep the name of the ccache readable */
+#define MAX_CC_NUM 99999
+
+/*
+ * Credentials are opaque to the KCM server
+ *
+ * Each ccache has a unique UUID.
+ */
+struct kcm_cred;
+
+/*
+ * An opaque ccache type and its operations
+ *
+ * Contains zero or some KCM credentials. One credential in the cache
+ * is marked as the default one. The client can set and get the default
+ * cache (e.g. with kswitch) but one cache is always the default -- we
+ * fall back to the one created first.
+ *
+ * Each cache has a name and a UUID. Heimdal allows the name to be changed,
+ * we don't (yet, because the MIT client doesn't allow that either)
+ *
+ * Each ccache also stores a client principal.
+ */
+struct kcm_ccache;
+
+/*
+ * Create a new KCM ccache owned by mem_ctx on the
+ * memory level.
+ *
+ * When created, the ccache contains no credentials
+ */
+errno_t kcm_cc_new(TALLOC_CTX *mem_ctx,
+ krb5_context k5c,
+ struct cli_creds *owner,
+ const char *name,
+ krb5_principal princ,
+ struct kcm_ccache **_cc);
+
+/*
+ * Duplicate the ccache. Only ccache and credentials are duplicated,
+ * but their data are a shallow copy.
+ */
+struct kcm_ccache *kcm_cc_dup(TALLOC_CTX *mem_ctx,
+ const struct kcm_ccache *cc);
+
+/*
+ * Returns true if a client can access a ccache.
+ *
+ * Note that root can access any ccache */
+bool kcm_cc_access(struct kcm_ccache *cc,
+ struct cli_creds *client);
+
+/*
+ * Since the kcm_ccache structure is opaque, the kcmsrv_ccache
+ * layer contains a number of getsetters to read and write
+ * properties of the kcm_ccache structure
+ */
+const char *kcm_cc_get_name(struct kcm_ccache *cc);
+errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid);
+krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc);
+int32_t kcm_cc_get_offset(struct kcm_ccache *cc);
+
+/* Mainly useful for creating a cred structure from a persistent
+ * storage
+ */
+struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob);
+
+/* Add a cred to ccache */
+errno_t kcm_cc_store_creds(struct kcm_ccache *cc,
+ struct kcm_cred *crd);
+
+/* Set cc header information from sec key and client */
+errno_t kcm_cc_set_header(struct kcm_ccache *cc,
+ const char *sec_key,
+ struct cli_creds *client);
+
+krb5_creds **kcm_cc_unmarshal(TALLOC_CTX *mem_ctx,
+ krb5_context krb_context,
+ struct kcm_ccache *cc);
+
+errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t uuid);
+
+/*
+ * At the moment, the credentials are stored without unmarshalling
+ * them, just as the clients sends the credentials.
+ */
+struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd);
+errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc,
+ struct sss_iobuf *cred_blob);
+ /*
+ * The KCM server can call kcm_cred_get_creds to fetch the first
+ * credential, then iterate over the credentials with
+ * kcm_cc_next_cred until it returns NULL
+ */
+struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc);
+struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd);
+
+/* An opaque database that contains all the ccaches */
+struct kcm_ccdb;
+
+/*
+ * Initialize a ccache database of type cc_be
+ */
+struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ const char *confdb_service_path,
+ enum kcm_ccdb_be cc_be);
+/*
+ * Prepare KCM ccache list for renewals
+ */
+errno_t kcm_ccdb_renew_tgts(TALLOC_CTX *mem_ctx,
+ struct krb5_ctx *kctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *cdb,
+ struct kcm_ccache ***_cc_list);
+
+/*
+ * In KCM, each ccache name is usually in the form of "UID:<num>
+ *
+ * The <num> is generated by the KCM ccache database. Use this function
+ * to retrieve the next number
+ */
+struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client);
+errno_t kcm_ccdb_nextid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **_nextid);
+
+/*
+ * List all ccaches that belong to a given client
+ *
+ * The cc_list the recv function returns is NULL-terminated.
+ *
+ * NOTE: Contrary to how Heimdal behaves, root CAN NOT list all ccaches
+ * of all users. This is a deliberate decision to treat root as any other
+ * user, except it can access a ccache of another user by name, just not
+ * list them.
+ *
+ * If a client has no ccaches, the function returns OK, but an empty list
+ * containing just the NULL sentinel.
+ */
+struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client);
+errno_t kcm_ccdb_list_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t **_uuid_list);
+
+/*
+ * Retrieve a ccache by name.
+ *
+ * If there is no such ccache, return EOK, but a NULL _cc pointer
+ */
+struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name);
+errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc);
+
+/*
+ * Retrieve a ccache by UUID
+ *
+ * If there is no such ccache, return EOK, but a NULL _cc pointer
+ */
+struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc);
+
+/*
+ * Retrieve the default ccache. If there is no default cache,
+ * return EOK, but a NULL UUID.
+ */
+struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client);
+errno_t kcm_ccdb_get_default_recv(struct tevent_req *req,
+ uuid_t *uuid);
+
+/*
+ * Translating name to UUID is often considerably faster than doing a full
+ * CC retrieval, hence this function and the converse. If the UUID cannot
+ * be found in the database, return ERR_KCM_CC_END
+ */
+struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ const char **_name);
+
+/*
+ * Translating UUID to name is often considerably faster than doing a full
+ * CC retrieval, hence this function and the converse. If the UUID cannot
+ * be found in the database, return ERR_KCM_CC_END
+ */
+struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name);
+errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t _uuid);
+
+/*
+ * Set the default ccache. Passing a NULL UUID is a legal operation
+ * that 'unsets' the default ccache.
+ */
+struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+errno_t kcm_ccdb_set_default_recv(struct tevent_req *req);
+
+/*
+ * Add a ccache to the database.
+ */
+struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ struct kcm_ccache *cc);
+errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req);
+
+/*
+ * Modify cache properties in a db
+ */
+struct kcm_mod_ctx {
+ int32_t kdc_offset;
+ krb5_principal client;
+ /* More settable properties (like name, when we support renames
+ * will be added later
+ */
+};
+
+struct kcm_mod_ctx *kcm_mod_ctx_new(TALLOC_CTX *mem_ctx);
+errno_t kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx);
+
+struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct kcm_mod_ctx *mod_cc);
+errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req);
+
+/*
+ * Store a credential in a cache
+ */
+struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob);
+errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req);
+
+/*
+ * Delete a ccache from the database
+ */
+struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req);
+
+void kcm_debug_uuid(uuid_t uuid);
+
+/*
+ * The KCM clients are not allowed (except root) to create ccaches
+ * with arbitrary names. Instead, we assert that the ccache name
+ * begins with UID where UID is the stringified representation of
+ * the client's UID number
+ */
+errno_t kcm_check_name(const char *name, struct cli_creds *client);
+
+/*
+ * ccahe marshalling to and from JSON. This is used when the ccaches
+ * are stored in the secrets store
+ */
+
+/*
+ * The secrets store is a key-value store at heart. We store the UUID
+ * and the name in the key to allow easy lookups be either key
+ */
+bool sec_key_match_name(const char *sec_key,
+ const char *name);
+
+bool sec_key_match_uuid(const char *sec_key,
+ uuid_t uuid);
+
+errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
+ const char *sec_key,
+ const char **_name,
+ uuid_t uuid);
+
+const char *sec_key_get_name(const char *sec_key);
+
+errno_t sec_key_get_uuid(const char *sec_key,
+ uuid_t uuid);
+
+const char *sec_key_create(TALLOC_CTX *mem_ctx,
+ const char *name,
+ uuid_t uuid);
+
+/*
+ * sec_key is a concatenation of the ccache's UUID and name
+ * sec_value is the binary representation of ccache.
+ */
+errno_t sec_kv_to_ccache_binary(TALLOC_CTX *mem_ctx,
+ const char *sec_key,
+ struct sss_iobuf *sec_value,
+ struct cli_creds *client,
+ struct kcm_ccache **_cc);
+
+/* Convert a kcm_ccache to its binary representation. */
+errno_t kcm_ccache_to_sec_input_binary(TALLOC_CTX *mem_ctx,
+ struct kcm_ccache *cc,
+ struct sss_iobuf **_payload);
+
+errno_t bin_to_krb_data(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *buf,
+ krb5_data *out);
+#endif /* _KCMSRV_CCACHE_H_ */
diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h
new file mode 100644
index 0000000..78d314e
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_be.h
@@ -0,0 +1,216 @@
+/*
+ SSSD
+
+ KCM Server - the KCM ccache database interface
+
+ This file should only be included from the ccache.c module.
+
+ Copyright (C) Red Hat, 2016
+
+ 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 _KCMSRV_CCACHE_BE_
+#define _KCMSRV_CCACHE_BE_
+
+#include "config.h"
+
+#include <talloc.h>
+#include "responder/kcm/kcmsrv_ccache.h"
+
+typedef errno_t
+(*ccdb_init_fn)(struct kcm_ccdb *db,
+ struct confdb_ctx *cdb,
+ const char *confdb_service_path);
+
+typedef struct tevent_req *
+(*ccdb_nextid_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client);
+typedef errno_t
+(*ccdb_nextid_recv_fn)(struct tevent_req *req,
+ unsigned int *_nextid);
+
+typedef struct tevent_req *
+(*ccdb_set_default_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+typedef errno_t
+(*ccdb_set_default_recv_fn)(struct tevent_req *req);
+
+typedef struct tevent_req *
+(*ccdb_get_default_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client);
+typedef errno_t
+(*ccdb_get_default_recv_fn)(struct tevent_req *req,
+ uuid_t dfl);
+
+
+typedef errno_t
+(*ccdb_list_all_cc_fn)(TALLOC_CTX *mem_ctx,
+ struct krb5_ctx *kctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *cdb,
+ struct kcm_ccache ***_cc_list);
+
+typedef struct tevent_req *
+(*ccdb_list_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client);
+typedef errno_t
+(*ccdb_list_recv_fn)(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t **_uuid_list);
+
+typedef struct tevent_req *
+(*ccdb_getbyname_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name);
+typedef errno_t
+(*ccdb_getbyname_recv_fn)(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc);
+
+typedef struct tevent_req *
+(*ccdb_getbyuuid_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+typedef errno_t
+(*ccdb_getbyuuid_recv_fn)(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc);
+
+typedef struct tevent_req *
+(*ccdb_name_by_uuid_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+typedef errno_t
+(*ccdb_name_by_uuid_recv_fn)(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ const char **_name);
+
+typedef struct tevent_req *
+(*ccdb_uuid_by_name_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name);
+typedef errno_t
+(*ccdb_uuid_by_name_recv_fn)(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t _uuid);
+
+typedef struct tevent_req *
+(*ccdb_create_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ struct kcm_ccache *cc);
+typedef errno_t
+(*ccdb_create_recv_fn)(struct tevent_req *req);
+
+typedef struct tevent_req *
+(*ccdb_mod_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct kcm_mod_ctx *mod_cc);
+typedef errno_t
+(*ccdb_mod_recv_fn)(struct tevent_req *req);
+
+typedef struct tevent_req *
+(*kcm_ccdb_store_cred_blob_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob);
+typedef errno_t
+(*kcm_ccdb_store_cred_blob_recv_fn)(struct tevent_req *req);
+
+typedef struct tevent_req *
+(*ccdb_delete_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid);
+typedef errno_t
+(*ccdb_delete_recv_fn)(struct tevent_req *req);
+
+/*
+ * Each ccache back end (for example memory or secdb) must implement
+ * all these functions. The functions are wrapped by the kcm_ccdb
+ * interface that performs additional sanity checks or contains shared
+ * logic such as access checks but in general doesn't assume anything
+ * about how the operations work.
+ */
+struct kcm_ccdb_ops {
+ ccdb_init_fn init;
+
+ ccdb_nextid_send_fn nextid_send;
+ ccdb_nextid_recv_fn nextid_recv;
+
+ ccdb_set_default_send_fn set_default_send;
+ ccdb_set_default_recv_fn set_default_recv;
+
+ ccdb_get_default_send_fn get_default_send;
+ ccdb_get_default_recv_fn get_default_recv;
+
+ ccdb_list_all_cc_fn list_all_cc;
+
+ ccdb_list_send_fn list_send;
+ ccdb_list_recv_fn list_recv;
+
+ ccdb_getbyname_send_fn getbyname_send;
+ ccdb_getbyname_recv_fn getbyname_recv;
+
+ ccdb_getbyuuid_send_fn getbyuuid_send;
+ ccdb_getbyuuid_recv_fn getbyuuid_recv;
+
+ ccdb_name_by_uuid_send_fn name_by_uuid_send;
+ ccdb_name_by_uuid_recv_fn name_by_uuid_recv;
+
+ ccdb_uuid_by_name_send_fn uuid_by_name_send;
+ ccdb_uuid_by_name_recv_fn uuid_by_name_recv;
+
+ ccdb_create_send_fn create_send;
+ ccdb_create_recv_fn create_recv;
+
+ ccdb_mod_send_fn mod_send;
+ ccdb_mod_recv_fn mod_recv;
+
+ kcm_ccdb_store_cred_blob_send_fn store_cred_send;
+ kcm_ccdb_store_cred_blob_recv_fn store_cred_recv;
+
+ ccdb_delete_send_fn delete_send;
+ ccdb_delete_recv_fn delete_recv;
+};
+
+extern const struct kcm_ccdb_ops ccdb_mem_ops;
+extern const struct kcm_ccdb_ops ccdb_secdb_ops;
+
+#endif /* _KCMSRV_CCACHE_BE_ */
diff --git a/src/responder/kcm/kcmsrv_ccache_binary.c b/src/responder/kcm/kcmsrv_ccache_binary.c
new file mode 100644
index 0000000..cc4191a
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_binary.c
@@ -0,0 +1,308 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2020 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 "config.h"
+
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/util_creds.h"
+#include "util/crypto/sss_crypto.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+
+static errno_t krb_data_to_bin(krb5_data *data, struct sss_iobuf *buf)
+{
+ return sss_iobuf_write_varlen(buf, (uint8_t *)data->data, data->length);
+}
+
+static errno_t princ_to_bin(krb5_principal princ, struct sss_iobuf *buf)
+{
+ errno_t ret;
+
+ if (princ == NULL) {
+ return sss_iobuf_write_uint8(buf, 0);
+ }
+
+ /* Mark that principal is not empty. */
+ ret = sss_iobuf_write_uint8(buf, 1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = krb_data_to_bin(&princ->realm, buf);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_iobuf_write_int32(buf, princ->type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_iobuf_write_int32(buf, princ->length);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ for (krb5_int32 i = 0; i < princ->length; i++) {
+ ret = krb_data_to_bin(&princ->data[i], buf);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t creds_to_bin(struct kcm_cred *creds, struct sss_iobuf *buf)
+{
+ struct kcm_cred *crd;
+ uint32_t count = 0;
+ errno_t ret;
+
+ DLIST_FOR_EACH(crd, creds) {
+ count++;
+ }
+
+ ret = sss_iobuf_write_uint32(buf, count);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ DLIST_FOR_EACH(crd, creds) {
+ ret = sss_iobuf_write_len(buf, (uint8_t *)crd->uuid, sizeof(uuid_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_iobuf_write_iobuf(buf, crd->cred_blob);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+errno_t kcm_ccache_to_sec_input_binary(TALLOC_CTX *mem_ctx,
+ struct kcm_ccache *cc,
+ struct sss_iobuf **_payload)
+{
+ struct sss_iobuf *buf;
+ errno_t ret;
+
+ buf = sss_iobuf_init_empty(mem_ctx, sizeof(krb5_principal_data), 0);
+ if (buf == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_iobuf_write_int32(buf, cc->kdc_offset);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = princ_to_bin(cc->client, buf);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = creds_to_bin(cc->creds, buf);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_payload = buf;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(buf);
+ }
+
+ return ret;
+}
+
+errno_t bin_to_krb_data(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *buf,
+ krb5_data *out)
+{
+ uint8_t *data;
+ size_t len;
+ errno_t ret;
+
+ ret = sss_iobuf_read_varlen(mem_ctx, buf, &data, &len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ out->magic = 0;
+ out->data = (char*)data;
+ out->length = len;
+
+ return EOK;
+}
+
+static errno_t bin_to_princ(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *buf,
+ krb5_principal *_princ)
+{
+ krb5_principal princ;
+ uint8_t non_empty;
+ krb5_int32 i;
+ errno_t ret;
+
+ ret = sss_iobuf_read_uint8(buf, &non_empty);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (non_empty == 0) {
+ *_princ = NULL;
+ return EOK;
+ }
+
+ princ = talloc_zero(mem_ctx, struct krb5_principal_data);
+ if (princ == NULL) {
+ return ENOMEM;
+ }
+ princ->magic = KV5M_PRINCIPAL;
+
+ ret = bin_to_krb_data(princ, buf, &princ->realm);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_iobuf_read_int32(buf, &princ->type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_iobuf_read_int32(buf, &princ->length);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ princ->data = talloc_array(princ, krb5_data, princ->length);
+ if (princ->length > 0 && princ->data == NULL) {
+ return ENOMEM;
+ }
+
+ for (i = 0; i < princ->length; i++) {
+ ret = bin_to_krb_data(princ, buf, &princ->data[i]);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ *_princ = princ;
+
+ return EOK;
+}
+
+static errno_t bin_to_creds(TALLOC_CTX *mem_ctx,
+ struct sss_iobuf *buf,
+ struct kcm_cred **_creds)
+{
+ struct kcm_cred *creds = NULL;
+ struct kcm_cred *crd;
+ struct sss_iobuf *cred_blob;
+ uint32_t count;
+ uuid_t uuid;
+ errno_t ret;
+
+ ret = sss_iobuf_read_uint32(buf, &count);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ ret = sss_iobuf_read_len(buf, sizeof(uuid_t), (uint8_t*)uuid);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_iobuf_read_iobuf(NULL, buf, &cred_blob);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ crd = kcm_cred_new(mem_ctx, uuid, cred_blob);
+ if (crd == NULL) {
+ talloc_free(cred_blob);
+ return ENOMEM;
+ }
+
+ DLIST_ADD(creds, crd);
+ }
+
+ *_creds = creds;
+
+ return EOK;
+}
+
+errno_t sec_kv_to_ccache_binary(TALLOC_CTX *mem_ctx,
+ const char *sec_key,
+ struct sss_iobuf *sec_value,
+ struct cli_creds *client,
+ struct kcm_ccache **_cc)
+{
+ struct kcm_ccache *cc;
+ errno_t ret;
+
+ cc = talloc_zero(mem_ctx, struct kcm_ccache);
+ if (cc == NULL) {
+ return ENOMEM;
+ }
+
+ ret = kcm_cc_set_header(cc, sec_key, client);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot store ccache header [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sss_iobuf_read_int32(sec_value, &cc->kdc_offset);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = bin_to_princ(cc, sec_value, &cc->client);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = bin_to_creds(cc, sec_value, &cc->creds);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ *_cc = cc;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cc);
+ }
+
+ return ret;
+}
diff --git a/src/responder/kcm/kcmsrv_ccache_key.c b/src/responder/kcm/kcmsrv_ccache_key.c
new file mode 100644
index 0000000..59d6045
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_key.c
@@ -0,0 +1,144 @@
+/*
+ SSSD
+
+ Copyright (C) Red Hat, 2020
+
+ 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 "config.h"
+
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+
+/*
+ * The secrets store is a key-value store at heart. We store the UUID
+ * and the name in the key to allow easy lookups by either part.
+ */
+#define SEC_KEY_SEPARATOR '-'
+
+const char *sec_key_create(TALLOC_CTX *mem_ctx,
+ const char *name,
+ uuid_t uuid)
+{
+ char uuid_str[UUID_STR_SIZE];
+
+ uuid_unparse(uuid, uuid_str);
+ return talloc_asprintf(mem_ctx,
+ "%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name);
+}
+
+static bool sec_key_valid(const char *sec_key)
+{
+ if (sec_key == NULL) {
+ return false;
+ }
+
+ if (strlen(sec_key) < UUID_STR_SIZE + 1) {
+ /* One char for separator (at UUID_STR_SIZE, because strlen doesn't
+ * include the '\0', but UUID_STR_SIZE does) and at least one for
+ * the name */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
+ return false;
+ }
+
+ if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
+ return false;
+ }
+
+ return true;
+}
+
+errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
+ const char *sec_key,
+ const char **_name,
+ uuid_t uuid)
+{
+ char uuid_str[UUID_STR_SIZE];
+
+ if (!sec_key_valid(sec_key)) {
+ return EINVAL;
+ }
+
+ strncpy(uuid_str, sec_key, sizeof(uuid_str) - 1);
+ if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
+ return EINVAL;
+ }
+ uuid_str[UUID_STR_SIZE - 1] = '\0';
+
+ *_name = talloc_strdup(mem_ctx, sec_key + UUID_STR_SIZE);
+ if (*_name == NULL) {
+ return ENOMEM;
+ }
+ uuid_parse(uuid_str, uuid);
+
+ return EOK;
+}
+
+errno_t sec_key_get_uuid(const char *sec_key,
+ uuid_t uuid)
+{
+ char uuid_str[UUID_STR_SIZE];
+
+ if (!sec_key_valid(sec_key)) {
+ return EINVAL;
+ }
+
+ strncpy(uuid_str, sec_key, UUID_STR_SIZE - 1);
+ uuid_str[UUID_STR_SIZE - 1] = '\0';
+ uuid_parse(uuid_str, uuid);
+ return EOK;
+}
+
+const char *sec_key_get_name(const char *sec_key)
+{
+ if (!sec_key_valid(sec_key)) {
+ return NULL;
+ }
+
+ return sec_key + UUID_STR_SIZE;
+}
+
+bool sec_key_match_name(const char *sec_key,
+ const char *name)
+{
+ if (!sec_key_valid(sec_key) || name == NULL) {
+ return false;
+ }
+
+ return strcmp(sec_key + UUID_STR_SIZE, name) == 0;
+}
+
+bool sec_key_match_uuid(const char *sec_key,
+ uuid_t uuid)
+{
+ errno_t ret;
+ uuid_t key_uuid;
+
+ /* Clear uuid value to avoid cppcheck warning. */
+ uuid_clear(key_uuid);
+
+ ret = sec_key_get_uuid(sec_key, key_uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot convert key to UUID\n");
+ return false;
+ }
+
+ return uuid_compare(key_uuid, uuid) == 0;
+}
diff --git a/src/responder/kcm/kcmsrv_ccache_mem.c b/src/responder/kcm/kcmsrv_ccache_mem.c
new file mode 100644
index 0000000..0e3a7b2
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_mem.c
@@ -0,0 +1,826 @@
+/*
+ SSSD
+
+ KCM Server - ccache in-memory storage
+
+ Copyright (C) Red Hat, 2016
+
+ 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 "config.h"
+
+#include <talloc.h>
+#include <stdio.h>
+
+#include "util/util.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+
+struct ccdb_mem;
+
+/*
+ * The KCM memory database is just a double-linked list of kcm_ccache structures
+ */
+struct ccache_mem_wrap {
+ struct kcm_ccache *cc;
+ bool is_default;
+
+ struct ccache_mem_wrap *next;
+ struct ccache_mem_wrap *prev;
+
+ struct ccdb_mem *mem_be;
+};
+
+struct ccdb_mem {
+ /* Both ccaches and the next-id are kept in memory */
+ struct ccache_mem_wrap *head;
+ unsigned int nextid;
+};
+
+static struct ccache_mem_wrap *memdb_get_by_uuid(struct ccdb_mem *memdb,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ uid_t uid;
+ struct ccache_mem_wrap *ccwrap = NULL;
+ struct ccache_mem_wrap *out = NULL;
+
+ uid = cli_creds_get_uid(client);
+
+ DLIST_FOR_EACH(ccwrap, memdb->head) {
+ if (ccwrap->cc == NULL) {
+ /* since KCM stores ccaches, better not crash.. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+ continue;
+ }
+
+ if (ccwrap->cc->owner.uid == uid) {
+ if (uuid_compare(uuid, ccwrap->cc->uuid) == 0) {
+ out = ccwrap;
+ break;
+ }
+ }
+ }
+
+ return out;
+}
+
+static struct ccache_mem_wrap *memdb_get_by_name(struct ccdb_mem *memdb,
+ struct cli_creds *client,
+ const char *name)
+{
+ uid_t uid;
+ struct ccache_mem_wrap *ccwrap = NULL;
+ struct ccache_mem_wrap *out = NULL;
+
+ uid = cli_creds_get_uid(client);
+
+ DLIST_FOR_EACH(ccwrap, memdb->head) {
+ if (ccwrap->cc == NULL) {
+ /* since KCM stores ccaches, better not crash.. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+ continue;
+ }
+
+ if (ccwrap->cc->owner.uid == uid) {
+ if (strcmp(ccwrap->cc->name, name) == 0) {
+ out = ccwrap;
+ break;
+ }
+ }
+ }
+
+ return out;
+}
+
+/* Since with the in-memory database, the database operations are just
+ * fake-async wrappers around otherwise sync operations, we don't often
+ * need any state, so we use this empty structure instead
+ */
+struct ccdb_mem_dummy_state {
+};
+
+static int ccwrap_destructor(void *ptr)
+{
+ struct ccache_mem_wrap *ccwrap = talloc_get_type(ptr, struct ccache_mem_wrap);
+
+ if (ccwrap == NULL) {
+ return 0;
+ }
+
+ if (ccwrap->cc != NULL) {
+ if (ccwrap->cc->creds) {
+ sss_erase_mem_securely(
+ sss_iobuf_get_data(ccwrap->cc->creds->cred_blob),
+ sss_iobuf_get_size(ccwrap->cc->creds->cred_blob));
+ }
+ }
+
+
+ DLIST_REMOVE(ccwrap->mem_be->head, ccwrap);
+
+ return 0;
+}
+
+static errno_t ccdb_mem_init(struct kcm_ccdb *db,
+ struct confdb_ctx *cdb,
+ const char *confdb_service_path)
+{
+ struct ccdb_mem *memdb = NULL;
+
+ memdb = talloc_zero(db, struct ccdb_mem);
+ if (memdb == NULL) {
+ return ENOMEM;
+ }
+ db->db_handle = memdb;
+
+ return EOK;
+}
+
+struct ccdb_mem_nextid_state {
+ unsigned int nextid;
+};
+
+static struct tevent_req *ccdb_mem_nextid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_nextid_state *state = NULL;
+ struct ccdb_mem *memdb = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_nextid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ if (memdb == NULL) {
+ ret = EIO;
+ goto immediate;
+ }
+
+ state->nextid = memdb->nextid++;
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_nextid_recv(struct tevent_req *req,
+ unsigned int *_nextid)
+{
+ struct ccdb_mem_nextid_state *state = tevent_req_data(req,
+ struct ccdb_mem_nextid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_nextid = state->nextid;
+ return EOK;
+}
+
+struct ccdb_mem_list_state {
+ uuid_t *uuid_list;
+};
+
+static struct tevent_req *ccdb_mem_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct ccache_mem_wrap *ccwrap = NULL;
+ struct ccdb_mem_list_state *state = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ size_t num_ccaches = 0;
+ size_t cc_index = 0;
+ errno_t ret;
+ uid_t uid;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ uid = cli_creds_get_uid(client);
+
+ DLIST_FOR_EACH(ccwrap, memdb->head) {
+ if (ccwrap->cc->owner.uid == uid) {
+ num_ccaches++;
+ }
+ }
+
+ state->uuid_list = talloc_zero_array(state, uuid_t, num_ccaches+1);
+ if (state->uuid_list == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ cc_index = 0;
+ DLIST_FOR_EACH(ccwrap, memdb->head) {
+ if (ccwrap->cc->owner.uid == uid) {
+ uuid_copy(state->uuid_list[cc_index], ccwrap->cc->uuid);
+ cc_index++;
+ }
+ }
+ uuid_clear(state->uuid_list[num_ccaches]);
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_list_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t **_uuid_list)
+{
+ struct ccdb_mem_list_state *state = tevent_req_data(req,
+ struct ccdb_mem_list_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_mem_set_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_dummy_state *state = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ struct ccache_mem_wrap *ccwrap = NULL;
+ uid_t uid = cli_creds_get_uid(client);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Reset all ccache defaults first */
+ DLIST_FOR_EACH(ccwrap, memdb->head) {
+ if (ccwrap->cc == NULL) {
+ /* since KCM stores ccaches, better not crash.. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+ continue;
+ }
+
+ if (ccwrap->cc->owner.uid == uid) {
+ ccwrap->is_default = false;
+ }
+ }
+
+ /* Then set the default for the right ccache. This also allows to
+ * pass a null uuid to just reset the old ccache (for example after
+ * deleting the default
+ */
+ ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+ if (ccwrap != NULL) {
+ ccwrap->is_default = true;
+ }
+
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_set_default_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+struct ccdb_mem_get_default_state {
+ uuid_t dfl_uuid;
+};
+
+static struct tevent_req *ccdb_mem_get_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_get_default_state *state = NULL;
+ struct ccache_mem_wrap *ccwrap = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ uid_t uid = cli_creds_get_uid(client);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_get_default_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+
+ /* Reset all ccache defaults first */
+ DLIST_FOR_EACH(ccwrap, memdb->head) {
+ if (ccwrap->cc == NULL) {
+ /* since KCM stores ccaches, better not crash.. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: ccwrap contains NULL cc\n");
+ continue;
+ }
+
+ if (ccwrap->cc->owner.uid == uid && ccwrap->is_default == true) {
+ break;
+ }
+ }
+
+ if (ccwrap == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "No ccache marked as default, returning null ccache\n");
+ uuid_clear(state->dfl_uuid);
+ } else {
+ uuid_copy(state->dfl_uuid, ccwrap->cc->uuid);
+ }
+
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_get_default_recv(struct tevent_req *req,
+ uuid_t dfl)
+{
+ struct ccdb_mem_get_default_state *state = tevent_req_data(req,
+ struct ccdb_mem_get_default_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ uuid_copy(dfl, state->dfl_uuid);
+ return EOK;
+}
+
+struct ccdb_mem_getbyuuid_state {
+ struct kcm_ccache *cc;
+};
+
+static struct tevent_req *ccdb_mem_getbyuuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_getbyuuid_state *state = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ struct ccache_mem_wrap *ccwrap = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyuuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+ if (ccwrap != NULL) {
+ /* In order to provide a consistent interface, we need to let the caller
+ * of getbyXXX own the ccache, therefore the memory back end returns a shallow
+ * copy of the ccache
+ */
+ state->cc = kcm_cc_dup(state, ccwrap->cc);
+ if (state->cc == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_getbyuuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct ccdb_mem_getbyuuid_state *state = tevent_req_data(req,
+ struct ccdb_mem_getbyuuid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+struct ccdb_mem_getbyname_state {
+ struct kcm_ccache *cc;
+};
+
+static struct tevent_req *ccdb_mem_getbyname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_getbyname_state *state = NULL;
+ struct ccache_mem_wrap *ccwrap = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_getbyname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ccwrap = memdb_get_by_name(memdb, client, name);
+ if (ccwrap != NULL) {
+ /* In order to provide a consistent interface, we need to let the caller
+ * of getbyXXX own the ccache, therefore the memory back end returns a shallow
+ * copy of the ccache
+ */
+ state->cc = kcm_cc_dup(state, ccwrap->cc);
+ if (state->cc == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_getbyname_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct ccdb_mem_getbyname_state *state = tevent_req_data(req,
+ struct ccdb_mem_getbyname_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+struct ccdb_mem_name_by_uuid_state {
+ const char *name;
+};
+
+struct tevent_req *ccdb_mem_name_by_uuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_name_by_uuid_state *state = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ struct ccache_mem_wrap *ccwrap = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_name_by_uuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+ if (ccwrap == NULL) {
+ ret = ERR_KCM_CC_END;
+ goto immediate;
+ }
+
+ state->name = talloc_strdup(state, ccwrap->cc->name);
+ if (state->name == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+errno_t ccdb_mem_name_by_uuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ const char **_name)
+{
+ struct ccdb_mem_name_by_uuid_state *state = tevent_req_data(req,
+ struct ccdb_mem_name_by_uuid_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_name = talloc_steal(mem_ctx, state->name);
+ return EOK;
+}
+
+struct ccdb_mem_uuid_by_name_state {
+ uuid_t uuid;
+};
+
+struct tevent_req *ccdb_mem_uuid_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_uuid_by_name_state *state = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ struct ccache_mem_wrap *ccwrap = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_uuid_by_name_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ccwrap = memdb_get_by_name(memdb, client, name);
+ if (ccwrap == NULL) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ }
+
+ uuid_copy(state->uuid, ccwrap->cc->uuid);
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+errno_t ccdb_mem_uuid_by_name_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t _uuid)
+{
+ struct ccdb_mem_uuid_by_name_state *state = tevent_req_data(req,
+ struct ccdb_mem_uuid_by_name_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ uuid_copy(_uuid, state->uuid);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_mem_create_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ struct kcm_ccache *cc)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_dummy_state *state = NULL;
+ struct ccache_mem_wrap *ccwrap;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ccwrap = talloc_zero(memdb, struct ccache_mem_wrap);
+ if (ccwrap == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ ccwrap->cc = cc;
+ ccwrap->mem_be = memdb;
+ talloc_steal(ccwrap, cc);
+
+ DLIST_ADD(memdb->head, ccwrap);
+ talloc_set_destructor((TALLOC_CTX *) ccwrap, ccwrap_destructor);
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_create_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_mem_mod_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct kcm_mod_ctx *mod_cc)
+{
+ errno_t ret;
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_dummy_state *state = NULL;
+ struct ccache_mem_wrap *ccwrap = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* UUID is immutable, so search by that */
+ ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+ if (ccwrap == NULL) {
+ ret = ERR_KCM_CC_END;
+ goto immediate;
+ }
+
+ ret = kcm_mod_cc(ccwrap->cc, mod_cc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot modify ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_mod_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_mem_store_cred_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_dummy_state *state = NULL;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ struct ccache_mem_wrap *ccwrap = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+ if (ccwrap == NULL) {
+ ret = ERR_KCM_CC_END;
+ goto immediate;
+ }
+
+ ret = kcm_cc_store_cred_blob(ccwrap->cc, cred_blob);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot store credentials to ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_store_cred_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_mem_delete_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_mem_dummy_state *state = NULL;
+ struct ccache_mem_wrap *ccwrap;
+ struct ccdb_mem *memdb = talloc_get_type(db->db_handle, struct ccdb_mem);
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_mem_dummy_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ccwrap = memdb_get_by_uuid(memdb, client, uuid);
+ if (ccwrap == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "BUG: Attempting to free unknown ccache\n");
+ ret = ERR_KCM_CC_END;
+ goto immediate;
+ }
+
+ ret = EOK;
+ /* Destructor takes care of everything */
+ talloc_free(ccwrap);
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_mem_delete_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+const struct kcm_ccdb_ops ccdb_mem_ops = {
+ .init = ccdb_mem_init,
+
+ .nextid_send = ccdb_mem_nextid_send,
+ .nextid_recv = ccdb_mem_nextid_recv,
+
+ .set_default_send = ccdb_mem_set_default_send,
+ .set_default_recv = ccdb_mem_set_default_recv,
+
+ .get_default_send = ccdb_mem_get_default_send,
+ .get_default_recv = ccdb_mem_get_default_recv,
+
+ .list_send = ccdb_mem_list_send,
+ .list_recv = ccdb_mem_list_recv,
+
+ .getbyname_send = ccdb_mem_getbyname_send,
+ .getbyname_recv = ccdb_mem_getbyname_recv,
+
+ .getbyuuid_send = ccdb_mem_getbyuuid_send,
+ .getbyuuid_recv = ccdb_mem_getbyuuid_recv,
+
+ .name_by_uuid_send = ccdb_mem_name_by_uuid_send,
+ .name_by_uuid_recv = ccdb_mem_name_by_uuid_recv,
+
+ .uuid_by_name_send = ccdb_mem_uuid_by_name_send,
+ .uuid_by_name_recv = ccdb_mem_uuid_by_name_recv,
+
+ .create_send = ccdb_mem_create_send,
+ .create_recv = ccdb_mem_create_recv,
+
+ .mod_send = ccdb_mem_mod_send,
+ .mod_recv = ccdb_mem_mod_recv,
+
+ .store_cred_send = ccdb_mem_store_cred_send,
+ .store_cred_recv = ccdb_mem_store_cred_recv,
+
+ .delete_send = ccdb_mem_delete_send,
+ .delete_recv = ccdb_mem_delete_recv,
+};
diff --git a/src/responder/kcm/kcmsrv_ccache_pvt.h b/src/responder/kcm/kcmsrv_ccache_pvt.h
new file mode 100644
index 0000000..9d5afed
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_pvt.h
@@ -0,0 +1,61 @@
+/*
+ SSSD
+
+ KCM Server - the KCM ccache operations - private structures
+
+ Should be accessed only from the ccache layer.
+
+ Copyright (C) Red Hat, 2016
+
+ 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 _KCMSRV_CCACHE_PVT_H
+#define _KCMSRV_CCACHE_PVT_H
+
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+
+struct kcm_ccache_owner {
+ uid_t uid;
+ gid_t gid;
+};
+
+struct kcm_cred {
+ struct sss_iobuf *cred_blob;
+ /* Randomly generated 16 bytes */
+ uuid_t uuid;
+
+ struct kcm_cred *next;
+ struct kcm_cred *prev;
+};
+
+struct kcm_ccdb {
+ struct tevent_context *ev;
+
+ void *db_handle;
+ const struct kcm_ccdb_ops *ops;
+};
+
+struct kcm_ccache {
+ const char *name;
+ struct kcm_ccache_owner owner;
+ uuid_t uuid;
+
+ krb5_principal client;
+ int32_t kdc_offset;
+
+ struct kcm_cred *creds;
+};
+
+#endif /* _KCMSRV_CCACHE_PVT_H */
diff --git a/src/responder/kcm/kcmsrv_ccache_secdb.c b/src/responder/kcm/kcmsrv_ccache_secdb.c
new file mode 100644
index 0000000..ff5e6e2
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ccache_secdb.c
@@ -0,0 +1,1671 @@
+/*
+ SSSD
+
+ KCM Server - ccache storage using libsss_secrets
+
+ Copyright (C) Red Hat, 2016
+
+ 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 "config.h"
+
+#include <talloc.h>
+#include <stdio.h>
+
+#include "util/util.h"
+#include "secrets/secrets.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/sss_krb5.h"
+#include "util/strtonum.h"
+#include "responder/kcm/kcmsrv_ccache_pvt.h"
+#include "responder/kcm/kcmsrv_ccache_be.h"
+#include "responder/kcm/kcm_renew.h"
+#include "providers/krb5/krb5_ccache.h"
+
+#define KCM_SECDB_URL "persistent"
+#define KCM_SECDB_BASE_FMT KCM_SECDB_URL"/%"SPRIuid"/"
+#define KCM_SECDB_CCACHE_FMT KCM_SECDB_BASE_FMT"ccache/"
+#define KCM_SECDB_DFL_FMT KCM_SECDB_BASE_FMT"default"
+
+static errno_t sec_get(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req,
+ struct sss_iobuf **_buf)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ uint8_t *data;
+ size_t len;
+ struct sss_iobuf *buf;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_sec_get(tmp_ctx, req, &data, &len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot retrieve the secret [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ buf = sss_iobuf_init_steal(tmp_ctx, data, len);
+ if (buf == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot init the iobuf\n");
+ ret = EIO;
+ goto done;
+ }
+
+ *_buf = talloc_steal(mem_ctx, buf);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sec_put(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req,
+ struct sss_iobuf *buf)
+{
+ errno_t ret;
+
+ ret = sss_sec_put(req, sss_iobuf_get_data(buf), sss_iobuf_get_size(buf));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot write the secret [%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static errno_t sec_update(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req,
+ struct sss_iobuf *buf)
+{
+ errno_t ret;
+
+ ret = sss_sec_update(req, sss_iobuf_get_data(buf), sss_iobuf_get_size(buf));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot write the secret [%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static const char *secdb_container_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client)
+{
+ return talloc_asprintf(mem_ctx,
+ KCM_SECDB_CCACHE_FMT,
+ cli_creds_get_uid(client));
+}
+
+static const char *secdb_cc_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client,
+ const char *secdb_key)
+{
+ return talloc_asprintf(mem_ctx,
+ KCM_SECDB_CCACHE_FMT"%s",
+ cli_creds_get_uid(client),
+ secdb_key);
+}
+
+static const char *secdb_dfl_url_create(TALLOC_CTX *mem_ctx,
+ struct cli_creds *client)
+{
+ return talloc_asprintf(mem_ctx,
+ KCM_SECDB_DFL_FMT,
+ cli_creds_get_uid(client));
+}
+
+static errno_t kcm_ccache_to_secdb_kv(TALLOC_CTX *mem_ctx,
+ struct kcm_ccache *cc,
+ struct cli_creds *client,
+ const char **_url,
+ struct sss_iobuf **_payload)
+{
+ errno_t ret;
+ const char *url;
+ const char *key;
+ TALLOC_CTX *tmp_ctx;
+ struct sss_iobuf *payload;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ key = sec_key_create(tmp_ctx, cc->name, cc->uuid);
+ if (key == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create key for %s\n", cc->name);
+ ret = ENOMEM;
+ goto done;
+ }
+
+ url = secdb_cc_url_create(tmp_ctx, client, key);
+ if (url == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create URL from %s\n", key);
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = kcm_ccache_to_sec_input_binary(mem_ctx, cc, &payload);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert ccache to a secret [%d][%s]\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Created URL %s\n", url);
+ *_url = talloc_steal(mem_ctx, url);
+ *_payload = talloc_steal(mem_ctx, payload);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct ccdb_secdb {
+ struct sss_sec_ctx *sctx;
+};
+
+/* Since with the synchronous database, the database operations are just
+ * fake-async wrappers around otherwise sync operations, we don't often
+ * need any state structure, unless the _recv() function returns anything,
+ * so we use this empty structure instead
+ */
+struct ccdb_secdb_state {
+};
+
+static errno_t secdb_container_url_req(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sctx,
+ struct cli_creds *client,
+ struct sss_sec_req **_sreq)
+{
+ const char *url;
+ struct sss_sec_req *sreq;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ url = secdb_container_url_create(tmp_ctx, client);
+ if (url == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_sec_new_req(tmp_ctx, sctx, url, geteuid(), &sreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Created request for URL %s\n", url);
+ *_sreq = talloc_steal(mem_ctx, sreq);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t secdb_cc_url_req(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sctx,
+ struct cli_creds *client,
+ const char *secdb_url,
+ struct sss_sec_req **_sreq)
+{
+ struct sss_sec_req *sreq;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_sec_new_req(tmp_ctx, sctx, secdb_url, geteuid(), &sreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Created request for URL %s\n", secdb_url);
+ *_sreq = talloc_steal(mem_ctx, sreq);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t secdb_cc_key_req(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sctx,
+ struct cli_creds *client,
+ const char *secdb_key,
+ struct sss_sec_req **_sreq)
+{
+ const char *url;
+ struct sss_sec_req *sreq;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ url = secdb_cc_url_create(tmp_ctx, client, secdb_key);
+ if (url == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = secdb_cc_url_req(tmp_ctx, sctx, client, url, &sreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Created request for URL %s\n", url);
+ *_sreq = talloc_steal(mem_ctx, sreq);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t secdb_dfl_url_req(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sctx,
+ struct cli_creds *client,
+ struct sss_sec_req **_sreq)
+{
+ const char *url;
+ struct sss_sec_req *sreq;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ url = secdb_dfl_url_create(tmp_ctx, client);
+ if (url == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_sec_new_req(tmp_ctx, sctx, url, geteuid(), &sreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Created request for URL %s\n", url);
+ *_sreq = talloc_steal(mem_ctx, sreq);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t key_by_uuid(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sctx,
+ struct cli_creds *client,
+ uuid_t uuid,
+ char **_key)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *key_match = NULL;
+ char **keys = NULL;
+ size_t nkeys;
+ struct sss_sec_req *sreq = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = secdb_container_url_req(tmp_ctx, sctx, client, &sreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sss_sec_list(tmp_ctx, sreq, &keys, &nkeys);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "The container was not found\n");
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ for (size_t i = 0; i < nkeys; i++) {
+ if (sec_key_match_uuid(keys[i], uuid)) {
+ key_match = keys[i];
+ break;
+ }
+ }
+
+ if (key_match == NULL) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No key matched\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found key %s\n", key_match);
+ *_key = talloc_steal(mem_ctx, key_match);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t key_by_name(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sctx,
+ struct cli_creds *client,
+ const char *name,
+ char **_key)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *key_match = NULL;
+ char **keys = NULL;
+ size_t nkeys;
+ struct sss_sec_req *sreq = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = secdb_container_url_req(tmp_ctx, sctx, client, &sreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sss_sec_list(tmp_ctx, sreq, &keys, &nkeys);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "The container was not found\n");
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ for (size_t i = 0; i < nkeys; i++) {
+ if (sec_key_match_name(keys[i], name)) {
+ key_match = keys[i];
+ break;
+ }
+ }
+
+ if (key_match == NULL) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No key matched\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found key %s\n", key_match);
+ *_key = talloc_steal(mem_ctx, key_match);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t secdb_get_cc(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sctx,
+ const char *secdb_key,
+ struct cli_creds *client,
+ struct kcm_ccache **_cc)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct kcm_ccache *cc = NULL;
+ struct sss_sec_req *sreq = NULL;
+ struct sss_iobuf *ccbuf;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = secdb_cc_key_req(tmp_ctx, sctx, client, secdb_key, &sreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot create secdb request [%d][%s]\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sec_get(tmp_ctx, sreq, &ccbuf);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get the secret [%d][%s]\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sec_kv_to_ccache_binary(tmp_ctx, secdb_key, ccbuf, client, &cc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot convert data to ccache [%d]: %s, "
+ "deleting this entry\n", ret, sss_strerror(ret));
+ ret = sss_sec_delete(sreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to delete entry: [%d]: %s",
+ ret, sss_strerror(ret));
+ }
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Fetched the ccache\n");
+ *_cc = talloc_steal(mem_ctx, cc);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t ccdb_secdb_init(struct kcm_ccdb *db,
+ struct confdb_ctx *cdb,
+ const char *confdb_service_path)
+{
+ struct ccdb_secdb *secdb = NULL;
+ errno_t ret;
+ struct sss_sec_quota *kcm_quota;
+ struct sss_sec_quota_opt dfl_kcm_nest_level = {
+ .opt_name = CONFDB_KCM_CONTAINERS_NEST_LEVEL,
+ .default_value = DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
+ };
+ struct sss_sec_quota_opt dfl_kcm_max_secrets = {
+ .opt_name = CONFDB_KCM_MAX_CCACHES,
+ .default_value = DEFAULT_SEC_KCM_MAX_SECRETS,
+ };
+ struct sss_sec_quota_opt dfl_kcm_max_uid_secrets = {
+ .opt_name = CONFDB_KCM_MAX_UID_CCACHES,
+ .default_value = DEFAULT_SEC_KCM_MAX_UID_SECRETS,
+ };
+ struct sss_sec_quota_opt dfl_kcm_max_payload_size = {
+ .opt_name = CONFDB_KCM_MAX_CCACHE_SIZE,
+ .default_value = DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE,
+ };
+
+
+ secdb = talloc_zero(db, struct ccdb_secdb);
+ if (secdb == NULL) {
+ return ENOMEM;
+ }
+
+ kcm_quota = talloc_zero(secdb, struct sss_sec_quota);
+ if (kcm_quota == NULL) {
+ talloc_free(secdb);
+ return ENOMEM;
+ }
+
+ ret = sss_sec_get_quota(cdb,
+ confdb_service_path,
+ &dfl_kcm_nest_level,
+ &dfl_kcm_max_secrets,
+ &dfl_kcm_max_uid_secrets,
+ &dfl_kcm_max_payload_size,
+ kcm_quota);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get KCM global quotas [%d]: %s\n",
+ ret, sss_strerror(ret));
+ talloc_free(secdb);
+ return ret;
+ }
+
+ if (kcm_quota->max_uid_secrets > 0) {
+ kcm_quota->max_uid_secrets += KCM_MAX_UID_EXTRA_SECRETS;
+ }
+
+ ret = sss_sec_init(db, kcm_quota, &secdb->sctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot initialize the security database\n");
+ talloc_free(secdb);
+ return ret;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "secdb initialized\n");
+ db->db_handle = secdb;
+ return EOK;
+}
+
+struct ccdb_secdb_nextid_state {
+ unsigned int nextid;
+};
+
+static bool is_in_use(char **keys, size_t nkeys, const char *nextid_name)
+{
+ for (size_t i = 0; i < nkeys; i++) {
+ if (sec_key_match_name(keys[i], nextid_name) == true) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static struct tevent_req *ccdb_secdb_nextid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_nextid_state *state = NULL;
+ struct ccdb_secdb *secdb = NULL;
+ const int maxtries = 3;
+ int numtry;
+ errno_t ret;
+ struct sss_sec_req *sreq = NULL;
+ char **keys = NULL;
+ size_t nkeys;
+ char *nextid_name = NULL;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Generating a new ID\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_nextid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ if (secdb == NULL) {
+ ret = EIO;
+ goto immediate;
+ }
+
+ ret = secdb_container_url_req(state, secdb->sctx, client, &sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sss_sec_list(state, sreq, &keys, &nkeys);
+ if (ret == ENOENT) {
+ keys = NULL;
+ nkeys = 0;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot list keys [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ for (numtry = 0; numtry < maxtries; numtry++) {
+ state->nextid = sss_rand() % MAX_CC_NUM;
+ nextid_name = talloc_asprintf(state, "%"SPRIuid":%u",
+ cli_creds_get_uid(client),
+ state->nextid);
+ if (nextid_name == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ if (!is_in_use(keys, nkeys, nextid_name)) {
+ break;
+ }
+ }
+
+ if (numtry >= maxtries) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to find a random ccache in %d tries\n", numtry);
+ ret = EBUSY;
+ goto immediate;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_LIBS, "Generated next ID %d\n", state->nextid);
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_nextid_recv(struct tevent_req *req,
+ unsigned int *_nextid)
+{
+ struct ccdb_secdb_nextid_state *state = tevent_req_data(req,
+ struct ccdb_secdb_nextid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_nextid = state->nextid;
+ return EOK;
+}
+
+static struct tevent_req *ccdb_secdb_set_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_state *state = NULL;
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ errno_t ret;
+ char uuid_str[UUID_STR_SIZE];
+ struct sss_sec_req *sreq = NULL;
+ struct sss_iobuf *iobuf;
+ char *cur_default;
+
+ uuid_unparse(uuid, uuid_str);
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Setting the default ccache to %s\n", uuid_str);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = secdb_dfl_url_req(state, secdb->sctx, client, &sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ iobuf = sss_iobuf_init_readonly(state,
+ (const uint8_t *) uuid_str,
+ UUID_STR_SIZE);
+ if (iobuf == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ ret = sss_sec_get(state, sreq, (uint8_t**)&cur_default, NULL);
+ if (ret == ENOENT) {
+ ret = sec_put(state, sreq, iobuf);
+ } else if (ret == EOK) {
+ ret = sec_update(state, sreq, iobuf);
+ }
+
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = EOK;
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Set the default ccache\n");
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_set_default_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+struct ccdb_secdb_get_default_state {
+ uuid_t uuid;
+};
+
+static struct tevent_req *ccdb_secdb_get_default_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_get_default_state *state = NULL;
+ errno_t ret;
+ struct sss_sec_req *sreq = NULL;
+ struct sss_iobuf *dfl_iobuf = NULL;
+ size_t uuid_size;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting the default ccache\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_get_default_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = secdb_dfl_url_req(state, secdb->sctx, client, &sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sec_get(state, sreq, &dfl_iobuf);
+ if (ret == ENOENT) {
+ uuid_clear(state->uuid);
+ ret = EOK;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ uuid_size = sss_iobuf_get_size(dfl_iobuf);
+ if (uuid_size != UUID_STR_SIZE) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unexpected UUID size %zu, deleting this entry\n", uuid_size);
+ ret = sss_sec_delete(sreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to delete entry: [%d]: %s, "
+ "consider manual removal of "SECRETS_DB_PATH"/secrets.ldb\n",
+ ret, sss_strerror(ret));
+ sss_log(SSS_LOG_CRIT,
+ "Can't delete an entry from "SECRETS_DB_PATH"/secrets.ldb, "
+ "content seems to be corrupted. Consider file removal. "
+ "(Take a note, this will delete all credentials managed "
+ "via sssd_kcm)");
+ }
+ uuid_clear(state->uuid);
+ ret = EOK;
+ goto immediate;
+ }
+
+ uuid_parse((const char *) sss_iobuf_get_data(dfl_iobuf), state->uuid);
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got the default ccache\n");
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_get_default_recv(struct tevent_req *req,
+ uuid_t uuid)
+{
+ struct ccdb_secdb_get_default_state *state = tevent_req_data(req,
+ struct ccdb_secdb_get_default_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ uuid_copy(uuid, state->uuid);
+ return EOK;
+}
+
+static errno_t ccdb_secdb_get_cc_for_uuid(TALLOC_CTX *mem_ctx,
+ size_t uuid_list_count,
+ const char **uuid_list,
+ const char **uid_list,
+ struct ccdb_secdb *secdb,
+ struct kcm_ccache ***_cc_list)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ uid_t uid;
+ char **list;
+ uuid_t uuid;
+ char *uuid_str;
+ char *secdb_key;
+ struct cli_creds cli_cred;
+ struct kcm_ccache **cc_list;
+ int real_count = 0;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ return ret;
+ }
+
+ cc_list = talloc_zero_array(tmp_ctx, struct kcm_ccache *, uuid_list_count + 1);
+ if (cc_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (size_t i = 0; i < uuid_list_count; i++) {
+ struct passwd *pwd;
+
+ ret = split_on_separator(tmp_ctx, uuid_list[i], ':', true, true,
+ &list, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "split on separator failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ uuid_str = list[0];
+ uuid_str[UUID_STR_SIZE - 1] = '\0';
+ ret = uuid_parse(uuid_str, uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "uuid parse of [%s] failed [%d]: %s\n",
+ list[0], ret, sss_strerror(ret));
+ goto done;
+ }
+ uid = strtouint32(uid_list[i], NULL, 10);
+ ret = errno;
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid UID [%s] conversion to uint32 "
+ "[%d]: %s\n", uid_list[i], ret,
+ sss_strerror(ret));
+ goto done;
+ }
+
+ errno = 0;
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to resolve user [%d] who "
+ "is the owner of an existing ccache [%d]: %s\n",
+ uid, ret, sss_strerror(ret));
+ /* Not fatal */
+ continue;
+ }
+
+ cli_cred.ucred.uid = pwd->pw_uid;
+ cli_cred.ucred.gid = pwd->pw_gid;
+
+ ret = key_by_uuid(tmp_ctx, secdb->sctx, &cli_cred, uuid, &secdb_key);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "key_by_uuid() failed for uuid = '%s'", uuid_str);
+ goto done;
+ }
+
+ ret = secdb_get_cc(cc_list, secdb->sctx, secdb_key, &cli_cred,
+ &cc_list[real_count]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to get ccache [%d]: %s\n", ret, sss_strerror(ret));
+ /* probably ccache in old format was met and purged, just skip */
+ continue;
+ }
+
+ if (cc_list[real_count] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to get cc for uuid = '%s' and uid = %s\n",
+ uuid_list[i], uid_list[i]);
+ ret = EIO;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Retrieved ccache [%s]\n", cc_list[real_count]->name);
+ real_count++;
+ }
+
+ cc_list = talloc_realloc(tmp_ctx, cc_list, struct kcm_ccache *,
+ real_count + 1);
+ if (cc_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cc_list[real_count] = NULL;
+ *_cc_list = talloc_steal(mem_ctx, cc_list);
+
+ return EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct ccdb_secdb_list_state {
+ uuid_t *uuid_list;
+};
+
+static errno_t ccdb_secdb_list_all_cc(TALLOC_CTX *mem_ctx,
+ struct krb5_ctx *krb5_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct kcm_ccache ***_cc_list)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ const char **uid_list;
+ const char **uuid_list;
+ size_t uuid_list_count;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Retrieving all ccaches\n");
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ return ret;
+ }
+
+ ret = sss_sec_list_cc_uuids(tmp_ctx, secdb->sctx, &uuid_list, &uid_list, &uuid_list_count);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Error retrieving ccache uuid list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ } else if (ret == ENOENT) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found [%zu] ccache uuids\n", uuid_list_count);
+
+ /* New count is full cc list size minus getpwuid() failures */
+ ret = ccdb_secdb_get_cc_for_uuid(mem_ctx, uuid_list_count, uuid_list,
+ uid_list, secdb, _cc_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Error getting cc list from uuid list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Retrieving all caches done\n");
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static struct tevent_req *ccdb_secdb_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_list_state *state = NULL;
+ errno_t ret;
+ char **keys = NULL;
+ size_t nkeys;
+ struct sss_sec_req *sreq = NULL;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all ccaches\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = secdb_container_url_req(state, secdb->sctx, client, &sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sss_sec_list(state, sreq, &keys, &nkeys);
+ if (ret == ENOENT) {
+ nkeys = 0;
+ /* Fall through and return an empty list */
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu ccaches\n", nkeys);
+
+ state->uuid_list = talloc_array(state, uuid_t, nkeys + 1);
+ if (state->uuid_list == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ for (size_t i = 0; i < nkeys; i++) {
+ ret = sec_key_get_uuid(keys[i],
+ state->uuid_list[i]);
+ if (ret != EOK) {
+ goto immediate;
+ }
+ }
+ /* Sentinel */
+ uuid_clear(state->uuid_list[nkeys]);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Listing all caches done\n");
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_list_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uuid_t **_uuid_list)
+{
+ struct ccdb_secdb_list_state *state = tevent_req_data(req,
+ struct ccdb_secdb_list_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_uuid_list = talloc_steal(mem_ctx, state->uuid_list);
+ return EOK;
+}
+
+struct ccdb_secdb_getbyuuid_state {
+ struct kcm_ccache *cc;
+};
+
+static struct tevent_req *ccdb_secdb_getbyuuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_getbyuuid_state *state = NULL;
+ errno_t ret;
+ char *secdb_key = NULL;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by UUID\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_getbyuuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = key_by_uuid(state, secdb->sctx, client, uuid, &secdb_key);
+ if (ret == ENOENT) {
+ state->cc = NULL;
+ ret = EOK;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = secdb_get_cc(state, secdb->sctx, secdb_key, client, &state->cc);
+ if (ret == ENOENT) {
+ state->cc = NULL;
+ ret = EOK;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_getbyuuid_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct ccdb_secdb_getbyuuid_state *state = tevent_req_data(req,
+ struct ccdb_secdb_getbyuuid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+struct ccdb_secdb_getbyname_state {
+ struct kcm_ccache *cc;
+};
+
+static struct tevent_req *ccdb_secdb_getbyname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_getbyname_state *state = NULL;
+ errno_t ret;
+ char *secdb_key = NULL;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Getting ccache by name\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_getbyname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = key_by_name(state, secdb->sctx, client, name, &secdb_key);
+ if (ret == ENOENT) {
+ state->cc = NULL;
+ ret = EOK;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = secdb_get_cc(state, secdb->sctx, secdb_key, client, &state->cc);
+ if (ret == ENOENT) {
+ state->cc = NULL;
+ ret = EOK;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by name\n");
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_getbyname_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ccache **_cc)
+{
+ struct ccdb_secdb_getbyname_state *state = tevent_req_data(req,
+ struct ccdb_secdb_getbyname_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_cc = talloc_steal(mem_ctx, state->cc);
+ return EOK;
+}
+
+
+struct ccdb_secdb_name_by_uuid_state {
+ const char *name;
+};
+
+struct tevent_req *ccdb_secdb_name_by_uuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_name_by_uuid_state *state = NULL;
+ errno_t ret;
+ char *key;
+ const char *name;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Translating UUID to name\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_name_by_uuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = key_by_uuid(state, secdb->sctx, client, uuid, &key);
+ if (ret == ENOENT) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ name = sec_key_get_name(key);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Malformed key, cannot get name\n");
+ goto immediate;
+ }
+
+ state->name = talloc_strdup(state, name);
+ if (state->name == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+errno_t ccdb_secdb_name_by_uuid_recv(struct tevent_req *req,
+ TALLOC_CTX *sec_ctx,
+ const char **_name)
+{
+ struct ccdb_secdb_name_by_uuid_state *state = tevent_req_data(req,
+ struct ccdb_secdb_name_by_uuid_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_name = talloc_steal(sec_ctx, state->name);
+ return EOK;
+}
+
+struct ccdb_secdb_uuid_by_name_state {
+ uuid_t uuid;
+};
+
+struct tevent_req *ccdb_secdb_uuid_by_name_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ const char *name)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_uuid_by_name_state *state = NULL;
+ errno_t ret;
+ char *key;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Translating name to UUID\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_uuid_by_name_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = key_by_name(state, secdb->sctx, client, name, &key);
+ if (ret == ENOENT) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sec_key_get_uuid(key, state->uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Malformed key, cannot get UUID\n");
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got ccache by UUID\n");
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_uuid_by_name_recv(struct tevent_req *req,
+ TALLOC_CTX *sec_ctx,
+ uuid_t _uuid)
+{
+ struct ccdb_secdb_uuid_by_name_state *state = tevent_req_data(req,
+ struct ccdb_secdb_uuid_by_name_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ uuid_copy(_uuid, state->uuid);
+ return EOK;
+}
+
+
+static struct tevent_req *ccdb_secdb_create_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ struct kcm_ccache *cc)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_state *state = NULL;
+ errno_t ret;
+ struct sss_sec_req *container_req = NULL;
+ struct sss_sec_req *ccache_req = NULL;
+ const char *url;
+ struct sss_iobuf *ccache_payload;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Creating ccache storage for %s\n", cc->name);
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Do the encoding asap so that if we fail, we don't even attempt any
+ * writes */
+ ret = kcm_ccache_to_secdb_kv(state, cc, client, &url, &ccache_payload);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot convert cache %s to JSON [%d]: %s\n",
+ cc->name, ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Creating the ccache container\n");
+ ret = secdb_container_url_req(state, secdb->sctx, client, &container_req);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sss_sec_create_container(container_req);
+ if (ret == EEXIST) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Container already exists, ignoring\n");
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to create the ccache container\n");
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "ccache container created\n");
+ DEBUG(SSSDBG_TRACE_INTERNAL, "creating empty ccache payload\n");
+
+ ret = secdb_cc_url_req(state, secdb->sctx, client, url, &ccache_req);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sec_put(state, ccache_req, ccache_payload);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "payload created\n");
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_create_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_secdb_mod_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct kcm_mod_ctx *mod_cc)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_state *state = NULL;
+ errno_t ret;
+ char *secdb_key = NULL;
+ struct kcm_ccache *cc = NULL;
+ struct sss_iobuf *payload = NULL;
+ struct sss_sec_req *sreq = NULL;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Modifying ccache\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = key_by_uuid(state, secdb->sctx, client, uuid, &secdb_key);
+ if (ret == ENOENT) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = secdb_get_cc(state, secdb->sctx, secdb_key, client, &cc);
+ if (ret == ENOENT) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = kcm_mod_cc(cc, mod_cc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot modify ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ ret = kcm_ccache_to_sec_input_binary(state, cc, &payload);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = secdb_cc_key_req(state, secdb->sctx, client, secdb_key, &sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sec_update(state, sreq, payload);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_mod_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_secdb_store_cred_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid,
+ struct sss_iobuf *cred_blob)
+{
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_state *state = NULL;
+ char *secdb_key = NULL;
+ struct kcm_ccache *cc = NULL;
+ struct sss_iobuf *payload = NULL;
+ struct sss_sec_req *sreq = NULL;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Storing creds in ccache\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = key_by_uuid(state, secdb->sctx, client, uuid, &secdb_key);
+ if (ret == ENOENT) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = secdb_get_cc(state, secdb->sctx, secdb_key, client, &cc);
+ if (ret == ENOENT) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = kcm_cc_store_cred_blob(cc, cred_blob);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot store credentials to ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ ret = kcm_ccache_to_sec_input_binary(state, cc, &payload);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = secdb_cc_key_req(state, secdb->sctx, client, secdb_key, &sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sec_update(state, sreq, payload);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_store_cred_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+static struct tevent_req *ccdb_secdb_delete_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ccdb *db,
+ struct cli_creds *client,
+ uuid_t uuid)
+{
+ struct tevent_req *req = NULL;
+ struct ccdb_secdb_state *state = NULL;
+ struct ccdb_secdb *secdb = talloc_get_type(db->db_handle, struct ccdb_secdb);
+ struct sss_sec_req *container_req = NULL;
+ struct sss_sec_req *sreq = NULL;
+ char *secdb_key = NULL;
+ char **keys = NULL;
+ size_t nkeys;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Deleting ccache\n");
+
+ req = tevent_req_create(mem_ctx, &state, struct ccdb_secdb_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = secdb_container_url_req(state, secdb->sctx, client, &container_req);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sss_sec_list(state, container_req, &keys, &nkeys);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No ccaches to delete\n");
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %zu ccaches\n", nkeys);
+
+ if (nkeys == 0) {
+ ret = EOK;
+ goto immediate;
+ }
+
+ ret = key_by_uuid(state, secdb->sctx, client, uuid, &secdb_key);
+ if (ret == ENOENT) {
+ ret = ERR_NO_CREDS;
+ goto immediate;
+ } else if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = secdb_cc_key_req(state, secdb->sctx, client, secdb_key, &sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sss_sec_delete(sreq);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ if (nkeys > 1) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "There are other ccaches, done\n");
+ ret = EOK;
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Removing ccache container\n");
+
+ ret = sss_sec_delete(container_req);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = EOK;
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t ccdb_secdb_delete_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}
+
+const struct kcm_ccdb_ops ccdb_secdb_ops = {
+ .init = ccdb_secdb_init,
+
+ .nextid_send = ccdb_secdb_nextid_send,
+ .nextid_recv = ccdb_secdb_nextid_recv,
+
+ .set_default_send = ccdb_secdb_set_default_send,
+ .set_default_recv = ccdb_secdb_set_default_recv,
+
+ .get_default_send = ccdb_secdb_get_default_send,
+ .get_default_recv = ccdb_secdb_get_default_recv,
+
+ .list_send = ccdb_secdb_list_send,
+ .list_recv = ccdb_secdb_list_recv,
+
+ .list_all_cc = ccdb_secdb_list_all_cc,
+
+ .getbyname_send = ccdb_secdb_getbyname_send,
+ .getbyname_recv = ccdb_secdb_getbyname_recv,
+
+ .getbyuuid_send = ccdb_secdb_getbyuuid_send,
+ .getbyuuid_recv = ccdb_secdb_getbyuuid_recv,
+
+ .name_by_uuid_send = ccdb_secdb_name_by_uuid_send,
+ .name_by_uuid_recv = ccdb_secdb_name_by_uuid_recv,
+
+ .uuid_by_name_send = ccdb_secdb_uuid_by_name_send,
+ .uuid_by_name_recv = ccdb_secdb_uuid_by_name_recv,
+
+ .create_send = ccdb_secdb_create_send,
+ .create_recv = ccdb_secdb_create_recv,
+
+ .mod_send = ccdb_secdb_mod_send,
+ .mod_recv = ccdb_secdb_mod_recv,
+
+ .store_cred_send = ccdb_secdb_store_cred_send,
+ .store_cred_recv = ccdb_secdb_store_cred_recv,
+
+ .delete_send = ccdb_secdb_delete_send,
+ .delete_recv = ccdb_secdb_delete_recv,
+};
diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
new file mode 100644
index 0000000..9c37e3c
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_cmd.c
@@ -0,0 +1,667 @@
+/*
+ SSSD
+
+ KCM Server - the KCM server request and reply parsing and dispatching
+
+ Copyright (C) Red Hat, 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/uio.h>
+#include <krb5/krb5.h>
+
+#include "config.h"
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcmsrv_ops.h"
+
+/* The first four bytes of a message is always the size */
+#define KCM_MSG_LEN_SIZE 4
+
+/* The return code is 32bits */
+#define KCM_RETCODE_SIZE 4
+
+/* KCM operation, its raw input and raw output and result */
+struct kcm_op_io {
+ struct kcm_op *op;
+ struct kcm_data request;
+ struct sss_iobuf *reply;
+};
+
+/**
+ * KCM IO-vector operations
+ */
+struct kcm_iovec {
+ /* We don't use iovec b/c void pointers don't allow for
+ * pointer arithmetics and it's convenient to keep track
+ * of processed bytes
+ */
+ uint8_t *kiov_base;
+ size_t kiov_len;
+ size_t nprocessed;
+};
+
+static errno_t kcm_iovec_op(int fd, struct kcm_iovec *kiov, bool do_read)
+{
+ ssize_t len;
+ struct iovec iov[1];
+
+ iov[0].iov_base = kiov->kiov_base + kiov->nprocessed;
+ iov[0].iov_len = kiov->kiov_len - kiov->nprocessed;
+ if (iov[0].iov_len == 0) {
+ /* This iovec is full (read) or depleted (write), proceed to the next one */
+ return EOK;
+ }
+
+ if (do_read) {
+ len = readv(fd, iov, 1);
+ } else {
+ len = writev(fd, iov, 1);
+ }
+
+ if (len == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+ return EAGAIN;
+ } else {
+ return errno;
+ }
+ }
+
+ if (len == 0) {
+ /* Read event on fd that doesn't yield data? error */
+ return ENODATA;
+ }
+
+ /* Decrease the amount of available free space in the iovec */
+ kiov->nprocessed += len;
+ return EOK;
+}
+
+static errno_t kcm_read_iovec(int fd, struct kcm_iovec *kiov)
+{
+ return kcm_iovec_op(fd, kiov, true);
+}
+
+static errno_t kcm_write_iovec(int fd, struct kcm_iovec *kiov)
+{
+ return kcm_iovec_op(fd, kiov, false);
+}
+
+/**
+ * Parsing KCM input
+ *
+ * The request is received as two IO vectors:
+ *
+ * first iovec:
+ * length 32-bit big-endian integer
+ *
+ * second iovec:
+ * major protocol number 8-bit big-endian integer
+ * minor protocol number 8-bit big-endian integer
+ * opcode 16-bit big-endian integer
+ * message payload buffer
+ */
+struct kcm_reqbuf {
+ uint8_t lenbuf[KCM_MSG_LEN_SIZE];
+ struct kcm_iovec v_len;
+
+ /* Includes the major, minor versions etc */
+ struct kcm_iovec v_msg;
+};
+
+static uint32_t kcm_input_get_payload_len(struct kcm_iovec *v)
+{
+ size_t lc = 0;
+ uint32_t len_be = 0;
+
+ /* The first 4 bytes before the payload is message length */
+ SAFEALIGN_COPY_UINT32_CHECK(&len_be, v->kiov_base, v->kiov_len, &lc);
+
+ return be32toh(len_be);
+}
+
+static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf,
+ struct kcm_op_io *op_io)
+{
+ size_t mc = 0;
+ uint16_t opcode_be = 0;
+ uint32_t msglen;
+ uint8_t proto_maj = 0;
+ uint8_t proto_min = 0;
+
+ msglen = kcm_input_get_payload_len(&reqbuf->v_len);
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Received message with length %"PRIu32"\n", msglen);
+
+ if (msglen == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Illegal zero-length message\n");
+ return EBADMSG;
+ }
+
+ if (msglen != reqbuf->v_msg.nprocessed) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Sender claims the message is %"PRIu32" bytes, "
+ "but received %zu\n",
+ msglen, reqbuf->v_msg.nprocessed);
+ return EBADMSG;
+ }
+
+ /* First 16 bits are 8 bit major and 8bit minor protocol version */
+ SAFEALIGN_COPY_UINT8_CHECK(&proto_maj,
+ reqbuf->v_msg.kiov_base + mc,
+ reqbuf->v_msg.kiov_len,
+ &mc);
+ SAFEALIGN_COPY_UINT8_CHECK(&proto_min,
+ reqbuf->v_msg.kiov_base + mc,
+ reqbuf->v_msg.kiov_len,
+ &mc);
+
+ if (proto_maj != KCM_PROTOCOL_VERSION_MAJOR) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected major version %d, got %"PRIu16"\n",
+ KCM_PROTOCOL_VERSION_MAJOR, (uint16_t) proto_maj);
+ return ERR_KCM_MALFORMED_IN_PKT;
+ }
+
+ if (proto_min != KCM_PROTOCOL_VERSION_MINOR) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Expected minor version %d, got %"PRIu16"\n",
+ KCM_PROTOCOL_VERSION_MINOR, (uint16_t) proto_maj);
+ return ERR_KCM_MALFORMED_IN_PKT;
+ }
+
+ SAFEALIGN_COPY_UINT16_CHECK(&opcode_be,
+ reqbuf->v_msg.kiov_base + mc,
+ reqbuf->v_msg.kiov_len,
+ &mc);
+
+ op_io->op = kcm_get_opt(be16toh(opcode_be));
+ if (op_io->op == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Did not find a KCM operation handler for the requested opcode\n");
+ return ERR_KCM_OP_NOT_IMPLEMENTED;
+ }
+
+ /* The operation only receives the payload, not the opcode or the protocol info */
+ op_io->request.data = reqbuf->v_msg.kiov_base + mc;
+ op_io->request.length = reqbuf->v_msg.nprocessed - mc;
+
+ return EOK;
+}
+
+/**
+ * Constructing a reply for failure and success
+ *
+ * The reply consists of three IO vectors:
+ * 1) length iovec:
+ * length: 32-bit big-endian
+ *
+ * 2) return code iovec:
+ * retcode: 32-bit big-endian. Non-zero on failure in the KCM server,
+ * zero if the KCM operation ran (even if the operation itself
+ * failed)
+ *
+ * 3) reply iovec
+ * message: buffer, first 32-bits of the buffer is the return code of
+ * the KCM operation, the rest depends on the operation itself.
+ * The buffer's length is specified by the first integer in the
+ * reply (very intuitive, right?)
+ *
+ * The client always reads the length and return code iovectors. However, the
+ * client reads the reply iovec only if retcode is 0 in the return code iovector
+ * (see kcmio_unix_socket_read() in the MIT tree)
+ */
+struct kcm_repbuf {
+ uint8_t lenbuf[KCM_MSG_LEN_SIZE];
+ struct kcm_iovec v_len;
+
+ uint8_t rcbuf[KCM_RETCODE_SIZE];
+ struct kcm_iovec v_rc;
+
+ struct kcm_iovec v_msg;
+};
+
+static errno_t kcm_failbuf_construct(errno_t ret,
+ struct kcm_repbuf *repbuf)
+{
+ size_t c;
+
+ c = 0;
+ SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, 0, &c);
+ c = 0;
+ SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c);
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Sent reply with error %d\n", ret);
+ return EOK;
+}
+
+/* retcode is 0 if the operation at least ran, non-zero if there
+ * was some kind of internal KCM error, like input couldn't be parsed
+ */
+static errno_t kcm_output_construct(TALLOC_CTX *mem_ctx,
+ struct kcm_op_io *op_io,
+ struct kcm_repbuf *repbuf)
+{
+ uint8_t *rep;
+ size_t replen;
+ size_t c;
+
+ replen = sss_iobuf_get_len(op_io->reply);
+ if (replen > KCM_PACKET_MAX_SIZE) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Reply exceeds the KCM protocol limit, aborting\n");
+ return E2BIG;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Sending a reply with %zu bytes of payload\n", replen);
+ c = 0;
+ SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, htobe32(replen), &c);
+
+ c = 0;
+ SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, 0, &c);
+
+ if (replen > 0) {
+ rep = talloc_array(mem_ctx, uint8_t, replen);
+ if (rep == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to allocate memory for the message\n");
+ return ENOMEM;
+ }
+
+ c = 0;
+ SAFEALIGN_MEMCPY_CHECK(rep,
+ sss_iobuf_get_data(op_io->reply),
+ replen,
+ replen,
+ &c);
+
+ /* Set the buffer and its length to send to KCM client */
+ repbuf->v_msg.kiov_base = rep;
+ repbuf->v_msg.kiov_len = replen;
+ }
+
+ return EOK;
+}
+
+/**
+ * Construct a reply buffer and send it to the KCM client
+ */
+static void kcm_reply_error(struct cli_ctx *cctx,
+ errno_t retcode,
+ struct kcm_repbuf *repbuf)
+{
+ errno_t ret;
+ krb5_error_code kerr;
+
+ DEBUG(retcode == ERR_KCM_OP_NOT_IMPLEMENTED ?
+ SSSDBG_MINOR_FAILURE : SSSDBG_OP_FAILURE,
+ "KCM operation returns failure [%d]: %s\n",
+ retcode, sss_strerror(retcode));
+ kerr = sss2krb5_error(retcode);
+
+ ret = kcm_failbuf_construct(kerr, repbuf);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot construct the reply buffer, terminating client\n");
+ talloc_free(cctx);
+ return;
+ }
+
+ TEVENT_FD_WRITEABLE(cctx->cfde);
+}
+
+/**
+ * Request-reply dispatcher
+ */
+struct kcm_req_ctx {
+ /* client context owns per-client buffers including this one */
+ struct cli_ctx *cctx;
+
+ /* raw IO buffers */
+ struct kcm_reqbuf reqbuf;
+ struct kcm_repbuf repbuf;
+
+ /* long-lived responder structures */
+ struct kcm_ctx *kctx;
+
+ struct kcm_op_io op_io;
+};
+
+static void kcm_send_reply(struct kcm_req_ctx *req_ctx)
+{
+ struct cli_ctx *cctx;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Sending a reply\n");
+
+ cctx = req_ctx->cctx;
+
+ ret = kcm_output_construct(req_ctx, &req_ctx->op_io, &req_ctx->repbuf);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot construct the reply buffer, terminating client\n");
+ kcm_reply_error(cctx, ret, &req_ctx->repbuf);
+ return;
+ }
+
+ TEVENT_FD_WRITEABLE(cctx->cfde);
+}
+
+static void kcm_cmd_request_done(struct tevent_req *req);
+
+static errno_t kcm_cmd_dispatch(struct kcm_ctx *kctx,
+ struct kcm_req_ctx *req_ctx)
+{
+ struct tevent_req *req;
+ struct cli_ctx *cctx;
+ struct kcm_conn_data *conn_data;
+
+ cctx = req_ctx->cctx;
+ conn_data = talloc_get_type(cctx->state_ctx, struct kcm_conn_data);
+
+ req = kcm_cmd_send(req_ctx,
+ cctx->ev,
+ kctx->qctx,
+ req_ctx->kctx->kcm_data,
+ conn_data,
+ req_ctx->cctx->creds,
+ &req_ctx->op_io.request,
+ req_ctx->op_io.op);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to schedule KCM operation.\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(req, kcm_cmd_request_done, req_ctx);
+ return EOK;
+}
+
+static void kcm_cmd_request_done(struct tevent_req *req)
+{
+ struct kcm_req_ctx *req_ctx;
+ errno_t ret;
+
+ req_ctx = tevent_req_callback_data(req, struct kcm_req_ctx);
+
+ ret = kcm_cmd_recv(req_ctx, req,
+ &req_ctx->op_io.reply);
+ talloc_free(req);
+ if (ret != EOK) {
+ if (ret == ERR_KCM_OP_NOT_IMPLEMENTED) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "%s\n", sss_strerror(ret));
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "KCM operation failed [%d]: %s\n", ret, sss_strerror(ret));
+ }
+ kcm_reply_error(req_ctx->cctx, ret, &req_ctx->repbuf);
+ return;
+ }
+
+ kcm_send_reply(req_ctx);
+}
+
+static errno_t kcm_recv_data(TALLOC_CTX *mem_ctx,
+ int fd,
+ struct kcm_reqbuf *reqbuf)
+{
+ uint8_t *msg;
+ uint32_t msglen;
+ errno_t ret;
+
+ ret = kcm_read_iovec(fd, &reqbuf->v_len);
+ if (ret != EOK) {
+ /* Not all errors are fatal, hence we don't print DEBUG messages
+ * here, but in the caller
+ */
+ return ret;
+ }
+
+ msglen = kcm_input_get_payload_len(&reqbuf->v_len);
+ if (msglen > KCM_PACKET_MAX_SIZE) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Request exceeds the KCM protocol limit, aborting\n");
+ return E2BIG;
+ }
+
+ msg = talloc_array(mem_ctx, uint8_t, msglen);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to allocate memory for the message\n");
+ return ENOMEM;
+ }
+
+ /* Set the buffer and its expected len to receive the data */
+ reqbuf->v_msg.kiov_base = msg;
+ reqbuf->v_msg.kiov_len = msglen;
+
+ ret = kcm_read_iovec(fd, &reqbuf->v_msg);
+ if (ret != EOK) {
+ /* Not all errors are fatal, hence we don't print DEBUG messages
+ * here, but in the caller
+ */
+ return ret;
+ }
+
+ return EOK;
+}
+
+/* Mind that kcm_new_req() does not take a mem_ctx argument on purpose as we
+ * really want the cctx to be the memory context here so that if the client
+ * disconnects, the request goes away. */
+static struct kcm_req_ctx *kcm_new_req(struct cli_ctx *cctx,
+ struct kcm_ctx *kctx)
+{
+ struct kcm_req_ctx *req;
+
+ req = talloc_zero(cctx, struct kcm_req_ctx);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ req->reqbuf.v_len.kiov_base = req->reqbuf.lenbuf;
+ req->reqbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE;
+
+ req->repbuf.v_len.kiov_base = req->repbuf.lenbuf;
+ req->repbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE;
+
+ req->repbuf.v_rc.kiov_base = req->repbuf.rcbuf;
+ req->repbuf.v_rc.kiov_len = KCM_RETCODE_SIZE;
+
+ req->cctx = cctx;
+ req->kctx = kctx;
+
+ return req;
+}
+
+static void kcm_recv(struct cli_ctx *cctx)
+{
+ struct kcm_req_ctx *req;
+ struct kcm_ctx *kctx;
+ int ret;
+
+ kctx = talloc_get_type(cctx->rctx->pvt_ctx, struct kcm_ctx);
+ req = talloc_get_type(cctx->protocol_ctx, struct kcm_req_ctx);
+ if (req == NULL) {
+ /* A new request comes in, setup data structures. */
+ req = kcm_new_req(cctx, kctx);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot set up client connection\n");
+ talloc_free(cctx);
+ return;
+ }
+
+ cctx->protocol_ctx = req;
+ }
+
+ /* Shared data between requests that originates in the same connection. */
+ if (cctx->state_ctx == NULL) {
+ cctx->state_ctx = talloc_zero(cctx, struct kcm_conn_data);
+ if (cctx->state_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up client state\n");
+ talloc_free(cctx);
+ return;
+ }
+ }
+
+ ret = kcm_recv_data(req, cctx->cfd, &req->reqbuf);
+ switch (ret) {
+ case ENODATA:
+ DEBUG(SSSDBG_TRACE_ALL, "Client closed connection.\n");
+ talloc_free(cctx);
+ return;
+ case EAGAIN:
+ DEBUG(SSSDBG_TRACE_ALL, "Retry later\n");
+ return;
+ case EOK:
+ /* all fine */
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to receive data (%d, %s), aborting client\n",
+ ret, sss_strerror(ret));
+ talloc_free(cctx);
+ return;
+ }
+
+ ret = kcm_input_parse(&req->reqbuf, &req->op_io);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to parse data (%d, %s), aborting client\n",
+ ret, sss_strerror(ret));
+ talloc_free(cctx);
+ return;
+ }
+
+ /* do not read anymore, client is done sending */
+ TEVENT_FD_NOT_READABLE(cctx->cfde);
+
+ ret = kcm_cmd_dispatch(kctx, req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to dispatch KCM operation [%d]: %s\n",
+ ret, sss_strerror(ret));
+ /* Fail with reply */
+ kcm_reply_error(cctx, ret, &req->repbuf);
+ return;
+ }
+
+ /* Dispatched request resumes in kcm_cmd_request_done */
+ return;
+}
+
+static int kcm_send_data(struct cli_ctx *cctx)
+{
+ struct kcm_req_ctx *req;
+ errno_t ret;
+
+ req = talloc_get_type(cctx->protocol_ctx, struct kcm_req_ctx);
+
+ ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to write the length iovec [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to write the retcode iovec [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to write the msg iovec [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static void kcm_send(struct cli_ctx *cctx)
+{
+ errno_t ret;
+
+ ret = kcm_send_data(cctx);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_TRACE_ALL, "Sending data again..\n");
+ return;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n");
+ talloc_free(cctx);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "All data sent!\n");
+ TEVENT_FD_NOT_WRITEABLE(cctx->cfde);
+ TEVENT_FD_READABLE(cctx->cfde);
+ talloc_zfree(cctx->protocol_ctx);
+ return;
+}
+
+static void kcm_fd_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *ptr)
+{
+ sss_client_fd_handler(ptr, kcm_recv, kcm_send, flags);
+}
+
+int kcm_connection_setup(struct cli_ctx *cctx)
+{
+ cctx->cfd_handler = kcm_fd_handler;
+ return EOK;
+}
+
+krb5_error_code sss2krb5_error(errno_t err)
+{
+ switch (err) {
+ case EOK:
+ return 0;
+ case ENOMEM:
+ return KRB5_CC_NOMEM;
+ case EACCES:
+ return KRB5_FCC_PERM;
+ case ERR_KCM_OP_NOT_IMPLEMENTED:
+ return KRB5_FCC_INTERNAL;
+ case ERR_WRONG_NAME_FORMAT:
+ return KRB5_CC_BADNAME;
+ case ERR_NO_MATCHING_CREDS:
+ return KRB5_FCC_NOFILE;
+ case ERR_NO_CREDS:
+ return KRB5_CC_NOTFOUND;
+ case ERR_KCM_CC_END:
+ return KRB5_CC_END;
+ case ERR_KCM_MALFORMED_IN_PKT:
+ case EINVAL:
+ case EIO:
+ return KRB5_CC_IO;
+ }
+
+ return KRB5_FCC_INTERNAL;
+}
+
+/* Dummy, not used here but required to link to other responder files */
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ return NULL;
+}
diff --git a/src/responder/kcm/kcmsrv_op_queue.c b/src/responder/kcm/kcmsrv_op_queue.c
new file mode 100644
index 0000000..29af521
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_op_queue.c
@@ -0,0 +1,332 @@
+/*
+ SSSD
+
+ KCM Server - the KCM operations wait queue
+
+ Copyright (C) Red Hat, 2017
+
+ 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 "util/util.h"
+#include "util/util_creds.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+
+struct kcm_ops_queue_entry {
+ struct tevent_req *req;
+
+ struct kcm_ops_queue *queue;
+
+ struct kcm_ops_queue_entry *next;
+ struct kcm_ops_queue_entry *prev;
+};
+
+struct kcm_ops_queue {
+ uid_t uid;
+ struct tevent_context *ev;
+ struct kcm_ops_queue_ctx *qctx;
+
+ struct kcm_ops_queue_entry *head;
+};
+
+struct kcm_ops_queue_ctx {
+ struct kcm_ctx *kctx;
+
+ /* UID:kcm_ops_queue */
+ hash_table_t *wait_queue_hash;
+};
+
+/*
+ * Per-UID wait queue
+ *
+ * They key in the hash table is the UID of the peer. The value of each
+ * hash table entry is kcm_ops_queue structure which in turn contains a
+ * linked list of kcm_ops_queue_entry structures * which primarily hold the
+ * tevent request being queued.
+ */
+struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx,
+ struct kcm_ctx *kctx)
+{
+ errno_t ret;
+ struct kcm_ops_queue_ctx *queue_ctx;
+
+ queue_ctx = talloc_zero(mem_ctx, struct kcm_ops_queue_ctx);
+ if (queue_ctx == NULL) {
+ return NULL;
+ }
+
+ ret = sss_hash_create_ex(mem_ctx, 0,
+ &queue_ctx->wait_queue_hash, 0, 0, 0, 0,
+ NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_hash_create failed [%d]: %s\n", ret, sss_strerror(ret));
+ talloc_free(queue_ctx);
+ return NULL;
+ }
+
+ queue_ctx->kctx = kctx;
+
+ return queue_ctx;
+}
+
+void queue_removal_cb(struct tevent_context *ctx,
+ struct tevent_immediate *imm,
+ void *private_data)
+{
+ struct kcm_ops_queue *kq = talloc_get_type(private_data,
+ struct kcm_ops_queue);
+ int ret;
+ hash_key_t key;
+
+ talloc_free(imm);
+
+ if (kq->head != NULL) {
+ DEBUG(SSSDBG_TRACE_LIBS, "The queue is no longer empty\n");
+ return;
+ }
+
+ key.type = HASH_KEY_ULONG;
+ key.ul = kq->uid;
+
+ /* If this was the last entry, remove the key (the UID) from the
+ * hash table to signal the queue is empty
+ */
+ ret = hash_delete(kq->qctx->wait_queue_hash, &key);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to remove wait queue for user %"SPRIuid"\n",
+ kq->uid);
+ return;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Removed queue for %"SPRIuid" \n", kq->uid);
+ talloc_free(kq);
+}
+
+static int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry)
+{
+ struct kcm_ops_queue_entry *next_entry;
+ struct tevent_immediate *imm;
+
+ if (entry == NULL) {
+ return 1;
+ /* Prevent use-after-free of req when shutting down with non-empty queue */
+ } else if (entry->queue->qctx->kctx->rctx->shutting_down) {
+ return 0;
+ }
+
+ /* Take the next entry from the queue */
+ next_entry = entry->next;
+
+ /* Remove the current entry from the queue */
+ DLIST_REMOVE(entry->queue->head, entry);
+
+ if (next_entry == NULL) {
+ /* If there was no other entry, schedule removal of the queue. Do it
+ * in another tevent tick to avoid issues with callbacks invoking
+ * the destructor while another request is touching the queue
+ */
+ imm = tevent_create_immediate(entry->queue);
+ if (imm == NULL) {
+ return 1;
+ }
+
+ tevent_schedule_immediate(imm, entry->queue->ev, queue_removal_cb, entry->queue);
+ return 0;
+ }
+
+ /* Otherwise, mark the current head as done to run the next request */
+ tevent_req_done(next_entry->req);
+ return 0;
+}
+
+static struct kcm_ops_queue *kcm_op_queue_get(struct kcm_ops_queue_ctx *qctx,
+ struct tevent_context *ev,
+ uid_t uid)
+{
+ errno_t ret;
+ hash_key_t key;
+ hash_value_t value;
+ struct kcm_ops_queue *kq;
+
+ key.type = HASH_KEY_ULONG;
+ key.ul = uid;
+
+ ret = hash_lookup(qctx->wait_queue_hash, &key, &value);
+ switch (ret) {
+ case HASH_SUCCESS:
+ if (value.type != HASH_VALUE_PTR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n");
+ return NULL;
+ }
+
+ kq = talloc_get_type(value.ptr, struct kcm_ops_queue);
+ if (kq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid queue pointer\n");
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Found existing queue for this ID\n");
+ break;
+
+ case HASH_ERROR_KEY_NOT_FOUND:
+ /* No request for this UID yet. Enqueue this request in case
+ * another one comes in and return EOK to run the current request
+ * immediately
+ */
+ DEBUG(SSSDBG_TRACE_LIBS, "No existing queue for this ID\n");
+
+ kq = talloc_zero(qctx->wait_queue_hash, struct kcm_ops_queue);
+ if (kq == NULL) {
+ return NULL;
+ }
+ kq->uid = uid;
+ kq->qctx = qctx;
+ kq->ev = ev;
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = kq;
+
+ ret = hash_enter(qctx->wait_queue_hash, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "hash_enter failed.\n");
+ return NULL;
+ }
+ break;
+
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n");
+ return NULL;
+ }
+
+ return kq;
+}
+
+struct kcm_op_queue_state {
+ struct kcm_ops_queue_entry *entry;
+};
+
+static errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq,
+ struct tevent_req *req);
+
+/*
+ * Enqueue a request.
+ *
+ * If the request queue /for the given ID/ is empty, that is, if this
+ * request is the first one in the queue, run the request immediately.
+ *
+ * Otherwise just add it to the queue and wait until the previous request
+ * finishes and only at that point mark the current request as done, which
+ * will trigger calling the recv function and allow the request to continue.
+ */
+struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ops_queue_ctx *qctx,
+ struct cli_creds *client)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct kcm_ops_queue *kq;
+ struct kcm_op_queue_state *state;
+ uid_t uid;
+
+ uid = cli_creds_get_uid(client);
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_queue_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Adding request by %"SPRIuid" to the wait queue\n", uid);
+
+ kq = kcm_op_queue_get(qctx, ev, uid);
+ if (kq == NULL) {
+ ret = EIO;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get queue [%d]: %s\n", ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ ret = kcm_op_queue_add_req(kq, req);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Queue was empty, running the request immediately\n");
+ goto immediate;
+ } else if (ret != EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot enqueue request [%d]: %s\n", ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Waiting our turn in the queue\n");
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq,
+ struct tevent_req *req)
+{
+ errno_t ret;
+ struct kcm_op_queue_state *state = tevent_req_data(req,
+ struct kcm_op_queue_state);
+
+ state->entry = talloc_zero(kq->qctx->wait_queue_hash, struct kcm_ops_queue_entry);
+ if (state->entry == NULL) {
+ return ENOMEM;
+ }
+ state->entry->req = req;
+ state->entry->queue = kq;
+ talloc_set_destructor(state->entry, kcm_op_queue_entry_destructor);
+
+ if (kq->head == NULL) {
+ /* First entry, will run callback at once */
+ ret = EOK;
+ } else {
+ /* Will wait for the previous callbacks to finish */
+ ret = EAGAIN;
+ }
+
+ DLIST_ADD_END(kq->head, state->entry, struct kcm_ops_queue_entry *);
+ return ret;
+}
+
+/*
+ * The queue recv function is called when this request is 'activated'. The queue
+ * entry should be allocated on the same memory context as the enqueued request
+ * to trigger freeing the kcm_ops_queue_entry structure destructor when the
+ * parent request is done and its tevent_req freed. This would in turn unblock
+ * the next request in the queue
+ */
+errno_t kcm_op_queue_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ops_queue_entry **_entry)
+{
+ struct kcm_op_queue_state *state = tevent_req_data(req,
+ struct kcm_op_queue_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_entry = talloc_steal(mem_ctx, state->entry);
+ return EOK;
+}
diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
new file mode 100644
index 0000000..8935a79
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ops.c
@@ -0,0 +1,2458 @@
+/*
+ SSSD
+
+ KCM Server - the KCM server operations
+
+ Copyright (C) Red Hat, 2016
+
+ 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 "config.h"
+
+#include <krb5/krb5.h>
+#include <dhash.h>
+
+#include "util/sss_iobuf.h"
+#include "util/sss_krb5.h"
+#include "util/sss_ptr_hash.h"
+#include "util/util_creds.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcmsrv_ops.h"
+#include "responder/kcm/kcmsrv_ccache.h"
+
+struct kcm_op_ctx {
+ struct kcm_resp_ctx *kcm_data;
+ struct kcm_conn_data *conn_data;
+ struct cli_creds *client;
+
+ struct sss_iobuf *input;
+ struct sss_iobuf *reply;
+};
+
+/* Each operation follows the same pattern and is implemented using
+ * functions with this prototype. The operation receives an op_ctx
+ * that serves as a state of the operation and can be used to keep
+ * track of any temporary data. The operation writes its output data
+ * into the op_ctx reply IO buffer and returns the op_ret status code
+ * separately.
+ *
+ * The operation always returns EOK unless an internal error occurs,
+ * the result of the operation is stored in the op_ret variable
+ */
+typedef struct tevent_req*
+(*kcm_srv_send_method)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx);
+typedef errno_t
+(*kcm_srv_recv_method)(struct tevent_req *req,
+ uint32_t *_op_ret);
+
+struct kcm_op {
+ const char *name;
+ kcm_srv_send_method fn_send;
+ kcm_srv_recv_method fn_recv;
+};
+
+struct kcm_cmd_state {
+ struct kcm_op *op;
+ struct tevent_context *ev;
+
+ struct kcm_ops_queue_entry *queue_entry;
+ struct kcm_op_ctx *op_ctx;
+ struct sss_iobuf *reply;
+
+ uint32_t op_ret;
+};
+
+static void kcm_cmd_queue_done(struct tevent_req *subreq);
+static void kcm_cmd_done(struct tevent_req *subreq);
+
+struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ops_queue_ctx *qctx,
+ struct kcm_resp_ctx *kcm_data,
+ struct kcm_conn_data *conn_data,
+ struct cli_creds *client,
+ struct kcm_data *input,
+ struct kcm_op *op)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_cmd_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_cmd_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op = op;
+ state->ev = ev;
+
+ if (op == NULL) {
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "KCM operation %s\n", op->name);
+ DEBUG(SSSDBG_TRACE_LIBS, "%zu bytes on KCM input\n", input->length);
+
+ state->reply = sss_iobuf_init_empty(state,
+ KCM_PACKET_INITIAL_SIZE,
+ KCM_PACKET_MAX_SIZE);
+ if (state->reply == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ if (op->fn_send == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "KCM op %s has no handler\n", kcm_opt_name(op));
+ ret = ERR_KCM_OP_NOT_IMPLEMENTED;
+ goto immediate;
+ }
+
+ /* Allocating op_ctx on the heap makes it possible for operations to use
+ * op_ctx as their temporary context and avoid tmp_ctx altogether
+ */
+ state->op_ctx = talloc_zero(state, struct kcm_op_ctx);
+ if (state->op_ctx == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ state->op_ctx->kcm_data = kcm_data;
+ state->op_ctx->conn_data = conn_data;
+ state->op_ctx->client = client;
+
+ state->op_ctx->input = sss_iobuf_init_readonly(state->op_ctx,
+ input->data,
+ input->length);
+ if (state->op_ctx->input == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ /*
+ * The internal operation returns the opcode and the buffer separately.
+ * The KCM server reply to the client also always contains zero if the
+ * operation ran to completion, both are uint32_t.
+ * FIXME:
+ * Alternatively, we could extend iobuf API so that we can just pass
+ * the reply's buffer+sizeof(2*uint32_t) and avoid the useless allocations
+ */
+ state->op_ctx->reply = sss_iobuf_init_empty(
+ state,
+ KCM_PACKET_INITIAL_SIZE,
+ KCM_PACKET_MAX_SIZE - 2*sizeof(uint32_t));
+ if (state->op_ctx->reply == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ subreq = kcm_op_queue_send(state, ev, qctx, client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_cmd_queue_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_cmd_queue_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct kcm_cmd_state *state = tevent_req_data(req, struct kcm_cmd_state);
+ errno_t ret;
+
+ /* When this request finishes, it frees the queue_entry which unblocks
+ * other requests by the same UID
+ */
+ ret = kcm_op_queue_recv(subreq, state, &state->queue_entry);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot acquire queue slot\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = state->op->fn_send(state, state->ev, state->op_ctx);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_cmd_done, req);
+}
+
+static void kcm_cmd_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct kcm_cmd_state *state = tevent_req_data(req, struct kcm_cmd_state);
+ errno_t ret;
+ krb5_error_code kerr;
+
+ ret = state->op->fn_recv(subreq, &state->op_ret);
+ talloc_free(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "op receive function failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "KCM operation %s returned [%d]: %s\n",
+ kcm_opt_name(state->op), state->op_ret, sss_strerror(state->op_ret));
+
+ kerr = sss2krb5_error(state->op_ret);
+
+ /* The first four bytes of the reply is the operation status code */
+ ret = sss_iobuf_write_uint32(state->reply, htobe32(kerr));
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sss_iobuf_write_len(state->reply,
+ sss_iobuf_get_data(state->op_ctx->reply),
+ sss_iobuf_get_len(state->op_ctx->reply));
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sss_iobuf **_reply)
+{
+ struct kcm_cmd_state *state = NULL;
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ state = tevent_req_data(req, struct kcm_cmd_state);
+
+ *_reply = talloc_steal(mem_ctx, state->reply);
+ return EOK;
+}
+
+/* ======= KCM operations ======= */
+
+/* Operations that don't return any extra information except for the op_ret
+ * can use this macro in the _recv function to avoid code duplication
+ */
+#define KCM_OP_RET_FROM_TYPE(req, state_type, _op_ret_out) do { \
+ state_type *state = NULL; \
+ state = tevent_req_data(req, state_type); \
+ TEVENT_REQ_RETURN_ON_ERROR(req); \
+ *_op_ret_out = state->op_ret; \
+ return EOK; \
+} while(0);
+
+struct kcm_op_common_state {
+ uint32_t op_ret;
+ struct kcm_op_ctx *op_ctx;
+ struct tevent_context *ev;
+};
+
+static errno_t kcm_op_common_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_op_ret = state->op_ret;
+ return EOK;
+}
+
+/* () -> (name) */
+static void kcm_op_gen_new_done(struct tevent_req *subreq);
+
+static struct tevent_req *kcm_op_gen_new_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_common_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ subreq = kcm_ccdb_nextid_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_gen_new_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_gen_new_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ char *newid;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+
+ ret = kcm_ccdb_nextid_recv(subreq, state, &newid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot generate a new ID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Generated a new ID %s\n", newid);
+
+ ret = sss_iobuf_write_stringz(state->op_ctx->reply, newid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot write generated ID %d: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+/* (princ) -> () */
+struct kcm_op_initialize_state {
+ uint32_t op_ret;
+ struct kcm_op_ctx *op_ctx;
+ struct tevent_context *ev;
+
+ struct kcm_ccache *new_cc;
+ const char *name;
+ krb5_principal princ;
+};
+
+static void kcm_op_initialize_got_byname(struct tevent_req *subreq);
+static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq);
+static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq);
+static void kcm_op_initialize_fill_princ_step(struct tevent_req *req);
+static void kcm_op_initialize_fill_princ_done(struct tevent_req *subreq);
+static void kcm_op_initialize_create_step(struct tevent_req *req);
+static void kcm_op_initialize_got_default(struct tevent_req *subreq);
+static void kcm_op_initialize_set_default_done(struct tevent_req *subreq);
+
+static struct tevent_req *kcm_op_initialize_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_initialize_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_initialize_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+ state->ev = ev;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &state->name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot read input name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Initializing ccache %s\n", state->name);
+
+ ret = kcm_check_name(state->name, op_ctx->client);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Name %s is malformed [%d]: %s\n",
+ state->name, ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ ret = sss_krb5_unmarshal_princ(op_ctx, op_ctx->input, &state->princ);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot unmarshal principal [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ subreq = kcm_ccdb_getbyname_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ state->name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_initialize_got_byname, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_initialize_got_byname(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_initialize_state *state = tevent_req_data(req,
+ struct kcm_op_initialize_state);
+ bool ok;
+ uuid_t uuid;
+
+ ret = kcm_ccdb_getbyname_recv(subreq, state, &state->new_cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->new_cc != NULL) {
+ if (kcm_cc_get_client_principal(state->new_cc) == NULL) {
+ /* This is a cache that was pre-created w/o a principal (sshd does this),
+ * let's fill in the principal and set the cache as default if not
+ * already
+ */
+ kcm_op_initialize_fill_princ_step(req);
+ return;
+ }
+
+ ok = kcm_cc_access(state->new_cc, state->op_ctx->client);
+ if (!ok) {
+ state->op_ret = EACCES;
+ tevent_req_done(req);
+ return;
+ }
+
+ /* `uuid` is output arg and isn't read in kcm_cc_get_uuid() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_cc_get_uuid(state->new_cc, uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get new ccache UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return;
+ }
+
+ /* Nuke any previous cache and its contents during initialization */
+ subreq = kcm_ccdb_delete_cc_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ uuid);
+ if (subreq == NULL) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_initialize_cc_delete_done, req);
+ return;
+ }
+
+ kcm_op_initialize_create_step(req);
+}
+
+static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ errno_t ret;
+
+ ret = kcm_ccdb_delete_cc_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot delete ccache from the db %d: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ kcm_op_initialize_create_step(req);
+}
+
+static void kcm_op_initialize_fill_princ_step(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct kcm_op_initialize_state *state = tevent_req_data(req,
+ struct kcm_op_initialize_state);
+ errno_t ret;
+ struct kcm_mod_ctx *mod_ctx;
+ uuid_t uuid;
+
+ mod_ctx = kcm_mod_ctx_new(state);
+ if (mod_ctx == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ mod_ctx->client = state->princ;
+
+ /* `uuid` is output arg and isn't read in kcm_cc_get_uuid() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_cc_get_uuid(state->new_cc, uuid);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = kcm_ccdb_mod_cc_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ uuid,
+ mod_ctx);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_initialize_fill_princ_done, req);
+}
+
+static void kcm_op_initialize_fill_princ_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_initialize_state *state = tevent_req_data(req,
+ struct kcm_op_initialize_state);
+ errno_t ret;
+
+ ret = kcm_ccdb_mod_cc_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot modify ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* Make sure the cache we just initialized is the default one */
+ subreq = kcm_ccdb_get_default_send(state, state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client);
+ if (subreq == NULL) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_initialize_got_default, req);
+}
+
+static void kcm_op_initialize_create_step(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct kcm_op_initialize_state *state = tevent_req_data(req,
+ struct kcm_op_initialize_state);
+ errno_t ret;
+
+ ret = kcm_cc_new(state->op_ctx,
+ state->op_ctx->kcm_data->k5c,
+ state->op_ctx->client,
+ state->name,
+ state->princ,
+ &state->new_cc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot create new ccache %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = kcm_ccdb_create_cc_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ state->new_cc);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_initialize_cc_create_done, req);
+}
+
+static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_initialize_state *state = tevent_req_data(req,
+ struct kcm_op_initialize_state);
+ errno_t ret;
+
+ ret = kcm_ccdb_create_cc_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot add ccache to db %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* If there was no previous default ccache, set this one as default */
+ subreq = kcm_ccdb_get_default_send(state, state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client);
+ if (subreq == NULL) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_initialize_got_default, req);
+}
+
+static void kcm_op_initialize_got_default(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_initialize_state *state = tevent_req_data(req,
+ struct kcm_op_initialize_state);
+ errno_t ret;
+ uuid_t dfl_uuid;
+ uuid_t old_dfl_uuid;
+
+ ret = kcm_ccdb_get_default_recv(subreq, &old_dfl_uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get default ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (uuid_is_null(old_dfl_uuid)) {
+ /* If there was no previous default ccache, switch to the initialized
+ * one by default
+ */
+ /* `dfl_uuid` is output arg and isn't read in kcm_cc_get_uuid() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_cc_get_uuid(state->new_cc, dfl_uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get new ccache UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "The default ccached was not set, switching to the "
+ "initialized\n");
+ subreq = kcm_ccdb_set_default_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ dfl_uuid);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_initialize_set_default_done, req);
+ return;
+ }
+
+ /* ENOENT, done */
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+static void kcm_op_initialize_set_default_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_initialize_state *state = tevent_req_data(req,
+ struct kcm_op_initialize_state);
+ errno_t ret;
+
+ ret = kcm_ccdb_set_default_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+static errno_t kcm_op_initialize_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_initialize_state, _op_ret);
+}
+
+/* (name) -> () */
+static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq);
+static void kcm_op_destroy_delete_done(struct tevent_req *subreq);
+
+static struct tevent_req *kcm_op_destroy_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_common_state *state = NULL;
+ errno_t ret;
+ const char *name;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+ state->ev = ev;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot unmarshall input name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Destroying credentials of %s\n", name);
+
+ subreq = kcm_ccdb_uuid_by_name_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_destroy_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+ uuid_t uuid;
+
+ /* `uuid` is output arg and isn't read in kcm_ccdb_uuid_by_name_recv() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot get matching ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_NO_MATCHING_CREDS;
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = kcm_ccdb_delete_cc_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ uuid);
+ if (subreq == NULL) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_destroy_delete_done, req);
+}
+
+static void kcm_op_destroy_delete_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+
+ ret = kcm_ccdb_delete_cc_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot delete ccache from the db [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+/* (name, cred) -> () */
+struct kcm_op_store_state {
+ uint32_t op_ret;
+ struct kcm_op_ctx *op_ctx;
+ struct tevent_context *ev;
+
+ struct sss_iobuf *cred_blob;
+};
+
+static void kcm_op_store_getbyname_done(struct tevent_req *subreq);
+static void kcm_op_store_done(struct tevent_req *subreq);
+
+static struct tevent_req *kcm_op_store_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_store_state *state = NULL;
+ errno_t ret;
+ const char *name;
+ size_t creds_len;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_store_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+ state->ev = ev;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot unmarshall input name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Storing credentials for %s\n", name);
+
+ creds_len = sss_iobuf_get_size(op_ctx->input) - strlen(name) -1;
+ if (creds_len > KCM_PACKET_MAX_SIZE) {
+ /* Protects against underflows and in general adds sanity */
+ ret = E2BIG;
+ goto immediate;
+ }
+
+ state->cred_blob = sss_iobuf_init_empty(state,
+ creds_len,
+ creds_len);
+ if (state->cred_blob == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ ret = sss_iobuf_read(op_ctx->input,
+ creds_len,
+ sss_iobuf_get_data(state->cred_blob),
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot unmarshall input cred blob [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ subreq = kcm_ccdb_uuid_by_name_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_store_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_store_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_store_state *state = tevent_req_data(req,
+ struct kcm_op_store_state);
+ uuid_t uuid;
+
+ /* `uuid` is output arg and isn't read in kcm_ccdb_uuid_by_name_recv() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = kcm_ccdb_store_cred_blob_send(state, state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ uuid,
+ state->cred_blob);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_store_done, req);
+}
+
+static void kcm_op_store_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_store_state *state = tevent_req_data(req,
+ struct kcm_op_store_state);
+
+ ret = kcm_ccdb_store_cred_blob_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot store credentials [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+static errno_t kcm_op_store_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_store_state, _op_ret);
+}
+
+/* (name) -> (princ) */
+static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq);
+
+static struct tevent_req *kcm_op_get_principal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_common_state *state = NULL;
+ errno_t ret;
+ const char *name;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Requested principal %s\n", name);
+
+ subreq = kcm_ccdb_getbyname_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_principal_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct kcm_ccache *cc;
+ krb5_principal princ;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+
+ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (cc == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n");
+ state->op_ret = ERR_NO_MATCHING_CREDS;
+ tevent_req_done(req);
+ return;
+ }
+
+ /* Marshall the principal to the reply */
+ princ = kcm_cc_get_client_principal(cc);
+ if (princ == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Credentials with no principal?\n");
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ ret = sss_krb5_marshal_princ(princ, state->op_ctx->reply);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot marshall principal [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+static void
+kcm_creds_table_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ /* Delete the old credential if it is being overwritten. */
+ talloc_free(item->value.ptr);
+}
+
+/* Store credentials in a hash table.
+ *
+ * If the table already exist we add the new credentials to the table and
+ * overwrite the ones that already exist. This allows us to correctly serve
+ * also parallel GET_CRED_UUID_LIST requests from the same connection since
+ * it will have its own uuid list and cursor on the client side and we make
+ * all uuid (old, updated and newly added) available.
+ */
+static errno_t
+kcm_creds_to_table(TALLOC_CTX *mem_ctx,
+ struct kcm_cred *creds,
+ hash_table_t **_table)
+{
+ char str[UUID_STR_SIZE];
+ uuid_t uuid;
+ errno_t ret;
+
+ if (*_table == NULL) {
+ *_table = sss_ptr_hash_create(mem_ctx, kcm_creds_table_delete_cb, NULL);
+ if (*_table == NULL) {
+ return ENOMEM;
+ }
+ }
+
+ for (struct kcm_cred *crd = creds;
+ crd != NULL;
+ crd = kcm_cc_next_cred(crd)) {
+ ret = kcm_cred_get_uuid(crd, uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Credential has no UUID, skipping\n");
+ continue;
+ }
+ uuid_unparse(uuid, str);
+
+ ret = sss_ptr_hash_add_or_override(*_table, str, crd, struct kcm_cred);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ talloc_steal(*_table, crd);
+ }
+
+ return EOK;
+}
+
+static struct kcm_cred *
+kcm_creds_lookup(hash_table_t *table, uuid_t uuid)
+{
+ char str[UUID_STR_SIZE];
+
+ if (uuid == NULL) {
+ return NULL;
+ }
+
+ uuid_unparse(uuid, str);
+ return sss_ptr_hash_lookup(table, str, struct kcm_cred);
+}
+
+/* (name) -> (uuid, ...) */
+static void kcm_op_get_cred_uuid_list_getbyname_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_cred_uuid_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_common_state *state = NULL;
+ errno_t ret;
+ const char *name;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Returning UUID list for %s\n", name);
+
+ subreq = kcm_ccdb_getbyname_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_cred_uuid_list_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_cred_uuid_list_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct kcm_ccache *cc;
+ struct kcm_cred *crd;
+ struct kcm_conn_data *conn_data;
+ uuid_t uuid;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+
+ conn_data = state->op_ctx->conn_data;
+
+ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (cc == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No ccache by that name\n");
+ state->op_ret = ERR_NO_CREDS;
+ tevent_req_done(req);
+ return;
+ }
+
+ ret = kcm_creds_to_table(conn_data, kcm_cc_get_cred(cc), &conn_data->creds);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to build credentials hash table "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ for (crd = kcm_cc_get_cred(cc);
+ crd != NULL;
+ crd = kcm_cc_next_cred(crd)) {
+ ret = kcm_cred_get_uuid(crd, uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Credential has no UUID, skipping\n");
+ continue;
+ }
+
+ kcm_debug_uuid(uuid);
+
+ ret = sss_iobuf_write_len(state->op_ctx->reply,
+ uuid, UUID_BYTES);
+ if (ret != EOK) {
+ char uuid_errbuf[UUID_STR_SIZE];
+ uuid_parse(uuid_errbuf, uuid);
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot marshall UUID %s [%d]: %s\n",
+ uuid_errbuf, ret, sss_strerror(ret));
+ continue;
+ }
+ }
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+static errno_t
+kcm_op_get_cred_by_uuid_reply(struct kcm_cred *crd,
+ struct sss_iobuf *reply)
+{
+ struct sss_iobuf *cred_blob;
+ errno_t ret;
+
+ cred_blob = kcm_cred_get_creds(crd);
+ if (cred_blob == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
+ return ERR_NO_CREDS;
+ }
+
+ ret = sss_iobuf_write_len(reply, sss_iobuf_get_data(cred_blob),
+ sss_iobuf_get_size(cred_blob));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot write ccache blob [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+struct kcm_op_get_cred_by_uuid_state {
+ struct kcm_op_common_state common;
+ uuid_t uuid;
+};
+
+/* (name, uuid) -> (cred) */
+static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_cred_by_uuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_get_cred_by_uuid_state *state;
+ struct kcm_cred *crd;
+ errno_t ret;
+ const char *name;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct kcm_op_get_cred_by_uuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->common.op_ctx = op_ctx;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sss_iobuf_read_len(state->common.op_ctx->input, UUID_BYTES,
+ state->uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read input UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ if (op_ctx->conn_data->creds != NULL) {
+ crd = kcm_creds_lookup(op_ctx->conn_data->creds, state->uuid);
+ if (crd == NULL) {
+ /* This should not happen, it can only happen if wrong UUID was
+ * requested which suggests bug in the caller application. */
+ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
+ kcm_debug_uuid(state->uuid);
+ state->common.op_ret = ERR_KCM_CC_END;
+ ret = EOK;
+ goto immediate;
+ } else {
+ ret = kcm_op_get_cred_by_uuid_reply(crd, op_ctx->reply);
+ if (ret == ERR_NO_CREDS) {
+ state->common.op_ret = ret;
+ ret = EOK;
+ }
+ goto immediate;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Returning creds by UUID for %s\n", name);
+
+ subreq = kcm_ccdb_getbyname_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_cred_by_uuid_getbyname_done, req);
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_get_cred_by_uuid_state *state = tevent_req_data(req,
+ struct kcm_op_get_cred_by_uuid_state);
+ errno_t ret;
+ struct kcm_ccache *cc;
+ struct kcm_cred *crd;
+ struct kcm_conn_data *conn_data;
+
+ conn_data = state->common.op_ctx->conn_data;
+
+ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = kcm_creds_to_table(conn_data, kcm_cc_get_cred(cc), &conn_data->creds);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to build credentials hash table "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (conn_data->creds != NULL) {
+ crd = kcm_creds_lookup(conn_data->creds, state->uuid);
+ if (crd == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
+ kcm_debug_uuid(state->uuid);
+ state->common.op_ret = ERR_KCM_CC_END;
+ } else {
+ ret = kcm_op_get_cred_by_uuid_reply(crd, state->common.op_ctx->reply);
+ if (ret != EOK && ret != ERR_NO_CREDS) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ state->common.op_ret = ret;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t kcm_op_get_cred_by_uuid_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ struct kcm_op_get_cred_by_uuid_state *state;
+
+ state = tevent_req_data(req, struct kcm_op_get_cred_by_uuid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_op_ret = state->common.op_ret;
+ return EOK;
+}
+
+/* (name, flags, credtag) -> () */
+/* FIXME */
+static struct tevent_req *
+kcm_op_remove_cred_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct kcm_op_common_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ state->op_ret = ERR_KCM_OP_NOT_IMPLEMENTED;
+ tevent_req_post(req, ev);
+ tevent_req_done(req);
+ return req;
+}
+
+/* () -> (uuid, ...) */
+static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_cache_uuid_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_common_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Returning full UUID list\n");
+
+ subreq = kcm_ccdb_list_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_cache_uuid_list_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+ errno_t ret;
+ uuid_t *uuid_list;
+
+ ret = kcm_ccdb_list_recv(subreq, state, &uuid_list);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot list the ccache DB [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (uuid_list == NULL || uuid_is_null(uuid_list[0])) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Nothing to list\n");
+ state->op_ret = ERR_NO_MATCHING_CREDS;
+ tevent_req_done(req);
+ return;
+ }
+
+ for (int i = 0;
+ uuid_is_null(uuid_list[i]) == false;
+ i++) {
+ kcm_debug_uuid(uuid_list[i]);
+
+ ret = sss_iobuf_write_len(state->op_ctx->reply,
+ uuid_list[i],
+ UUID_BYTES);
+ if (ret != EOK) {
+ char uuid_errbuf[UUID_STR_SIZE];
+ uuid_parse(uuid_errbuf, uuid_list[i]);
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot marshall UUID %s [%d]: %s\n",
+ uuid_errbuf, ret, sss_strerror(ret));
+ tevent_req_done(req);
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+/* (uuid) -> (name) */
+static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_cache_by_uuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_common_state *state = NULL;
+ errno_t ret;
+ uuid_t uuid_in;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Retrieving cache by UUID\n");
+
+ ret = sss_iobuf_read_len(op_ctx->input,
+ UUID_BYTES, uuid_in);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot read input UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+ kcm_debug_uuid(uuid_in);
+
+ subreq = kcm_ccdb_getbyuuid_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ uuid_in);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_cache_by_uuid_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct kcm_ccache *cc;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+ const char *name;
+
+ ret = kcm_ccdb_getbyuuid_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccache by UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (cc == NULL) {
+ state->op_ret = ERR_KCM_CC_END;
+ tevent_req_done(req);
+ return;
+ }
+
+ name = kcm_cc_get_name(cc);
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found %s by UUID\n", name);
+
+ ret = sss_iobuf_write_stringz(state->op_ctx->reply,
+ name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot write output name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+/* () -> (name) */
+struct kcm_op_get_default_ccache_state {
+ uint32_t op_ret;
+ struct kcm_op_ctx *op_ctx;
+ struct tevent_context *ev;
+
+ const char *name;
+};
+
+static void kcm_op_get_get_default_done(struct tevent_req *subreq);
+static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq);
+static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq);
+static errno_t
+kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state);
+
+static struct tevent_req *
+kcm_op_get_default_ccache_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_get_default_ccache_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct kcm_op_get_default_ccache_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+ state->ev = ev;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Getting client's default ccache\n");
+
+ subreq = kcm_ccdb_get_default_send(state, ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_get_default_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_get_default_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct kcm_op_get_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_get_default_ccache_state);
+ errno_t ret;
+ uuid_t dfl_uuid;
+
+ ret = kcm_ccdb_get_default_recv(subreq, &dfl_uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get default ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (uuid_is_null(dfl_uuid) == true) {
+ /* No cache marked as default -- get an existing ccache for ID
+ * and treat the default as simply the first one
+ */
+ subreq = kcm_ccdb_list_send(state, state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_default_ccache_list_done, req);
+ return;
+ }
+
+ /* Existing default */
+ subreq = kcm_ccdb_name_by_uuid_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ dfl_uuid);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req);
+ return;
+}
+
+static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct kcm_op_get_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_get_default_ccache_state);
+ errno_t ret;
+
+ ret = kcm_ccdb_name_by_uuid_recv(subreq, state, &state->name);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccahe by UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ /* Instead of failing the whole operation, return the first
+ * ccache as a fallback
+ */
+ subreq = kcm_ccdb_list_send(state, state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_default_ccache_list_done, req);
+ return;
+ }
+
+ ret = kcm_op_get_default_ccache_reply_step(state);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct kcm_op_get_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_get_default_ccache_state);
+ errno_t ret;
+ uuid_t *uuid_list;
+
+ ret = kcm_ccdb_list_recv(subreq, state, &uuid_list);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot list ccaches [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (uuid_list == NULL || uuid_is_null(uuid_list[0])) {
+ /* No cache at all, just send back a reply */
+ ret = kcm_op_get_default_ccache_reply_step(state);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+ }
+
+ /* Otherwise resolve the first cache and use it as a default */
+ subreq = kcm_ccdb_name_by_uuid_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ uuid_list[0]);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req);
+ return;
+}
+
+static errno_t
+kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state)
+{
+ errno_t ret;
+
+ if (state->name == NULL) {
+ state->name = talloc_asprintf(state,
+ "%"SPRIuid,
+ cli_creds_get_uid(state->op_ctx->client));
+ if (state->name == NULL) {
+ return ENOMEM;
+ }
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "The default ccache is %s\n", state->name);
+
+ ret = sss_iobuf_write_stringz(state->op_ctx->reply, state->name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot write output name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t kcm_op_get_default_ccache_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_get_default_ccache_state, _op_ret);
+}
+
+/* (name) -> () */
+static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq);
+static void kcm_op_set_default_create_step(struct tevent_req *req);
+static void kcm_op_set_default_create_step_done(struct tevent_req *subreq);
+static void kcm_op_set_default_step(struct tevent_req *req);
+static void kcm_op_set_default_done(struct tevent_req *subreq);
+
+struct kcm_op_set_default_ccache_state {
+ uint32_t op_ret;
+ struct kcm_op_ctx *op_ctx;
+ struct tevent_context *ev;
+
+ const char *name;
+ uuid_t dfl_uuid;
+ struct kcm_ccache *new_cc;
+};
+
+static struct tevent_req *
+kcm_op_set_default_ccache_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_set_default_ccache_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct kcm_op_set_default_ccache_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+ state->ev = ev;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &state->name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot read input name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Setting default ccache %s\n", state->name);
+
+ subreq = kcm_ccdb_uuid_by_name_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ state->name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_set_default_ccache_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+
+ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, state->dfl_uuid);
+ talloc_zfree(subreq);
+ if (ret == ERR_NO_CREDS) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "The ccache does not exist, creating a new one\n");
+ kcm_op_set_default_create_step(req);
+ return;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ kcm_op_set_default_step(req);
+}
+
+static void kcm_op_set_default_create_step(struct tevent_req *req)
+{
+ errno_t ret;
+ struct tevent_req *subreq;
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+
+ /* Only allow to create ccaches for 'self' */
+ ret = kcm_check_name(state->name, state->op_ctx->client);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Name %s is malformed [%d]: %s\n",
+ state->name, ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = kcm_cc_new(state->op_ctx,
+ state->op_ctx->kcm_data->k5c,
+ state->op_ctx->client,
+ state->name,
+ NULL,
+ &state->new_cc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot create new ccache %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = kcm_ccdb_create_cc_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ state->new_cc);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_set_default_create_step_done, req);
+}
+
+static void kcm_op_set_default_create_step_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+
+ ret = kcm_ccdb_create_cc_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot add ccache to db %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "The ccache was created, switching to it");
+
+ ret = kcm_cc_get_uuid(state->new_cc, state->dfl_uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get new ccache UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ kcm_op_set_default_step(req);
+}
+
+static void kcm_op_set_default_step(struct tevent_req *req)
+{
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+ struct tevent_req *subreq;
+
+ subreq = kcm_ccdb_set_default_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ state->dfl_uuid);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_set_default_done, req);
+ return;
+}
+
+static void kcm_op_set_default_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+
+ ret = kcm_ccdb_set_default_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+static errno_t kcm_op_set_default_ccache_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_set_default_ccache_state, _op_ret);
+}
+
+/* (name) -> (offset) */
+static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_kdc_offset_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_common_state *state = NULL;
+ errno_t ret;
+ const char *name;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot read input name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Requested offset for principal %s\n", name);
+
+ subreq = kcm_ccdb_getbyname_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_get_kdc_offset_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct kcm_ccache *cc;
+ int32_t offset;
+ int32_t offset_be;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_common_state *state = tevent_req_data(req,
+ struct kcm_op_common_state);
+
+ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get matching ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (cc == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No matching credentials\n");
+ state->op_ret = ERR_NO_MATCHING_CREDS;
+ tevent_req_done(req);
+ return;
+ }
+
+ offset = kcm_cc_get_offset(cc);
+ DEBUG(SSSDBG_TRACE_LIBS, "KDC offset: %"PRIi32"\n", offset);
+
+ offset_be = htobe32(offset);
+ ret = sss_iobuf_write_int32(state->op_ctx->reply, offset_be);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot write KDC offset [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+/* (name, offset) -> () */
+/* () -> (name) */
+struct kcm_op_set_kdc_offset_state {
+ uint32_t op_ret;
+ struct kcm_op_ctx *op_ctx;
+ struct tevent_context *ev;
+};
+
+static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq);
+static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_set_kdc_offset_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct kcm_op_set_kdc_offset_state *state = NULL;
+ errno_t ret;
+ const char *name;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_set_kdc_offset_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+ state->ev = ev;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot read input name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS, "Setting offset for principal %s\n", name);
+
+ subreq = kcm_ccdb_uuid_by_name_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_op_set_kdc_offset_getbyname_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct kcm_mod_ctx *mod_ctx;
+ int32_t offset_be;
+ uuid_t uuid;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req,
+ struct kcm_op_set_kdc_offset_state);
+
+ /* `uuid` is output arg and isn't read in kcm_ccdb_uuid_by_name_recv() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get matching ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sss_iobuf_read_int32(state->op_ctx->input, &offset_be);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot read KDC offset [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ mod_ctx = kcm_mod_ctx_new(state);
+ if (mod_ctx == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ mod_ctx->kdc_offset = be32toh(offset_be);
+
+ subreq = kcm_ccdb_mod_cc_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ uuid,
+ mod_ctx);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_set_kdc_offset_mod_done, req);
+}
+
+static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req,
+ struct kcm_op_set_kdc_offset_state);
+
+ ret = kcm_ccdb_mod_cc_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot modify ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->op_ret = EOK;
+ tevent_req_done(req);
+}
+
+static errno_t kcm_op_set_kdc_offset_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_set_kdc_offset_state, _op_ret);
+}
+
+static void kcm_op_get_cred_list_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_cred_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct kcm_op_common_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ const char *name;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Returning credentials for %s\n", name);
+
+ subreq = kcm_ccdb_getbyname_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ tevent_req_set_callback(subreq, kcm_op_get_cred_list_done, req);
+
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_cred_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct kcm_op_common_state *state;
+ struct kcm_ccache *cc;
+ struct kcm_cred *crd;
+ uint32_t num_creds;
+ struct sss_iobuf *crd_blob;
+ uint8_t *crd_data;
+ uint32_t crd_size;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct kcm_op_common_state);
+
+ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (cc == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No ccache by that name\n");
+ state->op_ret = ERR_NO_CREDS;
+ ret = EOK;
+ goto done;
+ }
+
+ num_creds = 0;
+ for (crd = kcm_cc_get_cred(cc); crd != NULL; crd = kcm_cc_next_cred(crd)) {
+ num_creds++;
+ }
+
+ ret = sss_iobuf_write_uint32(state->op_ctx->reply, htobe32(num_creds));
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (crd = kcm_cc_get_cred(cc); crd != NULL; crd = kcm_cc_next_cred(crd)) {
+ crd_blob = kcm_cred_get_creds(crd);
+ if (crd_blob == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
+ ret = ERR_NO_CREDS;
+ goto done;
+ }
+
+ crd_data = sss_iobuf_get_data(crd_blob);
+ crd_size = sss_iobuf_get_size(crd_blob);
+
+ ret = sss_iobuf_write_uint32(state->op_ctx->reply, htobe32(crd_size));
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sss_iobuf_write_len(state->op_ctx->reply, crd_data, crd_size);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ state->op_ret = EOK;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static struct kcm_op kcm_optable[] = {
+ { "NOOP", NULL, NULL },
+ { "GET_NAME", NULL, NULL },
+ { "RESOLVE", NULL, NULL },
+ { "GEN_NEW", kcm_op_gen_new_send, NULL },
+ { "INITIALIZE", kcm_op_initialize_send, kcm_op_initialize_recv },
+ { "DESTROY", kcm_op_destroy_send, NULL },
+ { "STORE", kcm_op_store_send, kcm_op_store_recv },
+ { "RETRIEVE", NULL, NULL },
+ { "GET_PRINCIPAL", kcm_op_get_principal_send, NULL },
+ { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list_send, NULL },
+ { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid_send, kcm_op_get_cred_by_uuid_recv },
+ { "REMOVE_CRED", kcm_op_remove_cred_send, NULL },
+ { "SET_FLAGS", NULL, NULL },
+ { "CHOWN", NULL, NULL },
+ { "CHMOD", NULL, NULL },
+ { "GET_INITIAL_TICKET", NULL, NULL },
+ { "GET_TICKET", NULL, NULL },
+ { "MOVE_CACHE", NULL, NULL },
+ { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list_send, NULL },
+ { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid_send, NULL },
+ { "GET_DEFAULT_CACHE", kcm_op_get_default_ccache_send, kcm_op_get_default_ccache_recv },
+ { "SET_DEFAULT_CACHE", kcm_op_set_default_ccache_send, kcm_op_set_default_ccache_recv },
+ { "GET_KDC_OFFSET", kcm_op_get_kdc_offset_send, NULL },
+ { "SET_KDC_OFFSET", kcm_op_set_kdc_offset_send, kcm_op_set_kdc_offset_recv },
+ { "ADD_NTLM_CRED", NULL, NULL },
+ { "HAVE_NTLM_CRED", NULL, NULL },
+ { "DEL_NTLM_CRED", NULL, NULL },
+ { "DO_NTLM_AUTH", NULL, NULL },
+ { "GET_NTLM_USER_LIST", NULL, NULL },
+
+ { NULL, NULL, NULL }
+};
+
+/* MIT EXTENSIONS, see private header src/include/kcm.h in krb5 sources */
+#define KCM_MIT_OFFSET 13001
+static struct kcm_op kcm_mit_optable[] = {
+ { "GET_CRED_LIST", kcm_op_get_cred_list_send, NULL },
+
+ { NULL, NULL, NULL }
+};
+
+struct kcm_op *kcm_get_opt(uint16_t opcode)
+{
+ struct kcm_op *table;
+ struct kcm_op *op;
+ size_t len;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "The client requested operation %"PRIu16"\n", opcode);
+
+ table = kcm_optable;
+ len = sizeof(kcm_optable) / sizeof(struct kcm_op);
+ if (opcode >= KCM_MIT_OFFSET) {
+ opcode -= KCM_MIT_OFFSET;
+ table = kcm_mit_optable;
+ len = sizeof(kcm_mit_optable) / sizeof(struct kcm_op);
+ }
+
+ if (opcode >= len) {
+ return NULL;
+ }
+
+ op = &table[opcode];
+ if (op->fn_recv == NULL) {
+ op->fn_recv = kcm_op_common_recv;
+ }
+
+ return op;
+}
+
+const char *kcm_opt_name(struct kcm_op *op)
+{
+ if (op == NULL || op->name == NULL) {
+ return "Unknown operation";
+ }
+
+ return op->name;
+}
diff --git a/src/responder/kcm/kcmsrv_ops.h b/src/responder/kcm/kcmsrv_ops.h
new file mode 100644
index 0000000..5d80352
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ops.h
@@ -0,0 +1,67 @@
+/*
+ SSSD
+
+ KCM Server - private header file
+
+ Copyright (C) Red Hat, 2016
+
+ 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 __KCMSRV_OPS_H__
+#define __KCMSRV_OPS_H__
+
+#include "config.h"
+
+#include <dhash.h>
+#include <sys/types.h>
+#include "util/sss_iobuf.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+
+/* The initial packet size, which can later be grown up to KCM_PACKET_MAX_SIZE.
+ * The initial size is a trade off that is expected to best serve most of the
+ * cases (typical credentials size).
+ */
+#define KCM_PACKET_INITIAL_SIZE 4096
+
+/* The maximum length of a request or reply as defined by the RPC
+ * protocol. This is the same constant size as MIT KRB5 uses
+ * This limit comes from:
+ * https://github.com/krb5/krb5/blob/c20251dafd6120fa08c76b19315cb9deb1a1b24e/src/lib/krb5/ccache/cc_kcm.c#L54
+ */
+#define KCM_PACKET_MAX_SIZE 10*1024*1024
+
+struct kcm_op;
+struct kcm_op *kcm_get_opt(uint16_t opcode);
+const char *kcm_opt_name(struct kcm_op *op);
+
+struct kcm_conn_data {
+ /* Credentials obtained by GET_CRED_UUID_LIST. We use to improve performance
+ * by avoiding ccache lookups in GET_CRED_BY_UUID. */
+ hash_table_t *creds;
+};
+
+struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ops_queue_ctx *qctx,
+ struct kcm_resp_ctx *kcm_data,
+ struct kcm_conn_data *conn_data,
+ struct cli_creds *client,
+ struct kcm_data *input,
+ struct kcm_op *op);
+errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sss_iobuf **_reply);
+
+#endif /* __KCMSRV_OPS_H__ */
diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
new file mode 100644
index 0000000..0536a2c
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_pvt.h
@@ -0,0 +1,106 @@
+/*
+ SSSD
+
+ KCM Server - private header file
+
+ Copyright (C) Red Hat, 2016
+
+ 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 __KCMSRV_PVT_H__
+#define __KCMSRV_PVT_H__
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <krb5/krb5.h>
+#include "responder/common/responder.h"
+
+#define KCM_PROTOCOL_VERSION_MAJOR 2
+#define KCM_PROTOCOL_VERSION_MINOR 0
+
+/* This should ideally be in RUNSTATEDIR, but Heimdal uses a hardcoded
+ * /var/run, and we need to use the same default path. */
+#define DEFAULT_KCM_SOCKET_PATH "/var/run/.heim_org.h5l.kcm-socket"
+
+/*
+ * KCM IO structure
+ *
+ * In theory we cold use sss_iobuf there, but since iobuf was
+ * made opaque, this allows it to allocate the structures on
+ * the stack in one go.
+ * */
+struct kcm_data {
+ uint8_t *data;
+ size_t length;
+};
+
+/*
+ * To avoid leaking the sssd-specific responder data to other
+ * modules, the ccache databases and other KCM specific data
+ * are kept separately
+ */
+struct kcm_resp_ctx {
+ krb5_context k5c;
+ struct kcm_ccdb *db;
+};
+
+/* Supported ccache back ends */
+enum kcm_ccdb_be {
+ CCDB_BE_MEMORY,
+ CCDB_BE_SECDB,
+};
+
+/*
+ * responder context that contains both the responder data,
+ * like the ccaches and the sssd-specific stuff like the
+ * generic responder ctx
+ */
+struct kcm_ctx {
+ struct resp_ctx *rctx;
+ int fd_limit;
+ char *socket_path;
+ enum kcm_ccdb_be cc_be;
+ struct kcm_ops_queue_ctx *qctx;
+
+ struct kcm_resp_ctx *kcm_data;
+};
+
+int kcm_connection_setup(struct cli_ctx *cctx);
+
+/*
+ * Internally in SSSD-KCM we use SSSD-internal error codes so that we
+ * can always the same sss_strerror() functions to format the errors
+ * nicely, but the client expects libkrb5 error codes.
+ */
+krb5_error_code sss2krb5_error(errno_t err);
+
+/* We enqueue all requests by the same UID to avoid concurrency issues.
+ */
+struct kcm_ops_queue_entry;
+
+struct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx,
+ struct kcm_ctx *kctx);
+
+struct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_ops_queue_ctx *qctx,
+ struct cli_creds *client);
+
+errno_t kcm_op_queue_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct kcm_ops_queue_entry **_entry);
+
+#endif /* __KCMSRV_PVT_H__ */
diff --git a/src/responder/kcm/secrets/config.c b/src/responder/kcm/secrets/config.c
new file mode 100644
index 0000000..84462b8
--- /dev/null
+++ b/src/responder/kcm/secrets/config.c
@@ -0,0 +1,92 @@
+/*
+ SSSD
+
+ Local secrets database -- configuration
+
+ Copyright (C) Red Hat 2018
+
+ 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 "util/util.h"
+#include "secrets.h"
+
+errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
+ const char *section_config_path,
+ struct sss_sec_quota_opt *dfl_max_containers_nest_level,
+ struct sss_sec_quota_opt *dfl_max_num_secrets,
+ struct sss_sec_quota_opt *dfl_max_num_uid_secrets,
+ struct sss_sec_quota_opt *dfl_max_payload,
+ struct sss_sec_quota *quota)
+{
+ int ret;
+
+ if (cdb == NULL || section_config_path == NULL || quota == NULL) {
+ return EINVAL;
+ }
+
+ ret = confdb_get_int(cdb,
+ section_config_path,
+ dfl_max_containers_nest_level->opt_name,
+ dfl_max_containers_nest_level->default_value,
+ &quota->containers_nest_level);
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get container nesting level for %s\n",
+ section_config_path);
+ return ret;
+ }
+
+ ret = confdb_get_int(cdb,
+ section_config_path,
+ dfl_max_num_secrets->opt_name,
+ dfl_max_num_secrets->default_value,
+ &quota->max_secrets);
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get maximum number of entries for %s\n",
+ section_config_path);
+ return ret;
+ }
+
+ ret = confdb_get_int(cdb,
+ section_config_path,
+ dfl_max_num_uid_secrets->opt_name,
+ dfl_max_num_uid_secrets->default_value,
+ &quota->max_uid_secrets);
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get maximum number of per-UID entries for %s\n",
+ section_config_path);
+ return ret;
+ }
+
+ ret = confdb_get_int(cdb,
+ section_config_path,
+ dfl_max_payload->opt_name,
+ dfl_max_payload->default_value,
+ &quota->max_payload_size);
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get payload's maximum size for an entry in %s\n",
+ section_config_path);
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/responder/kcm/secrets/sec_pvt.h b/src/responder/kcm/secrets/sec_pvt.h
new file mode 100644
index 0000000..4118eaa
--- /dev/null
+++ b/src/responder/kcm/secrets/sec_pvt.h
@@ -0,0 +1,47 @@
+/*
+ SSSD
+
+ Local secrets database - private header
+
+ Copyright (C) Red Hat 2018
+
+ 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 __SECRETS_PVT_H_
+#define __SECRETS_PVT_H_
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "secrets.h"
+
+struct sss_sec_ctx {
+ struct ldb_context *ldb;
+
+ struct sss_sec_quota *quota_kcm;
+};
+
+struct sss_sec_req {
+ char *path;
+ const char *basedn;
+ struct ldb_dn *req_dn;
+ struct sss_sec_quota *quota;
+
+ struct sss_sec_ctx *sctx;
+};
+
+#endif /* __SECRETS_PVT_H_ */
diff --git a/src/responder/kcm/secrets/secrets.c b/src/responder/kcm/secrets/secrets.c
new file mode 100644
index 0000000..a37edcc
--- /dev/null
+++ b/src/responder/kcm/secrets/secrets.c
@@ -0,0 +1,1229 @@
+/*
+ SSSD
+
+ Local secrets database
+
+ Copyright (C) Red Hat 2018
+
+ 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 "config.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <uuid/uuid.h>
+
+#include "responder/kcm/kcmsrv_ccache.h"
+#include "util/util.h"
+#include "util/util_creds.h"
+#include "util/sss_iobuf.h"
+#include "util/strtonum.h"
+#include "util/crypto/sss_crypto.h"
+#include "sec_pvt.h"
+#include "secrets.h"
+
+#define KCM_PEER_UID 0
+
+#define KCM_BASEDN "cn=kcm"
+
+#define LOCAL_CONTAINER_FILTER "(type=container)"
+#define LOCAL_NON_CONTAINER_FILTER "(!"LOCAL_CONTAINER_FILTER")"
+
+#define SEC_ATTR_SECRET "secret"
+#define SEC_ATTR_TYPE "type"
+#define SEC_ATTR_CTIME "creationTime"
+
+static struct sss_sec_quota default_kcm_quota = {
+ .max_secrets = DEFAULT_SEC_KCM_MAX_SECRETS,
+ .max_uid_secrets = DEFAULT_SEC_KCM_MAX_UID_SECRETS,
+ .max_payload_size = DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE,
+ .containers_nest_level = DEFAULT_SEC_CONTAINERS_NEST_LEVEL,
+};
+
+static char *local_dn_to_path(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ struct ldb_dn *dn);
+
+static int local_db_check_containers(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sec_ctx,
+ struct ldb_dn *leaf_dn)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { NULL};
+ struct ldb_result *res = NULL;
+ struct ldb_dn *dn;
+ int num;
+ int ret;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) return ENOMEM;
+
+ dn = ldb_dn_copy(tmp_ctx, leaf_dn);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* We need to exclude the leaf as that will be the new child entry,
+ * We also do not care for the synthetic containers that constitute the
+ * base path (cn=<uidnumber>,cn=users,cn=secrets), so in total we remove
+ * 4 components */
+ num = ldb_dn_get_comp_num(dn) - 4;
+
+ for (int i = 0; i < num; i++) {
+ /* remove the child first (we do not want to check the leaf) */
+ if (!ldb_dn_remove_child_components(dn, 1)) return EFAULT;
+
+ /* and check the parent container exists */
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Searching for [%s] at [%s] with scope=base\n",
+ LOCAL_CONTAINER_FILTER, ldb_dn_get_linearized(dn));
+
+ ret = ldb_search(sec_ctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
+ attrs, LOCAL_CONTAINER_FILTER);
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "DN [%s] does not exist\n", ldb_dn_get_linearized(dn));
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int local_db_check_number_of_secrets(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { NULL };
+ struct ldb_result *res = NULL;
+ struct ldb_dn *dn;
+ int ret;
+
+ if (req->quota->max_secrets == 0) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) return ENOMEM;
+
+ dn = ldb_dn_new(tmp_ctx, req->sctx->ldb, req->basedn);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(req->sctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+ attrs, LOCAL_NON_CONTAINER_FILTER);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_search returned %d: %s\n", ret, ldb_strerror(ret));
+ goto done;
+ }
+
+ if (res->count >= req->quota->max_secrets) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot store any more secrets as the maximum allowed limit (%d) "
+ "has been reached\n", req->quota->max_secrets);
+ ret = ERR_SEC_INVALID_TOO_MANY_SECRETS;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static struct ldb_dn *per_uid_container(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *req_dn)
+{
+ int user_comp;
+ int num_comp;
+ struct ldb_dn *uid_base_dn;
+
+ uid_base_dn = ldb_dn_copy(mem_ctx, req_dn);
+ if (uid_base_dn == NULL) {
+ return NULL;
+ }
+
+ /* Remove all the components up to the per-user base path which consists
+ * of three components:
+ * cn=<uidnumber>,cn=users,cn=secrets
+ */
+ user_comp = ldb_dn_get_comp_num(uid_base_dn) - 3;
+
+ if (!ldb_dn_remove_child_components(uid_base_dn, user_comp)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot remove child components\n");
+ talloc_free(uid_base_dn);
+ return NULL;
+ }
+
+ num_comp = ldb_dn_get_comp_num(uid_base_dn);
+ if (num_comp != 3) {
+ DEBUG(SSSDBG_OP_FAILURE, "Expected 3 components got %d\n", num_comp);
+ talloc_free(uid_base_dn);
+ return NULL;
+ }
+
+ return uid_base_dn;
+}
+
+static errno_t get_secret_expiration_time(uint8_t *key, size_t key_length,
+ uint8_t *sec, size_t sec_length,
+ time_t *_expiration)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ time_t expiration = 0;
+ struct cli_creds client = {};
+ struct kcm_ccache *cc;
+ struct sss_iobuf *iobuf;
+ krb5_creds **cred_list, **cred;
+ const char *key_str;
+
+ if (_expiration == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ key_str = talloc_strndup(tmp_ctx, (const char *) key, key_length);
+ if (key_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ iobuf = sss_iobuf_init_readonly(tmp_ctx, sec, sec_length);
+ if (iobuf == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sec_kv_to_ccache_binary(tmp_ctx, key_str, iobuf, &client, &cc);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ cred_list = kcm_cc_unmarshal(tmp_ctx, NULL, cc);
+ if (cred_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (cred = cred_list; *cred != NULL; cred++) {
+ if ((*cred)->times.endtime != 0) {
+ expiration = (time_t) (*cred)->times.endtime;
+ break;
+ }
+ }
+
+ *_expiration = expiration;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t local_db_remove_oldest_expired_secret(struct ldb_result *res,
+ struct sss_sec_req *req)
+{
+ struct sss_sec_req *new_req = NULL;
+ const struct ldb_val *val;
+ const struct ldb_val *rdn;
+ struct ldb_message *msg;
+ struct ldb_message_element *elem;
+ struct ldb_dn *basedn;
+ struct ldb_dn *oldest_dn = NULL;
+ time_t oldest_time = time(NULL);
+ time_t expiration;
+ unsigned int i;
+ int ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Removing the oldest expired credential\n");
+ /* Between all the messages in result, there is also the key we are
+ * currently treating, but because yet it doesn't have an expiration time,
+ * it will be skipped.
+ */
+ for (i = 0; i < res->count; i++) {
+ msg = res->msgs[i];
+
+ /* Skip cn=default,... or any non cn=... */
+ rdn = ldb_dn_get_rdn_val(msg->dn);
+ if (strcmp(ldb_dn_get_rdn_name(msg->dn), "cn") != 0
+ || strncmp("default", (char *) rdn->data, rdn->length) == 0) {
+ continue;
+ }
+
+ elem = ldb_msg_find_element(msg, SEC_ATTR_SECRET);
+ if (elem != NULL) {
+ if (elem->num_values != 1) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Element %s has %u values. Ignoring it.\n",
+ SEC_ATTR_SECRET, elem->num_values);
+ ret = ERR_MALFORMED_ENTRY;
+ goto done;
+ }
+
+ val = &elem->values[0];
+ ret = get_secret_expiration_time(rdn->data, rdn->length,
+ val->data, val->length,
+ &expiration);
+ if (ret != EOK) {
+ goto done;
+ }
+ if (expiration > 0 && expiration < oldest_time) {
+ oldest_dn = msg->dn;
+ oldest_time = expiration;
+ }
+ }
+ }
+
+ if (oldest_dn == NULL) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found no expired credential to remove\n");
+ ret = ERR_NO_MATCHING_CREDS;
+ goto done;
+ }
+
+ new_req = talloc_zero(NULL, struct sss_sec_req);
+ if (new_req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate the new request\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ basedn = ldb_dn_new(new_req, req->sctx->ldb, req->basedn);
+ if (basedn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to create a dn: %s\n", req->basedn);
+ ret = EINVAL;
+ goto done;
+ }
+
+ new_req->basedn = req->basedn;
+ new_req->quota = req->quota;
+ new_req->req_dn = oldest_dn;
+ new_req->sctx = req->sctx;
+ new_req->path = local_dn_to_path(new_req, basedn, oldest_dn);
+ if (new_req->path == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to create the path\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sss_sec_delete(new_req);
+
+done:
+ if (new_req != NULL)
+ talloc_free(new_req);
+
+ return ret;
+}
+
+
+static int local_db_check_peruid_number_of_secrets(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { SEC_ATTR_SECRET, NULL };
+ struct ldb_result *res = NULL;
+ struct ldb_dn *cli_basedn = NULL;
+ int ret;
+
+ if (req->quota->max_uid_secrets == 0) {
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ cli_basedn = per_uid_container(tmp_ctx, req->req_dn);
+ if (cli_basedn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_search(req->sctx->ldb, tmp_ctx, &res, cli_basedn, LDB_SCOPE_SUBTREE,
+ attrs, LOCAL_NON_CONTAINER_FILTER);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_search returned %d: %s\n", ret, ldb_strerror(ret));
+ goto done;
+ }
+
+ if (res->count >= req->quota->max_uid_secrets) {
+ /* We reached the limit. Let's try to removed the
+ * oldest expired credential to free some space. */
+ ret = local_db_remove_oldest_expired_secret(res, req);
+ if (ret != EOK) {
+ if (ret == ERR_NO_MATCHING_CREDS) {
+ /* max_uid_secrets is incremented for internal entries. */
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot store any more secrets for this client (basedn %s) "
+ "as the maximum allowed limit (%d) has been reached\n",
+ ldb_dn_get_linearized(cli_basedn),
+ req->quota->max_uid_secrets - KCM_MAX_UID_EXTRA_SECRETS);
+ ret = ERR_SEC_INVALID_TOO_MANY_SECRETS;
+ }
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int local_check_max_payload_size(struct sss_sec_req *req,
+ int payload_size)
+{
+ int max_payload_size;
+
+ if (req->quota->max_payload_size == 0) {
+ return EOK;
+ }
+
+ max_payload_size = req->quota->max_payload_size * 1024; /* KiB */
+ if (payload_size > max_payload_size) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Secrets' payload size [%d KiB (%d B)] exceeds the maximum "
+ "allowed payload size [%d KiB (%d B)]\n",
+ payload_size / 1024, /* KiB */
+ payload_size,
+ req->quota->max_payload_size, /* KiB */
+ max_payload_size);
+
+ return ERR_SEC_PAYLOAD_SIZE_IS_TOO_LARGE;
+ }
+
+ return EOK;
+}
+
+static int local_db_check_containers_nest_level(struct sss_sec_req *req,
+ struct ldb_dn *leaf_dn)
+{
+ int nest_level;
+
+ if (req->quota->containers_nest_level == 0) {
+ return EOK;
+ }
+
+ /* We need do not care for the synthetic containers that constitute the
+ * base path (cn=<uidnumber>,cn=user,cn=secrets). */
+ nest_level = ldb_dn_get_comp_num(leaf_dn) - 3;
+ if (nest_level > req->quota->containers_nest_level) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot create a nested container of depth %d as the maximum"
+ "allowed number of nested containers is %d.\n",
+ nest_level, req->quota->containers_nest_level);
+
+ return ERR_SEC_INVALID_CONTAINERS_NEST_LEVEL;
+ }
+
+ return EOK;
+}
+
+static int local_db_create(struct sss_sec_req *req)
+{
+ struct ldb_message *msg;
+ int ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Creating a container at [%s]\n", req->path);
+
+ msg = ldb_msg_new(req);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = req->req_dn;
+
+ /* make sure containers exist */
+ ret = local_db_check_containers(msg, req->sctx, msg->dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_db_check_containers failed for [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(msg->dn), ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = local_db_check_containers_nest_level(req, msg->dn);
+ if (ret != EOK) goto done;
+
+ ret = ldb_msg_add_string(msg, SEC_ATTR_TYPE, "container");
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_msg_add_string failed adding type:container [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = ldb_msg_add_fmt(msg, SEC_ATTR_CTIME, "%lu", time(NULL));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_msg_add_string failed adding creationTime [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = ldb_add(req->sctx->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Secret %s already exists\n", ldb_dn_get_linearized(msg->dn));
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to add secret [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(msg->dn), ret, ldb_strerror(ret));
+ }
+ ret = sss_ldb_error_to_errno (ret);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(msg);
+ return ret;
+}
+
+/* non static since it is used in test_kcm_renewals.c */
+errno_t sss_sec_init_with_path(TALLOC_CTX *mem_ctx,
+ struct sss_sec_quota *quota,
+ const char *dbpath,
+ struct sss_sec_ctx **_sec_ctx)
+{
+ struct sss_sec_ctx *sec_ctx;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ if (_sec_ctx == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ sec_ctx = talloc_zero(tmp_ctx, struct sss_sec_ctx);
+ if (sec_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (quota) {
+ sec_ctx->quota_kcm = quota;
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS, "No custom quota set, using defaults\n");
+ sec_ctx->quota_kcm = &default_kcm_quota;
+ }
+
+ sec_ctx->ldb = ldb_init(sec_ctx, NULL);
+ if (sec_ctx->ldb == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_connect(sec_ctx->ldb, dbpath, 0, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_connect(%s) returned %d: %s\n",
+ dbpath, ret, ldb_strerror(ret));
+ talloc_free(sec_ctx->ldb);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+ *_sec_ctx = talloc_steal(mem_ctx, sec_ctx);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sss_sec_init(TALLOC_CTX *mem_ctx,
+ struct sss_sec_quota *quota,
+ struct sss_sec_ctx **_sec_ctx)
+{
+ const char *dbpath = SECRETS_DB_PATH"/secrets.ldb";
+ errno_t ret;
+
+ ret = sss_sec_init_with_path(mem_ctx, quota, dbpath, _sec_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize secdb [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ return ret;
+}
+
+static int local_db_dn(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *basedn,
+ const char *req_path,
+ struct ldb_dn **req_dn)
+{
+ struct ldb_dn *dn;
+ const char *s, *e;
+ int ret;
+
+ dn = ldb_dn_new(mem_ctx, ldb, basedn);
+ if (!dn) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ s = req_path;
+
+ while (s && *s) {
+ e = strchr(s, '/');
+ if (e) {
+ if (e == s) {
+ s++;
+ continue;
+ }
+ if (!ldb_dn_add_child_fmt(dn, "cn=%.*s", (int)(e - s), s)) {
+ ret = ENOMEM;
+ goto done;
+ }
+ s = e + 1;
+ } else {
+ if (!ldb_dn_add_child_fmt(dn, "cn=%s", s)) {
+ ret = ENOMEM;
+ goto done;
+ }
+ s = NULL;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Local path for [%s] is [%s]\n",
+ req_path, ldb_dn_get_linearized(dn));
+ *req_dn = dn;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+errno_t sss_sec_new_req(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sec_ctx,
+ const char *url,
+ uid_t client,
+ struct sss_sec_req **_req)
+{
+ struct sss_sec_req *req;
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+
+ if (sec_ctx == NULL || url == NULL || _req == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ req = talloc_zero(tmp_ctx, struct sss_sec_req);
+ if (req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ req->sctx = sec_ctx;
+
+ /* drop the prefix and select a basedn instead */
+ if (geteuid() != KCM_PEER_UID && client != KCM_PEER_UID) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "UID %"SPRIuid" is not allowed to access the KCM hive\n",
+ client);
+ ret = EPERM;
+ goto done;
+ }
+
+ req->basedn = KCM_BASEDN;
+ req->quota = sec_ctx->quota_kcm;
+ req->path = talloc_strdup(req, url);
+ if (req->path == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = local_db_dn(req, sec_ctx->ldb, req->basedn, req->path, &req->req_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to map request to local db DN\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Local DB path is %s\n", req->path);
+
+ ret = EOK;
+ *_req = talloc_steal(mem_ctx, req);
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static char *local_dn_to_path(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ struct ldb_dn *dn)
+{
+ int basecomps;
+ int dncomps;
+ char *path = NULL;
+
+ basecomps = ldb_dn_get_comp_num(basedn);
+ dncomps = ldb_dn_get_comp_num(dn);
+
+ for (int i = dncomps - basecomps; i > 0; i--) {
+ const struct ldb_val *val;
+
+ val = ldb_dn_get_component_val(dn, i - 1);
+ if (!val) return NULL;
+
+ if (path) {
+ path = talloc_strdup_append_buffer(path, "/");
+ if (!path) return NULL;
+ path = talloc_strndup_append_buffer(path, (char *)val->data,
+ val->length);
+ } else {
+ path = talloc_strndup(mem_ctx, (char *)val->data, val->length);
+ }
+ if (!path) return NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Secrets path for [%s] is [%s]\n",
+ ldb_dn_get_linearized(dn), path);
+ return path;
+}
+
+/* Complete list of ccache names(UUID:name) */
+errno_t sss_sec_list_cc_uuids(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sec,
+ const char ***_uuid_list,
+ const char ***_uid_list,
+ size_t *_uuid_list_count)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ struct ldb_dn *dn;
+ const struct ldb_val *name_val;
+ const struct ldb_val *uid_val;
+ static const char *attrs[] = { "distinguishedName", NULL };
+ const char **uuid_list;
+ const char **uid_list;
+ size_t real_count = 0;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, sec->ldb, "cn=persistent,cn=kcm");
+
+ ret = ldb_search(sec->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+ attrs, LOCAL_NON_CONTAINER_FILTER);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_search returned [%d]: %s\n", ret, ldb_strerror(ret));
+ ret = EIO;
+ goto done;
+ }
+
+ if (res->count == 0) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No ccaches found\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ uuid_list = talloc_zero_array(tmp_ctx, const char *, res->count);
+ if (uuid_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ uid_list = talloc_zero_array(tmp_ctx, const char *, res->count);
+ if (uid_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < res->count; i++) {
+ name_val = ldb_dn_get_component_val(res->msgs[i]->dn, 0);
+ uid_val = ldb_dn_get_component_val(res->msgs[i]->dn, 2);
+ if (strcmp((const char *)name_val->data, "default") == 0) {
+ continue;
+ }
+
+ uuid_list[real_count] = talloc_strdup(uuid_list, (const char *)name_val->data);
+ if (uuid_list[real_count] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate UUID\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ uid_list[real_count] = talloc_strdup(uid_list, (const char *)uid_val->data);
+ if (uid_list[real_count] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate uid\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ real_count++;
+ }
+
+ *_uid_list = talloc_steal(mem_ctx, uid_list);
+ *_uuid_list = talloc_steal(mem_ctx, uuid_list);
+ *_uuid_list_count = real_count;
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sss_sec_list(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req,
+ char ***_keys,
+ size_t *_num_keys)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { SEC_ATTR_SECRET, NULL };
+ struct ldb_result *res;
+ char **keys;
+ int ret;
+
+ if (req == NULL || _keys == NULL || _num_keys == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Listing keys at [%s]\n", req->path);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Searching at [%s] with scope=subtree\n",
+ ldb_dn_get_linearized(req->req_dn));
+
+ ret = ldb_search(req->sctx->ldb, tmp_ctx, &res, req->req_dn, LDB_SCOPE_SUBTREE,
+ attrs, LOCAL_NON_CONTAINER_FILTER);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_search returned [%d]: %s\n", ret, ldb_strerror(ret));
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (res->count == 0) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No secrets found\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ keys = talloc_array(mem_ctx, char *, res->count);
+ if (!keys) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (unsigned i = 0; i < res->count; i++) {
+ keys[i] = local_dn_to_path(keys, req->req_dn, res->msgs[i]->dn);
+ if (!keys[i]) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_keys = keys;
+ DEBUG(SSSDBG_TRACE_LIBS, "Returning %d secrets\n", res->count);
+ *_num_keys = res->count;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sss_sec_get(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req,
+ uint8_t **_secret,
+ size_t *_secret_len)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { SEC_ATTR_SECRET, NULL };
+ struct ldb_result *res;
+ const struct ldb_val *attr_secret;
+ int ret;
+
+ if (req == NULL || _secret == NULL) {
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Retrieving a secret from [%s]\n", req->path);
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Searching at [%s] with scope=base\n",
+ ldb_dn_get_linearized(req->req_dn));
+
+ ret = ldb_search(req->sctx->ldb, tmp_ctx, &res, req->req_dn, LDB_SCOPE_BASE,
+ attrs, LOCAL_NON_CONTAINER_FILTER);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_search returned [%d]: %s\n", ret, ldb_strerror(ret));
+ ret = ENOENT;
+ goto done;
+ }
+
+ switch (res->count) {
+ case 0:
+ DEBUG(SSSDBG_TRACE_LIBS, "No secret found\n");
+ ret = ENOENT;
+ goto done;
+ case 1:
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Too many secrets returned with BASE search\n");
+ ret = E2BIG;
+ goto done;
+ }
+
+ attr_secret = ldb_msg_find_ldb_val(res->msgs[0], SEC_ATTR_SECRET);
+ if ((!attr_secret) || (attr_secret->length == 0)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "The 'secret' attribute is missing\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ *_secret = talloc_memdup(mem_ctx, attr_secret->data, attr_secret->length);
+ if (*_secret == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (_secret_len) {
+ *_secret_len = attr_secret->length;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sss_sec_put(struct sss_sec_req *req,
+ uint8_t *secret,
+ size_t secret_len)
+{
+ struct ldb_message *msg;
+ struct ldb_val secret_val;
+ int ret;
+
+ if (req == NULL || secret == NULL) {
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding a secret to [%s]\n", req->path);
+
+ msg = ldb_msg_new(req);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = req->req_dn;
+
+ /* make sure containers exist */
+ ret = local_db_check_containers(msg, req->sctx, msg->dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_db_check_containers failed for [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(msg->dn), ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = local_db_check_peruid_number_of_secrets(msg, req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_db_check_peruid_number_of_secrets failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = local_db_check_number_of_secrets(msg, req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_db_check_number_of_secrets failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = local_check_max_payload_size(req, secret_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_check_max_payload_size failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ secret_val.length = secret_len;
+ secret_val.data = talloc_memdup(req->sctx, secret, secret_len);
+ if (!secret_val.data) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_value(msg, SEC_ATTR_SECRET, &secret_val, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_msg_add_string failed adding secret [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = ldb_msg_add_fmt(msg, SEC_ATTR_CTIME, "%lu", time(NULL));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_msg_add_string failed adding creationTime [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = ldb_add(req->sctx->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Secret %s already exists\n", ldb_dn_get_linearized(msg->dn));
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to add secret [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(msg->dn), ret, ldb_strerror(ret));
+ }
+ ret = sss_ldb_error_to_errno (ret);
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(msg);
+ return ret;
+}
+
+errno_t sss_sec_update(struct sss_sec_req *req,
+ uint8_t *secret,
+ size_t secret_len)
+{
+ struct ldb_message *msg;
+ struct ldb_val secret_val;
+ int ret;
+
+ if (req == NULL || secret == NULL) {
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding a secret to [%s]\n", req->path);
+
+ msg = ldb_msg_new(req);
+ if (!msg) {
+ ret = ENOMEM;
+ goto done;
+ }
+ msg->dn = req->req_dn;
+
+ /* make sure containers exist */
+ ret = local_db_check_containers(msg, req->sctx, msg->dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_db_check_containers failed for [%s]: [%d]: %s\n",
+ ldb_dn_get_linearized(msg->dn), ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = local_db_check_peruid_number_of_secrets(msg, req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_db_check_peruid_number_of_secrets failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = local_db_check_number_of_secrets(msg, req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_db_check_number_of_secrets failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = local_check_max_payload_size(req, secret_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "local_check_max_payload_size failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ secret_val.length = secret_len;
+ secret_val.data = talloc_memdup(req->sctx, secret, secret_len);
+ if (!secret_val.data) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* FIXME - should we have a lastUpdate timestamp? */
+ ret = ldb_msg_add_empty(msg, SEC_ATTR_SECRET, LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_msg_add_empty failed: [%s]\n", ldb_strerror(ret));
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_msg_add_value(msg, SEC_ATTR_SECRET, &secret_val, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_msg_add_string failed: [%s]\n", ldb_strerror(ret));
+ ret = EIO;
+ goto done;
+ }
+
+ ret = ldb_modify(req->sctx->ldb, msg);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No such object to modify\n");
+ ret = sss_ldb_error_to_errno (ret);
+ goto done;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "ldb_modify failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(req->sctx->ldb));
+ ret = sss_ldb_error_to_errno (ret);
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(msg);
+ return ret;
+}
+
+errno_t sss_sec_delete(struct sss_sec_req *req)
+{
+ TALLOC_CTX *tmp_ctx;
+ static const char *attrs[] = { NULL };
+ struct ldb_result *res;
+ int ret;
+
+ if (req == NULL) {
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Removing a secret from [%s]\n", req->path);
+
+ tmp_ctx = talloc_new(req);
+ if (!tmp_ctx) return ENOMEM;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Searching for [%s] at [%s] with scope=base\n",
+ LOCAL_CONTAINER_FILTER, ldb_dn_get_linearized(req->req_dn));
+
+ ret = ldb_search(req->sctx->ldb, tmp_ctx, &res, req->req_dn, LDB_SCOPE_BASE,
+ attrs, LOCAL_CONTAINER_FILTER);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_search returned %d: %s\n", ret, ldb_strerror(ret));
+ goto done;
+ }
+
+ if (res->count == 1) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Searching for children of [%s]\n", ldb_dn_get_linearized(req->req_dn));
+ ret = ldb_search(req->sctx->ldb, tmp_ctx, &res, req->req_dn, LDB_SCOPE_ONELEVEL,
+ attrs, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_search returned %d: %s\n", ret, ldb_strerror(ret));
+ goto done;
+ }
+
+ if (res->count > 0) {
+ ret = EEXIST;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to remove '%s': Container is not empty\n",
+ ldb_dn_get_linearized(req->req_dn));
+
+ goto done;
+ }
+ }
+
+ ret = ldb_delete(req->sctx->ldb, req->req_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "ldb_delete returned %d: %s\n", ret, ldb_strerror(ret));
+ /* fall through */
+ }
+
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "LDB returned unexpected error: [%s]\n",
+ ldb_strerror(ret));
+ }
+ ret = sss_ldb_error_to_errno (ret);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sss_sec_create_container(struct sss_sec_req *req)
+{
+ int plen;
+
+ if (req == NULL) {
+ return EINVAL;
+ }
+
+ plen = strlen(req->path);
+
+ if (req->path[plen - 1] != '/') {
+ return EINVAL;
+ }
+
+ req->path[plen - 1] = '\0';
+ return local_db_create(req);
+}
diff --git a/src/responder/kcm/secrets/secrets.h b/src/responder/kcm/secrets/secrets.h
new file mode 100644
index 0000000..537f6c9
--- /dev/null
+++ b/src/responder/kcm/secrets/secrets.h
@@ -0,0 +1,114 @@
+/*
+ SSSD
+
+ Local secrets database
+
+ Copyright (C) Red Hat 2018
+
+ 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 __SECRETS_H_
+#define __SECRETS_H_
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <talloc.h>
+#include <uuid/uuid.h>
+
+#include "confdb/confdb.h"
+
+#define DEFAULT_SEC_CONTAINERS_NEST_LEVEL 4
+
+/* The number of secrets in the /kcm hive should be quite small,
+ * but the secret size must be large because one secret in the /kcm
+ * hive holds the whole ccache which consists of several credentials
+ */
+#define DEFAULT_SEC_KCM_MAX_SECRETS 0 /* unlimited */
+#define DEFAULT_SEC_KCM_MAX_UID_SECRETS 64
+#define DEFAULT_SEC_KCM_MAX_PAYLOAD_SIZE 65536
+
+/* Even cn=default is considered a secret that adds up to
+ * the quota. To avoid off-by-one-confusion, increase
+ * the quota by two to 1) account for the cn=default object
+ * and 2) always allow writing to cn=defaults even if we
+ * are exactly at the quota limit
+ */
+#define KCM_MAX_UID_EXTRA_SECRETS 2
+
+struct sss_sec_ctx;
+
+struct sss_sec_req;
+
+struct sss_sec_quota_opt {
+ const char *opt_name;
+ int default_value;
+};
+
+struct sss_sec_quota {
+ int max_secrets;
+ int max_uid_secrets;
+ int max_payload_size;
+ int containers_nest_level;
+};
+
+errno_t sss_sec_init(TALLOC_CTX *mem_ctx,
+ struct sss_sec_quota *quota,
+ struct sss_sec_ctx **_sec_ctx);
+
+errno_t sss_sec_new_req(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sec_ctx,
+ const char *url,
+ uid_t client,
+ struct sss_sec_req **_req);
+
+errno_t sss_sec_delete(struct sss_sec_req *req);
+
+errno_t sss_sec_list_cc_uuids(TALLOC_CTX *mem_ctx,
+ struct sss_sec_ctx *sec_ctx,
+ const char ***_uuid_list,
+ const char ***_uid_list,
+ size_t *uuid_list_count);
+
+errno_t sss_sec_list(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req,
+ char ***_keys,
+ size_t *num_keys);
+
+errno_t sss_sec_get(TALLOC_CTX *mem_ctx,
+ struct sss_sec_req *req,
+ uint8_t **_secret,
+ size_t *_secret_len);
+
+errno_t sss_sec_put(struct sss_sec_req *req,
+ uint8_t *secret,
+ size_t secret_len);
+
+errno_t sss_sec_update(struct sss_sec_req *req,
+ uint8_t *secret,
+ size_t secret_len);
+
+errno_t sss_sec_create_container(struct sss_sec_req *req);
+
+
+errno_t sss_sec_get_quota(struct confdb_ctx *cdb,
+ const char *section_config_path,
+ struct sss_sec_quota_opt *dfl_max_containers_nest_level,
+ struct sss_sec_quota_opt *dfl_max_num_secrets,
+ struct sss_sec_quota_opt *dfl_max_num_uid_secrets,
+ struct sss_sec_quota_opt *dfl_max_payload,
+ struct sss_sec_quota *quota);
+
+#endif /* __SECRETS_H_ */
diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
new file mode 100644
index 0000000..dd80113
--- /dev/null
+++ b/src/responder/nss/nss_cmd.c
@@ -0,0 +1,1439 @@
+/*
+ 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 <tevent.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "db/sysdb.h"
+#include "responder/nss/nss_private.h"
+#include "responder/nss/nss_protocol.h"
+
+static struct sss_nss_cmd_ctx *
+sss_nss_cmd_ctx_create(TALLOC_CTX *mem_ctx,
+ struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct sss_nss_cmd_ctx *cmd_ctx;
+
+ cmd_ctx = talloc_zero(mem_ctx, struct sss_nss_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ return NULL;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+ cmd_ctx->nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ cmd_ctx->state_ctx = talloc_get_type(cli_ctx->state_ctx,
+ struct sss_nss_state_ctx);
+ cmd_ctx->type = type;
+ cmd_ctx->fill_fn = fill_fn;
+
+ return cmd_ctx;
+}
+
+static errno_t eval_flags(struct sss_nss_cmd_ctx *cmd_ctx,
+ struct cache_req_data *data)
+{
+ if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
+ && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Flags SSS_NSS_EX_FLAG_NO_CACHE and "
+ "SSS_NSS_EX_FLAG_INVALIDATE_CACHE are "
+ "mutually exclusive.\n");
+ return EINVAL;
+ }
+
+ if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
+ cache_req_data_set_bypass_cache(data, true);
+ } else if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
+ cache_req_data_set_bypass_dp(data, true);
+ }
+
+ return EOK;
+}
+
+static void sss_nss_getby_done(struct tevent_req *subreq);
+static void sss_nss_getlistby_done(struct tevent_req *subreq);
+
+static errno_t sss_nss_getby_name(struct cli_ctx *cli_ctx,
+ bool ex_version,
+ enum cache_req_type type,
+ const char **attrs,
+ enum sss_mc_type memcache,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cache_req_data *data;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ const char *rawname;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->flags = 0;
+ if (ex_version) {
+ ret = sss_nss_protocol_parse_name_ex(cli_ctx, &rawname, &cmd_ctx->flags);
+ } else {
+ ret = sss_nss_protocol_parse_name(cli_ctx, &rawname);
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Input name: %s\n", rawname);
+
+ data = cache_req_data_name_attrs(cmd_ctx, type, rawname, attrs);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = eval_flags(cmd_ctx, data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "eval_flags failed.\n");
+ goto done;
+ }
+
+ subreq = sss_nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+ data, memcache, rawname, 0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_get_object_send() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_getby_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static errno_t sss_nss_getby_id(struct cli_ctx *cli_ctx,
+ bool ex_version,
+ enum cache_req_type type,
+ const char **attrs,
+ enum sss_mc_type memcache,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cache_req_data *data;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ uint32_t id;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (ex_version) {
+ ret = sss_nss_protocol_parse_id_ex(cli_ctx, &id, &cmd_ctx->flags);
+ } else {
+ ret = sss_nss_protocol_parse_id(cli_ctx, &id);
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Input ID: %u (looking up '%s')\n", id,
+ (fill_fn == sss_nss_protocol_fill_sid) ? "SID" : "POSIX data");
+
+ data = cache_req_data_id_attrs(cmd_ctx, type, id, attrs);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = eval_flags(cmd_ctx, data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "eval_flags failed.\n");
+ goto done;
+ }
+
+ subreq = sss_nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+ data, memcache, NULL, id);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_get_object_send() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_getby_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static errno_t sss_nss_getby_svc(struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ const char *protocol,
+ const char *name,
+ uint16_t port,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cache_req_data *data;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->svc_protocol = protocol;
+
+ data = cache_req_data_svc(cmd_ctx, type, name, protocol, port);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Input name: %s, protocol: %s, port: %u\n",
+ (name == NULL ? "<none>" : name),
+ (protocol == NULL ? "<none>" : protocol),
+ port);
+
+ subreq = sss_nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+ data, SSS_MC_NONE, NULL, 0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_get_object_send() failed\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_getby_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static errno_t sss_nss_getlistby_cert(struct cli_ctx *cli_ctx,
+ enum cache_req_type type)
+{
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ const char *cert;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, NULL);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->sid_id_type = SSS_ID_TYPE_UID;
+
+ ret = sss_nss_protocol_parse_cert(cli_ctx, &cert);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Input cert: %s\n", get_last_x_chars(cert, 10));
+
+ subreq = cache_req_user_by_cert_send(cmd_ctx, cli_ctx->ev, cli_ctx->rctx,
+ cli_ctx->rctx->ncache, 0,
+ CACHE_REQ_ANY_DOM, NULL,
+ cert);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, sss_nss_getlistby_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static void sss_nss_getlistby_done(struct tevent_req *subreq)
+{
+ struct cache_req_result **results;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ errno_t ret;
+ struct cli_protocol *pctx;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct sss_nss_cmd_ctx);
+
+ ret = cache_req_recv(cmd_ctx, subreq, &results);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert request failed.\n");
+ goto done;
+ }
+
+ pctx = talloc_get_type(cmd_ctx->cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sss_nss_protocol_fill_name_list_all_domains(cmd_ctx->nss_ctx, cmd_ctx,
+ pctx->creq->out, results);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+
+done:
+ sss_nss_protocol_done(cmd_ctx->cli_ctx, ret);
+ talloc_free(cmd_ctx);
+}
+
+static errno_t sss_nss_getby_cert(struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cache_req_data *data;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ const char *cert;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->sid_id_type = SSS_ID_TYPE_UID;
+
+ ret = sss_nss_protocol_parse_cert(cli_ctx, &cert);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ data = cache_req_data_cert(cmd_ctx, type, cert);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Input cert: %s\n", get_last_x_chars(cert, 10));
+
+ subreq = sss_nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+ data, SSS_MC_NONE, NULL, 0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_get_object_send() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_getby_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static errno_t sss_nss_getby_sid(struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cache_req_data *data;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ const char *sid;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* It will be detected when constructing output packet. */
+ cmd_ctx->sid_id_type = SSS_ID_TYPE_NOT_SPECIFIED;
+
+ ret = sss_nss_protocol_parse_sid(cli_ctx, &sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Input SID: %s (looking up '%s')\n", sid,
+ (fill_fn == sss_nss_protocol_fill_name) ? "name"
+ : ((fill_fn == sss_nss_protocol_fill_id) ? "id" : ""));
+
+ data = cache_req_data_sid(cmd_ctx, type, sid, NULL);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sss_nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+ data, SSS_MC_NONE, NULL, 0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_get_object_send() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_getby_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static errno_t sss_nss_getby_addr(struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ enum sss_mc_type memcache,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cache_req_data *data;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ uint8_t *addr;
+ uint32_t addrlen;
+ uint32_t af;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->flags = 0;
+ ret = sss_nss_protocol_parse_addr(cli_ctx, &af, &addrlen, &addr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse address: %s\n",
+ strerror(ret));
+ goto done;
+ }
+
+ data = cache_req_data_addr(cmd_ctx, type, af, addrlen, addr);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set cache request data!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sss_nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
+ data, memcache, NULL, 0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_get_object_send() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_getby_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static errno_t invalidate_cache(struct sss_nss_cmd_ctx *cmd_ctx,
+ struct cache_req_result *result)
+{
+ int ret;
+ enum sss_mc_type memcache_type;
+ const char *name;
+ bool is_user;
+ struct sysdb_attrs *attrs = NULL;
+
+ switch (cmd_ctx->type) {
+ case CACHE_REQ_INITGROUPS:
+ case CACHE_REQ_INITGROUPS_BY_UPN:
+ memcache_type = SSS_MC_INITGROUPS;
+ is_user = true;
+ break;
+ case CACHE_REQ_USER_BY_NAME:
+ case CACHE_REQ_USER_BY_ID:
+ memcache_type = SSS_MC_PASSWD;
+ is_user = true;
+ break;
+ case CACHE_REQ_GROUP_BY_NAME:
+ case CACHE_REQ_GROUP_BY_ID:
+ memcache_type = SSS_MC_GROUP;
+ is_user = false;
+ break;
+ default:
+ /* nothing to do -
+ * other requests don't support SSS_NSS_EX_FLAG_INVALIDATE_CACHE
+ */
+ return EOK;
+ }
+
+ /* Find output name to invalidate memory cache entry */
+ name = sss_get_name_from_msg(result->domain, result->msgs[0]);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Found object has no name.\n");
+ return EINVAL;
+ }
+
+ memcache_delete_entry(cmd_ctx->nss_ctx, cmd_ctx->nss_ctx->rctx, NULL,
+ name, 0, memcache_type);
+ if (memcache_type == SSS_MC_INITGROUPS) {
+ /* Invalidate the passwd data as well */
+ memcache_delete_entry(cmd_ctx->nss_ctx, cmd_ctx->nss_ctx->rctx,
+ result->domain, name, 0, SSS_MC_PASSWD);
+ }
+
+ /* Use sysdb name to invalidate disk cache entry */
+ name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Found object has no name.\n");
+ return EINVAL;
+ }
+
+ if (memcache_type == SSS_MC_INITGROUPS) {
+ attrs = sysdb_new_attrs(cmd_ctx);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, 1);
+ if (ret != EOK) {
+ talloc_free(attrs);
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_time_t failed.\n");
+ return ret;
+ }
+
+ ret = sysdb_set_user_attr(result->domain, name, attrs, SYSDB_MOD_REP);
+ talloc_free(attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_user_attr failed.\n");
+ return ret;
+ }
+ }
+
+ ret = sysdb_invalidate_cache_entry(result->domain, name, is_user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_invalidate_cache_entry failed.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static void sss_nss_getby_done(struct tevent_req *subreq)
+{
+ struct cache_req_result *result;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct sss_nss_cmd_ctx);
+
+ ret = sss_nss_get_object_recv(cmd_ctx, subreq, &result, &cmd_ctx->rawname);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ sss_nss_protocol_done(cmd_ctx->cli_ctx, ret);
+ goto done;
+ }
+
+ if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
+ ret = invalidate_cache(cmd_ctx, result);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to invalidate cache for [%s].\n",
+ cmd_ctx->rawname);
+ sss_nss_protocol_done(cmd_ctx->cli_ctx, ret);
+ goto done;
+ }
+ }
+
+ sss_nss_protocol_reply(cmd_ctx->cli_ctx, cmd_ctx->nss_ctx, cmd_ctx,
+ result, cmd_ctx->fill_fn);
+
+done:
+ talloc_free(cmd_ctx);
+}
+
+static void sss_nss_setent_done(struct tevent_req *subreq);
+
+static errno_t sss_nss_setent(struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ struct sss_nss_enum_ctx *enum_ctx)
+{
+ struct tevent_req *subreq;
+
+ subreq = sss_nss_setent_send(cli_ctx, cli_ctx->ev, cli_ctx, type, enum_ctx);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_setent_send() failed\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_setent_done, cli_ctx);
+
+ return EOK;
+}
+
+static void sss_nss_setent_done(struct tevent_req *subreq)
+{
+ struct cli_ctx *cli_ctx;
+ errno_t ret;
+
+ cli_ctx = tevent_req_callback_data(subreq, struct cli_ctx);
+
+ ret = sss_nss_setent_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ENOENT) {
+ sss_nss_protocol_done(cli_ctx, ret);
+ return;
+ }
+
+ /* Both EOK and ENOENT means that setent was successful. */
+ sss_nss_protocol_done(cli_ctx, EOK);
+}
+
+static void sss_nss_getent_done(struct tevent_req *subreq);
+
+static errno_t sss_nss_getent(struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ struct sss_nss_enum_index *idx,
+ sss_nss_protocol_fill_packet_fn fill_fn,
+ struct sss_nss_enum_ctx *enum_ctx)
+{
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_nss_protocol_parse_limit(cli_ctx, &cmd_ctx->enum_limit);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ cmd_ctx->enumeration = true;
+ cmd_ctx->enum_ctx = enum_ctx;
+ cmd_ctx->enum_index = idx;
+
+ subreq = sss_nss_setent_send(cli_ctx, cli_ctx->ev, cli_ctx, type, enum_ctx);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_setent_send() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_getent_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return ret;
+}
+
+static struct cache_req_result *
+sss_nss_getent_get_result(struct sss_nss_enum_ctx *enum_ctx,
+ struct sss_nss_enum_index *idx)
+{
+ struct cache_req_result *result;
+
+ if (enum_ctx->result == NULL) {
+ /* Nothing was found. */
+ return NULL;
+ }
+
+ result = enum_ctx->result[idx->domain];
+
+ if (result != NULL && idx->result >= result->count) {
+ /* Switch to next domain. */
+ idx->result = 0;
+ idx->domain++;
+
+ result = enum_ctx->result[idx->domain];
+ }
+
+ return result;
+}
+
+static void sss_nss_getent_done(struct tevent_req *subreq)
+{
+ struct cache_req_result *limited;
+ struct cache_req_result *result;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct sss_nss_cmd_ctx);
+
+ ret = sss_nss_setent_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ result = sss_nss_getent_get_result(cmd_ctx->enum_ctx, cmd_ctx->enum_index);
+ if (result == NULL) {
+ /* No more records to return. */
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* Create copy of the result with limited number of records. */
+ limited = cache_req_copy_limited_result(cmd_ctx, result,
+ cmd_ctx->enum_index->result,
+ cmd_ctx->enum_limit);
+ if (limited == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ cmd_ctx->enum_index->result += result->count;
+
+ /* Reply with limited result. */
+ sss_nss_protocol_reply(cmd_ctx->cli_ctx, cmd_ctx->nss_ctx, cmd_ctx,
+ result, cmd_ctx->fill_fn);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ sss_nss_protocol_done(cmd_ctx->cli_ctx, ret);
+ }
+
+ talloc_free(cmd_ctx);
+}
+
+static void sss_nss_setnetgrent_done(struct tevent_req *subreq);
+
+/* This function's name started to collide with external nss symbol,
+ * so it has additional sss_* prefix unlike other functions here. */
+static errno_t sss_nss_setnetgrent(struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ const char *netgroup;
+ errno_t ret;
+
+ cmd_ctx = sss_nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ nss_ctx = cmd_ctx->nss_ctx;
+ state_ctx = cmd_ctx->state_ctx;
+
+ ret = sss_nss_protocol_parse_name(cli_ctx, &netgroup);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ state_ctx->netgrent.domain = 0;
+ state_ctx->netgrent.result = 0;
+
+ talloc_zfree(state_ctx->netgroup);
+ state_ctx->netgroup = talloc_strdup(state_ctx, netgroup);
+ if (state_ctx->netgroup == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* enum_limit is not used for setnetgrent, all results will be returned at
+ * once to allow innetgr() to be implemented in the thread-safe way. */
+ cmd_ctx->enum_limit = 0;
+ cmd_ctx->enumeration = true;
+ cmd_ctx->enum_ctx = NULL; /* We will determine it later. */
+ cmd_ctx->enum_index = &cmd_ctx->state_ctx->netgrent;
+
+ subreq = sss_nss_setnetgrent_send(cli_ctx, cli_ctx->ev, cli_ctx, type,
+ nss_ctx->netgrent, state_ctx->netgroup);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_nss_setnetgrent_send() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_setnetgrent_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return sss_nss_protocol_done(cli_ctx, ret);
+ }
+
+ return EOK;
+}
+
+static void sss_nss_setnetgrent_done(struct tevent_req *subreq)
+{
+ struct sss_nss_enum_ctx *enum_ctx;
+ struct sss_nss_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct sss_nss_cmd_ctx);
+
+ ret = sss_nss_setent_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ enum_ctx = sss_ptr_hash_lookup(cmd_ctx->nss_ctx->netgrent,
+ cmd_ctx->state_ctx->netgroup,
+ struct sss_nss_enum_ctx);
+ if (enum_ctx == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ cmd_ctx->enum_ctx = enum_ctx;
+
+ /* Reply with result. */
+ sss_nss_protocol_reply(cmd_ctx->cli_ctx, cmd_ctx->nss_ctx, cmd_ctx,
+ NULL, cmd_ctx->fill_fn);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ sss_nss_protocol_done(cmd_ctx->cli_ctx, ret);
+ }
+
+ cmd_ctx->state_ctx->netgrent.domain = 0;
+ cmd_ctx->state_ctx->netgrent.result = 0;
+
+ talloc_free(cmd_ctx);
+}
+
+static errno_t sss_nss_endent(struct cli_ctx *cli_ctx,
+ struct sss_nss_enum_index *idx)
+{
+ DEBUG(SSSDBG_CONF_SETTINGS, "Resetting enumeration state\n");
+
+ idx->domain = 0;
+ idx->result = 0;
+
+ sss_nss_protocol_done(cli_ctx, EOK);
+
+ return EOK;
+}
+
+static errno_t sss_nss_cmd_getpwnam(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_USER_BY_NAME, NULL,
+ SSS_MC_PASSWD, sss_nss_protocol_fill_pwent);
+}
+
+static errno_t sss_nss_cmd_getpwuid(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_id(cli_ctx, false, CACHE_REQ_USER_BY_ID, NULL,
+ SSS_MC_PASSWD, sss_nss_protocol_fill_pwent);
+}
+
+static errno_t sss_nss_cmd_getpwnam_ex(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, true, CACHE_REQ_USER_BY_NAME, NULL,
+ SSS_MC_PASSWD, sss_nss_protocol_fill_pwent);
+}
+
+static errno_t sss_nss_cmd_getpwuid_ex(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_id(cli_ctx, true, CACHE_REQ_USER_BY_ID, NULL,
+ SSS_MC_PASSWD, sss_nss_protocol_fill_pwent);
+}
+
+static errno_t sss_nss_cmd_setpwent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+ state_ctx->pwent.domain = 0;
+ state_ctx->pwent.result = 0;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ return sss_nss_setent(cli_ctx, CACHE_REQ_ENUM_USERS, nss_ctx->pwent);
+}
+
+static errno_t sss_nss_cmd_getpwent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_getent(cli_ctx, CACHE_REQ_ENUM_USERS,
+ &state_ctx->pwent, sss_nss_protocol_fill_pwent,
+ nss_ctx->pwent);
+}
+
+static errno_t sss_nss_cmd_endpwent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_endent(cli_ctx, &state_ctx->pwent);
+}
+
+static errno_t sss_nss_cmd_getgrnam(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_GROUP_BY_NAME, NULL,
+ SSS_MC_GROUP, sss_nss_protocol_fill_grent);
+}
+
+static errno_t sss_nss_cmd_getgrgid(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_id(cli_ctx, false, CACHE_REQ_GROUP_BY_ID, NULL,
+ SSS_MC_GROUP, sss_nss_protocol_fill_grent);
+}
+
+static errno_t sss_nss_cmd_getgrnam_ex(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, true, CACHE_REQ_GROUP_BY_NAME, NULL,
+ SSS_MC_GROUP, sss_nss_protocol_fill_grent);
+}
+
+static errno_t sss_nss_cmd_getgrgid_ex(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_id(cli_ctx, true, CACHE_REQ_GROUP_BY_ID, NULL,
+ SSS_MC_GROUP, sss_nss_protocol_fill_grent);
+}
+
+
+static errno_t sss_nss_cmd_setgrent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+ state_ctx->grent.domain = 0;
+ state_ctx->grent.result = 0;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ return sss_nss_setent(cli_ctx, CACHE_REQ_ENUM_GROUPS, nss_ctx->grent);
+}
+
+static errno_t sss_nss_cmd_getgrent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_getent(cli_ctx, CACHE_REQ_ENUM_GROUPS,
+ &state_ctx->grent, sss_nss_protocol_fill_grent,
+ nss_ctx->grent);
+}
+
+static errno_t sss_nss_cmd_endgrent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_endent(cli_ctx, &state_ctx->grent);
+}
+
+static errno_t sss_nss_cmd_initgroups(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_INITGROUPS, NULL,
+ SSS_MC_INITGROUPS, sss_nss_protocol_fill_initgr);
+}
+
+static errno_t sss_nss_cmd_initgroups_ex(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, true, CACHE_REQ_INITGROUPS, NULL,
+ SSS_MC_INITGROUPS, sss_nss_protocol_fill_initgr);
+}
+
+static errno_t sss_nss_cmd_subid_ranges(struct cli_ctx *cli_ctx)
+{
+#ifdef BUILD_SUBID
+ const char *attrs[] =
+ {
+ SYSDB_SUBID_UID_COUND,
+ SYSDB_SUBID_GID_COUNT,
+ SYSDB_SUBID_UID_NUMBER,
+ SYSDB_SUBID_GID_NUMBER,
+ NULL
+ };
+
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_SUBID_RANGES_BY_NAME, attrs,
+ SSS_MC_NONE, sss_nss_protocol_fill_subid_ranges);
+#else
+ return ENOTSUP;
+#endif
+}
+
+static errno_t sss_nss_cmd_setnetgrent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+ state_ctx->netgrent.domain = 0;
+ state_ctx->netgrent.result = 0;
+
+ return sss_nss_setnetgrent(cli_ctx, CACHE_REQ_NETGROUP_BY_NAME,
+ sss_nss_protocol_fill_netgrent);
+}
+
+static errno_t sss_nss_cmd_getservbyname(struct cli_ctx *cli_ctx)
+{
+ const char *name;
+ const char *protocol;
+ errno_t ret;
+
+ ret = sss_nss_protocol_parse_svc_name(cli_ctx, &name, &protocol);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sss_nss_getby_svc(cli_ctx, CACHE_REQ_SVC_BY_NAME, protocol, name, 0,
+ sss_nss_protocol_fill_svcent);
+}
+
+static errno_t sss_nss_cmd_getservbyport(struct cli_ctx *cli_ctx)
+{
+ const char *protocol;
+ uint16_t port;
+ errno_t ret;
+
+ ret = sss_nss_protocol_parse_svc_port(cli_ctx, &port, &protocol);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sss_nss_getby_svc(cli_ctx, CACHE_REQ_SVC_BY_PORT, protocol, NULL, port,
+ sss_nss_protocol_fill_svcent);
+}
+
+static errno_t sss_nss_cmd_setservent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+ state_ctx->svcent.domain = 0;
+ state_ctx->svcent.result = 0;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ return sss_nss_setent(cli_ctx, CACHE_REQ_ENUM_SVC, nss_ctx->svcent);
+}
+
+static errno_t sss_nss_cmd_getservent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_getent(cli_ctx, CACHE_REQ_ENUM_SVC,
+ &state_ctx->svcent, sss_nss_protocol_fill_svcent,
+ nss_ctx->svcent);
+}
+
+static errno_t sss_nss_cmd_endservent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_endent(cli_ctx, &state_ctx->svcent);
+}
+
+static errno_t sss_nss_cmd_getsidbyname(struct cli_ctx *cli_ctx)
+{
+ /* The attributes besides SYSDB_SID_STR are needed to handle some corner
+ * cases with respect to user-private-groups */
+ const char *attrs[] = { SYSDB_SID_STR, SYSDB_UIDNUM, SYSDB_GIDNUM,
+ SYSDB_OBJECTCATEGORY, NULL };
+
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_OBJECT_BY_NAME, attrs,
+ SSS_MC_NONE, sss_nss_protocol_fill_sid);
+}
+
+static errno_t sss_nss_cmd_getsidbyusername(struct cli_ctx *cli_ctx)
+{
+ /* The attributes besides SYSDB_SID_STR are needed to handle some corner
+ * cases with respect to user-private-groups */
+ const char *attrs[] = { SYSDB_SID_STR, SYSDB_UIDNUM, SYSDB_GIDNUM,
+ SYSDB_OBJECTCATEGORY, NULL };
+
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_USER_BY_NAME, attrs,
+ SSS_MC_NONE, sss_nss_protocol_fill_sid);
+}
+
+static errno_t sss_nss_cmd_getsidbygroupname(struct cli_ctx *cli_ctx)
+{
+ /* The attributes besides SYSDB_SID_STR are needed to handle some corner
+ * cases with respect to user-private-groups */
+ const char *attrs[] = { SYSDB_SID_STR, SYSDB_UIDNUM, SYSDB_GIDNUM,
+ SYSDB_OBJECTCATEGORY, NULL };
+
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_GROUP_BY_NAME, attrs,
+ SSS_MC_NONE, sss_nss_protocol_fill_sid);
+}
+
+static errno_t sss_nss_cmd_getsidbyid(struct cli_ctx *cli_ctx)
+{
+ const char *attrs[] = { SYSDB_SID_STR, SYSDB_UIDNUM, SYSDB_GIDNUM,
+ SYSDB_OBJECTCATEGORY, NULL };
+
+ return sss_nss_getby_id(cli_ctx, false, CACHE_REQ_OBJECT_BY_ID, attrs,
+ SSS_MC_SID, sss_nss_protocol_fill_sid);
+}
+
+static errno_t sss_nss_cmd_getsidbyuid(struct cli_ctx *cli_ctx)
+{
+ const char *attrs[] = { SYSDB_SID_STR, SYSDB_UIDNUM, SYSDB_GIDNUM,
+ SYSDB_OBJECTCATEGORY, NULL };
+
+ return sss_nss_getby_id(cli_ctx, false, CACHE_REQ_USER_BY_ID, attrs,
+ SSS_MC_SID, sss_nss_protocol_fill_sid);
+}
+
+static errno_t sss_nss_cmd_getsidbygid(struct cli_ctx *cli_ctx)
+{
+ const char *attrs[] = { SYSDB_SID_STR, SYSDB_UIDNUM, SYSDB_GIDNUM,
+ SYSDB_OBJECTCATEGORY, NULL };
+
+ return sss_nss_getby_id(cli_ctx, false, CACHE_REQ_GROUP_BY_ID, attrs,
+ SSS_MC_SID, sss_nss_protocol_fill_sid);
+}
+
+static errno_t sss_nss_cmd_getnamebysid(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_sid(cli_ctx, CACHE_REQ_OBJECT_BY_SID,
+ sss_nss_protocol_fill_name);
+}
+
+static errno_t sss_nss_cmd_getidbysid(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_sid(cli_ctx, CACHE_REQ_OBJECT_BY_SID,
+ sss_nss_protocol_fill_id);
+}
+
+static errno_t sss_nss_cmd_getorigbyname_common(struct cli_ctx *cli_ctx,
+ enum cache_req_type type)
+{
+ errno_t ret;
+ struct sss_nss_ctx *nss_ctx;
+ const char **attrs;
+ static const char *cache_attrs[] = { SYSDB_NAME,
+ SYSDB_OBJECTCATEGORY,
+ SYSDB_SID_STR,
+ SYSDB_DEFAULT_ATTRS,
+ NULL };
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ ret = add_strings_lists_ex(cli_ctx, cache_attrs, nss_ctx->full_attribute_list,
+ false, true, &attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to concatenate attributes [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ENOMEM;
+ }
+
+ return sss_nss_getby_name(cli_ctx, false, type, attrs,
+ SSS_MC_NONE, sss_nss_protocol_fill_orig);
+}
+
+static errno_t sss_nss_cmd_getorigbyname(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_cmd_getorigbyname_common(cli_ctx, CACHE_REQ_OBJECT_BY_NAME);
+}
+
+static errno_t sss_nss_cmd_getorigbyusername(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_cmd_getorigbyname_common(cli_ctx, CACHE_REQ_USER_BY_NAME);
+}
+
+static errno_t sss_nss_cmd_getorigbygroupname(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_cmd_getorigbyname_common(cli_ctx, CACHE_REQ_GROUP_BY_NAME);
+}
+
+
+static errno_t sss_nss_cmd_getnamebycert(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT,
+ sss_nss_protocol_fill_single_name);
+}
+
+static errno_t sss_nss_cmd_getlistbycert(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getlistby_cert(cli_ctx, CACHE_REQ_USER_BY_CERT);
+}
+
+static errno_t sss_nss_cmd_gethostbyname(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_IP_HOST_BY_NAME, NULL,
+ SSS_MC_NONE, sss_nss_protocol_fill_hostent);
+}
+
+static errno_t sss_nss_cmd_gethostbyaddr(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_addr(cli_ctx, CACHE_REQ_IP_HOST_BY_ADDR,
+ SSS_MC_NONE, sss_nss_protocol_fill_hostent);
+}
+
+static errno_t sss_nss_cmd_sethostent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+ state_ctx->hostent.domain = 0;
+ state_ctx->hostent.result = 0;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ return sss_nss_setent(cli_ctx, CACHE_REQ_ENUM_HOST, nss_ctx->hostent);
+}
+
+static errno_t sss_nss_cmd_gethostent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_getent(cli_ctx, CACHE_REQ_ENUM_HOST,
+ &state_ctx->hostent, sss_nss_protocol_fill_hostent,
+ nss_ctx->hostent);
+}
+
+static errno_t sss_nss_cmd_endhostent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_endent(cli_ctx, &state_ctx->hostent);
+}
+
+static errno_t sss_nss_cmd_getnetbyname(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_name(cli_ctx, false, CACHE_REQ_IP_NETWORK_BY_NAME, NULL,
+ SSS_MC_NONE, sss_nss_protocol_fill_netent);
+}
+
+static errno_t sss_nss_cmd_getnetbyaddr(struct cli_ctx *cli_ctx)
+{
+ return sss_nss_getby_addr(cli_ctx, CACHE_REQ_IP_NETWORK_BY_ADDR,
+ SSS_MC_NONE, sss_nss_protocol_fill_netent);
+}
+
+
+static errno_t sss_nss_cmd_setnetent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+ state_ctx->netent.domain = 0;
+ state_ctx->netent.result = 0;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ return sss_nss_setent(cli_ctx, CACHE_REQ_ENUM_IP_NETWORK, nss_ctx->netent);
+}
+
+static errno_t sss_nss_cmd_getnetent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_getent(cli_ctx, CACHE_REQ_ENUM_IP_NETWORK,
+ &state_ctx->netent, sss_nss_protocol_fill_netent,
+ nss_ctx->netent);
+}
+
+static errno_t sss_nss_cmd_endnetent(struct cli_ctx *cli_ctx)
+{
+ struct sss_nss_state_ctx *state_ctx;
+
+ state_ctx = talloc_get_type(cli_ctx->state_ctx, struct sss_nss_state_ctx);
+
+ return sss_nss_endent(cli_ctx, &state_ctx->netent);
+}
+
+struct sss_cmd_table *get_sss_nss_cmds(void)
+{
+ static struct sss_cmd_table nss_cmds[] = {
+ { SSS_GET_VERSION, sss_cmd_get_version },
+ { SSS_NSS_GETPWNAM, sss_nss_cmd_getpwnam },
+ { SSS_NSS_GETPWUID, sss_nss_cmd_getpwuid },
+ { SSS_NSS_SETPWENT, sss_nss_cmd_setpwent },
+ { SSS_NSS_GETPWENT, sss_nss_cmd_getpwent },
+ { SSS_NSS_ENDPWENT, sss_nss_cmd_endpwent },
+ { SSS_NSS_GETGRNAM, sss_nss_cmd_getgrnam },
+ { SSS_NSS_GETGRGID, sss_nss_cmd_getgrgid },
+ { SSS_NSS_SETGRENT, sss_nss_cmd_setgrent },
+ { SSS_NSS_GETGRENT, sss_nss_cmd_getgrent },
+ { SSS_NSS_ENDGRENT, sss_nss_cmd_endgrent },
+ { SSS_NSS_INITGR, sss_nss_cmd_initgroups },
+ { SSS_NSS_GET_SUBID_RANGES, sss_nss_cmd_subid_ranges },
+ { SSS_NSS_SETNETGRENT, sss_nss_cmd_setnetgrent },
+ /* { SSS_NSS_GETNETGRENT, "not needed" }, */
+ /* { SSS_NSS_ENDNETGRENT, "not needed" }, */
+ { SSS_NSS_GETSERVBYNAME, sss_nss_cmd_getservbyname },
+ { SSS_NSS_GETSERVBYPORT, sss_nss_cmd_getservbyport },
+ { SSS_NSS_SETSERVENT, sss_nss_cmd_setservent },
+ { SSS_NSS_GETSERVENT, sss_nss_cmd_getservent },
+ { SSS_NSS_ENDSERVENT, sss_nss_cmd_endservent },
+ { SSS_NSS_GETSIDBYNAME, sss_nss_cmd_getsidbyname },
+ { SSS_NSS_GETSIDBYUSERNAME, sss_nss_cmd_getsidbyusername },
+ { SSS_NSS_GETSIDBYGROUPNAME, sss_nss_cmd_getsidbygroupname },
+ { SSS_NSS_GETSIDBYID, sss_nss_cmd_getsidbyid },
+ { SSS_NSS_GETSIDBYUID, sss_nss_cmd_getsidbyuid },
+ { SSS_NSS_GETSIDBYGID, sss_nss_cmd_getsidbygid },
+ { SSS_NSS_GETNAMEBYSID, sss_nss_cmd_getnamebysid },
+ { SSS_NSS_GETIDBYSID, sss_nss_cmd_getidbysid },
+ { SSS_NSS_GETORIGBYNAME, sss_nss_cmd_getorigbyname },
+ { SSS_NSS_GETORIGBYUSERNAME, sss_nss_cmd_getorigbyusername },
+ { SSS_NSS_GETORIGBYGROUPNAME, sss_nss_cmd_getorigbygroupname },
+ { SSS_NSS_GETNAMEBYCERT, sss_nss_cmd_getnamebycert },
+ { SSS_NSS_GETLISTBYCERT, sss_nss_cmd_getlistbycert },
+ { SSS_NSS_GETPWNAM_EX, sss_nss_cmd_getpwnam_ex },
+ { SSS_NSS_GETPWUID_EX, sss_nss_cmd_getpwuid_ex },
+ { SSS_NSS_GETGRNAM_EX, sss_nss_cmd_getgrnam_ex },
+ { SSS_NSS_GETGRGID_EX, sss_nss_cmd_getgrgid_ex },
+ { SSS_NSS_INITGR_EX, sss_nss_cmd_initgroups_ex },
+ { SSS_NSS_GETHOSTBYNAME, sss_nss_cmd_gethostbyname },
+ { SSS_NSS_GETHOSTBYNAME2, sss_nss_cmd_gethostbyname },
+ { SSS_NSS_GETHOSTBYADDR, sss_nss_cmd_gethostbyaddr },
+ { SSS_NSS_SETHOSTENT, sss_nss_cmd_sethostent },
+ { SSS_NSS_GETHOSTENT, sss_nss_cmd_gethostent },
+ { SSS_NSS_ENDHOSTENT, sss_nss_cmd_endhostent },
+ { SSS_NSS_GETNETBYNAME, sss_nss_cmd_getnetbyname },
+ { SSS_NSS_GETNETBYADDR, sss_nss_cmd_getnetbyaddr },
+ { SSS_NSS_SETNETENT, sss_nss_cmd_setnetent },
+ { SSS_NSS_GETNETENT, sss_nss_cmd_getnetent },
+ { SSS_NSS_ENDNETENT, sss_nss_cmd_endnetent },
+ { SSS_CLI_NULL, NULL }
+ };
+
+ return nss_cmds;
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version nss_cli_protocol_version[] = {
+ { 1, "2008-09-05", "initial version, \\0 terminated strings" },
+ { 0, NULL, NULL }
+ };
+
+ return nss_cli_protocol_version;
+}
+
+int sss_nss_connection_setup(struct cli_ctx *cli_ctx)
+{
+ int ret;
+
+ ret = sss_connection_setup(cli_ctx);
+ if (ret != EOK) return ret;
+
+ cli_ctx->state_ctx = talloc_zero(cli_ctx, struct sss_nss_state_ctx);
+ if (cli_ctx->state_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_enum.c b/src/responder/nss/nss_enum.c
new file mode 100644
index 0000000..2b4fb60
--- /dev/null
+++ b/src/responder/nss/nss_enum.c
@@ -0,0 +1,361 @@
+/*
+ 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 <tevent.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "responder/nss/nss_private.h"
+
+typedef errno_t (*sss_nss_setent_set_timeout_fn)(struct tevent_context *ev,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_enum_ctx *enum_ctx);
+
+struct sss_nss_setent_internal_state {
+ struct tevent_context *ev;
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_enum_ctx *enum_ctx;
+ sss_nss_setent_set_timeout_fn timeout_handler;
+ enum cache_req_type type;
+};
+
+static void sss_nss_setent_internal_done(struct tevent_req *subreq);
+
+/* Cache request data is stealed on internal state. */
+static struct tevent_req *
+sss_nss_setent_internal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ struct cache_req_data *data,
+ enum cache_req_type type,
+ struct sss_nss_enum_ctx *enum_ctx,
+ sss_nss_setent_set_timeout_fn timeout_handler)
+{
+ struct sss_nss_setent_internal_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_nss_setent_internal_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ talloc_steal(state, data);
+
+ state->ev = ev;
+ state->nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ state->enum_ctx = enum_ctx;
+ state->type = type;
+ state->timeout_handler = timeout_handler;
+
+ if (state->enum_ctx->is_ready) {
+ /* Object is already constructed, just return here. */
+ talloc_free(data);
+ ret = EOK;
+ goto done;
+ }
+
+ if (state->enum_ctx->ongoing != NULL) {
+ /* Object is being constructed. Register ourselves for
+ * notification when it is finished. */
+ ret = setent_add_ref(state, &state->enum_ctx->notify_list, req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to register setent reference [%d]: %s!\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EAGAIN;
+ goto done;
+ }
+
+ /* Create new object. */
+ state->enum_ctx->is_ready = false;
+ subreq = cache_req_send(req, ev, cli_ctx->rctx, cli_ctx->rctx->ncache,
+ state->nss_ctx->cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, NULL, data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_nss_setent_internal_done, req);
+ state->enum_ctx->ongoing = subreq;
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sss_nss_setent_internal_done(struct tevent_req *subreq)
+{
+ struct cache_req_result **result;
+ struct sss_nss_setent_internal_state *state;
+ struct setent_req_list **notify_list;
+ struct tevent_req *req;
+ errno_t ret;
+ errno_t tret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_nss_setent_internal_state);
+
+ /* This is the ongoing request and it is finished. Remove it. */
+ state->enum_ctx->ongoing = NULL;
+
+ ret = cache_req_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+
+ switch (ret) {
+ case EOK:
+ talloc_zfree(state->enum_ctx->result);
+ state->enum_ctx->result = talloc_steal(state->enum_ctx, result);
+
+ if (state->type == CACHE_REQ_NETGROUP_BY_NAME) {
+ /* We need to expand the netgroup into triples and members. */
+ ret = sysdb_netgr_to_entries(state->enum_ctx,
+ result[0]->ldb_result,
+ &state->enum_ctx->netgroup,
+ &state->enum_ctx->netgroup_count);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+ break;
+ case ENOENT:
+ /* Reset the result but build it again next time setent is called. */
+ talloc_zfree(state->enum_ctx->result);
+ talloc_zfree(state->enum_ctx->netgroup);
+ goto done;
+ default:
+ /* In case of an error, we do not touch the enumeration context. */
+ goto done;
+ }
+
+ /* Expire the result object after its timeout is reached. */
+ tret = state->timeout_handler(state->ev, state->nss_ctx, state->enum_ctx);
+ if (tret != EOK) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* The object is ready now. */
+ state->enum_ctx->is_ready = true;
+
+ ret = EOK;
+
+done:
+ /* We want to finish the requests in correct order, this was the
+ * first request, notify_list contain the subsequent request.
+ *
+ * Because callback invoked from tevent_req_done will free state,
+ * we must remember notify_list explicitly to avoid segfault.
+ */
+ notify_list = &state->enum_ctx->notify_list;
+
+ if (ret == EOK) {
+ tevent_req_done(req);
+ setent_notify_done(notify_list);
+ } else {
+ tevent_req_error(req, ret);
+ setent_notify(notify_list, ret);
+ }
+}
+
+static errno_t
+sss_nss_setent_internal_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static void
+sss_nss_setent_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *pvt)
+{
+ struct sss_nss_enum_ctx *enum_ctx = pvt;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Enumeration result object has expired.\n");
+
+ /* Reset enumeration context. */
+ talloc_zfree(enum_ctx->result);
+ enum_ctx->is_ready = false;
+}
+
+static errno_t
+sss_nss_setent_set_timeout(struct tevent_context *ev,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_enum_ctx *enum_ctx)
+{
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ tv = tevent_timeval_current_ofs(nss_ctx->enum_cache_timeout, 0);
+ te = tevent_add_timer(ev, nss_ctx, tv, sss_nss_setent_timeout, enum_ctx);
+ if (te == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not set up life timer for enumeration object.\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+struct tevent_req *
+sss_nss_setent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ struct sss_nss_enum_ctx *enum_ctx)
+{
+ struct cache_req_data *data;
+
+ data = cache_req_data_enum(mem_ctx, type);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set cache request data!\n");
+ return NULL;
+ }
+
+ return sss_nss_setent_internal_send(mem_ctx, ev, cli_ctx, data, type, enum_ctx,
+ sss_nss_setent_set_timeout);
+}
+
+errno_t sss_nss_setent_recv(struct tevent_req *req)
+{
+ return sss_nss_setent_internal_recv(req);
+}
+
+static void
+sss_nss_setnetgrent_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *pvt)
+{
+ struct sss_nss_enum_ctx *enum_ctx;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Enumeration result object has expired.\n");
+
+ /* Free enumeration context. This will also remove it from the table. */
+ enum_ctx = talloc_get_type(pvt, struct sss_nss_enum_ctx);
+ talloc_free(enum_ctx);
+}
+
+static errno_t
+sss_nss_setnetgrent_set_timeout(struct tevent_context *ev,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_enum_ctx *enum_ctx)
+{
+ struct tevent_timer *te;
+ struct timeval tv;
+ uint32_t timeout;
+
+ if (nss_ctx->cache_refresh_percent) {
+ timeout = enum_ctx->result[0]->domain->netgroup_timeout *
+ (nss_ctx->cache_refresh_percent / 100.0);
+ } else {
+ timeout = enum_ctx->result[0]->domain->netgroup_timeout;
+ }
+
+ /* In order to not trash the cache between setnetgrent()/getnetgrent()
+ * calls with too low timeout values, we only allow 10 seconds as
+ * the minimal timeout
+ */
+ if (timeout < 10) timeout = 10;
+
+ tv = tevent_timeval_current_ofs(timeout, 0);
+ te = tevent_add_timer(ev, enum_ctx, tv, sss_nss_setnetgrent_timeout, enum_ctx);
+ if (te == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not set up life timer for enumeration object.\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static struct sss_nss_enum_ctx *
+sss_nss_setnetgrent_set_enum_ctx(hash_table_t *table,
+ const char *netgroup)
+{
+ struct sss_nss_enum_ctx *enum_ctx;
+ errno_t ret;
+
+ enum_ctx = sss_ptr_hash_lookup(table, netgroup, struct sss_nss_enum_ctx);
+ if (enum_ctx != NULL) {
+ return enum_ctx;
+ }
+
+ enum_ctx = talloc_zero(table, struct sss_nss_enum_ctx);
+ if (enum_ctx == NULL) {
+ return NULL;
+ }
+
+ ret = sss_ptr_hash_add(table, netgroup, enum_ctx, struct sss_nss_enum_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to add enumeration context into table [%d]: %s\n",
+ ret, sss_strerror(ret));
+ talloc_free(enum_ctx);
+ return NULL;
+ }
+
+ return enum_ctx;
+}
+
+struct tevent_req *
+sss_nss_setnetgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ hash_table_t *table,
+ const char *netgroup)
+{
+ struct sss_nss_enum_ctx *enum_ctx;
+ struct cache_req_data *data;
+
+ enum_ctx = sss_nss_setnetgrent_set_enum_ctx(table, netgroup);
+ if (enum_ctx == NULL) {
+ return NULL;
+ }
+
+ data = cache_req_data_name(mem_ctx, type, netgroup);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return sss_nss_setent_internal_send(mem_ctx, ev, cli_ctx, data, type, enum_ctx,
+ sss_nss_setnetgrent_set_timeout);
+}
diff --git a/src/responder/nss/nss_get_object.c b/src/responder/nss/nss_get_object.c
new file mode 100644
index 0000000..29f9cb5
--- /dev/null
+++ b/src/responder/nss/nss_get_object.c
@@ -0,0 +1,546 @@
+/*
+ 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 <tevent.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "responder/nss/nss_private.h"
+#include "responder/nss/nsssrv_mmap_cache.h"
+
+static errno_t
+memcache_delete_entry_by_name(struct sss_nss_ctx *nss_ctx,
+ struct sized_string *name,
+ enum sss_mc_type type)
+{
+ errno_t ret;
+
+ switch (type) {
+ case SSS_MC_PASSWD:
+ ret = sss_mmap_cache_pw_invalidate(&nss_ctx->pwd_mc_ctx, name);
+ break;
+ case SSS_MC_GROUP:
+ ret = sss_mmap_cache_gr_invalidate(&nss_ctx->grp_mc_ctx, name);
+ break;
+ case SSS_MC_INITGROUPS:
+ ret = sss_mmap_cache_initgr_invalidate(&nss_ctx->initgr_mc_ctx, name);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (ret == EOK || ret == ENOENT) {
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Internal failure in memory cache code: %d [%s]\n",
+ ret, sss_strerror(ret));
+
+ return ret;
+}
+
+static errno_t
+memcache_delete_entry_by_id(struct sss_nss_ctx *nss_ctx,
+ uint32_t id,
+ enum sss_mc_type type)
+{
+ errno_t ret;
+
+ switch (type) {
+ case SSS_MC_PASSWD:
+ ret = sss_mmap_cache_pw_invalidate_uid(&nss_ctx->pwd_mc_ctx, (uid_t)id);
+ break;
+ case SSS_MC_GROUP:
+ ret = sss_mmap_cache_gr_invalidate_gid(&nss_ctx->grp_mc_ctx, (gid_t)id);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (ret == EOK || ret == ENOENT) {
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Internal failure in memory cache code: %d [%s]\n",
+ ret, sss_strerror(ret));
+
+ return ret;
+}
+
+errno_t
+memcache_delete_entry(struct sss_nss_ctx *nss_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ uint32_t id,
+ enum sss_mc_type type)
+{
+ struct sss_domain_info *dom;
+ struct sized_string *sized_name;
+ errno_t ret;
+
+ for (dom = rctx->domains;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+
+ if (domain == dom) {
+ /* We found entry in this domain so we don't
+ * wont to invalidate it here. */
+ continue;
+ }
+
+ if (name != NULL) {
+ ret = sized_output_name(NULL, rctx, name, dom, &sized_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to create sized name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = memcache_delete_entry_by_name(nss_ctx, sized_name, type);
+ talloc_zfree(sized_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to delete '%s' from domain '%s' memory cache!\n",
+ name, dom->name);
+ continue;
+ }
+ } else if (id == 0) {
+ /*
+ * As "root" is not handled by SSSD, let's just return EOK here
+ * instead of erroring out.
+ */
+ return EOK;
+ } else if (id != 0) {
+ ret = memcache_delete_entry_by_id(nss_ctx, id, type);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to delete '%u' from domain '%s' memory cache!\n",
+ id, dom->name);
+ continue;
+ }
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "Bug: invalid input!");
+ return ERR_INTERNAL;
+ }
+ }
+
+ return EOK;
+}
+
+static struct cache_req_data *
+hybrid_domain_retry_data(TALLOC_CTX *mem_ctx,
+ struct cache_req_data *orig,
+ const char *input_name,
+ uint32_t input_id)
+{
+ enum cache_req_type cr_type = cache_req_data_get_type(orig);
+ struct cache_req_data *hybrid_data = NULL;
+
+ if (cr_type == CACHE_REQ_GROUP_BY_ID) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Retrying group-by-ID lookup in user space\n");
+ hybrid_data = cache_req_data_id(mem_ctx,
+ CACHE_REQ_USER_BY_ID,
+ input_id);
+ } else if (cr_type == CACHE_REQ_GROUP_BY_NAME) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Retrying group-by-name lookup in user space\n");
+ hybrid_data = cache_req_data_name(mem_ctx,
+ CACHE_REQ_USER_BY_NAME,
+ input_name);
+ }
+
+ if (hybrid_data != NULL) {
+ cache_req_data_set_hybrid_lookup(hybrid_data, true);
+ }
+
+ return hybrid_data;
+}
+
+static struct cache_req_data *
+hybrid_domain_verify_gid_data(TALLOC_CTX *mem_ctx,
+ struct cache_req_result *user_group)
+{
+ gid_t gid;
+
+ /* read the GID of this 'group' and use it to construct
+ * a cache_req_data struct
+ */
+ gid = sss_view_ldb_msg_find_attr_as_uint64(user_group->domain,
+ user_group->msgs[0],
+ SYSDB_GIDNUM,
+ 0);
+ if (gid == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no GID?\n");
+ return NULL;
+ }
+
+ return cache_req_data_id(mem_ctx,
+ CACHE_REQ_GROUP_BY_ID,
+ gid);
+}
+
+static int
+hybrid_domain_user_to_group(struct cache_req_result *result)
+{
+ errno_t ret;
+ uid_t uid;
+ gid_t gid;
+
+ /* There must be exactly one entry.. */
+ if (result == NULL || result->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "No result or wrong number of entries, expected 1 entry\n");
+ return ENOENT;
+ }
+
+ /* ...which has uidNumber equal to gidNumber */
+ uid = sss_view_ldb_msg_find_attr_as_uint64(result->domain,
+ result->msgs[0],
+ SYSDB_UIDNUM,
+ 0);
+
+ gid = sss_view_ldb_msg_find_attr_as_uint64(result->domain,
+ result->msgs[0],
+ SYSDB_GIDNUM,
+ 0);
+
+ if (uid == 0 || uid != gid) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "UID and GID differ\n");
+ return ENOENT;
+ }
+
+ /* OK, we have a user with uid == gid; let's pretend this is a group */
+ ret = ldb_msg_add_string(result->msgs[0],
+ SYSDB_OBJECTCATEGORY,
+ SYSDB_GROUP_CLASS);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add group class\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+struct sss_nss_get_object_state {
+ struct sss_nss_ctx *nss_ctx;
+ struct resp_ctx *rctx;
+ struct tevent_context *ev;
+ struct cli_ctx *cli_ctx;
+ struct cache_req_data *data;
+
+ /* We delete object from memory cache if it is not found */
+ enum sss_mc_type memcache;
+ const char *input_name;
+ uint32_t input_id;
+
+ struct cache_req_result *result;
+};
+
+static void sss_nss_get_object_done(struct tevent_req *subreq);
+static bool sss_nss_is_hybrid_object_enabled(struct sss_domain_info *domains);
+static errno_t sss_nss_get_hybrid_object_step(struct tevent_req *req);
+static void sss_nss_get_hybrid_object_done(struct tevent_req *subreq);
+static void sss_nss_get_hybrid_gid_verify_done(struct tevent_req *subreq);
+static void sss_nss_get_object_finish_req(struct tevent_req *req,
+ errno_t ret);
+
+/* Cache request data memory context is stolen to internal state. */
+struct tevent_req *
+sss_nss_get_object_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ struct cache_req_data *data,
+ enum sss_mc_type memcache,
+ const char *input_name,
+ uint32_t input_id)
+{
+ struct sss_nss_get_object_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_nss_get_object_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli_ctx = cli_ctx;
+ state->data = talloc_steal(state, data);
+
+ state->rctx = cli_ctx->rctx;
+ state->nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+ state->memcache = memcache;
+ state->input_id = input_id;
+ state->input_name = talloc_strdup(state, input_name);
+ if (input_name != NULL && state->input_name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = cache_req_send(req, ev, cli_ctx->rctx, cli_ctx->rctx->ncache,
+ state->nss_ctx->cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM, NULL, data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Client [%p][%d]: unable to send cache request!\n",
+ cli_ctx, cli_ctx->cfd);
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Client [%p][%d]: sent cache request #%u\n",
+ cli_ctx, cli_ctx->cfd, cache_req_get_reqid(subreq));
+
+ tevent_req_set_callback(subreq, sss_nss_get_object_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sss_nss_get_object_done(struct tevent_req *subreq)
+{
+ struct sss_nss_get_object_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+ errno_t retry_ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_nss_get_object_state);
+
+ ret = cache_req_single_domain_recv(state, subreq, &state->result);
+ talloc_zfree(subreq);
+
+ /* Try to process hybrid object if any domain enables it. This will issue a
+ * cache_req that will iterate only over domains with MPG_HYBRID. */
+ if (ret == ENOENT
+ && sss_nss_is_hybrid_object_enabled(state->nss_ctx->rctx->domains)) {
+ retry_ret = sss_nss_get_hybrid_object_step(req);
+ if (retry_ret == EAGAIN) {
+ /* Retrying hybrid search */
+ return;
+ }
+ /* Otherwise return the value of ret as returned from
+ * cache_req_single_domain_recv
+ */
+ }
+
+ sss_nss_get_object_finish_req(req, ret);
+ return;
+}
+
+static void sss_nss_get_object_finish_req(struct tevent_req *req,
+ errno_t ret)
+{
+ struct sss_nss_get_object_state *state;
+
+ state = tevent_req_data(req, struct sss_nss_get_object_state);
+
+ switch (ret) {
+ case EOK:
+ tevent_req_done(req);
+ break;
+ case ENOENT:
+ if ((state->memcache != SSS_MC_NONE) && (state->memcache != SSS_MC_SID)) {
+ /* Delete entry from all domains. */
+ memcache_delete_entry(state->nss_ctx, state->rctx, NULL,
+ state->input_name, state->input_id,
+ state->memcache);
+ }
+
+ tevent_req_error(req, ENOENT);
+ break;
+ default:
+ tevent_req_error(req, ret);
+ break;
+ }
+}
+
+static bool sss_nss_is_hybrid_object_enabled(struct sss_domain_info *domains)
+{
+ struct sss_domain_info *dom;
+
+ for (dom = domains; dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ if (dom->mpg_mode == MPG_HYBRID) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static errno_t sss_nss_get_hybrid_object_step(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct sss_nss_get_object_state *state;
+
+ state = tevent_req_data(req, struct sss_nss_get_object_state);
+
+ state->data = hybrid_domain_retry_data(state,
+ state->data,
+ state->input_name,
+ state->input_id);
+ if (state->data == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "This request cannot be retried\n");
+ return EOK;
+ }
+
+ subreq = cache_req_send(req,
+ state->ev,
+ state->cli_ctx->rctx,
+ state->cli_ctx->rctx->ncache,
+ state->nss_ctx->cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM,
+ NULL,
+ state->data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n");
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, sss_nss_get_hybrid_object_done, req);
+
+ return EAGAIN;
+}
+
+static void sss_nss_get_hybrid_object_done(struct tevent_req *subreq)
+{
+ struct sss_nss_get_object_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_nss_get_object_state);
+
+ ret = cache_req_single_domain_recv(state, subreq, &state->result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Converting user object to a group\n");
+ ret = hybrid_domain_user_to_group(state->result);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* If the "group" was requested by name, we also must verify that
+ * no other group with this ID exists in any domain, otherwise
+ * we would have returned a private group that should be shadowed,
+ * this record would have been inserted into the memcache and then
+ * even getgrgid() would return this unexpected group
+ */
+ if (cache_req_data_get_type(state->data) == CACHE_REQ_USER_BY_NAME) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Will verify if MPG group is shadowed\n");
+ talloc_zfree(state->data);
+ state->data = hybrid_domain_verify_gid_data(state, state->result);
+ if (state->data == NULL) {
+ sss_nss_get_object_finish_req(req, EINVAL);
+ return;
+ }
+
+ subreq = cache_req_send(req,
+ state->ev,
+ state->cli_ctx->rctx,
+ state->cli_ctx->rctx->ncache,
+ state->nss_ctx->cache_refresh_percent,
+ CACHE_REQ_POSIX_DOM,
+ NULL,
+ state->data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send cache request!\n");
+ tevent_req_error(req, ENOENT);
+ return;
+ }
+ tevent_req_set_callback(subreq, sss_nss_get_hybrid_gid_verify_done, req);
+ return;
+ }
+
+done:
+ sss_nss_get_object_finish_req(req, ret);
+ return;
+}
+
+static void sss_nss_get_hybrid_gid_verify_done(struct tevent_req *subreq)
+{
+ struct sss_nss_get_object_state *state;
+ struct cache_req_result *real_gr_result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_nss_get_object_state);
+
+ ret = cache_req_single_domain_recv(state, subreq, &real_gr_result);
+ talloc_zfree(subreq);
+ if (ret == ENOENT) {
+ /* There is no real group with the same GID as the autogenerated
+ * one we were checking, so let's return the autogenerated one
+ */
+ ret = EOK;
+ goto done;
+ } else if (ret == EOK) {
+ /* The autogenerated group is shadowed by a real one. Don't return
+ * anything.
+ */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "A real entry would be shadowed by MPG entry\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+done:
+ sss_nss_get_object_finish_req(req, ret);
+ return;
+}
+
+errno_t
+sss_nss_get_object_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result **_result,
+ const char **_rawname)
+{
+ struct sss_nss_get_object_state *state;
+ state = tevent_req_data(req, struct sss_nss_get_object_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_result != NULL) {
+ *_result = talloc_steal(mem_ctx, state->result);
+ }
+
+ if (_rawname != NULL) {
+ *_rawname = talloc_steal(mem_ctx, state->input_name);
+ }
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_iface.c b/src/responder/nss/nss_iface.c
new file mode 100644
index 0000000..db743f8
--- /dev/null
+++ b/src/responder/nss/nss_iface.c
@@ -0,0 +1,242 @@
+/*
+ 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 "responder/nss/nss_private.h"
+#include "responder/nss/nss_iface.h"
+#include "sss_iface/sss_iface_async.h"
+
+static void
+sss_nss_update_initgr_memcache(struct sss_nss_ctx *nctx,
+ const char *fq_name, const char *domain,
+ int gnum, uint32_t *groups)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct sss_domain_info *dom;
+ struct ldb_result *res;
+ struct sized_string *delete_name;
+ bool changed = false;
+ uint32_t id;
+ uint32_t gids[gnum];
+ int ret;
+ int i, j;
+
+ for (dom = nctx->rctx->domains;
+ dom;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ if (strcasecmp(dom->name, domain) == 0) {
+ break;
+ }
+ }
+
+ if (dom == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unknown domain (%s) requested by provider\n", domain);
+ return;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ ret = sized_output_name(tmp_ctx, nctx->rctx, fq_name, dom, &delete_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sized_output_name failed for '%s': %d [%s]\n",
+ fq_name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_initgroups(tmp_ctx, dom, fq_name, &res);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_initgroups() failed [%d][%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ /* copy, we need the original intact in case we need to invalidate
+ * all the original groups */
+ memcpy(gids, groups, gnum * sizeof(uint32_t));
+
+ if (ret == ENOENT || res->count == 0) {
+ /* The user is gone. Invalidate the mc record */
+ ret = sss_mmap_cache_pw_invalidate(&nctx->pwd_mc_ctx, delete_name);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Internal failure in memory cache code: %d [%s]\n",
+ ret, strerror(ret));
+ }
+
+ /* Also invalidate his groups */
+ changed = true;
+ } else {
+ /* we skip the first entry, it's the user itself */
+ for (i = 0; i < res->count; i++) {
+ id = ldb_msg_find_attr_as_uint(res->msgs[i], SYSDB_GIDNUM, 0);
+ if (id == 0) {
+ /* probably non-POSIX group, skip */
+ continue;
+ }
+ for (j = 0; j < gnum; j++) {
+ if (gids[j] == id) {
+ gids[j] = 0;
+ break;
+ }
+ }
+ if (j >= gnum) {
+ /* we couldn't find a match, this means the groups have
+ * changed after the refresh */
+ changed = true;
+ break;
+ }
+ }
+
+ if (!changed) {
+ for (j = 0; j < gnum; j++) {
+ if (gids[j] != 0) {
+ /* we found an un-cleared groups, this means the groups
+ * have changed after the refresh (some got deleted) */
+ changed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ for (i = 0; i < gnum; i++) {
+ id = groups[i];
+
+ ret = sss_mmap_cache_gr_invalidate_gid(&nctx->grp_mc_ctx, id);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Internal failure in memory cache code: %d [%s]\n",
+ ret, strerror(ret));
+ }
+ }
+
+ to_sized_string(delete_name, fq_name);
+ ret = sss_mmap_cache_initgr_invalidate(&nctx->initgr_mc_ctx,
+ delete_name);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Internal failure in memory cache code: %d [%s]\n",
+ ret, strerror(ret));
+ }
+ }
+
+done:
+ talloc_free(tmp_ctx);
+}
+
+static errno_t
+sss_nss_memorycache_invalidate_users(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nctx)
+{
+ DEBUG(SSSDBG_TRACE_LIBS, "Invalidating all users in memory cache\n");
+ sss_mmap_cache_reset(nctx->pwd_mc_ctx);
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_memorycache_invalidate_groups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nctx)
+{
+ DEBUG(SSSDBG_TRACE_LIBS, "Invalidating all groups in memory cache\n");
+ sss_mmap_cache_reset(nctx->grp_mc_ctx);
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_memorycache_invalidate_initgroups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nctx)
+{
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Invalidating all initgroup records in memory cache\n");
+ sss_mmap_cache_reset(nctx->initgr_mc_ctx);
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_memorycache_update_initgroups(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nctx,
+ const char *user,
+ const char *domain,
+ uint32_t *groups)
+{
+ DEBUG(SSSDBG_TRACE_LIBS, "Updating initgroups memory cache of [%s@%s]\n",
+ user, domain);
+
+ sss_nss_update_initgr_memcache(nctx, user, domain,
+ talloc_array_length(groups), groups);
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_memorycache_invalidate_group_by_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nctx,
+ uint32_t gid)
+{
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Invalidating group %u from memory cache\n", gid);
+
+ sss_mmap_cache_gr_invalidate_gid(&nctx->grp_mc_ctx, gid);
+
+ return EOK;
+}
+
+errno_t
+sss_nss_register_backend_iface(struct sbus_connection *conn,
+ struct sss_nss_ctx *nss_ctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface,
+ sssd_nss_MemoryCache,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_nss_MemoryCache, UpdateInitgroups, sss_nss_memorycache_update_initgroups, nss_ctx),
+ SBUS_SYNC(METHOD, sssd_nss_MemoryCache, InvalidateAllUsers, sss_nss_memorycache_invalidate_users, nss_ctx),
+ SBUS_SYNC(METHOD, sssd_nss_MemoryCache, InvalidateAllGroups, sss_nss_memorycache_invalidate_groups, nss_ctx),
+ SBUS_SYNC(METHOD, sssd_nss_MemoryCache, InvalidateAllInitgroups, sss_nss_memorycache_invalidate_initgroups, nss_ctx),
+ SBUS_SYNC(METHOD, sssd_nss_MemoryCache, InvalidateGroupById, sss_nss_memorycache_invalidate_group_by_id, nss_ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+ );
+
+ ret = sbus_connection_add_path(conn, SSS_BUS_PATH, &iface);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
diff --git a/src/responder/nss/nss_iface.h b/src/responder/nss/nss_iface.h
new file mode 100644
index 0000000..8aabddb
--- /dev/null
+++ b/src/responder/nss/nss_iface.h
@@ -0,0 +1,31 @@
+/*
+ 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 _NSS_IFACE_H_
+#define _NSS_IFACE_H_
+
+#include "sss_iface/sss_iface_async.h"
+#include "responder/nss/nss_private.h"
+
+errno_t
+sss_nss_register_backend_iface(struct sbus_connection *conn,
+ struct sss_nss_ctx *nss_ctx);
+
+#endif /* _NSS_IFACE_H_ */
diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h
new file mode 100644
index 0000000..e2f5a3e
--- /dev/null
+++ b/src/responder/nss/nss_private.h
@@ -0,0 +1,155 @@
+/*
+ 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 _NSS_PRIVATE_H_
+#define _NSS_PRIVATE_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include <dhash.h>
+#include <ldb.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/nss/nsssrv_mmap_cache.h"
+#include "lib/idmap/sss_idmap.h"
+
+struct sss_nss_enum_index {
+ unsigned int domain;
+ unsigned int result;
+};
+
+struct sss_nss_enum_ctx {
+ struct cache_req_result **result;
+ struct sysdb_netgroup_ctx **netgroup;
+ size_t netgroup_count;
+
+ /* Ongoing cache request that is constructing enumeration result. */
+ struct tevent_req *ongoing;
+
+ /* If true, the object is already constructed. */
+ bool is_ready;
+
+ /* List of setent requests awaiting the result. We finish
+ * them when the ongoing cache request is completed. */
+ struct setent_req_list *notify_list;
+};
+
+struct sss_nss_state_ctx {
+ struct sss_nss_enum_index pwent;
+ struct sss_nss_enum_index grent;
+ struct sss_nss_enum_index svcent;
+ struct sss_nss_enum_index netgrent;
+ struct sss_nss_enum_index hostent;
+ struct sss_nss_enum_index netent;
+
+ const char *netgroup;
+};
+
+struct sss_nss_ctx {
+ struct resp_ctx *rctx;
+ struct sss_idmap_ctx *idmap_ctx;
+
+ /* Options. */
+ int cache_refresh_percent;
+ int enum_cache_timeout;
+ bool filter_users_in_groups;
+ char *pwfield;
+ char *override_homedir;
+ char *fallback_homedir;
+ char *homedir_substr;
+ const char **extra_attributes;
+ const char **full_attribute_list;
+
+ /* Enumeration. */
+ struct sss_nss_enum_ctx *pwent;
+ struct sss_nss_enum_ctx *grent;
+ struct sss_nss_enum_ctx *svcent;
+ struct sss_nss_enum_ctx *hostent;
+ struct sss_nss_enum_ctx *netent;
+ hash_table_t *netgrent;
+
+ /* Memory cache. */
+ struct sss_mc_ctx *pwd_mc_ctx;
+ struct sss_mc_ctx *grp_mc_ctx;
+ struct sss_mc_ctx *initgr_mc_ctx;
+ struct sss_mc_ctx *sid_mc_ctx;
+ uid_t mc_uid;
+ gid_t mc_gid;
+};
+
+struct sss_cmd_table *get_sss_nss_cmds(void);
+
+int sss_nss_connection_setup(struct cli_ctx *cli_ctx);
+
+errno_t
+memcache_delete_entry(struct sss_nss_ctx *nss_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ const char *name,
+ uint32_t id,
+ enum sss_mc_type type);
+
+struct tevent_req *
+sss_nss_get_object_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ struct cache_req_data *data,
+ enum sss_mc_type memcache,
+ const char *input_name,
+ uint32_t input_id);
+
+errno_t
+sss_nss_get_object_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result **_result,
+ const char **_rawname);
+
+struct tevent_req *
+sss_nss_setent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ struct sss_nss_enum_ctx *enum_ctx);
+
+errno_t
+sss_nss_setent_recv(struct tevent_req *req);
+
+struct tevent_req *
+sss_nss_setnetgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ enum cache_req_type type,
+ hash_table_t *table,
+ const char *netgroup);
+
+/* Utils. */
+
+const char *
+sss_nss_get_name_from_msg(struct sss_domain_info *domain,
+ struct ldb_message *msg);
+
+const char *
+sss_nss_get_pwfield(struct sss_nss_ctx *nctx,
+ struct sss_domain_info *dom);
+
+#endif /* _NSS_PRIVATE_H_ */
diff --git a/src/responder/nss/nss_protocol.c b/src/responder/nss/nss_protocol.c
new file mode 100644
index 0000000..e6dc702
--- /dev/null
+++ b/src/responder/nss/nss_protocol.c
@@ -0,0 +1,487 @@
+/*
+ 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 "util/util.h"
+#include "util/cert.h"
+#include "lib/idmap/sss_idmap.h"
+#include "responder/nss/nss_protocol.h"
+#include <arpa/inet.h>
+
+errno_t
+sss_nss_protocol_done(struct cli_ctx *cli_ctx, errno_t error)
+{
+ struct cli_protocol *pctx;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ switch (error) {
+ case EOK:
+ /* Create empty packet if none was provided. */
+ if (pctx->creq->out == NULL) {
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: success\n");
+ ret = EOK;
+ goto done;
+ case ENOENT:
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: not found\n");
+ ret = sss_cmd_send_empty(cli_ctx);
+ goto done;
+ default:
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: error [%d]: %s\n",
+ error, sss_strerror(error));
+ ret = sss_cmd_send_error(cli_ctx, error);
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send reply [%d]: %s!\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ sss_cmd_done(cli_ctx, NULL);
+ return EOK;
+}
+
+void sss_nss_protocol_reply(struct cli_ctx *cli_ctx,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct cache_req_result *result,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cli_protocol *pctx;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = fill_fn(nss_ctx, cmd_ctx, pctx->creq->out, result);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+
+done:
+ sss_nss_protocol_done(cli_ctx, ret);
+}
+
+errno_t
+sss_nss_protocol_parse_name(struct cli_ctx *cli_ctx, const char **_rawname)
+{
+ struct cli_protocol *pctx;
+ const char *rawname;
+ uint8_t *body;
+ size_t blen;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated!\n");
+ return EINVAL;
+ }
+
+ /* If the body isn't valid UTF-8, fail */
+ if (!sss_utf8_check(body, blen - 1)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not UTF-8 string!\n");
+ return EINVAL;
+ }
+
+ rawname = (const char *)body;
+ if (rawname[0] == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "An empty name was provided!\n");
+ return EINVAL;
+ }
+
+ *_rawname = rawname;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_name_ex(struct cli_ctx *cli_ctx, const char **_rawname,
+ uint32_t *_flags)
+{
+ struct cli_protocol *pctx;
+ const char *rawname;
+ uint8_t *body;
+ size_t blen;
+ uint8_t *p;
+ uint32_t flags;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen < 1 + sizeof(uint32_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body too short!\n");
+ return EINVAL;
+ }
+
+ /* If first argument not terminated fail. */
+ if (body[blen - 1 - sizeof(uint32_t)] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated!\n");
+ return EINVAL;
+ }
+
+ p = memchr(body, '\0', blen);
+ /* Although body for sure is null terminated, let's add this check here
+ * so static analyzers are happier. */
+ if (p == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "memchr() returned NULL, body is not null terminated!\n");
+ return EINVAL;
+ }
+
+ /* If the body isn't valid UTF-8, fail */
+ if (!sss_utf8_check(body, (p - body))) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "First argument is not UTF-8 string!\n");
+ return EINVAL;
+ }
+
+ rawname = (const char *)body;
+ if (rawname[0] == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "An empty name was provided!\n");
+ return EINVAL;
+ }
+
+ p++;
+ if ((p - body) + sizeof(uint32_t) != blen) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body has unexpected size!\n");
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&flags, p, NULL);
+
+ *_rawname = rawname;
+ *_flags = flags;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_id(struct cli_ctx *cli_ctx, uint32_t *_id)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+ uint32_t id;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen != sizeof(uint32_t)) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&id, body, NULL);
+
+ *_id = id;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_id_ex(struct cli_ctx *cli_ctx, uint32_t *_id,
+ uint32_t *_flags)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+ uint32_t id;
+ uint32_t flags;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen != 2 * sizeof(uint32_t)) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&id, body, NULL);
+ SAFEALIGN_COPY_UINT32(&flags, body + sizeof(uint32_t), NULL);
+
+ *_id = id;
+ *_flags = flags;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_limit(struct cli_ctx *cli_ctx, uint32_t *_limit)
+{
+ return sss_nss_protocol_parse_id(cli_ctx, _limit);
+}
+
+errno_t
+sss_nss_protocol_parse_svc_name(struct cli_ctx *cli_ctx,
+ const char **_name,
+ const char **_protocol)
+{
+ struct cli_protocol *pctx;
+ const char *protocol;
+ const char *name;
+ size_t protocol_len;
+ size_t name_len;
+ uint8_t *body;
+ size_t blen;
+ int i;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ /* Calculate service name length. */
+ for (i = 0, name_len = 0; body[i] != '\0'; i++) {
+ name_len++;
+ }
+
+ /* Calculate protocol name length, use index from previous cycle. */
+ for (protocol_len = 0; body[i + 1] != '\0'; i++) {
+ protocol_len++;
+ }
+
+ if (name_len == 0) {
+ return EINVAL;
+ }
+
+ name = (const char *)body;
+ protocol = protocol_len == 0 ? NULL : (const char *)(body + name_len + 1);
+
+ if (!sss_utf8_check((const uint8_t *)name, name_len)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Name is not UTF-8 string\n");
+ return EINVAL;
+ }
+
+ if (!sss_utf8_check((const uint8_t *)protocol, protocol_len)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Protocol is not UTF-8 string\n");
+ return EINVAL;
+ }
+
+ *_name = name;
+ *_protocol = protocol;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_svc_port(struct cli_ctx *cli_ctx,
+ uint16_t *_port,
+ const char **_protocol)
+{
+ struct cli_protocol *pctx;
+ const char *protocol;
+ size_t protocol_len;
+ uint16_t port;
+ uint8_t *body;
+ size_t blen;
+ int i;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT16(&port, body, NULL);
+ port = ntohs(port);
+
+ /* Move behind the port and padding to get the protocol. */
+ body = body + 2 * sizeof(uint16_t) + sizeof(uint32_t);
+
+ /* Calculate protocol name length. */
+ for (protocol_len = 0, i = 0; body[i] != '\0'; i++) {
+ protocol_len++;
+ }
+
+ protocol = protocol_len == 0 ? NULL : (const char *)body;
+
+ if (!sss_utf8_check((const uint8_t *)protocol, protocol_len)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Protocol is not UTF-8 string\n");
+ return EINVAL;
+ }
+
+ *_port = port;
+ *_protocol = protocol;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_cert(struct cli_ctx *cli_ctx,
+ const char **_derb64)
+{
+ struct cli_protocol *pctx;
+ const char *derb64;
+ size_t pem_size;
+ char *pem_cert;
+ uint8_t *body;
+ size_t blen;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ derb64 = (const char *)body;
+
+ DEBUG(SSSDBG_TRACE_ALL, "Input certificate [%s]\n", derb64);
+
+ /* Check input. */
+ ret = sss_cert_derb64_to_pem(cli_ctx, derb64, &pem_cert, &pem_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to convert certificate to pem [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ talloc_free(pem_cert);
+
+ *_derb64 = derb64;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_sid(struct cli_ctx *cli_ctx,
+ const char **_sid)
+{
+ struct cli_protocol *pctx;
+ struct sss_nss_ctx *nss_ctx;
+ const char *sid;
+ uint8_t *bin_sid;
+ size_t bin_len;
+ uint8_t *body;
+ size_t blen;
+ enum idmap_error_code err;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ sid = (const char *)body;
+
+ /* If the body isn't a SID, fail */
+ err = sss_idmap_sid_to_bin_sid(nss_ctx->idmap_ctx, sid, &bin_sid,
+ &bin_len);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to convert SID to binary [%s].\n", sid);
+ return EINVAL;
+ }
+
+ sss_idmap_free_bin_sid(nss_ctx->idmap_ctx, bin_sid);
+
+ DEBUG(SSSDBG_TRACE_ALL, "Input SID [%s]\n", sid);
+
+ *_sid = sid;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_addr(struct cli_ctx *cli_ctx,
+ uint32_t *_af,
+ uint32_t *_addrlen,
+ uint8_t **_addr)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+ uint32_t af;
+ uint8_t *addr;
+ socklen_t addrlen;
+ char buf[INET6_ADDRSTRLEN];
+ const char *addrstr = NULL;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen < sizeof(uint32_t) * 2) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&af, body, NULL);
+ SAFEALIGN_COPY_UINT32(&addrlen, body + sizeof(uint32_t), NULL);
+
+ addr = body + sizeof(uint32_t) * 2;
+
+ /* If the body isn't a addr, fail */
+ addrstr = inet_ntop(af, addr, buf, INET6_ADDRSTRLEN);
+ if (addrstr == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to parse address: %s\n", strerror(errno));
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Input address [%s]\n", addrstr);
+
+ *_af = af;
+ *_addr = addr;
+ *_addrlen = addrlen;
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h
new file mode 100644
index 0000000..13ff870
--- /dev/null
+++ b/src/responder/nss/nss_protocol.h
@@ -0,0 +1,217 @@
+/*
+ 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 _NSS_PROTOCOL_H_
+#define _NSS_PROTOCOL_H_
+
+#include <stdint.h>
+
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/nss/nss_private.h"
+#include "sss_client/idmap/sss_nss_idmap.h"
+
+struct sss_nss_cmd_ctx;
+
+/**
+ * Fill SSSD response packet.
+ *
+ * @return EOK If packet is successfully created and should be sent to client.
+ * @return Other errno code on error, an error reply will be sent to client.
+ */
+typedef errno_t
+(*sss_nss_protocol_fill_packet_fn)(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+struct sss_nss_cmd_ctx {
+ enum cache_req_type type;
+ struct cli_ctx *cli_ctx;
+ struct sss_nss_ctx *nss_ctx;
+ struct sss_nss_state_ctx *state_ctx;
+ sss_nss_protocol_fill_packet_fn fill_fn;
+ uint32_t flags;
+
+ /* For initgroups- */
+ const char *rawname;
+
+ /* For enumeration. */
+ bool enumeration;
+ struct sss_nss_enum_ctx *enum_ctx;
+ struct sss_nss_enum_index *enum_index;
+ uint32_t enum_limit;
+
+ /* For services. */
+ const char *svc_protocol;
+
+ /* For SID lookups. */
+ enum sss_id_type sid_id_type;
+};
+
+/**
+ * If error is EOK, send existing reply packet to the client.
+ * If error is ENOENT, create and send empty response.
+ * On other error code, create and send an error.
+ */
+errno_t sss_nss_protocol_done(struct cli_ctx *cli_ctx, errno_t error);
+
+/**
+ * Create and send SSSD response packet to the client.
+ */
+void sss_nss_protocol_reply(struct cli_ctx *cli_ctx,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct cache_req_result *result,
+ sss_nss_protocol_fill_packet_fn fill_fn);
+
+/* Parse input packet. */
+
+errno_t
+sss_nss_protocol_parse_name(struct cli_ctx *cli_ctx, const char **_rawname);
+
+errno_t
+sss_nss_protocol_parse_name_ex(struct cli_ctx *cli_ctx, const char **_rawname,
+ uint32_t *_flags);
+
+errno_t
+sss_nss_protocol_parse_id(struct cli_ctx *cli_ctx, uint32_t *_id);
+
+errno_t
+sss_nss_protocol_parse_id_ex(struct cli_ctx *cli_ctx, uint32_t *_id,
+ uint32_t *_flags);
+
+errno_t
+sss_nss_protocol_parse_limit(struct cli_ctx *cli_ctx, uint32_t *_limit);
+
+errno_t
+sss_nss_protocol_parse_svc_name(struct cli_ctx *cli_ctx,
+ const char **_name,
+ const char **_protocol);
+
+errno_t
+sss_nss_protocol_parse_svc_port(struct cli_ctx *cli_ctx,
+ uint16_t *_port,
+ const char **_protocol);
+
+errno_t
+sss_nss_protocol_parse_cert(struct cli_ctx *cli_ctx,
+ const char **_derb64);
+
+errno_t
+sss_nss_protocol_parse_sid(struct cli_ctx *cli_ctx,
+ const char **_sid);
+
+errno_t
+sss_nss_protocol_parse_addr(struct cli_ctx *cli_ctx,
+ uint32_t *_af,
+ uint32_t *_addrlen,
+ uint8_t **_addr);
+
+/* Create response packet. */
+
+errno_t
+sss_nss_protocol_fill_pwent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_grent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_initgr(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+#ifdef BUILD_SUBID
+errno_t
+sss_nss_protocol_fill_subid_ranges(struct sss_nss_ctx *sss_nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+#endif
+
+errno_t
+sss_nss_protocol_fill_netgrent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_svcent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_sid(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_orig(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_name(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_single_name(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_name_list_all_domains(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result **results);
+
+errno_t
+sss_nss_protocol_fill_id(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+errno_t
+sss_nss_protocol_fill_hostent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+errno_t
+sss_nss_protocol_fill_netent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result);
+
+#endif /* _NSS_PROTOCOL_H_ */
diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
new file mode 100644
index 0000000..887501a
--- /dev/null
+++ b/src/responder/nss/nss_protocol_grent.c
@@ -0,0 +1,495 @@
+/*
+ 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 "responder/nss/nss_protocol.h"
+#include "util/sss_format.h"
+
+static errno_t
+sss_nss_get_grent(TALLOC_CTX *mem_ctx,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ uint32_t *_gid,
+ struct sized_string **_name)
+{
+ const char *name;
+ uint32_t gid;
+ errno_t ret;
+
+ /* Check object class. */
+ if (!ldb_msg_check_string_attribute(msg, SYSDB_OBJECTCATEGORY,
+ SYSDB_GROUP_CLASS)) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Wrong object (%s) found on stack!\n",
+ ldb_dn_get_linearized(msg->dn));
+ return ERR_INTERNAL;
+ }
+
+ /* Get fields. */
+ name = sss_get_name_from_msg(domain, msg);
+ gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0);
+
+ if (name == NULL || gid == 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Incomplete group object for %s[%u]! Skipping\n",
+ name ? name : "<NULL>", gid);
+ return EINVAL;
+ }
+
+ /* Convert to sized strings. */
+ ret = sized_output_name(mem_ctx, nss_ctx->rctx, name, domain, _name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sized_output_name failed, skipping [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ *_gid = gid;
+
+ return EOK;
+}
+
+static struct ldb_message_element *
+sss_nss_get_group_members(struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ struct ldb_message_element *el;
+
+ if (domain->ignore_group_members) {
+ return NULL;
+ }
+
+ /* Unconditionally prefer OVERRIDE_PREFIX SYSDB_MEMBERUID, it
+ * might contain override names from the default view. */
+ el = ldb_msg_find_element(msg, OVERRIDE_PREFIX SYSDB_MEMBERUID);
+ if (el == NULL) {
+ el = ldb_msg_find_element(msg, SYSDB_MEMBERUID);
+ }
+
+ return el;
+}
+
+static struct ldb_message_element *
+sss_nss_get_group_ghosts(struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *group_name)
+{
+ struct ldb_message_element *el;
+
+ if (domain->ignore_group_members) {
+ return NULL;
+ }
+
+ el = ldb_msg_find_element(msg, SYSDB_GHOST);
+ if (el == NULL) {
+ return NULL;
+ }
+
+ if (DOM_HAS_VIEWS(domain) && !is_local_view(domain->view_name)
+ && el->num_values != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Domain has a view [%s] but group [%s] still has "
+ "ghost members.\n", domain->view_name, group_name);
+ return NULL;
+ }
+
+ return el;
+}
+
+static errno_t
+sss_nss_protocol_fill_members(struct sss_packet *packet,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *group_name,
+ size_t *_rp,
+ uint32_t *_num_members)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct resp_ctx *rctx = nss_ctx->rctx;
+ struct ldb_message_element *members[2];
+ struct ldb_message_element *el;
+ struct sized_string *name;
+ const char *member_name;
+ uint32_t num_members = 0;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+ int i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ members[0] = sss_nss_get_group_members(domain, msg);
+ members[1] = sss_nss_get_group_ghosts(domain, msg, group_name);
+
+ if (is_files_provider(domain) && members[1] != NULL) {
+ /* If there is a ghost member in files provider it means that we
+ * did not store the user on purpose (e.g. it has uid or gid 0).
+ * Therefore nss_files does handle the user and therefore we
+ * must let nss_files to also handle this group in order to
+ * provide correct membership. */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Unknown members found. nss_files will handle it.\n");
+
+ ret = sss_ncache_set_group(rctx->ncache, false, domain, group_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_ncache_set_group failed.\n");
+ }
+
+ ret = ENOENT;
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ for (i = 0; i < sizeof(members) / sizeof(members[0]); i++) {
+ el = members[i];
+ if (el == NULL) {
+ continue;
+ }
+
+ for (j = 0; j < el->num_values; j++) {
+ member_name = (const char *)el->values[j].data;
+
+ if (nss_ctx->filter_users_in_groups) {
+ ret = sss_ncache_check_user(rctx->ncache, domain, member_name);
+ if (ret == EEXIST) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Group [%s] member [%s] filtered out! "
+ "(negative cache)\n", group_name, member_name);
+ continue;
+ }
+ }
+
+ ret = sized_domain_name(tmp_ctx, rctx, member_name, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to get sized name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sss_packet_grow(packet, name->len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_SET_STRING(&body[*_rp], name->str, name->len, _rp);
+
+ num_members++;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ *_num_members = num_members;
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sss_nss_protocol_fill_grent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct sized_string *name;
+ struct sized_string pwfield;
+ uint32_t gid;
+ uint32_t num_results;
+ uint32_t num_members;
+ char *members;
+ size_t members_size;
+ size_t rp;
+ size_t rp_members;
+ size_t rp_num_members;
+ size_t body_len;
+ uint8_t *body;
+ int i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* First two fields (length and reserved), filled up later. */
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ rp = 2 * sizeof(uint32_t);
+
+ num_results = 0;
+ for (i = 0; i < result->count; i++) {
+ talloc_free_children(tmp_ctx);
+ msg = result->msgs[i];
+
+ /* Password field content. */
+ to_sized_string(&pwfield, sss_nss_get_pwfield(nss_ctx, result->domain));
+
+ ret = sss_nss_get_grent(tmp_ctx, nss_ctx, result->domain, msg,
+ &gid, &name);
+ if (ret != EOK) {
+ continue;
+ }
+
+ /* Adjust packet size: gid, num_members + string fields. */
+
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t)
+ + name->len + pwfield.len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ /* Fill packet. */
+
+ SAFEALIGN_SET_UINT32(&body[rp], gid, &rp);
+
+ /* Remember pointer to number of members field. */
+ rp_num_members = rp;
+ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], name->str, name->len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], pwfield.str, pwfield.len, &rp);
+ rp_members = rp;
+
+ /* Fill members. */
+ ret = sss_nss_protocol_fill_members(packet, nss_ctx, result->domain, msg,
+ name->str, &rp, &num_members);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_SET_UINT32(&body[rp_num_members], num_members, NULL);
+
+ num_results++;
+
+ /* Do not store entry in memory cache during enumeration or when
+ * requested or if cache explicitly disabled. */
+ if (!cmd_ctx->enumeration
+ && ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0)
+ && (nss_ctx->grp_mc_ctx != NULL)) {
+ members = (char *)&body[rp_members];
+ members_size = body_len - rp_members;
+ ret = sss_mmap_cache_gr_store(&nss_ctx->grp_mc_ctx, name, &pwfield,
+ gid, num_members, members,
+ members_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to store group %s (%s) in mem-cache [%d]: %s!\n",
+ name->str, result->domain->name, ret, sss_strerror(ret));
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ sss_packet_set_size(packet, 0);
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(body, &num_results, NULL);
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
+
+ return EOK;
+}
+
+static bool is_group_filtered(struct sss_nc_ctx *ncache,
+ struct sss_domain_info *domain,
+ const char *grp_name, gid_t gid)
+{
+ int ret;
+
+ if (grp_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Group with gid [%"SPRIgid"] has no name, this should never "
+ "happen, trying to continue without.\n", gid);
+ } else {
+ ret = sss_ncache_check_group(ncache, domain, grp_name);
+ if (ret == EEXIST) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Group [%s] is filtered out! "
+ "(negative cache)", grp_name);
+ return true;
+ }
+ }
+ ret = sss_ncache_check_gid(ncache, domain, gid);
+ if (ret == EEXIST) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Group [%"SPRIgid"] is filtered out! "
+ "(negative cache)", gid);
+ return true;
+ }
+
+ return false;
+}
+
+errno_t
+sss_nss_protocol_fill_initgr(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ struct sss_domain_info *domain;
+ struct sss_domain_info *grp_dom;
+ struct ldb_message *user;
+ struct ldb_message *msg;
+ struct ldb_message *primary_group_msg;
+ const char *posix;
+ struct sized_string rawname;
+ struct sized_string canonical_name;
+ uint32_t num_results;
+ uint8_t *body;
+ size_t body_len;
+ size_t rp;
+ gid_t gid;
+ const char *grp_name;
+ gid_t orig_gid;
+ errno_t ret;
+ int i;
+
+ if (result->count == 0) {
+ return ENOENT;
+ }
+
+ domain = result->domain;
+
+ /* num_results, reserved + gids */
+ ret = sss_packet_grow(packet, (2 + result->count) * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+ sss_packet_get_body(packet, &body, &body_len);
+ rp = 2 * sizeof(uint32_t);
+
+ user = result->msgs[0];
+ gid = sss_view_ldb_msg_find_attr_as_uint64(domain, user, SYSDB_GIDNUM, 0);
+ orig_gid = sss_view_ldb_msg_find_attr_as_uint64(domain, user,
+ SYSDB_PRIMARY_GROUP_GIDNUM,
+ 0);
+
+ /* Try to get the real gid in case the primary group's gid was overridden. */
+ ret = sysdb_search_group_by_origgid(NULL, domain, orig_gid, NULL,
+ &primary_group_msg);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ "There is no override for group %" SPRIgid "\n",
+ orig_gid);
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to find the original group id attribute for %" SPRIgid
+ ". Assuming there is none. [%d] %s\n",
+ orig_gid, ret, sss_strerror(ret));
+ }
+ /* Just continue with what we have. */
+ } else {
+ orig_gid = ldb_msg_find_attr_as_uint64(primary_group_msg, SYSDB_GIDNUM,
+ orig_gid);
+ talloc_free(primary_group_msg);
+ }
+
+ /* If the GID of the original primary group is available but equal to the
+ * current primary GID it must not be added. */
+ orig_gid = orig_gid == gid ? 0 : orig_gid;
+
+ /* First message is user, skip it. */
+ num_results = 0;
+ for (i = 1; i < result->count; i++) {
+ msg = result->msgs[i];
+ grp_dom = find_domain_by_msg(domain, msg);
+ gid = sss_view_ldb_msg_find_attr_as_uint64(grp_dom, msg, SYSDB_GIDNUM,
+ 0);
+ posix = ldb_msg_find_attr_as_string(msg, SYSDB_POSIX, NULL);
+ grp_name = sss_view_ldb_msg_find_attr_as_string(grp_dom, msg, SYSDB_NAME,
+ NULL);
+
+ if (gid == 0) {
+ if (posix != NULL && strcmp(posix, "FALSE") == 0) {
+ continue;
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Incomplete group object [%s] for initgroups! "
+ "Skipping.\n", ldb_dn_get_linearized(msg->dn));
+ continue;
+ }
+ }
+
+ if (is_group_filtered(nss_ctx->rctx->ncache, grp_dom, grp_name, gid)) {
+ continue;
+ }
+
+ SAFEALIGN_COPY_UINT32(&body[rp], &gid, &rp);
+ num_results++;
+
+ /* Do not add the GID of the original primary group if the user is
+ * already an explicit member of the group. */
+ if (orig_gid == gid) {
+ orig_gid = 0;
+ }
+ }
+
+ if (orig_gid == 0) {
+ /* Initialize allocated memory to be safe and make Valgrind happy. */
+ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp);
+ } else {
+ /* Insert original primary group into the result. */
+ SAFEALIGN_COPY_UINT32(&body[rp], &orig_gid, &rp);
+ num_results++;
+ }
+
+ if (nss_ctx->initgr_mc_ctx
+ && ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0)
+ && (nss_ctx->initgr_mc_ctx != NULL)) {
+ to_sized_string(&rawname, cmd_ctx->rawname);
+ to_sized_string(&canonical_name, sss_get_name_from_msg(domain, user));
+
+ ret = sss_mmap_cache_initgr_store(&nss_ctx->initgr_mc_ctx, &rawname,
+ &canonical_name, num_results,
+ body + 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to store initgroups %s (%s) in mem-cache [%d]: %s!\n",
+ rawname.str, domain->name, ret, sss_strerror(ret));
+ sss_packet_set_size(packet, 0);
+ return ret;
+ }
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(body, &num_results, NULL);
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol_hostent.c b/src/responder/nss/nss_protocol_hostent.c
new file mode 100644
index 0000000..687baad
--- /dev/null
+++ b/src/responder/nss/nss_protocol_hostent.c
@@ -0,0 +1,299 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 "db/sysdb.h"
+#include "db/sysdb_iphosts.h"
+#include "responder/nss/nss_protocol.h"
+
+static errno_t
+sss_nss_get_hostent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ struct sized_string *_name)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Get name */
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (name == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, name, domain->case_preserve);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Set output variables */
+
+ talloc_steal(mem_ctx, name);
+
+ to_sized_string(_name, name);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sss_nss_get_host_aliases(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *name,
+ struct sized_string **_aliases,
+ uint32_t *_num_aliases)
+{
+ struct ldb_message_element *el;
+ struct sized_string *aliases = NULL;
+ uint32_t num_aliases;
+ const char *alias;
+ errno_t ret;
+ int i;
+
+ el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS);
+ if (el == NULL) {
+ *_num_aliases = 0;
+ *_aliases = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ aliases = talloc_zero_array(mem_ctx, struct sized_string,
+ el->num_values + 1);
+ if (aliases == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_aliases = 0;
+ for (i = 0; i < el->num_values; i++) {
+ alias = (const char *)el->values[i].data;
+
+ if (sss_string_equal(domain->case_sensitive, alias, name)) {
+ continue;
+ }
+
+ /* Element value remains in the message, we don't need to strdup it. */
+ to_sized_string(&aliases[num_aliases], alias);
+ num_aliases++;
+ }
+
+ *_aliases = aliases;
+ *_num_aliases = num_aliases;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(aliases);
+ }
+
+ return ret;
+}
+
+static errno_t
+sss_nss_get_host_addresses(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *name,
+ struct sized_string **_addresses,
+ uint32_t *_num_addresses)
+{
+ struct ldb_message_element *el;
+ struct sized_string *addresses = NULL;
+ uint32_t num_addresses;
+ const char *addr;
+ errno_t ret;
+ int i;
+
+ el = ldb_msg_find_element(msg, SYSDB_IP_HOST_ATTR_ADDRESS);
+ if (el == NULL) {
+ *_num_addresses = 0;
+ *_addresses = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ addresses = talloc_zero_array(mem_ctx, struct sized_string,
+ el->num_values + 1);
+ if (addresses == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_addresses = 0;
+ for (i = 0; i < el->num_values; i++) {
+ addr = (const char *)el->values[i].data;
+
+ /* Element value remains in the message, we don't need to strdup it. */
+ to_sized_string(&addresses[num_addresses], addr);
+ num_addresses++;
+ }
+
+ *_addresses = addresses;
+ *_num_addresses = num_addresses;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(addresses);
+ }
+
+ return ret;
+}
+
+errno_t
+sss_nss_protocol_fill_hostent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct sized_string name;
+ struct sized_string *aliases;
+ struct sized_string *addresses;
+ uint32_t num_aliases;
+ uint32_t num_addresses;
+ uint32_t num_results;
+ size_t rp;
+ size_t body_len;
+ uint8_t *body;
+ int i;
+ int j;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* First two fields (length and reserved), filled up later. */
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ rp = 2 * sizeof(uint32_t);
+
+ num_results = 0;
+ for (i = 0; i < result->count; i++) {
+ talloc_free_children(tmp_ctx);
+ msg = result->msgs[i];
+
+ ret = sss_nss_get_hostent(tmp_ctx, result->domain, msg, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get host information, skipping... [%d]: %s\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ ret = sss_nss_get_host_aliases(tmp_ctx, result->domain, msg, name.str,
+ &aliases, &num_aliases);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get host aliases, skipping... [%d]: %s\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ ret = sss_nss_get_host_addresses(tmp_ctx, result->domain, msg, name.str,
+ &addresses, &num_addresses);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get host addresses, skipping... [%d]: %s\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ /* Adjust packet size */
+
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t) + name.len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ /* Fill packet */
+
+ SAFEALIGN_SET_UINT32(&body[rp], num_aliases, &rp);
+ SAFEALIGN_SET_UINT32(&body[rp], num_addresses, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], name.str, name.len, &rp);
+
+ /* Store aliases */
+ for (j = 0; j < num_aliases; j++) {
+ ret = sss_packet_grow(packet, aliases[j].len);
+ if (ret != EOK) {
+ goto done;
+ }
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_STRING(&body[rp], aliases[j].str, aliases[j].len,
+ &rp);
+ }
+
+ /* Store addresses */
+ for (j = 0; j < num_addresses; j++) {
+ ret = sss_packet_grow(packet, addresses[j].len);
+ if (ret != EOK) {
+ goto done;
+ }
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_STRING(&body[rp], addresses[j].str, addresses[j].len,
+ &rp);
+ }
+
+ num_results++;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ sss_packet_set_size(packet, 0);
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(body, &num_results, NULL);
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol_netent.c b/src/responder/nss/nss_protocol_netent.c
new file mode 100644
index 0000000..0517726
--- /dev/null
+++ b/src/responder/nss/nss_protocol_netent.c
@@ -0,0 +1,243 @@
+/*
+ SSSD
+
+ Authors:
+ Samuel Cabrero <scabrero@suse.com>
+
+ Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
+
+ 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 "db/sysdb.h"
+#include "db/sysdb_ipnetworks.h"
+#include "responder/nss/nss_protocol.h"
+
+static errno_t
+sss_nss_get_netent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ struct sized_string *_name,
+ struct sized_string *_addr)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *name;
+ const char *addr;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Get name */
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (name == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, name, domain->case_preserve);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Get address */
+ addr = ldb_msg_find_attr_as_string(msg, SYSDB_IP_NETWORK_ATTR_NUMBER,
+ NULL);
+ if (addr == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ addr = sss_get_cased_name(tmp_ctx, addr, domain->case_preserve);
+ if (addr == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Set output variables */
+
+ talloc_steal(mem_ctx, name);
+ talloc_steal(mem_ctx, addr);
+
+ to_sized_string(_name, name);
+ to_sized_string(_addr, addr);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sss_nss_get_network_aliases(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *name,
+ struct sized_string **_aliases,
+ uint32_t *_num_aliases)
+{
+ struct ldb_message_element *el;
+ struct sized_string *aliases = NULL;
+ uint32_t num_aliases;
+ const char *alias;
+ errno_t ret;
+ int i;
+
+ el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS);
+ if (el == NULL) {
+ *_num_aliases = 0;
+ *_aliases = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ aliases = talloc_zero_array(mem_ctx, struct sized_string,
+ el->num_values + 1);
+ if (aliases == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_aliases = 0;
+ for (i = 0; i < el->num_values; i++) {
+ alias = (const char *)el->values[i].data;
+
+ if (sss_string_equal(domain->case_sensitive, alias, name)) {
+ continue;
+ }
+
+ /* Element value remains in the message, we don't need to strdup it. */
+ to_sized_string(&aliases[num_aliases], alias);
+ num_aliases++;
+ }
+
+ *_aliases = aliases;
+ *_num_aliases = num_aliases;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(aliases);
+ }
+
+ return ret;
+}
+
+errno_t
+sss_nss_protocol_fill_netent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct sized_string name;
+ struct sized_string addr;
+ struct sized_string *aliases;
+ uint32_t num_aliases;
+ uint32_t num_results;
+ size_t rp;
+ size_t body_len;
+ uint8_t *body;
+ int i;
+ int j;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* First two fields (length and reserved), filled up later. */
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ rp = 2 * sizeof(uint32_t);
+
+ num_results = 0;
+ for (i = 0; i < result->count; i++) {
+ talloc_free_children(tmp_ctx);
+ msg = result->msgs[i];
+
+ ret = sss_nss_get_netent(tmp_ctx, result->domain, msg, &name, &addr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get network information, skipping... [%d]: %s\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ ret = sss_nss_get_network_aliases(tmp_ctx, result->domain, msg, name.str,
+ &aliases, &num_aliases);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get network aliases, skipping... [%d]: %s\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ /* Adjust packet size */
+
+ ret = sss_packet_grow(packet, sizeof(uint32_t) + name.len + addr.len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ /* Fill packet */
+
+ SAFEALIGN_SET_UINT32(&body[rp], num_aliases, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], name.str, name.len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], addr.str, addr.len, &rp);
+
+ /* Store aliases */
+ for (j = 0; j < num_aliases; j++) {
+ ret = sss_packet_grow(packet, aliases[j].len);
+ if (ret != EOK) {
+ goto done;
+ }
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_STRING(&body[rp], aliases[j].str, aliases[j].len,
+ &rp);
+ }
+
+ num_results++;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ sss_packet_set_size(packet, 0);
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(body, &num_results, NULL);
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol_netgr.c b/src/responder/nss/nss_protocol_netgr.c
new file mode 100644
index 0000000..be3380b
--- /dev/null
+++ b/src/responder/nss/nss_protocol_netgr.c
@@ -0,0 +1,181 @@
+/*
+ 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 "db/sysdb.h"
+#include "db/sysdb_services.h"
+#include "responder/nss/nss_protocol.h"
+
+static errno_t
+sss_nss_protocol_fill_netgr_triple(struct sss_packet *packet,
+ struct sysdb_netgroup_ctx *entry,
+ size_t *_rp)
+{
+ struct sized_string host;
+ struct sized_string user;
+ struct sized_string domain;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+
+ to_sized_string(&host, entry->value.triple.hostname);
+ to_sized_string(&user, entry->value.triple.username);
+ to_sized_string(&domain, entry->value.triple.domainname);
+
+ if (host.len == 0) {
+ host.len = 1;
+ host.str = "";
+ }
+
+ if (user.len == 0) {
+ user.len = 1;
+ user.str = "";
+ }
+
+ if (domain.len == 0) {
+ domain.len = 1;
+ domain.str = "";
+ }
+
+ ret = sss_packet_grow(packet, sizeof(uint32_t)
+ + host.len + user.len + domain.len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to grow packet!\n");
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[*_rp], SSS_NETGR_REP_TRIPLE, _rp);
+ SAFEALIGN_SET_STRING(&body[*_rp], host.str, host.len, _rp);
+ SAFEALIGN_SET_STRING(&body[*_rp], user.str, user.len, _rp);
+ SAFEALIGN_SET_STRING(&body[*_rp], domain.str, domain.len, _rp);
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_protocol_fill_netgr_member(struct sss_packet *packet,
+ struct sysdb_netgroup_ctx *entry,
+ size_t *_rp)
+{
+ struct sized_string group;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+
+ if (entry->value.groupname == NULL || entry->value.groupname[0] == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Empty netgroup member!\n");
+ return EINVAL;
+ }
+
+ to_sized_string(&group, entry->value.groupname);
+
+ ret = sss_packet_grow(packet, sizeof(uint32_t) + group.len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to grow packet!\n");
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[*_rp], SSS_NETGR_REP_GROUP, _rp);
+ SAFEALIGN_SET_STRING(&body[*_rp], group.str, group.len, _rp);
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_fill_netgrent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ struct sysdb_netgroup_ctx **entries;
+ struct sysdb_netgroup_ctx *entry;
+ struct sss_nss_enum_index *idx;
+ uint32_t num_results;
+ size_t rp;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+
+ idx = cmd_ctx->enum_index;
+ entries = cmd_ctx->enum_ctx->netgroup;
+
+ if (idx->result > cmd_ctx->enum_ctx->netgroup_count) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Inconsistent state while processing netgroups.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* First two fields (length and reserved), filled up later. */
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ rp = 2 * sizeof(uint32_t);
+
+ if (entries == NULL) {
+ num_results = 0;
+ ret = EOK;
+ goto done;
+ }
+
+ num_results = 1; /* group was found */
+ for (; entries[idx->result] != NULL; idx->result++) {
+
+ entry = entries[idx->result];
+
+ switch (entry->type) {
+ case SYSDB_NETGROUP_TRIPLE_VAL:
+ ret = sss_nss_protocol_fill_netgr_triple(packet, entry, &rp);
+ break;
+ case SYSDB_NETGROUP_GROUP_VAL:
+ ret = sss_nss_protocol_fill_netgr_member(packet, entry, &rp);
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected value type %d!\n", entry->type);
+ ret = ERR_INTERNAL;
+ break;
+ }
+
+ if (ret != EOK) {
+ goto done;
+ }
+
+ num_results++;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ sss_packet_set_size(packet, 0);
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(body, &num_results, NULL);
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
new file mode 100644
index 0000000..3e1f31f
--- /dev/null
+++ b/src/responder/nss/nss_protocol_pwent.c
@@ -0,0 +1,338 @@
+/*
+ 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 "responder/nss/nss_protocol.h"
+#include "util/sss_nss.h"
+
+static uint32_t
+sss_nss_get_gid(struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ uint32_t gid;
+
+ /* First, try to return overridden gid. */
+ if (DOM_HAS_VIEWS(domain)) {
+ gid = ldb_msg_find_attr_as_uint64(msg, OVERRIDE_PREFIX SYSDB_GIDNUM,
+ 0);
+ if (gid != 0) {
+ return gid;
+ }
+ }
+
+ /* Try to return domain gid override. */
+ if (domain->override_gid != 0) {
+ return domain->override_gid;
+ }
+
+ /* Return original gid. */
+ return ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
+}
+
+static const char *
+sss_nss_get_homedir_override(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct sss_nss_ctx *nctx,
+ struct sss_domain_info *dom,
+ struct sss_nss_homedir_ctx *homedir_ctx)
+{
+ const char *homedir;
+ bool is_override = false;
+
+ homedir = sss_view_ldb_msg_find_attr_as_string_ex(dom, msg, SYSDB_HOMEDIR,
+ NULL, &is_override);
+ homedir_ctx->original = homedir;
+
+ /* Check to see which homedir_prefix to use. */
+ if (dom->homedir_substr != NULL) {
+ homedir_ctx->config_homedir_substr = dom->homedir_substr;
+ } else if (nctx->homedir_substr != NULL) {
+ homedir_ctx->config_homedir_substr = nctx->homedir_substr;
+ }
+
+ /* Individual overrides have the highest priority, only templates will be
+ * expanded and no further options will be evaluated. */
+ if (is_override) {
+ return expand_homedir_template(mem_ctx, homedir,
+ dom->case_preserve, homedir_ctx);
+ }
+
+ /* Here we skip the files provider as it should always return *only*
+ * what's in the files and nothing else.
+ */
+ if (!is_files_provider(dom)) {
+ /* Check whether we are unconditionally overriding the server
+ * for home directory locations.
+ */
+ if (dom->override_homedir) {
+ return expand_homedir_template(mem_ctx, dom->override_homedir,
+ dom->case_preserve, homedir_ctx);
+ } else if (nctx->override_homedir) {
+ return expand_homedir_template(mem_ctx, nctx->override_homedir,
+ dom->case_preserve, homedir_ctx);
+ }
+ }
+
+ if (!homedir || *homedir == '\0') {
+ /* In the case of a NULL or empty homedir, check to see if
+ * we have a fallback homedir to use.
+ */
+ if (dom->fallback_homedir) {
+ return expand_homedir_template(mem_ctx, dom->fallback_homedir,
+ dom->case_preserve, homedir_ctx);
+ } else if (nctx->fallback_homedir) {
+ return expand_homedir_template(mem_ctx, nctx->fallback_homedir,
+ dom->case_preserve, homedir_ctx);
+ }
+ }
+
+ /* Provider can also return template, try to expand it.*/
+ return expand_homedir_template(mem_ctx, homedir,
+ dom->case_preserve, homedir_ctx);
+}
+
+static const char *
+sss_nss_get_homedir(TALLOC_CTX *mem_ctx,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *orig_name,
+ const char *upn,
+ uid_t uid)
+{
+ struct sss_nss_homedir_ctx hd_ctx = { 0 };
+ const char *homedir;
+
+ hd_ctx.username = orig_name;
+ hd_ctx.uid = uid;
+ hd_ctx.domain = domain->name;
+ hd_ctx.upn = upn;
+
+ homedir = sss_nss_get_homedir_override(mem_ctx, msg, nss_ctx, domain, &hd_ctx);
+ if (homedir == NULL) {
+ return "";
+ }
+
+ return homedir;
+}
+
+static errno_t
+sss_nss_get_shell(struct sss_nss_ctx *nss_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *name,
+ uint32_t uid,
+ const char **_shell)
+{
+ const char *shell = NULL;
+
+ if (nss_ctx->rctx->sr_conf.scope != SESSION_RECORDING_SCOPE_NONE) {
+ const char *sr_enabled;
+ sr_enabled = ldb_msg_find_attr_as_string(
+ msg, SYSDB_SESSION_RECORDING, NULL);
+ if (sr_enabled == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "%s attribute not found for %s[%u]! Skipping\n",
+ SYSDB_SESSION_RECORDING, name, uid);
+ return EINVAL;
+ } else if (strcmp(sr_enabled, "TRUE") == 0) {
+ shell = SESSION_RECORDING_SHELL;
+ } else if (strcmp(sr_enabled, "FALSE") != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Skipping %s[%u] "
+ "because its %s attribute value is invalid: %s\n",
+ name, uid, SYSDB_SESSION_RECORDING, sr_enabled);
+ return EINVAL;
+ }
+ }
+ if (shell == NULL) {
+ shell = sss_resp_get_shell_override(msg, nss_ctx->rctx, domain);
+ }
+
+ *_shell = shell;
+ return EOK;
+}
+
+static errno_t
+sss_nss_get_pwent(TALLOC_CTX *mem_ctx,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ uint32_t *_uid,
+ uint32_t *_gid,
+ struct sized_string **_name,
+ struct sized_string *_gecos,
+ struct sized_string *_homedir,
+ struct sized_string *_shell)
+{
+ const char *upn;
+ const char *name;
+ const char *gecos;
+ const char *homedir;
+ const char *shell;
+ uint32_t gid;
+ uint32_t uid;
+ errno_t ret;
+
+ /* Get fields. */
+ upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
+ name = sss_get_name_from_msg(domain, msg);
+ gid = sss_nss_get_gid(domain, msg);
+ uid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_UIDNUM, 0);
+
+ if (name == NULL || uid == 0 || gid == 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Incomplete user object for %s[%u]! Skipping\n",
+ name ? name : "<NULL>", uid);
+ return EINVAL;
+ }
+
+ gecos = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_GECOS,
+ NULL);
+ homedir = sss_nss_get_homedir(mem_ctx, nss_ctx, domain, msg, name, upn, uid);
+ ret = sss_nss_get_shell(nss_ctx, domain, msg, name, uid, &shell);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "failed retrieving shell for %s[%u], skipping [%d]: %s\n",
+ name, uid, ret, sss_strerror(ret));
+ return ret;
+ }
+
+ /* Convert to sized strings. */
+ ret = sized_output_name(mem_ctx, nss_ctx->rctx, name, domain, _name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sized_output_name failed, skipping [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ to_sized_string(_gecos, gecos == NULL ? "" : gecos);
+ to_sized_string(_shell, shell);
+ to_sized_string(_homedir, homedir);
+
+ *_gid = gid;
+ *_uid = uid;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_fill_pwent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct sized_string pwfield;
+ struct sized_string *name;
+ struct sized_string gecos;
+ struct sized_string homedir;
+ struct sized_string shell;
+ uint32_t gid;
+ uint32_t uid;
+ uint32_t num_results;
+ size_t rp;
+ size_t body_len;
+ uint8_t *body;
+ int i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* First two fields (length and reserved), filled up later. */
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ rp = 2 * sizeof(uint32_t);
+
+ num_results = 0;
+ for (i = 0; i < result->count; i++) {
+ talloc_free_children(tmp_ctx);
+ msg = result->msgs[i];
+
+ /* Password field content. */
+ to_sized_string(&pwfield, sss_nss_get_pwfield(nss_ctx, result->domain));
+
+ ret = sss_nss_get_pwent(tmp_ctx, nss_ctx, result->domain, msg, &uid, &gid,
+ &name, &gecos, &homedir, &shell);
+ if (ret != EOK) {
+ continue;
+ }
+
+ /* Adjust packet size: uid, gid + string fields. */
+
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t)
+ + name->len + gecos.len + homedir.len
+ + shell.len + pwfield.len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ /* Fill packet. */
+
+ SAFEALIGN_SET_UINT32(&body[rp], uid, &rp);
+ SAFEALIGN_SET_UINT32(&body[rp], gid, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], name->str, name->len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], pwfield.str, pwfield.len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], gecos.str, gecos.len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], homedir.str, homedir.len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], shell.str, shell.len, &rp);
+
+ num_results++;
+
+ /* Do not store entry in memory cache during enumeration or when
+ * requested or if cache explicitly disabled. */
+ if (!cmd_ctx->enumeration
+ && ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0)
+ && (nss_ctx->pwd_mc_ctx != NULL)) {
+ ret = sss_mmap_cache_pw_store(&nss_ctx->pwd_mc_ctx, name, &pwfield,
+ uid, gid, &gecos, &homedir, &shell);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to store user %s (%s) in mmap cache [%d]: %s!\n",
+ name->str, result->domain->name, ret, sss_strerror(ret));
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ sss_packet_set_size(packet, 0);
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(body, &num_results, NULL);
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol_sid.c b/src/responder/nss/nss_protocol_sid.c
new file mode 100644
index 0000000..69d61bb
--- /dev/null
+++ b/src/responder/nss/nss_protocol_sid.c
@@ -0,0 +1,704 @@
+/*
+ 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 "util/crypto/sss_crypto.h"
+#include "responder/nss/nss_protocol.h"
+
+static errno_t
+find_sss_id_type(struct ldb_message *msg,
+ bool mpg,
+ enum sss_id_type *id_type)
+{
+ size_t c;
+ struct ldb_message_element *el;
+ struct ldb_val *val = NULL;
+
+ el = ldb_msg_find_element(msg, SYSDB_OBJECTCATEGORY);
+ if (el == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Objectcategory attribute not found.\n");
+ return EINVAL;
+ }
+
+ for (c = 0; c < el->num_values; c++) {
+ val = &(el->values[c]);
+ if (strncasecmp(SYSDB_USER_CLASS,
+ (char *)val->data, val->length) == 0) {
+ break;
+ }
+ }
+
+ if (c == el->num_values) {
+ *id_type = SSS_ID_TYPE_GID;
+ } else {
+ if (mpg) {
+ *id_type = SSS_ID_TYPE_BOTH;
+ } else {
+ *id_type = SSS_ID_TYPE_UID;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_get_id_type(struct sss_nss_cmd_ctx *cmd_ctx,
+ struct cache_req_result *result,
+ enum sss_id_type *_type)
+{
+ errno_t ret;
+ bool mpg;
+
+ /* Well known objects are always groups. */
+ if (result->well_known_object) {
+ *_type = SSS_ID_TYPE_GID;
+ return EOK;
+ }
+
+ mpg = sss_domain_is_mpg(result->domain) || sss_domain_is_hybrid(result->domain);
+ ret = find_sss_id_type(result->msgs[0], mpg, _type);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to find ID type [%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_get_sid_id_type(struct sss_nss_cmd_ctx *cmd_ctx,
+ struct cache_req_result *result,
+ const char **_sid,
+ uint64_t *_id,
+ enum sss_id_type *_type)
+{
+ errno_t ret;
+ size_t c;
+ const char *tmp;
+ const char *user_sid = NULL;
+ const char *group_sid = NULL;
+ uint64_t user_uid = 0;
+ uint64_t user_gid = 0;
+ uint64_t group_gid = 0;
+ enum sss_id_type ltype;
+
+ if (result->count == 1) {
+ *_sid = ldb_msg_find_attr_as_string(result->msgs[0],
+ SYSDB_SID_STR, NULL);
+ if (*_sid == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing SID.\n");
+ return EINVAL;
+ }
+ ret = sss_nss_get_id_type(cmd_ctx, result, _type);
+ if (ret == EOK ) {
+ if (*_type == SSS_ID_TYPE_GID) {
+ *_id = ldb_msg_find_attr_as_uint64(result->msgs[0],
+ SYSDB_GIDNUM, 0);
+ } else {
+ *_id = ldb_msg_find_attr_as_uint64(result->msgs[0],
+ SYSDB_UIDNUM, 0);
+ }
+ }
+ return ret;
+ }
+
+ for (c = 0; c < result->count; c++) {
+ ret = find_sss_id_type(result->msgs[c],
+ false /* we are only interested in the type
+ * of the object, so mpg setting can
+ * be ignored */,
+ &ltype);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to find ID type, ignored [%d][%s].\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ tmp = ldb_msg_find_attr_as_string(result->msgs[c],
+ SYSDB_SID_STR, NULL);
+ if (tmp == NULL) {
+ continue;
+ }
+
+ if (ltype == SSS_ID_TYPE_GID) {
+ if (tmp != NULL && group_sid != NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Search for SID found multiple groups with SIDs: %s, %s;"
+ " request failed.\n", tmp, group_sid);
+ return EINVAL;
+ }
+ group_sid = tmp;
+ group_gid = ldb_msg_find_attr_as_uint64(result->msgs[c],
+ SYSDB_GIDNUM, 0);
+ } else {
+ if (tmp != NULL && user_sid != NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Search for SID found multiple users with SIDs: %s, %s; "
+ "request failed.\n", tmp, user_sid);
+ return EINVAL;
+ }
+ user_sid = tmp;
+ user_uid = ldb_msg_find_attr_as_uint64(result->msgs[c],
+ SYSDB_UIDNUM, 0);
+ user_gid = ldb_msg_find_attr_as_uint64(result->msgs[c],
+ SYSDB_GIDNUM, 0);
+ }
+ }
+
+ if (user_sid == NULL && group_sid == NULL) {
+ /* No SID in the results */
+ return ENOENT;
+ } else if (user_sid != NULL && group_sid == NULL) {
+ /* There is only one user with a SID in the results */
+ *_sid = user_sid;
+ *_id = user_uid;
+ *_type = SSS_ID_TYPE_UID;
+ } else if (user_sid == NULL && group_sid != NULL) {
+ /* There is only one group with a SID in the results */
+ *_sid = group_sid;
+ *_id = group_gid;
+ *_type = SSS_ID_TYPE_GID;
+ } else if (user_sid != NULL && group_sid != NULL && user_uid != 0
+ && user_uid == user_gid && user_gid == group_gid) {
+ /* Manually created user-private-group */
+ *_sid = user_sid;
+ *_id = user_uid;
+ *_type = SSS_ID_TYPE_UID;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Found user with SID [%s] and group with SID [%s] during a "
+ "single request, cannot handle this case.\n",
+ user_sid, group_sid);
+ /* Unrelated user and group both with SIDs are returned, we cannot
+ * handle this case. */
+ return EINVAL;
+ }
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_fill_sid(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ struct sized_string sz_sid;
+ enum sss_id_type id_type;
+ const char *sid;
+ uint64_t id;
+ size_t rp = 0;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+
+ ret = sss_nss_get_sid_id_type(cmd_ctx, result, &sid, &id, &id_type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ to_sized_string(&sz_sid, sid);
+
+ ret = sss_packet_grow(packet, sz_sid.len + 3 * sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[rp], 1, &rp); /* Num results. */
+ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */
+ SAFEALIGN_SET_UINT32(&body[rp], id_type, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], sz_sid.str, sz_sid.len, &rp);
+
+ if (nss_ctx->sid_mc_ctx != NULL) {
+ /* no need to check for SSS_NSS_EX_FLAG_INVALIDATE_CACHE since
+ * SID related requests don't support 'flags'
+ */
+ if (id == 0 || id >= UINT32_MAX) {
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid POSIX ID %lu\n", id);
+ return EOK;
+ }
+ ret = sss_mmap_cache_sid_store(&nss_ctx->sid_mc_ctx, &sz_sid,
+ (uint32_t)id, id_type,
+ cmd_ctx->type != CACHE_REQ_OBJECT_BY_ID);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to store SID='%s' / ID=%lu in mmap cache [%d]: %s!\n",
+ sz_sid.str, id, ret, sss_strerror(ret));
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t process_attr_list(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char **attr_list,
+ struct sized_string **_keys,
+ struct sized_string **_vals,
+ size_t *array_size, size_t *sum,
+ size_t *found)
+{
+ size_t c;
+ size_t d;
+ struct sized_string *keys;
+ struct sized_string *vals;
+ struct ldb_val val;
+ struct ldb_message_element *el;
+ bool use_base64;
+
+ keys = *_keys;
+ vals = *_vals;
+
+ for (c = 0; attr_list[c] != NULL; c++) {
+ el = ldb_msg_find_element(msg, attr_list[c]);
+ if (el != NULL && el->num_values > 0) {
+ if (el->num_values > 1) {
+ *array_size += el->num_values;
+ keys = talloc_realloc(mem_ctx, keys, struct sized_string,
+ *array_size);
+ vals = talloc_realloc(mem_ctx, vals, struct sized_string,
+ *array_size);
+ if (keys == NULL || vals == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ return ENOMEM;
+ }
+ }
+
+ use_base64 = false;
+ if (strcmp(attr_list[c], SYSDB_USER_CERT) == 0) {
+ use_base64 = true;
+ }
+
+ for (d = 0; d < el->num_values; d++) {
+ to_sized_string(&keys[*found], attr_list[c]);
+ *sum += keys[*found].len;
+ if (use_base64) {
+ val.data = (uint8_t *)sss_base64_encode(vals,
+ el->values[d].data,
+ el->values[d].length);
+ if (val.data != NULL) {
+ val.length = strlen((char *)val.data);
+ }
+ } else {
+ val = el->values[d];
+ }
+
+ if (val.data == NULL || val.data[val.length] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unexpected attribute value found for [%s].\n",
+ attr_list[c]);
+ return EINVAL;
+ }
+ to_sized_string(&vals[*found], (const char *)val.data);
+ *sum += vals[*found].len;
+
+ (*found)++;
+ }
+ }
+ }
+
+ *_keys = keys;
+ *_vals = vals;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_fill_orig(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg = result->msgs[0];
+ const char **full_attrs = NULL;
+ enum sss_id_type id_type;
+ struct sized_string *keys;
+ struct sized_string *vals;
+ size_t full_attrs_count = 0;
+ size_t array_size;
+ size_t sum;
+ size_t found;
+ size_t i;
+ size_t rp = 0;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+
+ if (result->count != 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unexpected number of results [%u], expected [1].\n",
+ result->count);
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_nss_get_id_type(cmd_ctx, result, &id_type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (nss_ctx->full_attribute_list != NULL) {
+ full_attrs = nss_ctx->full_attribute_list;
+ for (full_attrs_count = 0;
+ full_attrs[full_attrs_count] != NULL;
+ full_attrs_count++);
+ }
+
+ array_size = full_attrs_count;
+ keys = talloc_array(tmp_ctx, struct sized_string, array_size);
+ vals = talloc_array(tmp_ctx, struct sized_string, array_size);
+ if (keys == NULL || vals == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ sum = 0;
+ found = 0;
+
+ if (full_attrs_count != 0) {
+ ret = process_attr_list(tmp_ctx, msg, full_attrs, &keys, &vals,
+ &array_size, &sum, &found);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "process_attr_list failed.\n");
+ goto done;
+ }
+ }
+
+ ret = sss_packet_grow(packet, sum + 3 * sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_SETMEM_UINT32(&body[rp], 1, &rp); /* Num results */
+ SAFEALIGN_SETMEM_UINT32(&body[rp], 0, &rp); /* reserved */
+ SAFEALIGN_COPY_UINT32(&body[rp], &id_type, &rp);
+ for (i = 0; i < found; i++) {
+ SAFEALIGN_SET_STRING(&body[rp], keys[i].str, keys[i].len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], vals[i].str, vals[i].len, &rp);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sss_nss_get_well_known_name(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct cache_req_result *result,
+ struct sized_string **_sz_name)
+{
+ struct sized_string *sz_name;
+ const char *fq_name = NULL;
+ const char *domname;
+ const char *name;
+
+ name = result->lookup_name;
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing name.\n");
+ return EINVAL;
+ }
+
+ sz_name = talloc_zero(mem_ctx, struct sized_string);
+ if (sz_name == NULL) {
+ return ENOMEM;
+ }
+
+ domname = result->domain != NULL
+ ? result->domain->name
+ : result->well_known_domain;
+
+ if (domname != NULL) {
+ fq_name = sss_tc_fqname2(sz_name, rctx->global_names,
+ domname, domname, name);
+ if (fq_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Conversion to fqname failed.\n");
+ talloc_free(sz_name);
+ return ENOMEM;
+ }
+
+ name = fq_name;
+ }
+
+ to_sized_string(sz_name, name);
+
+ *_sz_name = sz_name;
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_get_ad_name(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct cache_req_result *result,
+ struct sized_string **_sz_name)
+{
+ struct ldb_message *msg = result->msgs[0];
+ const char *name;
+ errno_t ret;
+
+ if (result->well_known_object) {
+ return sss_nss_get_well_known_name(mem_ctx, rctx, result, _sz_name);
+ }
+
+ name = ldb_msg_find_attr_as_string(msg, ORIGINALAD_PREFIX SYSDB_NAME,
+ NULL);
+ if (name == NULL) {
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing name.\n");
+ return EINVAL;
+ }
+
+ ret = sized_output_name(mem_ctx, rctx, name, result->domain, _sz_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to create sized name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_fill_single_name(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ if (result->ldb_result->count > 1) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Lookup returned more than one result "
+ "but only one was expected.\n");
+ return EEXIST;
+ }
+
+ return sss_nss_protocol_fill_name(nss_ctx, cmd_ctx, packet, result);
+}
+
+errno_t
+sss_nss_protocol_fill_name(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ struct sized_string *sz_name;
+ enum sss_id_type id_type;
+ size_t rp = 0;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+
+ ret = sss_nss_get_id_type(cmd_ctx, result, &id_type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_nss_get_ad_name(cmd_ctx, nss_ctx->rctx, result, &sz_name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sss_packet_grow(packet, sz_name->len + 3 * sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
+ talloc_free(sz_name);
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[rp], 1, &rp); /* Num results. */
+ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */
+ SAFEALIGN_SET_UINT32(&body[rp], id_type, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], sz_name->str, sz_name->len, &rp);
+
+ talloc_free(sz_name);
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_fill_id(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ struct ldb_message *msg = result->msgs[0];
+ enum sss_id_type id_type;
+ uint64_t id64;
+ uint32_t id;
+ const char *sid = NULL;
+ struct sized_string sid_key;
+ size_t rp = 0;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+
+ if (result->ldb_result == NULL) {
+ /* This was a well known SID. This is currently unsupported with id. */
+ return EINVAL;
+ }
+
+ ret = sss_nss_get_id_type(cmd_ctx, result, &id_type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (id_type == SSS_ID_TYPE_GID) {
+ id64 = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
+ } else {
+ id64 = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
+ }
+
+ if (id64 == 0 || id64 >= UINT32_MAX) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid POSIX ID.\n");
+ return EINVAL;
+ }
+
+ id = (uint32_t)id64;
+
+ ret = sss_packet_grow(packet, 4 * sizeof(uint32_t));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[rp], 1, &rp); /* Num results. */
+ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */
+ SAFEALIGN_SET_UINT32(&body[rp], id_type, &rp);
+ SAFEALIGN_SET_UINT32(&body[rp], id, &rp);
+
+ if (nss_ctx->sid_mc_ctx != NULL) {
+ /* no need to check for SSS_NSS_EX_FLAG_INVALIDATE_CACHE since
+ * SID related requests don't support 'flags'
+ */
+ sid = ldb_msg_find_attr_as_string(msg, SYSDB_SID_STR, NULL);
+ if (!sid) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing SID?!\n");
+ return EOK;
+ }
+ to_sized_string(&sid_key, sid);
+ ret = sss_mmap_cache_sid_store(&nss_ctx->sid_mc_ctx, &sid_key, id, id_type,
+ cmd_ctx->type != CACHE_REQ_OBJECT_BY_ID);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to store SID='%s' / ID=%d in mmap cache [%d]: %s!\n",
+ sid, id, ret, sss_strerror(ret));
+ }
+ }
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_fill_name_list_all_domains(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result **results)
+{
+ enum sss_id_type *id_types;
+ size_t rp = 0;
+ size_t body_len;
+ uint8_t *body;
+ errno_t ret;
+ struct sized_string *sz_names;
+ size_t len;
+ size_t c;
+ const char *tmp_str;
+ size_t d;
+ size_t total = 0;
+ size_t iter = 0;
+
+ if (results == NULL) {
+ return EINVAL;
+ }
+
+ for (d = 0; results[d] != NULL; d++) {
+ total += results[d]->count;
+ }
+
+ sz_names = talloc_array(cmd_ctx, struct sized_string, total);
+ if (sz_names == NULL) {
+ return ENOMEM;
+ }
+
+ id_types = talloc_array(cmd_ctx, enum sss_id_type, total);
+ if (id_types == NULL) {
+ return ENOMEM;
+ }
+
+ len = 0;
+ for (d = 0; results[d] != NULL; d++) {
+ for (c = 0; c < results[d]->count; c++) {
+ ret = sss_nss_get_id_type(cmd_ctx, results[d], &(id_types[iter]));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ tmp_str = sss_get_name_from_msg(results[d]->domain,
+ results[d]->msgs[c]);
+ if (tmp_str == NULL) {
+ return EINVAL;
+ }
+ to_sized_string(&(sz_names[iter]), tmp_str);
+
+ len += sz_names[iter].len;
+ iter++;
+ }
+ }
+
+ len += (2 + total) * sizeof(uint32_t);
+
+ ret = sss_packet_grow(packet, len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[rp], total, &rp); /* Num results. */
+ SAFEALIGN_SET_UINT32(&body[rp], 0, &rp); /* Reserved. */
+ for (c = 0; c < total; c++) {
+ SAFEALIGN_SET_UINT32(&body[rp], id_types[c], &rp);
+ SAFEALIGN_SET_STRING(&body[rp], sz_names[c].str, sz_names[c].len,
+ &rp);
+ }
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol_subid.c b/src/responder/nss/nss_protocol_subid.c
new file mode 100644
index 0000000..c06ab0b
--- /dev/null
+++ b/src/responder/nss/nss_protocol_subid.c
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2021 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 "responder/nss/nss_protocol.h"
+
+errno_t
+sss_nss_protocol_fill_subid_ranges(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ static const uint32_t one = 1;
+ errno_t ret;
+ uint8_t *body;
+ size_t body_len;
+ size_t rp = 0;
+ uint32_t gid, uid, gidCount, uidCount;
+
+ if (!result->count || !result->msgs) {
+ return ENOENT;
+ }
+
+ uid = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_UID_NUMBER, 0);
+ uidCount = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_UID_COUND, 0);
+ gid = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_GID_NUMBER, 0);
+ gidCount = ldb_msg_find_attr_as_uint(result->msgs[0], SYSDB_SUBID_GID_COUNT, 0);
+ if (!uid || !gid || !gidCount || !uidCount) {
+ return ENOENT;
+ }
+
+ /* only single uid & gid range is expected currently */
+ ret = sss_packet_grow(packet, (2 + 2*2) * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(&body[rp], &one, &rp);
+ SAFEALIGN_COPY_UINT32(&body[rp], &one, &rp);
+ SAFEALIGN_COPY_UINT32(&body[rp], &uid, &rp);
+ SAFEALIGN_COPY_UINT32(&body[rp], &uidCount, &rp);
+ SAFEALIGN_COPY_UINT32(&body[rp], &gid, &rp);
+ SAFEALIGN_COPY_UINT32(&body[rp], &gidCount, &rp);
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_protocol_svcent.c b/src/responder/nss/nss_protocol_svcent.c
new file mode 100644
index 0000000..68e20af
--- /dev/null
+++ b/src/responder/nss/nss_protocol_svcent.c
@@ -0,0 +1,270 @@
+/*
+ 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 "db/sysdb.h"
+#include "db/sysdb_services.h"
+#include "responder/nss/nss_protocol.h"
+
+static errno_t
+sss_nss_get_svcent(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *requested_protocol,
+ struct sized_string *_name,
+ struct sized_string *_protocol,
+ uint16_t *_port)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message_element *el;
+ const char *protocol;
+ const char *name;
+ uint16_t port;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Get name. */
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (name == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ name = sss_get_cased_name(tmp_ctx, name, domain->case_preserve);
+ if (name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Get port. */
+ port = (uint16_t)ldb_msg_find_attr_as_uint(msg, SYSDB_SVC_PORT, 0);
+ if (port == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No port for service [%s]\n", name);
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Get protocol.
+ *
+ * Use the requested protocol if present, otherwise take the
+ * first protocol returned by the sysdb. */
+ if (requested_protocol != NULL) {
+ protocol = requested_protocol;
+ } else {
+ el = ldb_msg_find_element(msg, SYSDB_SVC_PROTO);
+ if (el == NULL || el->num_values == 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ protocol = (const char *)el->values[0].data;
+ if (protocol == NULL) {
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ }
+
+ protocol = sss_get_cased_name(tmp_ctx, protocol, domain->case_preserve);
+ if (protocol == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Set output variables. */
+
+ talloc_steal(mem_ctx, name);
+ talloc_steal(mem_ctx, protocol);
+
+ to_sized_string(_name, name);
+ to_sized_string(_protocol, protocol);
+ *_port = port;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sss_nss_get_svc_aliases(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg,
+ const char *name,
+ struct sized_string **_aliases,
+ uint32_t *_num_aliases)
+{
+ struct ldb_message_element *el;
+ struct sized_string *aliases = NULL;
+ uint32_t num_aliases;
+ const char *alias;
+ errno_t ret;
+ int i;
+
+ el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS);
+ if (el == NULL) {
+ *_num_aliases = 0;
+ *_aliases = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ aliases = talloc_zero_array(mem_ctx, struct sized_string,
+ el->num_values + 1);
+ if (aliases == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ num_aliases = 0;
+ for (i = 0; i < el->num_values; i++) {
+ alias = (const char *)el->values[i].data;
+
+ if (sss_string_equal(domain->case_sensitive, alias, name)) {
+ continue;
+ }
+
+ /* Element value remains in the message, we don't need to strdup it. */
+ to_sized_string(&aliases[num_aliases], alias);
+ num_aliases++;
+ }
+
+ *_aliases = aliases;
+ *_num_aliases = num_aliases;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(aliases);
+ }
+
+ return ret;
+}
+
+errno_t
+sss_nss_protocol_fill_svcent(struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct sss_packet *packet,
+ struct cache_req_result *result)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct sized_string name;
+ struct sized_string protocol;
+ struct sized_string *aliases;
+ uint32_t num_aliases;
+ uint16_t port;
+ uint32_t num_results;
+ size_t rp;
+ size_t body_len;
+ uint8_t *body;
+ int i;
+ int j;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* First two fields (length and reserved), filled up later. */
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ return ret;
+ }
+
+ rp = 2 * sizeof(uint32_t);
+
+ num_results = 0;
+ for (i = 0; i < result->count; i++) {
+ talloc_free_children(tmp_ctx);
+ msg = result->msgs[i];
+
+ ret = sss_nss_get_svcent(tmp_ctx, result->domain, msg,
+ cmd_ctx->svc_protocol, &name, &protocol, &port);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get service information, skipping... [%d]: %s\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ ret = sss_nss_get_svc_aliases(tmp_ctx, result->domain, msg, name.str,
+ &aliases, &num_aliases);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unable to get service aliases, skipping... [%d]: %s\n",
+ ret, sss_strerror(ret));
+ continue;
+ }
+
+ /* Adjust packet size. */
+
+ ret = sss_packet_grow(packet, 2 * sizeof(uint16_t) + sizeof(uint32_t)
+ + name.len + protocol.len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ /* Fill packet. */
+
+ SAFEALIGN_SET_UINT32(&body[rp], (uint32_t)htons(port), &rp);
+ SAFEALIGN_SET_UINT32(&body[rp], num_aliases, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], name.str, name.len, &rp);
+ SAFEALIGN_SET_STRING(&body[rp], protocol.str, protocol.len, &rp);
+
+ /* Store aliases. */
+ for (j = 0; j < num_aliases; j++) {
+ ret = sss_packet_grow(packet, aliases[j].len);
+ if (ret != EOK) {
+ goto done;
+ }
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_STRING(&body[rp], aliases[j].str, aliases[j].len,
+ &rp);
+ }
+
+ num_results++;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ sss_packet_set_size(packet, 0);
+ return ret;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+ SAFEALIGN_COPY_UINT32(body, &num_results, NULL);
+ SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
+
+ return EOK;
+}
diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c
new file mode 100644
index 0000000..cec55e6
--- /dev/null
+++ b/src/responder/nss/nss_utils.c
@@ -0,0 +1,38 @@
+/*
+ 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 <ldb.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/nss/nss_private.h"
+
+const char *
+sss_nss_get_pwfield(struct sss_nss_ctx *nctx,
+ struct sss_domain_info *dom)
+{
+ if (dom->pwfield != NULL) {
+ return dom->pwfield;
+ }
+
+ return nctx->pwfield;
+}
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
new file mode 100644
index 0000000..4673a64
--- /dev/null
+++ b/src/responder/nss/nsssrv.c
@@ -0,0 +1,740 @@
+/*
+ SSSD
+
+ NSS Responder
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <popt.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "util/mmap_cache.h"
+#include "responder/nss/nss_private.h"
+#include "responder/nss/nss_iface.h"
+#include "responder/nss/nsssrv_mmap_cache.h"
+#include "responder/common/negcache.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "providers/data_provider.h"
+#include "util/util_sss_idmap.h"
+#include "sss_iface/sss_iface_async.h"
+
+#define DEFAULT_PWFIELD "*"
+#define DEFAULT_NSS_FD_LIMIT 8192
+
+static errno_t
+sss_nss_clear_memcache(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nctx)
+{
+ int memcache_timeout;
+ errno_t ret;
+
+ if (access(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG, F_OK) < 0) {
+ ret = errno;
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "CLEAR_MC_FLAG not found. Nothing to do.\n");
+ return EOK; /* Most probably log rotation SIGHUP to monitor */
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to check existence of "CLEAR_MC_FLAG": %s.\n",
+ strerror(ret));
+ return ret;
+ }
+ }
+
+ /*
+ * CLEAR_MC_FLAG flag file found.
+ * This file existance indicates that SIGHUP was called by sss_cache
+ * as trigger for the memory cache cleanup.
+ * sss_cache is waiting for CLEAR_MC_FLAG file deletion
+ * as confirmation that memory cache cleaning has finished.
+ */
+
+ ret = confdb_get_int(nctx->rctx->cdb,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_MEMCACHE_TIMEOUT,
+ 300, &memcache_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to get memory cache entry timeout [%s].\n",
+ CONFDB_MEMCACHE_TIMEOUT);
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Clearing memory caches.\n");
+ ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid,
+ -1, /* keep current size */
+ (time_t) memcache_timeout,
+ &nctx->pwd_mc_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "passwd mmap cache invalidation failed\n");
+ goto done;
+ }
+
+ ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid,
+ -1, /* keep current size */
+ (time_t) memcache_timeout,
+ &nctx->grp_mc_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "group mmap cache invalidation failed\n");
+ goto done;
+ }
+
+ ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid,
+ -1, /* keep current size */
+ (time_t)memcache_timeout,
+ &nctx->initgr_mc_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "initgroups mmap cache invalidation failed\n");
+ goto done;
+ }
+
+done:
+ if (unlink(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG) != 0) {
+ if (errno != ENOENT)
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unlink file: %s.\n",
+ strerror(errno));
+ }
+ return ret;
+}
+
+static errno_t
+sss_nss_clear_negcache(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nctx)
+{
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Clearing negative cache non-permament entries\n");
+
+ ret = sss_ncache_reset_users(nctx->rctx->ncache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Negative cache clearing users failed\n");
+ goto done;
+ }
+
+ ret = sss_ncache_reset_groups(nctx->rctx->ncache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Negative cache clearing groups failed\n");
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static errno_t
+sss_nss_clear_netgroup_hash_table(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sss_nss_ctx *nss_ctx)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Invalidating netgroup hash table\n");
+
+ sss_ptr_hash_delete_all(nss_ctx->netgrent, false);
+
+ return EOK;
+}
+
+static int sss_nss_get_config(struct sss_nss_ctx *nctx,
+ struct confdb_ctx *cdb)
+{
+ int ret;
+ char *tmp_str;
+ static const char *orig_attrs[] = { SYSDB_SID_STR,
+ ORIGINALAD_PREFIX SYSDB_NAME,
+ ORIGINALAD_PREFIX SYSDB_UIDNUM,
+ ORIGINALAD_PREFIX SYSDB_GIDNUM,
+ ORIGINALAD_PREFIX SYSDB_HOMEDIR,
+ ORIGINALAD_PREFIX SYSDB_GECOS,
+ ORIGINALAD_PREFIX SYSDB_SHELL,
+ SYSDB_UPN,
+ SYSDB_DEFAULT_OVERRIDE_NAME,
+ SYSDB_AD_ACCOUNT_EXPIRES,
+ SYSDB_AD_USER_ACCOUNT_CONTROL,
+ SYSDB_SSH_PUBKEY,
+ SYSDB_USER_CERT,
+ SYSDB_USER_EMAIL,
+ SYSDB_ORIG_DN,
+ SYSDB_ORIG_MEMBEROF,
+ NULL };
+
+ ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_ENUM_CACHE_TIMEOUT, 120,
+ &nctx->enum_cache_timeout);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_bool(cdb, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_FILTER_USERS_IN_GROUPS, true,
+ &nctx->filter_users_in_groups);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_ENTRY_CACHE_NOWAIT_PERCENTAGE, 50,
+ &nctx->cache_refresh_percent);
+ if (ret != EOK) goto done;
+ if (nctx->cache_refresh_percent < 0 ||
+ nctx->cache_refresh_percent > 99) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Configuration error: entry_cache_nowait_percentage is "
+ "invalid. Disabling feature.\n");
+ nctx->cache_refresh_percent = 0;
+ }
+
+ ret = sss_ncache_prepopulate(nctx->rctx->ncache, cdb, nctx->rctx);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_PWFIELD, DEFAULT_PWFIELD,
+ &nctx->pwfield);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_OVERRIDE_HOMEDIR, NULL,
+ &nctx->override_homedir);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_FALLBACK_HOMEDIR, NULL,
+ &nctx->fallback_homedir);
+ if (ret != EOK) goto done;
+
+ ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_HOMEDIR_SUBSTRING,
+ CONFDB_DEFAULT_HOMEDIR_SUBSTRING,
+ &nctx->homedir_substr);
+ if (ret != EOK) goto done;
+
+
+ ret = confdb_get_string(cdb, nctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_IFP_USER_ATTR_LIST, NULL, &tmp_str);
+ if (ret != EOK) goto done;
+
+ if (tmp_str == NULL) {
+ ret = confdb_get_string(cdb, nctx, CONFDB_IFP_CONF_ENTRY,
+ CONFDB_IFP_USER_ATTR_LIST, NULL, &tmp_str);
+ if (ret != EOK) goto done;
+ }
+
+ if (tmp_str != NULL) {
+ nctx->extra_attributes = parse_attr_list_ex(nctx, tmp_str, NULL);
+ if (nctx->extra_attributes == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = add_strings_lists_ex(nctx, nctx->extra_attributes, orig_attrs, false,
+ true, &nctx->full_attribute_list);
+ if (ret != EOK) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ return ret;
+}
+
+static int setup_memcaches(struct sss_nss_ctx *nctx)
+{
+ /* Default memcache sizes */
+ static const size_t SSS_MC_CACHE_SLOTS_PER_MB = 1024*1024/MC_SLOT_SIZE;
+ static const size_t SSS_MC_CACHE_PASSWD_SIZE = 8;
+ static const size_t SSS_MC_CACHE_GROUP_SIZE = 6;
+ static const size_t SSS_MC_CACHE_INITGROUP_SIZE = 10;
+ static const size_t SSS_MC_CACHE_SID_SIZE = 6;
+
+ int ret;
+ int memcache_timeout;
+ int mc_size_passwd;
+ int mc_size_group;
+ int mc_size_initgroups;
+ int mc_size_sid;
+
+ /* Remove the CLEAR_MC_FLAG file if exists. */
+ ret = unlink(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG);
+ if (ret != 0 && errno != ENOENT) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to unlink file [%s]. This can cause memory cache to "
+ "be purged when next log rotation is requested. %d: %s\n",
+ SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG, ret, strerror(ret));
+ }
+
+ ret = confdb_get_int(nctx->rctx->cdb,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_MEMCACHE_TIMEOUT,
+ 300, &memcache_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get 'memcache_timeout' option from confdb.\n");
+ return ret;
+ }
+
+ /* Get all memcache sizes from confdb (pwd, grp, initgr, sid) */
+
+ ret = confdb_get_int(nctx->rctx->cdb,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_MEMCACHE_SIZE_PASSWD,
+ SSS_MC_CACHE_PASSWD_SIZE,
+ &mc_size_passwd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get '"CONFDB_NSS_MEMCACHE_SIZE_PASSWD
+ "' option from confdb.\n");
+ return ret;
+ }
+
+ ret = confdb_get_int(nctx->rctx->cdb,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_MEMCACHE_SIZE_GROUP,
+ SSS_MC_CACHE_GROUP_SIZE,
+ &mc_size_group);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get '"CONFDB_NSS_MEMCACHE_SIZE_GROUP
+ "' option from confdb.\n");
+ return ret;
+ }
+
+ ret = confdb_get_int(nctx->rctx->cdb,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_MEMCACHE_SIZE_INITGROUPS,
+ SSS_MC_CACHE_INITGROUP_SIZE,
+ &mc_size_initgroups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get '"CONFDB_NSS_MEMCACHE_SIZE_INITGROUPS
+ "' option from confdb.\n");
+ return ret;
+ }
+
+ ret = confdb_get_int(nctx->rctx->cdb,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_MEMCACHE_SIZE_SID,
+ SSS_MC_CACHE_SID_SIZE,
+ &mc_size_sid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get '"CONFDB_NSS_MEMCACHE_SIZE_SID
+ "' option from confdb.\n");
+ return ret;
+ }
+
+ /* Initialize the fast in-memory caches if they were not disabled */
+
+ ret = sss_mmap_cache_init(nctx, "passwd",
+ nctx->mc_uid, nctx->mc_gid,
+ SSS_MC_PASSWD,
+ mc_size_passwd * SSS_MC_CACHE_SLOTS_PER_MB,
+ (time_t)memcache_timeout,
+ &nctx->pwd_mc_ctx);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to initialize passwd mmap cache: '%s'\n",
+ sss_strerror(ret));
+ }
+
+ ret = sss_mmap_cache_init(nctx, "group",
+ nctx->mc_uid, nctx->mc_gid,
+ SSS_MC_GROUP,
+ mc_size_group * SSS_MC_CACHE_SLOTS_PER_MB,
+ (time_t)memcache_timeout,
+ &nctx->grp_mc_ctx);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to initialize group mmap cache: '%s'\n",
+ sss_strerror(ret));
+ }
+
+ ret = sss_mmap_cache_init(nctx, "initgroups",
+ nctx->mc_uid, nctx->mc_gid,
+ SSS_MC_INITGROUPS,
+ mc_size_initgroups * SSS_MC_CACHE_SLOTS_PER_MB,
+ (time_t)memcache_timeout,
+ &nctx->initgr_mc_ctx);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to initialize initgroups mmap cache: '%s'\n",
+ sss_strerror(ret));
+ }
+
+ ret = sss_mmap_cache_init(nctx, "sid",
+ nctx->mc_uid, nctx->mc_gid,
+ SSS_MC_SID,
+ mc_size_sid * SSS_MC_CACHE_SLOTS_PER_MB,
+ (time_t)memcache_timeout,
+ &nctx->sid_mc_ctx);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to initialize sid mmap cache: '%s'\n",
+ sss_strerror(ret));
+ }
+
+ return EOK;
+}
+
+static errno_t
+sss_nss_register_service_iface(struct sss_nss_ctx *nss_ctx,
+ struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface_svc,
+ sssd_service,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_service, rotateLogs, responder_logrotate, rctx),
+ SBUS_SYNC(METHOD, sssd_service, clearEnumCache, sss_nss_clear_netgroup_hash_table, nss_ctx),
+ SBUS_SYNC(METHOD, sssd_service, clearMemcache, sss_nss_clear_memcache, nss_ctx),
+ SBUS_SYNC(METHOD, sssd_service, clearNegcache, sss_nss_clear_negcache, nss_ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, sssd_service, debug_level, generic_get_debug_level, NULL),
+ SBUS_SYNC(SETTER, sssd_service, debug_level, generic_set_debug_level, NULL)
+ )
+ );
+
+ ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static int sssd_supplementary_group(struct sss_nss_ctx *nss_ctx)
+{
+ errno_t ret;
+ int size;
+ gid_t *supp_gids = NULL;
+
+ /*
+ * We explicitly read the IDs of the SSSD user even though the server
+ * receives --uid and --gid by parameters to account for the case where
+ * the SSSD is compiled --with-sssd-user=sssd but the default of the
+ * user option is root (this is what RHEL does)
+ */
+ ret = sss_user_by_name_or_uid(SSSD_USER,
+ &nss_ctx->mc_uid,
+ &nss_ctx->mc_gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get info on "SSSD_USER);
+ return ret;
+ }
+
+ if (getgid() == nss_ctx->mc_gid) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Already running as the sssd group\n");
+ return EOK;
+ }
+
+ size = getgroups(0, NULL);
+ if (size == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (size > 0) {
+ supp_gids = talloc_zero_array(NULL, gid_t, size);
+ if (supp_gids == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ size = getgroups(size, supp_gids);
+ if (size == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (int i = 0; i < size; i++) {
+ if (supp_gids[i] == nss_ctx->mc_gid) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Already assigned to the SSSD supplementary group\n");
+ ret = EOK;
+ goto done;
+ }
+ }
+ }
+
+ ret = setgroups(1, &nss_ctx->mc_gid);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot setgroups [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(supp_gids);
+ return ret;
+}
+
+int sss_nss_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *nss_cmds;
+ struct be_conn *iter;
+ struct sss_nss_ctx *nctx;
+ int ret;
+ enum idmap_error_code err;
+ int fd_limit;
+
+ nss_cmds = get_sss_nss_cmds();
+
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ nss_cmds,
+ SSS_NSS_SOCKET_NAME, -1, NULL, -1,
+ CONFDB_NSS_CONF_ENTRY,
+ SSS_BUS_NSS, NSS_SBUS_SERVICE_NAME,
+ sss_nss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ nctx = talloc_zero(rctx, struct sss_nss_ctx);
+ if (!nctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing nss_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ nctx->rctx = rctx;
+ nctx->rctx->pvt_ctx = nctx;
+
+ ret = sss_nss_get_config(nctx, cdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting nss config\n");
+ goto fail;
+ }
+
+ for (iter = nctx->rctx->be_conns; iter; iter = iter->next) {
+ ret = sss_nss_register_backend_iface(iter->conn, nctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+ }
+
+ err = sss_idmap_init(sss_idmap_talloc, nctx, sss_idmap_talloc_free,
+ &nctx->idmap_ctx);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_idmap_init failed.\n");
+ ret = EFAULT;
+ goto fail;
+ }
+
+ nctx->pwent = talloc_zero(nctx, struct sss_nss_enum_ctx);
+ if (nctx->pwent == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize pwent context!\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ nctx->grent = talloc_zero(nctx, struct sss_nss_enum_ctx);
+ if (nctx->grent == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize grent context!\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ nctx->svcent = talloc_zero(nctx, struct sss_nss_enum_ctx);
+ if (nctx->svcent == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize svcent context!\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ nctx->netgrent = sss_ptr_hash_create(nctx, NULL, NULL);
+ if (nctx->netgrent == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize netgroups table!\n");
+ ret = EFAULT;
+ goto fail;
+ }
+
+ nctx->hostent = talloc_zero(nctx, struct sss_nss_enum_ctx);
+ if (nctx->hostent == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize hostent context!\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ nctx->netent = talloc_zero(nctx, struct sss_nss_enum_ctx);
+ if (nctx->netent == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize netent context!\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * Adding the NSS process to the SSSD supplementary group avoids
+ * dac_override AVC messages from SELinux in case sssd_nss runs
+ * as root and tries to write to memcache owned by sssd:sssd
+ */
+ ret = sssd_supplementary_group(nctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot add process to the sssd supplementary group [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto fail;
+ }
+
+ ret = setup_memcaches(nctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ /* Set up file descriptor limits */
+ ret = confdb_get_int(nctx->rctx->cdb,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_SERVICE_FD_LIMIT,
+ DEFAULT_NSS_FD_LIMIT,
+ &fd_limit);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to set up file descriptor limit\n");
+ goto fail;
+ }
+ responder_set_fd_limit(fd_limit);
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, nctx->rctx->ncache,
+ NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_NSS,
+ NSS_SBUS_SERVICE_NAME,
+ NSS_SBUS_SERVICE_VERSION, MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = sss_nss_register_service_iface(nctx, rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "NSS Initialization complete\n");
+
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_nss";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ ret = server_setup("nss", true, 0, uid, gid, CONFDB_NSS_CONF_ENTRY,
+ &main_ctx, false);
+ if (ret != EOK) return 2;
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set up to exit when parent process does\n");
+ }
+
+ ret = sss_nss_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) return 3;
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
+
diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c
new file mode 100644
index 0000000..cacdc7c
--- /dev/null
+++ b/src/responder/nss/nsssrv_mmap_cache.c
@@ -0,0 +1,1626 @@
+/*
+ SSSD
+
+ NSS Responder - Mmap Cache
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2011
+
+ 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 "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "confdb/confdb.h"
+#include <sys/mman.h>
+#include <fcntl.h>
+#include "util/mmap_cache.h"
+#include "sss_client/idmap/sss_nss_idmap.h"
+#include "responder/nss/nss_private.h"
+#include "responder/nss/nsssrv_mmap_cache.h"
+
+#define MC_NEXT_BARRIER(val) ((((val) + 1) & 0x00ffffff) | 0xf0000000)
+
+#define MC_RAISE_BARRIER(m) do { \
+ m->b2 = MC_NEXT_BARRIER(m->b1); \
+ __sync_synchronize(); \
+} while (0)
+
+#define MC_LOWER_BARRIER(m) do { \
+ __sync_synchronize(); \
+ m->b1 = m->b2; \
+} while (0)
+
+#define MC_RAISE_INVALID_BARRIER(m) do { \
+ m->b2 = MC_INVALID_VAL; \
+ __sync_synchronize(); \
+} while (0)
+
+struct sss_mc_ctx {
+ char *name; /* mmap cache name */
+ enum sss_mc_type type; /* mmap cache type */
+ char *file; /* mmap cache file name */
+ int fd; /* file descriptor */
+
+ uid_t uid; /* User ID of owner */
+ gid_t gid; /* Group ID of owner */
+
+ uint32_t seed; /* pseudo-random seed to avoid collision attacks */
+ time_t valid_time_slot; /* maximum time the entry is valid in seconds */
+
+ void *mmap_base; /* base address of mmap */
+ size_t mmap_size; /* total size of mmap */
+
+ uint32_t *hash_table; /* hash table address (in mmap) */
+ uint32_t ht_size; /* size of hash table */
+
+ uint8_t *free_table; /* free list bitmaps */
+ uint32_t ft_size; /* size of free table */
+ uint32_t next_slot; /* the next slot after last allocation done via erasure */
+
+ uint8_t *data_table; /* data table address (in mmap) */
+ uint32_t dt_size; /* size of data table */
+};
+
+#define MC_FIND_BIT(base, num) \
+ uint32_t n = (num); \
+ uint8_t *b = (base) + n / 8; \
+ uint8_t c = 0x80 >> (n % 8);
+
+#define MC_SET_BIT(base, num) do { \
+ MC_FIND_BIT(base, num) \
+ *b |= c; \
+} while (0)
+
+#define MC_CLEAR_BIT(base, num) do { \
+ MC_FIND_BIT(base, num) \
+ *b &= ~c; \
+} while (0)
+
+#define MC_PROBE_BIT(base, num, used) do { \
+ MC_FIND_BIT(base, num) \
+ if (*b & c) used = true; \
+ else used = false; \
+} while (0)
+
+static inline
+uint32_t sss_mc_next_slot_with_hash(struct sss_mc_rec *rec,
+ uint32_t hash)
+{
+ if (rec->hash1 == hash) {
+ return rec->next1;
+ } else if (rec->hash2 == hash) {
+ return rec->next2;
+ } else {
+ /* it should never happen. */
+ return MC_INVALID_VAL;
+ }
+}
+
+static inline
+void sss_mc_chain_slot_to_record_with_hash(struct sss_mc_rec *rec,
+ uint32_t hash,
+ uint32_t slot)
+{
+ /* changing a single uint32_t is atomic, so there is no
+ * need to use barriers in this case */
+ if (rec->hash1 == hash) {
+ rec->next1 = slot;
+ } else if (rec->hash2 == hash) {
+ rec->next2 = slot;
+ }
+}
+
+/* This function will store corrupted memcache to disk for later
+ * analysis. */
+static void sss_mc_save_corrupted(struct sss_mc_ctx *mc_ctx)
+{
+ int err;
+ int fd = -1;
+ ssize_t written = -1;
+ char *file = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ if (mc_ctx == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Cannot store uninitialized cache. Nothing to do.\n");
+ return;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
+ return;
+ }
+
+ file = talloc_asprintf(tmp_ctx, "%s_%s",
+ mc_ctx->file, "corrupted");
+ if (file == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
+ goto done;
+ }
+
+ /* We will always store only the last problematic cache state */
+ fd = creat(file, 0600);
+ if (fd == -1) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to open file '%s' [%d]: %s\n",
+ file, err, strerror(err));
+ goto done;
+ }
+
+ written = sss_atomic_write_s(fd, mc_ctx->mmap_base, mc_ctx->mmap_size);
+ if (written != mc_ctx->mmap_size) {
+ if (written == -1) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "write() failed [%d]: %s\n", err, strerror(err));
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "write() returned %zd (expected (%zd))\n",
+ written, mc_ctx->mmap_size);
+ }
+ goto done;
+ }
+
+ sss_log(SSS_LOG_NOTICE,
+ "Stored copy of corrupted mmap cache in file '%s\n'", file);
+done:
+ if (fd != -1) {
+ close(fd);
+ if (written == -1) {
+ err = unlink(file);
+ if (err != 0) {
+ err = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to remove file '%s': %s.\n", file,
+ strerror(err));
+ }
+ }
+ }
+ talloc_free(tmp_ctx);
+}
+
+static uint32_t sss_mc_hash(struct sss_mc_ctx *mcc,
+ const char *key, size_t len)
+{
+ return murmurhash3(key, len, mcc->seed) % MC_HT_ELEMS(mcc->ht_size);
+}
+
+static void sss_mc_add_rec_to_chain(struct sss_mc_ctx *mcc,
+ struct sss_mc_rec *rec,
+ uint32_t hash)
+{
+ struct sss_mc_rec *cur;
+ uint32_t slot;
+
+ if (hash > MC_HT_ELEMS(mcc->ht_size)) {
+ /* Invalid hash. This should never happen, but better
+ * return than trying to access out of bounds memory */
+ return;
+ }
+
+ slot = mcc->hash_table[hash];
+ if (slot == MC_INVALID_VAL) {
+ /* no previous record/collision, just add to hash table */
+ mcc->hash_table[hash] = MC_PTR_TO_SLOT(mcc->data_table, rec);
+ return;
+ }
+
+ do {
+ cur = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ if (cur == rec) {
+ /* rec already stored in hash chain */
+ return;
+ }
+ slot = sss_mc_next_slot_with_hash(cur, hash);
+ } while (slot != MC_INVALID_VAL);
+ /* end of chain, append our record here */
+
+ slot = MC_PTR_TO_SLOT(mcc->data_table, rec);
+ sss_mc_chain_slot_to_record_with_hash(cur, hash, slot);
+}
+
+static void sss_mc_rm_rec_from_chain(struct sss_mc_ctx *mcc,
+ struct sss_mc_rec *rec,
+ uint32_t hash)
+{
+ struct sss_mc_rec *prev = NULL;
+ struct sss_mc_rec *cur = NULL;
+ uint32_t slot;
+
+ if (hash > MC_HT_ELEMS(mcc->ht_size)) {
+ /* It can happen if rec->hash1 and rec->hash2 was the same.
+ * or it is invalid hash. It is better to return
+ * than trying to access out of bounds memory
+ */
+ return;
+ }
+
+ slot = mcc->hash_table[hash];
+ if (slot == MC_INVALID_VAL) {
+ /* record has already been removed. It may happen if rec->hash1 and
+ * rec->has2 are the same. (It is not very likely).
+ */
+ return;
+ }
+ cur = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ if (cur == rec) {
+ mcc->hash_table[hash] = sss_mc_next_slot_with_hash(rec, hash);
+ } else {
+ slot = sss_mc_next_slot_with_hash(cur, hash);
+ while (slot != MC_INVALID_VAL) {
+ prev = cur;
+ cur = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ if (cur == rec) {
+ slot = sss_mc_next_slot_with_hash(cur, hash);
+
+ sss_mc_chain_slot_to_record_with_hash(prev, hash, slot);
+ slot = MC_INVALID_VAL;
+ } else {
+ slot = sss_mc_next_slot_with_hash(cur, hash);
+ }
+ }
+ }
+}
+
+static void sss_mc_free_slots(struct sss_mc_ctx *mcc, struct sss_mc_rec *rec)
+{
+ uint32_t slot;
+ uint32_t num;
+ uint32_t i;
+
+ slot = MC_PTR_TO_SLOT(mcc->data_table, rec);
+ num = MC_SIZE_TO_SLOTS(rec->len);
+ for (i = 0; i < num; i++) {
+ MC_CLEAR_BIT(mcc->free_table, slot + i);
+ }
+}
+
+static void sss_mc_invalidate_rec(struct sss_mc_ctx *mcc,
+ struct sss_mc_rec *rec)
+{
+ if (rec->b1 == MC_INVALID_VAL) {
+ /* record already invalid */
+ return;
+ }
+
+ /* Remove from hash chains */
+ /* hash chain 1 */
+ sss_mc_rm_rec_from_chain(mcc, rec, rec->hash1);
+ /* hash chain 2 */
+ sss_mc_rm_rec_from_chain(mcc, rec, rec->hash2);
+
+ /* Clear from free_table */
+ sss_mc_free_slots(mcc, rec);
+
+ /* Invalidate record fields */
+ MC_RAISE_INVALID_BARRIER(rec);
+ memset(rec->data, MC_INVALID_VAL8, ((MC_SLOT_SIZE * MC_SIZE_TO_SLOTS(rec->len))
+ - sizeof(struct sss_mc_rec)));
+ rec->len = MC_INVALID_VAL32;
+ rec->expire = MC_INVALID_VAL64;
+ rec->next1 = MC_INVALID_VAL32;
+ rec->next2 = MC_INVALID_VAL32;
+ rec->hash1 = MC_INVALID_VAL32;
+ rec->hash2 = MC_INVALID_VAL32;
+ MC_LOWER_BARRIER(rec);
+}
+
+static bool sss_mc_is_valid_rec(struct sss_mc_ctx *mcc, struct sss_mc_rec *rec)
+{
+ struct sss_mc_rec *self;
+ uint32_t slot;
+
+ if (((uint8_t *)rec < mcc->data_table) ||
+ ((uint8_t *)rec > (mcc->data_table + mcc->dt_size - MC_SLOT_SIZE))) {
+ return false;
+ }
+
+ if ((rec->b1 == MC_INVALID_VAL) ||
+ (rec->b1 != rec->b2)) {
+ return false;
+ }
+
+ if (!MC_CHECK_RECORD_LENGTH(mcc, rec)) {
+ return false;
+ }
+
+ if (rec->expire == MC_INVALID_VAL64) {
+ return false;
+ }
+
+ /* next record can be invalid if there are no next records */
+
+ if (rec->hash1 == MC_INVALID_VAL32) {
+ return false;
+ } else {
+ self = NULL;
+ slot = mcc->hash_table[rec->hash1];
+ while (slot != MC_INVALID_VAL32 && self != rec) {
+ self = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ slot = sss_mc_next_slot_with_hash(self, rec->hash1);
+ }
+ if (self != rec) {
+ return false;
+ }
+ }
+ if (rec->hash2 != MC_INVALID_VAL32) {
+ self = NULL;
+ slot = mcc->hash_table[rec->hash2];
+ while (slot != MC_INVALID_VAL32 && self != rec) {
+ self = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ slot = sss_mc_next_slot_with_hash(self, rec->hash2);
+ }
+ if (self != rec) {
+ return false;
+ }
+ }
+
+ /* all tests passed */
+ return true;
+}
+
+static const char *mc_type_to_str(enum sss_mc_type type)
+{
+ switch (type) {
+ case SSS_MC_PASSWD:
+ return "PASSWD";
+ case SSS_MC_GROUP:
+ return "GROUP";
+ case SSS_MC_INITGROUPS:
+ return "INITGROUPS";
+ case SSS_MC_SID:
+ return "SID";
+ default:
+ return "-UNKNOWN-";
+ }
+}
+
+/* FIXME: This is a very simplistic, inefficient, memory allocator,
+ * it will just free the oldest entries regardless of expiration if it
+ * cycled the whole free bits map and found no empty slot */
+static errno_t sss_mc_find_free_slots(struct sss_mc_ctx *mcc,
+ int num_slots, uint32_t *free_slot)
+{
+ struct sss_mc_rec *rec;
+ uint32_t tot_slots;
+ uint32_t cur;
+ uint32_t i;
+ uint32_t t;
+ bool used;
+
+ tot_slots = mcc->ft_size * 8;
+
+ /* Try to find a free slot w/o removing anything first */
+ /* FIXME: Is it really worth it? Maybe it is easier to
+ * just recycle the next set of slots? */
+ if ((mcc->next_slot + num_slots) > tot_slots) {
+ cur = 0;
+ } else {
+ cur = mcc->next_slot;
+ }
+
+ /* search for enough (num_slots) consecutive zero bits, indicating
+ * consecutive empty slots */
+ for (i = 0; i < mcc->ft_size; i++) {
+ t = cur / 8;
+ /* if all full in this byte skip directly to the next */
+ if (mcc->free_table[t] == 0xff) {
+ cur = ((cur + 8) & ~7);
+ if (cur >= tot_slots) {
+ cur = 0;
+ }
+ continue;
+ }
+
+ /* at least one bit in this byte is marked as empty */
+ for (t = ((cur + 8) & ~7) ; cur < t; cur++) {
+ MC_PROBE_BIT(mcc->free_table, cur, used);
+ if (!used) break;
+ }
+ /* check if we have enough slots before hitting the table end */
+ if ((cur + num_slots) > tot_slots) {
+ cur = 0;
+ continue;
+ }
+
+ /* check if we have at least num_slots empty starting from the first
+ * we found in the previous steps */
+ for (t = cur + num_slots; cur < t; cur++) {
+ MC_PROBE_BIT(mcc->free_table, cur, used);
+ if (used) break;
+ }
+ if (cur == t) {
+ /* ok found num_slots consecutive free bits */
+ *free_slot = cur - num_slots;
+ /* `mcc->next_slot` is not updated here intentionally.
+ * For details see discussion in https://github.com/SSSD/sssd/pull/999
+ */
+ return EOK;
+ }
+ }
+
+ /* no free slots found, free occupied slots after next_slot */
+ if ((mcc->next_slot + num_slots) > tot_slots) {
+ cur = 0;
+ } else {
+ cur = mcc->next_slot;
+ }
+ if (cur == 0) {
+ /* inform only once per full loop to avoid excessive spam */
+ DEBUG(SSSDBG_IMPORTANT_INFO, "mmap cache of type '%s' is full\n",
+ mc_type_to_str(mcc->type));
+ sss_log(SSS_LOG_NOTICE, "mmap cache of type '%s' is full, if you see "
+ "this message often then please consider increase of cache size",
+ mc_type_to_str(mcc->type));
+ }
+ for (i = 0; i < num_slots; i++) {
+ MC_PROBE_BIT(mcc->free_table, cur + i, used);
+ if (used) {
+ /* the first used slot should be a record header, however we
+ * carefully check it is a valid header and hardfail if not */
+ rec = MC_SLOT_TO_PTR(mcc->data_table, cur + i, struct sss_mc_rec);
+ if (!sss_mc_is_valid_rec(mcc, rec)) {
+ /* this is a fatal error, the caller should probably just
+ * invalidate the whole cache */
+ return EFAULT;
+ }
+ /* next loop skip the whole record */
+ i += MC_SIZE_TO_SLOTS(rec->len) - 1;
+
+ /* finally invalidate record completely */
+ sss_mc_invalidate_rec(mcc, rec);
+ }
+ }
+
+ mcc->next_slot = cur + num_slots;
+ *free_slot = cur;
+ return EOK;
+}
+
+static errno_t sss_mc_get_strs_offset(struct sss_mc_ctx *mcc,
+ size_t *_offset)
+{
+ switch (mcc->type) {
+ case SSS_MC_PASSWD:
+ *_offset = offsetof(struct sss_mc_pwd_data, strs);
+ return EOK;
+ case SSS_MC_GROUP:
+ *_offset = offsetof(struct sss_mc_grp_data, strs);
+ return EOK;
+ case SSS_MC_INITGROUPS:
+ *_offset = offsetof(struct sss_mc_initgr_data, gids);
+ return EOK;
+ case SSS_MC_SID:
+ *_offset = offsetof(struct sss_mc_sid_data, sid);
+ return EOK;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unknown memory cache type.\n");
+ return EINVAL;
+ }
+}
+
+static errno_t sss_mc_get_strs_len(struct sss_mc_ctx *mcc,
+ struct sss_mc_rec *rec,
+ size_t *_len)
+{
+ switch (mcc->type) {
+ case SSS_MC_PASSWD:
+ *_len = ((struct sss_mc_pwd_data *)&rec->data)->strs_len;
+ return EOK;
+ case SSS_MC_GROUP:
+ *_len = ((struct sss_mc_grp_data *)&rec->data)->strs_len;
+ return EOK;
+ case SSS_MC_INITGROUPS:
+ *_len = ((struct sss_mc_initgr_data *)&rec->data)->data_len;
+ return EOK;
+ case SSS_MC_SID:
+ *_len = ((struct sss_mc_sid_data *)&rec->data)->sid_len;
+ return EOK;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unknown memory cache type.\n");
+ return EINVAL;
+ }
+}
+
+static struct sss_mc_rec *sss_mc_find_record(struct sss_mc_ctx *mcc,
+ const struct sized_string *key)
+{
+ struct sss_mc_rec *rec = NULL;
+ uint32_t hash;
+ uint32_t slot;
+ rel_ptr_t name_ptr;
+ char *t_key;
+ size_t strs_offset;
+ size_t strs_len;
+ uint8_t *max_addr;
+ errno_t ret;
+
+ hash = sss_mc_hash(mcc, key->str, key->len);
+
+ slot = mcc->hash_table[hash];
+ if (!MC_SLOT_WITHIN_BOUNDS(slot, mcc->dt_size)) {
+ return NULL;
+ }
+
+ /* Get max address of data table. */
+ max_addr = mcc->data_table + mcc->dt_size;
+
+ ret = sss_mc_get_strs_offset(mcc, &strs_offset);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ while (slot != MC_INVALID_VAL) {
+ if (!MC_SLOT_WITHIN_BOUNDS(slot, mcc->dt_size)) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Corrupted memcache. Slot number too big.\n");
+ sss_mc_save_corrupted(mcc);
+ sss_mmap_cache_reset(mcc);
+ return NULL;
+ }
+
+ rec = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ ret = sss_mc_get_strs_len(mcc, rec, &strs_len);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ if (key->len > strs_len) {
+ /* The string cannot be in current record */
+ slot = sss_mc_next_slot_with_hash(rec, hash);
+ continue;
+ }
+
+ safealign_memcpy(&name_ptr, rec->data, sizeof(rel_ptr_t), NULL);
+ t_key = (char *)rec->data + name_ptr;
+ /* name_ptr must point to some data in the strs/gids area of the data
+ * payload. Since it is a pointer relative to rec->data it must be
+ * larger/equal to strs_offset and must be smaller then strs_offset + strs_len.
+ * Additionally the area must not end outside of the data table and
+ * t_key must be a zero-terminated string. */
+ if (name_ptr < strs_offset
+ || name_ptr >= strs_offset + strs_len
+ || (uint8_t *)rec->data > max_addr
+ || strs_offset > max_addr - (uint8_t *)rec->data
+ || strs_len > max_addr - (uint8_t *)rec->data - strs_offset) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Corrupted memcache entry at slot %u. "
+ "name_ptr value is %u.\n", slot, name_ptr);
+ sss_mc_save_corrupted(mcc);
+ sss_mmap_cache_reset(mcc);
+ return NULL;
+ }
+
+ if (strcmp(key->str, t_key) == 0) {
+ return rec;
+ }
+
+ slot = sss_mc_next_slot_with_hash(rec, hash);
+ }
+
+ return NULL;
+}
+
+static errno_t sss_mc_get_record(struct sss_mc_ctx **_mcc,
+ size_t rec_len,
+ const struct sized_string *key,
+ struct sss_mc_rec **_rec)
+{
+ struct sss_mc_ctx *mcc = *_mcc;
+ struct sss_mc_rec *old_rec = NULL;
+ struct sss_mc_rec *rec;
+ int old_slots;
+ int num_slots;
+ uint32_t base_slot;
+ errno_t ret;
+ int i;
+
+ num_slots = MC_SIZE_TO_SLOTS(rec_len);
+
+ old_rec = sss_mc_find_record(mcc, key);
+ if (old_rec) {
+ old_slots = MC_SIZE_TO_SLOTS(old_rec->len);
+
+ if (old_slots == num_slots) {
+ *_rec = old_rec;
+ return EOK;
+ }
+
+ /* slot size changed, invalidate record and fall through to get a
+ * fully new record */
+ sss_mc_invalidate_rec(mcc, old_rec);
+ }
+
+ /* we are going to use more space, find enough free slots */
+ ret = sss_mc_find_free_slots(mcc, num_slots, &base_slot);
+ if (ret != EOK) {
+ if (ret == EFAULT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Fatal internal mmap cache error, invalidating cache!\n");
+ (void)sss_mmap_cache_reinit(talloc_parent(mcc),
+ -1, -1, -1, -1,
+ _mcc);
+ }
+ return ret;
+ }
+
+ rec = MC_SLOT_TO_PTR(mcc->data_table, base_slot, struct sss_mc_rec);
+
+ /* mark as not valid yet */
+ MC_RAISE_INVALID_BARRIER(rec);
+ rec->len = rec_len;
+ rec->next1 = MC_INVALID_VAL;
+ rec->next2 = MC_INVALID_VAL;
+ rec->padding = MC_INVALID_VAL;
+ MC_LOWER_BARRIER(rec);
+
+ /* and now mark slots as used */
+ for (i = 0; i < num_slots; i++) {
+ MC_SET_BIT(mcc->free_table, base_slot + i);
+ }
+
+ *_rec = rec;
+ return EOK;
+}
+
+static inline void sss_mmap_set_rec_header(struct sss_mc_ctx *mcc,
+ struct sss_mc_rec *rec,
+ size_t len, time_t ttl,
+ const char *key1, size_t key1_len,
+ const char *key2, size_t key2_len)
+{
+ rec->len = len;
+ rec->expire = time(NULL) + ttl;
+ rec->hash1 = sss_mc_hash(mcc, key1, key1_len);
+ rec->hash2 = sss_mc_hash(mcc, key2, key2_len);
+}
+
+static inline void sss_mmap_chain_in_rec(struct sss_mc_ctx *mcc,
+ struct sss_mc_rec *rec)
+{
+ /* name first */
+ sss_mc_add_rec_to_chain(mcc, rec, rec->hash1);
+ /* then uid/gid */
+ sss_mc_add_rec_to_chain(mcc, rec, rec->hash2);
+}
+
+/***************************************************************************
+ * generic invalidation
+ ***************************************************************************/
+
+static errno_t sss_mmap_cache_validate_or_reinit(struct sss_mc_ctx **_mcc);
+
+static errno_t sss_mmap_cache_invalidate(struct sss_mc_ctx **_mcc,
+ const struct sized_string *key)
+{
+ struct sss_mc_ctx *mcc;
+ struct sss_mc_rec *rec;
+ int ret;
+
+ ret = sss_mmap_cache_validate_or_reinit(_mcc);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ mcc = *_mcc;
+
+ rec = sss_mc_find_record(mcc, key);
+ if (rec == NULL) {
+ /* nothing to invalidate */
+ return ENOENT;
+ }
+
+ sss_mc_invalidate_rec(mcc, rec);
+
+ return EOK;
+}
+
+static errno_t sss_mmap_cache_validate_or_reinit(struct sss_mc_ctx **_mcc)
+{
+ struct sss_mc_ctx *mcc = *_mcc;
+ struct stat fdstat;
+ bool reinit = false;
+ errno_t ret;
+
+ /* No mcc initialized? Memory cache may be disabled. */
+ if (mcc == NULL || mcc->fd < 0) {
+ ret = EINVAL;
+ reinit = false;
+ goto done;
+ }
+
+ if (fstat(mcc->fd, &fdstat) == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to stat memory cache [file=%s, fd=%d] [%d]: %s\n",
+ mcc->file, mcc->fd, ret, sss_strerror(ret));
+ reinit = true;
+ goto done;
+ }
+
+ if (fdstat.st_nlink == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Memory cache file was removed\n");
+ ret = ENOENT;
+ reinit = true;
+ goto done;
+ }
+
+ if (fdstat.st_size != mcc->mmap_size) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Memory cache is corrupted, invalid size [file=%s, fd=%d, "
+ "expected_size=%zu, real_size=%zu]\n",
+ mcc->file, mcc->fd, mcc->mmap_size, fdstat.st_size);
+ ret = EINVAL;
+ reinit = true;
+ goto done;
+ }
+
+ ret = EOK;
+ reinit = false;
+
+done:
+ if (reinit) {
+ return sss_mmap_cache_reinit(talloc_parent(mcc), -1, -1, -1, -1, _mcc);
+ }
+
+ return ret;
+}
+
+/***************************************************************************
+ * passwd map
+ ***************************************************************************/
+
+errno_t sss_mmap_cache_pw_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name,
+ const struct sized_string *pw,
+ uid_t uid, gid_t gid,
+ const struct sized_string *gecos,
+ const struct sized_string *homedir,
+ const struct sized_string *shell)
+{
+ struct sss_mc_ctx *mcc;
+ struct sss_mc_rec *rec;
+ struct sss_mc_pwd_data *data;
+ struct sized_string uidkey;
+ char uidstr[11];
+ size_t data_len;
+ size_t rec_len;
+ size_t pos;
+ int ret;
+
+ ret = sss_mmap_cache_validate_or_reinit(_mcc);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ mcc = *_mcc;
+
+ ret = snprintf(uidstr, 11, "%ld", (long)uid);
+ if (ret > 10) {
+ return EINVAL;
+ }
+ to_sized_string(&uidkey, uidstr);
+
+ data_len = name->len + pw->len + gecos->len + homedir->len + shell->len;
+ rec_len = sizeof(struct sss_mc_rec) +
+ sizeof(struct sss_mc_pwd_data) +
+ data_len;
+ if (rec_len > mcc->dt_size) {
+ return ENOMEM;
+ }
+
+ ret = sss_mc_get_record(_mcc, rec_len, name, &rec);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ data = (struct sss_mc_pwd_data *)rec->data;
+ pos = 0;
+
+ MC_RAISE_BARRIER(rec);
+
+ /* header */
+ sss_mmap_set_rec_header(mcc, rec, rec_len, mcc->valid_time_slot,
+ name->str, name->len, uidkey.str, uidkey.len);
+
+ /* passwd struct */
+ data->name = MC_PTR_DIFF(data->strs, data);
+ data->uid = uid;
+ data->gid = gid;
+ data->strs_len = data_len;
+ memcpy(&data->strs[pos], name->str, name->len);
+ pos += name->len;
+ memcpy(&data->strs[pos], pw->str, pw->len);
+ pos += pw->len;
+ memcpy(&data->strs[pos], gecos->str, gecos->len);
+ pos += gecos->len;
+ memcpy(&data->strs[pos], homedir->str, homedir->len);
+ pos += homedir->len;
+ memcpy(&data->strs[pos], shell->str, shell->len);
+
+ MC_LOWER_BARRIER(rec);
+
+ /* finally chain the rec in the hash table */
+ sss_mmap_chain_in_rec(mcc, rec);
+
+ return EOK;
+}
+
+errno_t sss_mmap_cache_pw_invalidate(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name)
+{
+ return sss_mmap_cache_invalidate(_mcc, name);
+}
+
+errno_t sss_mmap_cache_pw_invalidate_uid(struct sss_mc_ctx **_mcc, uid_t uid)
+{
+ struct sss_mc_ctx *mcc;
+ struct sss_mc_rec *rec = NULL;
+ struct sss_mc_pwd_data *data;
+ uint32_t hash;
+ uint32_t slot;
+ char *uidstr;
+ errno_t ret;
+
+ ret = sss_mmap_cache_validate_or_reinit(_mcc);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ mcc = *_mcc;
+
+ uidstr = talloc_asprintf(NULL, "%ld", (long)uid);
+ if (!uidstr) {
+ return ENOMEM;
+ }
+
+ hash = sss_mc_hash(mcc, uidstr, strlen(uidstr) + 1);
+
+ slot = mcc->hash_table[hash];
+ if (!MC_SLOT_WITHIN_BOUNDS(slot, mcc->dt_size)) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ while (slot != MC_INVALID_VAL) {
+ if (!MC_SLOT_WITHIN_BOUNDS(slot, mcc->dt_size)) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Corrupted memcache.\n");
+ sss_mc_save_corrupted(mcc);
+ sss_mmap_cache_reset(mcc);
+ ret = ENOENT;
+ goto done;
+ }
+
+ rec = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ data = (struct sss_mc_pwd_data *)(&rec->data);
+
+ if (uid == data->uid) {
+ break;
+ }
+
+ slot = sss_mc_next_slot_with_hash(rec, hash);
+ }
+
+ if (slot == MC_INVALID_VAL) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ sss_mc_invalidate_rec(mcc, rec);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(uidstr);
+ return ret;
+}
+
+/***************************************************************************
+ * group map
+ ***************************************************************************/
+
+int sss_mmap_cache_gr_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name,
+ const struct sized_string *pw,
+ gid_t gid, size_t memnum,
+ const char *membuf, size_t memsize)
+{
+ struct sss_mc_ctx *mcc;
+ struct sss_mc_rec *rec;
+ struct sss_mc_grp_data *data;
+ struct sized_string gidkey;
+ char gidstr[11];
+ size_t data_len;
+ size_t rec_len;
+ size_t pos;
+ int ret;
+
+ ret = sss_mmap_cache_validate_or_reinit(_mcc);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ mcc = *_mcc;
+
+ ret = snprintf(gidstr, 11, "%ld", (long)gid);
+ if (ret > 10) {
+ return EINVAL;
+ }
+ to_sized_string(&gidkey, gidstr);
+
+ data_len = name->len + pw->len + memsize;
+ rec_len = sizeof(struct sss_mc_rec) +
+ sizeof(struct sss_mc_grp_data) +
+ data_len;
+ if (rec_len > mcc->dt_size) {
+ return ENOMEM;
+ }
+
+ ret = sss_mc_get_record(_mcc, rec_len, name, &rec);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ data = (struct sss_mc_grp_data *)rec->data;
+ pos = 0;
+
+ MC_RAISE_BARRIER(rec);
+
+ /* header */
+ sss_mmap_set_rec_header(mcc, rec, rec_len, mcc->valid_time_slot,
+ name->str, name->len, gidkey.str, gidkey.len);
+
+ /* group struct */
+ data->name = MC_PTR_DIFF(data->strs, data);
+ data->gid = gid;
+ data->members = memnum;
+ data->strs_len = data_len;
+ memcpy(&data->strs[pos], name->str, name->len);
+ pos += name->len;
+ memcpy(&data->strs[pos], pw->str, pw->len);
+ pos += pw->len;
+ memcpy(&data->strs[pos], membuf, memsize);
+
+ MC_LOWER_BARRIER(rec);
+
+ /* finally chain the rec in the hash table */
+ sss_mmap_chain_in_rec(mcc, rec);
+
+ return EOK;
+}
+
+errno_t sss_mmap_cache_gr_invalidate(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name)
+{
+ return sss_mmap_cache_invalidate(_mcc, name);
+}
+
+errno_t sss_mmap_cache_gr_invalidate_gid(struct sss_mc_ctx **_mcc, gid_t gid)
+{
+ struct sss_mc_ctx *mcc;
+ struct sss_mc_rec *rec = NULL;
+ struct sss_mc_grp_data *data;
+ uint32_t hash;
+ uint32_t slot;
+ char *gidstr;
+ errno_t ret;
+
+ ret = sss_mmap_cache_validate_or_reinit(_mcc);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ mcc = *_mcc;
+
+ gidstr = talloc_asprintf(NULL, "%ld", (long)gid);
+ if (!gidstr) {
+ return ENOMEM;
+ }
+
+ hash = sss_mc_hash(mcc, gidstr, strlen(gidstr) + 1);
+
+ slot = mcc->hash_table[hash];
+ if (!MC_SLOT_WITHIN_BOUNDS(slot, mcc->dt_size)) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ while (slot != MC_INVALID_VAL) {
+ if (!MC_SLOT_WITHIN_BOUNDS(slot, mcc->dt_size)) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Corrupted memcache.\n");
+ sss_mc_save_corrupted(mcc);
+ sss_mmap_cache_reset(mcc);
+ ret = ENOENT;
+ goto done;
+ }
+
+ rec = MC_SLOT_TO_PTR(mcc->data_table, slot, struct sss_mc_rec);
+ data = (struct sss_mc_grp_data *)(&rec->data);
+
+ if (gid == data->gid) {
+ break;
+ }
+
+ slot = sss_mc_next_slot_with_hash(rec, hash);
+ }
+
+ if (slot == MC_INVALID_VAL) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ sss_mc_invalidate_rec(mcc, rec);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(gidstr);
+ return ret;
+}
+
+errno_t sss_mmap_cache_initgr_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name,
+ const struct sized_string *unique_name,
+ uint32_t num_groups,
+ const uint8_t *gids_buf)
+{
+ struct sss_mc_ctx *mcc;
+ struct sss_mc_rec *rec;
+ struct sss_mc_initgr_data *data;
+ size_t data_len;
+ size_t rec_len;
+ size_t pos;
+ int ret;
+
+ ret = sss_mmap_cache_validate_or_reinit(_mcc);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ mcc = *_mcc;
+
+ /* array of gids + name + unique_name */
+ data_len = num_groups * sizeof(uint32_t) + name->len + unique_name->len;
+ rec_len = sizeof(struct sss_mc_rec) + sizeof(struct sss_mc_initgr_data)
+ + data_len;
+ if (rec_len > mcc->dt_size) {
+ return ENOMEM;
+ }
+
+ /* use unique name for searching potential old records */
+ ret = sss_mc_get_record(_mcc, rec_len, unique_name, &rec);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ data = (struct sss_mc_initgr_data *)rec->data;
+ pos = 0;
+
+ MC_RAISE_BARRIER(rec);
+
+ sss_mmap_set_rec_header(mcc, rec, rec_len, mcc->valid_time_slot,
+ name->str, name->len,
+ unique_name->str, unique_name->len);
+
+ /* initgroups struct */
+ data->strs_len = name->len + unique_name->len;
+ data->data_len = data_len;
+ data->num_groups = num_groups;
+ memcpy((char *)data->gids + pos, gids_buf, num_groups * sizeof(uint32_t));
+ pos += num_groups * sizeof(uint32_t);
+
+ memcpy((char *)data->gids + pos, unique_name->str, unique_name->len);
+ data->strs = data->unique_name = MC_PTR_DIFF((char *)data->gids + pos, data);
+ pos += unique_name->len;
+
+ memcpy((char *)data->gids + pos, name->str, name->len);
+ data->name = MC_PTR_DIFF((char *)data->gids + pos, data);
+
+ MC_LOWER_BARRIER(rec);
+
+ /* finally chain the rec in the hash table */
+ sss_mmap_chain_in_rec(mcc, rec);
+
+ return EOK;
+}
+
+errno_t sss_mmap_cache_initgr_invalidate(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name)
+{
+ return sss_mmap_cache_invalidate(_mcc, name);
+}
+
+errno_t sss_mmap_cache_sid_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *sid,
+ uint32_t id,
+ uint32_t type,
+ bool explicit_lookup)
+{
+ struct sss_mc_ctx *mcc;
+ struct sss_mc_rec *rec;
+ struct sss_mc_sid_data *data;
+ char idkey[16];
+ size_t rec_len;
+ int ret;
+
+ ret = sss_mmap_cache_validate_or_reinit(_mcc);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ mcc = *_mcc;
+
+ ret = snprintf(idkey, sizeof(idkey), "%d-%ld",
+ (type == SSS_ID_TYPE_GID) ? SSS_ID_TYPE_GID : SSS_ID_TYPE_UID,
+ (long)id);
+ if (ret > (sizeof(idkey) - 1)) {
+ return EINVAL;
+ }
+
+ rec_len = sizeof(struct sss_mc_rec) +
+ sizeof(struct sss_mc_sid_data) +
+ sid->len;
+
+ ret = sss_mc_get_record(_mcc, rec_len, sid, &rec);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ data = (struct sss_mc_sid_data *)rec->data;
+ MC_RAISE_BARRIER(rec);
+
+ sss_mmap_set_rec_header(mcc, rec, rec_len, mcc->valid_time_slot,
+ sid->str, sid->len, idkey, strlen(idkey) + 1);
+
+ data->name = MC_PTR_DIFF(data->sid, data);
+ data->type = type;
+ data->id = id;
+ data->populated_by = (explicit_lookup ? 1 : 0);
+ data->sid_len = sid->len;
+ memcpy(data->sid, sid->str, sid->len);
+
+ MC_LOWER_BARRIER(rec);
+ sss_mmap_chain_in_rec(mcc, rec);
+
+ return EOK;
+}
+
+/***************************************************************************
+ * initialization
+ ***************************************************************************/
+
+/* Copy of sss_mc_set_recycled is present in the src/tools/tools_mc_util.c.
+ * If you modify this function, you should modify the duplicated function
+ * too. */
+static errno_t sss_mc_set_recycled(int fd)
+{
+ uint32_t w = SSS_MC_HEADER_RECYCLED;
+ off_t offset;
+ off_t pos;
+ ssize_t written;
+
+ offset = offsetof(struct sss_mc_header, status);
+
+ pos = lseek(fd, offset, SEEK_SET);
+ if (pos == -1) {
+ /* What do we do now? */
+ return errno;
+ }
+
+ errno = 0;
+ written = sss_atomic_write_s(fd, (uint8_t *)&w, sizeof(w));
+ if (written == -1) {
+ return errno;
+ }
+
+ if (written != sizeof(w)) {
+ /* Write error */
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static void sss_mc_destroy_file(const char *filename)
+{
+ const useconds_t t = 50000;
+ const int retries = 3;
+ int ofd;
+ int ret;
+
+ ofd = open(filename, O_RDWR);
+ if (ofd != -1) {
+ ret = sss_br_lock_file(ofd, 0, 1, retries, t);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to lock file %s.\n", filename);
+ }
+ ret = sss_mc_set_recycled(ofd);
+ if (ret) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to mark mmap file %s as"
+ " recycled: %d (%s)\n",
+ filename, ret, strerror(ret));
+ }
+ close(ofd);
+ } else if (errno != ENOENT) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to open old memory cache file %s: %d (%s)\n",
+ filename, ret, strerror(ret));
+ }
+
+ errno = 0;
+ ret = unlink(filename);
+ if (ret == -1 && errno != ENOENT) {
+ ret = errno;
+ DEBUG(SSSDBG_TRACE_FUNC, "Failed to delete mmap file %s: %d (%s)\n",
+ filename, ret, strerror(ret));
+ }
+}
+
+static errno_t sss_mc_create_file(struct sss_mc_ctx *mc_ctx)
+{
+ const useconds_t t = 50000;
+ const int retries = 3;
+ mode_t old_mask;
+ int ret, uret;
+
+ /* temporarily relax umask as we need the file to be readable
+ * by everyone for now */
+ old_mask = umask(0022);
+
+ errno = 0;
+ mc_ctx->fd = open(mc_ctx->file, O_CREAT | O_EXCL | O_RDWR, 0644);
+ umask(old_mask);
+ if (mc_ctx->fd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to open mmap file %s: %d(%s)\n",
+ mc_ctx->file, ret, strerror(ret));
+ return ret;
+ }
+
+ /* Make sure that the memory cache files are chowned to sssd.sssd even
+ * if the nss responder runs as root. This is because the specfile
+ * has the ownership recorded as sssd.sssd
+ */
+ ret = fchown(mc_ctx->fd, mc_ctx->uid, mc_ctx->gid);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to chown mmap file %s: %d(%s)\n",
+ mc_ctx->file, ret, strerror(ret));
+ return ret;
+ }
+
+ ret = fchmod(mc_ctx->fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to chmod mmap file %s: %d(%s)\n",
+ mc_ctx->file, ret, strerror(ret));
+ return ret;
+ }
+
+ ret = sss_br_lock_file(mc_ctx->fd, 0, 1, retries, t);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to lock file %s.\n", mc_ctx->file);
+ close(mc_ctx->fd);
+ mc_ctx->fd = -1;
+
+ /* Report on unlink failures but don't overwrite the errno
+ * from sss_br_lock_file
+ */
+ errno = 0;
+ uret = unlink(mc_ctx->file);
+ if (uret == -1) {
+ uret = errno;
+ DEBUG(SSSDBG_TRACE_FUNC, "Failed to rm mmap file %s: %d(%s)\n",
+ mc_ctx->file, uret, strerror(uret));
+ }
+
+ return ret;
+ }
+
+ return ret;
+}
+
+static void sss_mc_header_update(struct sss_mc_ctx *mc_ctx, int status)
+{
+ struct sss_mc_header *h;
+
+ /* update header using barriers */
+ h = (struct sss_mc_header *)mc_ctx->mmap_base;
+ MC_RAISE_BARRIER(h);
+ if (status == SSS_MC_HEADER_ALIVE) {
+ /* no reason to update anything else if the file is recycled or
+ * right before reset */
+ h->hash_table = MC_PTR_DIFF(mc_ctx->hash_table, mc_ctx->mmap_base);
+ h->free_table = MC_PTR_DIFF(mc_ctx->free_table, mc_ctx->mmap_base);
+ h->data_table = MC_PTR_DIFF(mc_ctx->data_table, mc_ctx->mmap_base);
+ h->ht_size = mc_ctx->ht_size;
+ h->ft_size = mc_ctx->ft_size;
+ h->dt_size = mc_ctx->dt_size;
+ h->major_vno = SSS_MC_MAJOR_VNO;
+ h->minor_vno = SSS_MC_MINOR_VNO;
+ h->seed = mc_ctx->seed;
+ h->reserved = 0;
+ }
+ h->status = status;
+ MC_LOWER_BARRIER(h);
+}
+
+static int mc_ctx_destructor(struct sss_mc_ctx *mc_ctx)
+{
+ int ret;
+
+ /* Print debug message to logs if munmap() or close()
+ * fail but always return 0 */
+
+ if (mc_ctx->mmap_base != NULL) {
+ ret = munmap(mc_ctx->mmap_base, mc_ctx->mmap_size);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to unmap old memory cache file."
+ "[%d]: %s\n", ret, strerror(ret));
+ }
+ }
+
+ if (mc_ctx->fd != -1) {
+ ret = close(mc_ctx->fd);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to close old memory cache file."
+ "[%d]: %s\n", ret, strerror(ret));
+ }
+ }
+
+ return 0;
+}
+
+#define POSIX_FALLOCATE_ATTEMPTS 3
+
+errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
+ uid_t uid, gid_t gid,
+ enum sss_mc_type type, size_t n_elem,
+ time_t timeout, struct sss_mc_ctx **mcc)
+{
+ /* sss_mc_rec alone occupies whole slot,
+ * so each entry takes 2 slots at the very least
+ */
+ static const int PAYLOAD_FACTOR = 2;
+
+ struct sss_mc_ctx *mc_ctx = NULL;
+ int ret, dret;
+ char *filename;
+
+ filename = talloc_asprintf(mem_ctx, "%s/%s", SSS_NSS_MCACHE_DIR, name);
+ if (!filename) {
+ return ENOMEM;
+ }
+ /*
+ * First of all mark the current file as recycled
+ * and unlink so active clients will abandon its use ASAP
+ */
+ sss_mc_destroy_file(filename);
+
+ if ((timeout == 0) || (n_elem == 0)) {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Fast '%s' mmap cache is explicitly DISABLED\n",
+ mc_type_to_str(type));
+ *mcc = NULL;
+ return EOK;
+ }
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Fast '%s' mmap cache: memcache_timeout = %"SPRItime", slots = %zu\n",
+ mc_type_to_str(type), timeout, n_elem);
+
+ mc_ctx = talloc_zero(mem_ctx, struct sss_mc_ctx);
+ if (!mc_ctx) {
+ talloc_free(filename);
+ return ENOMEM;
+ }
+ mc_ctx->fd = -1;
+ talloc_set_destructor(mc_ctx, mc_ctx_destructor);
+
+ mc_ctx->name = talloc_strdup(mc_ctx, name);
+ if (!mc_ctx->name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ mc_ctx->uid = uid;
+ mc_ctx->gid = gid;
+
+ mc_ctx->type = type;
+
+ mc_ctx->valid_time_slot = timeout;
+
+ mc_ctx->file = talloc_steal(mc_ctx, filename);
+
+ /* elements must always be multiple of 8 to make things easier to handle,
+ * so we increase by the necessary amount if they are not a multiple */
+ /* We can use MC_ALIGN64 for this */
+ n_elem = MC_ALIGN64(n_elem);
+
+ /* hash table is double the size because it will store both forward and
+ * reverse keys (name/uid, name/gid, ..) */
+ mc_ctx->ht_size = MC_HT_SIZE(2 * n_elem / PAYLOAD_FACTOR);
+ mc_ctx->dt_size = n_elem * MC_SLOT_SIZE;
+ mc_ctx->ft_size = n_elem / 8; /* 1 bit per slot */
+ mc_ctx->mmap_size = MC_HEADER_SIZE +
+ MC_ALIGN64(mc_ctx->dt_size) +
+ MC_ALIGN64(mc_ctx->ft_size) +
+ MC_ALIGN64(mc_ctx->ht_size);
+
+
+ ret = sss_mc_create_file(mc_ctx);
+ if (ret) {
+ goto done;
+ }
+
+ /* Attempt allocation several times, in case of EINTR */
+ for (int i = 0; i < POSIX_FALLOCATE_ATTEMPTS; i++) {
+ ret = posix_fallocate(mc_ctx->fd, 0, mc_ctx->mmap_size);
+ if (ret != EINTR)
+ break;
+ }
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate file %s: %d(%s)\n",
+ mc_ctx->file, ret, strerror(ret));
+ goto done;
+ }
+
+ mc_ctx->mmap_base = mmap(NULL, mc_ctx->mmap_size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, mc_ctx->fd, 0);
+ if (mc_ctx->mmap_base == MAP_FAILED) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to mmap file %s(%zu): %d(%s)\n",
+ mc_ctx->file, mc_ctx->mmap_size,
+ ret, strerror(ret));
+ goto done;
+ }
+
+ mc_ctx->data_table = MC_PTR_ADD(mc_ctx->mmap_base, MC_HEADER_SIZE);
+ mc_ctx->free_table = MC_PTR_ADD(mc_ctx->data_table,
+ MC_ALIGN64(mc_ctx->dt_size));
+ mc_ctx->hash_table = MC_PTR_ADD(mc_ctx->free_table,
+ MC_ALIGN64(mc_ctx->ft_size));
+
+ memset(mc_ctx->data_table, 0xff, mc_ctx->dt_size);
+ memset(mc_ctx->free_table, 0x00, mc_ctx->ft_size);
+ memset(mc_ctx->hash_table, 0xff, mc_ctx->ht_size);
+
+ /* generate a pseudo-random seed.
+ * Needed to fend off dictionary based collision attacks */
+ ret = sss_generate_csprng_buffer((uint8_t *)&mc_ctx->seed, sizeof(mc_ctx->seed));
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_mc_header_update(mc_ctx, SSS_MC_HEADER_ALIVE);
+
+ ret = EOK;
+
+done:
+ if (ret) {
+ /* Closing the file descriptor and unmapping the file
+ * from memory is done in the mc_ctx_destructor. */
+ if (mc_ctx && mc_ctx->file && mc_ctx->fd != -1) {
+ dret = unlink(mc_ctx->file);
+ if (dret == -1) {
+ dret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to rm mmap file %s: %d(%s)\n", mc_ctx->file,
+ dret, strerror(dret));
+ }
+ }
+
+ talloc_free(mc_ctx);
+ } else {
+ *mcc = mc_ctx;
+ }
+ return ret;
+}
+
+errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx,
+ uid_t uid, gid_t gid,
+ size_t n_elem,
+ time_t timeout, struct sss_mc_ctx **mc_ctx)
+{
+ errno_t ret;
+ TALLOC_CTX* tmp_ctx = NULL;
+ char *name;
+ enum sss_mc_type type;
+
+ if (mc_ctx == NULL || (*mc_ctx) == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to re-init uninitialized memory cache.\n");
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
+ return ENOMEM;
+ }
+
+ name = talloc_strdup(tmp_ctx, (*mc_ctx)->name);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ type = (*mc_ctx)->type;
+
+ if (n_elem == (size_t)-1) {
+ n_elem = (*mc_ctx)->ft_size * 8;
+ }
+
+ if (timeout == (time_t)-1) {
+ timeout = (*mc_ctx)->valid_time_slot;
+ }
+
+ if (uid == (uid_t)-1) {
+ uid = (*mc_ctx)->uid;
+ }
+
+ if (gid == (gid_t)-1) {
+ gid = (*mc_ctx)->gid;
+ }
+
+ talloc_free(*mc_ctx);
+
+ /* make sure we do not leave a potentially freed pointer around */
+ *mc_ctx = NULL;
+
+ ret = sss_mmap_cache_init(mem_ctx,
+ name,
+ uid, gid,
+ type,
+ n_elem,
+ timeout,
+ mc_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to re-initialize mmap cache.\n");
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* Erase all contents of the mmap cache. This will bring the cache
+ * to the same state as if it was just initialized. */
+void sss_mmap_cache_reset(struct sss_mc_ctx *mc_ctx)
+{
+ if (mc_ctx == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Fastcache not initialized. Nothing to do.\n");
+ return;
+ }
+
+ sss_mc_header_update(mc_ctx, SSS_MC_HEADER_UNINIT);
+
+ /* Reset the mmapped area */
+ memset(mc_ctx->data_table, 0xff, mc_ctx->dt_size);
+ memset(mc_ctx->free_table, 0x00, mc_ctx->ft_size);
+ memset(mc_ctx->hash_table, 0xff, mc_ctx->ht_size);
+
+ sss_mc_header_update(mc_ctx, SSS_MC_HEADER_ALIVE);
+}
diff --git a/src/responder/nss/nsssrv_mmap_cache.h b/src/responder/nss/nsssrv_mmap_cache.h
new file mode 100644
index 0000000..28ee5ad
--- /dev/null
+++ b/src/responder/nss/nsssrv_mmap_cache.h
@@ -0,0 +1,86 @@
+/*
+ SSSD
+
+ NSS Responder - Mmap Cache
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2011
+
+ 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 _NSSSRV_MMAP_CACHE_H_
+#define _NSSSRV_MMAP_CACHE_H_
+
+struct sss_mc_ctx;
+
+enum sss_mc_type {
+ SSS_MC_NONE = 0,
+ SSS_MC_PASSWD,
+ SSS_MC_GROUP,
+ SSS_MC_INITGROUPS,
+ SSS_MC_SID,
+};
+
+errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
+ uid_t uid, gid_t gid,
+ enum sss_mc_type type, size_t n_elem,
+ time_t valid_time, struct sss_mc_ctx **mcc);
+
+errno_t sss_mmap_cache_pw_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name,
+ const struct sized_string *pw,
+ uid_t uid, gid_t gid,
+ const struct sized_string *gecos,
+ const struct sized_string *homedir,
+ const struct sized_string *shell);
+
+errno_t sss_mmap_cache_gr_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name,
+ const struct sized_string *pw,
+ gid_t gid, size_t memnum,
+ const char *membuf, size_t memsize);
+
+errno_t sss_mmap_cache_initgr_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name,
+ const struct sized_string *unique_name,
+ uint32_t num_groups,
+ const uint8_t *gids_buf);
+
+errno_t sss_mmap_cache_sid_store(struct sss_mc_ctx **_mcc,
+ const struct sized_string *sid,
+ uint32_t id,
+ uint32_t type, /* enum sss_id_type*/
+ bool explicit_lookup); /* false ~ by_id(), true ~ by_uid/gid() */
+
+errno_t sss_mmap_cache_pw_invalidate(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name);
+
+errno_t sss_mmap_cache_pw_invalidate_uid(struct sss_mc_ctx **_mcc, uid_t uid);
+
+errno_t sss_mmap_cache_gr_invalidate(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name);
+
+errno_t sss_mmap_cache_gr_invalidate_gid(struct sss_mc_ctx **_mcc, gid_t gid);
+
+errno_t sss_mmap_cache_initgr_invalidate(struct sss_mc_ctx **_mcc,
+ const struct sized_string *name);
+
+errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx,
+ uid_t uid, gid_t gid,
+ size_t n_elem,
+ time_t timeout, struct sss_mc_ctx **mc_ctx);
+
+void sss_mmap_cache_reset(struct sss_mc_ctx *mc_ctx);
+
+#endif /* _NSSSRV_MMAP_CACHE_H_ */
diff --git a/src/responder/pac/pacsrv.c b/src/responder/pac/pacsrv.c
new file mode 100644
index 0000000..8d3db9d
--- /dev/null
+++ b/src/responder/pac/pacsrv.c
@@ -0,0 +1,230 @@
+/*
+ SSSD
+
+ PAC Responder
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <popt.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "responder/pac/pacsrv.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "providers/data_provider.h"
+#include "util/util_sss_idmap.h"
+#include "sss_iface/sss_iface_async.h"
+
+#define SSS_PAC_PIPE_NAME "pac"
+#define DEFAULT_PAC_FD_LIMIT 8192
+#define DEFAULT_ALLOWED_UIDS "0"
+
+int pac_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *pac_cmds;
+ struct pac_ctx *pac_ctx;
+ int ret;
+ enum idmap_error_code err;
+ int fd_limit;
+ char *uid_str;
+
+ pac_cmds = get_pac_cmds();
+
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ pac_cmds,
+ SSS_PAC_SOCKET_NAME, -1, NULL, -1,
+ CONFDB_PAC_CONF_ENTRY,
+ SSS_BUS_PAC, PAC_SBUS_SERVICE_NAME,
+ sss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ pac_ctx = talloc_zero(rctx, struct pac_ctx);
+ if (!pac_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing pac_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ pac_ctx->rctx = rctx;
+ pac_ctx->rctx->pvt_ctx = pac_ctx;
+
+ ret = confdb_get_string(pac_ctx->rctx->cdb, pac_ctx->rctx,
+ CONFDB_PAC_CONF_ENTRY, CONFDB_SERVICE_ALLOWED_UIDS,
+ DEFAULT_ALLOWED_UIDS, &uid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get allowed UIDs.\n");
+ goto fail;
+ }
+
+ ret = csv_string_to_uid_array(pac_ctx->rctx, uid_str,
+ &pac_ctx->rctx->allowed_uids_count,
+ &pac_ctx->rctx->allowed_uids);
+ talloc_free(uid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set allowed UIDs.\n");
+ goto fail;
+ }
+
+ err = sss_idmap_init(sss_idmap_talloc, pac_ctx, sss_idmap_talloc_free,
+ &pac_ctx->idmap_ctx);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_idmap_init failed.\n");
+ ret = EFAULT;
+ goto fail;
+ }
+
+ /* Set up file descriptor limits */
+ ret = confdb_get_int(pac_ctx->rctx->cdb,
+ CONFDB_PAC_CONF_ENTRY,
+ CONFDB_SERVICE_FD_LIMIT,
+ DEFAULT_PAC_FD_LIMIT,
+ &fd_limit);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to set up file descriptor limit\n");
+ goto fail;
+ }
+ responder_set_fd_limit(fd_limit);
+
+ ret = confdb_get_int(pac_ctx->rctx->cdb, CONFDB_PAC_CONF_ENTRY,
+ CONFDB_PAC_LIFETIME, 300,
+ &pac_ctx->pac_lifetime);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to setup pac lifetime timeout [%s].\n",
+ CONFDB_PAC_LIFETIME);
+ goto fail;
+ }
+
+ ret = get_pac_check_config(pac_ctx->rctx->cdb, &pac_ctx->pac_check_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get pac_check");
+ goto fail;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_PAC,
+ PAC_SBUS_SERVICE_NAME,
+ PAC_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = sss_resp_register_service_iface(rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "PAC Initialization complete\n");
+
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_pac";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ ret = server_setup("pac", true, 0, uid, gid,
+ CONFDB_PAC_CONF_ENTRY, &main_ctx, true);
+ if (ret != EOK) return 2;
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit when parent process does\n");
+ }
+
+ ret = pac_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) return 3;
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
diff --git a/src/responder/pac/pacsrv.h b/src/responder/pac/pacsrv.h
new file mode 100644
index 0000000..f3b8e45
--- /dev/null
+++ b/src/responder/pac/pacsrv.h
@@ -0,0 +1,42 @@
+/*
+ SSSD
+
+ PAC Responder, header file
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2011
+
+ 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 __PACSRV_H__
+#define __PACSRV_H__
+
+#include "config.h"
+
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "lib/idmap/sss_idmap.h"
+
+struct pac_ctx {
+ struct resp_ctx *rctx;
+ struct sss_idmap_ctx *idmap_ctx;
+ struct dom_sid *my_dom_sid;
+ struct local_mapping_ranges *range_map;
+ int pac_lifetime;
+ uint32_t pac_check_opts;
+};
+
+struct sss_cmd_table *get_pac_cmds(void);
+
+#endif /* __PACSRV_H__ */
diff --git a/src/responder/pac/pacsrv_cmd.c b/src/responder/pac/pacsrv_cmd.c
new file mode 100644
index 0000000..abfc2c9
--- /dev/null
+++ b/src/responder/pac/pacsrv_cmd.c
@@ -0,0 +1,311 @@
+/*
+ SSSD
+
+ PAC Responder
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2012, 2016
+ Jan Zeleny <jzeleny@redhat.com> 2012
+
+ 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 "util/util.h"
+#include "responder/pac/pacsrv.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "confdb/confdb.h"
+
+#include "providers/ad/ad_pac.h"
+
+static errno_t pac_cmd_done(struct cli_ctx *cctx, int cmd_ret)
+{
+ struct cli_protocol *pctx;
+ int ret;
+
+ if (cmd_ret == EAGAIN) {
+ /* async processing, just return here */
+ return EOK;
+ }
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_new failed [%d][%s].\n",
+ ret, strerror(ret));
+ return ret;
+ }
+
+ sss_packet_set_error(pctx->creq->out, cmd_ret);
+ if (cmd_ret != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Sending error [%d][%s].\n", cmd_ret,
+ sss_strerror(cmd_ret));
+ }
+
+ sss_cmd_done(cctx, NULL);
+
+ return EOK;
+}
+
+struct pac_req_ctx {
+ struct cli_ctx *cctx;
+ struct pac_ctx *pac_ctx;
+ const char *domain_name;
+ struct sss_domain_info *dom;
+
+ uint8_t *blob;
+ size_t blen;
+
+ struct PAC_LOGON_INFO *logon_info;
+ struct PAC_UPN_DNS_INFO *upn_dns_info;
+
+ char *user_sid_str;
+ char *user_dom_sid_str;
+};
+
+static errno_t pac_resolve_user_sid_next(struct pac_req_ctx *pr_ctx);
+static void pac_resolve_user_sid_done(struct tevent_req *req);
+static void pac_get_domains_done(struct tevent_req *req);
+
+static errno_t pac_add_pac_user(struct cli_ctx *cctx)
+{
+ int ret;
+ uint8_t *body;
+ size_t blen;
+ struct pac_req_ctx *pr_ctx;
+ struct tevent_req *req;
+ enum idmap_error_code err;
+ struct cli_protocol *pctx;
+
+ pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ pr_ctx = talloc_zero(cctx, struct pac_req_ctx);
+ if (pr_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ pr_ctx->cctx = cctx;
+ pr_ctx->blob = body;
+ pr_ctx->blen = blen;
+
+ pr_ctx->pac_ctx = talloc_get_type(cctx->rctx->pvt_ctx, struct pac_ctx);
+ if (pr_ctx->pac_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Cannot find pac responder context.\n");
+ return EINVAL;
+ }
+
+ ret = ad_get_data_from_pac(pr_ctx, pr_ctx->pac_ctx->pac_check_opts,
+ body, blen,
+ &pr_ctx->logon_info, &pr_ctx->upn_dns_info);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "ad_get_data_from_pac failed.\n");
+ goto done;
+ }
+
+ pr_ctx->domain_name = pr_ctx->logon_info->info3.base.logon_domain.string;
+ if (pr_ctx->domain_name == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "No domain name in PAC\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ err = sss_idmap_smb_sid_to_sid(pr_ctx->pac_ctx->idmap_ctx,
+ pr_ctx->logon_info->info3.base.domain_sid,
+ &pr_ctx->user_dom_sid_str);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_smb_sid_to_sid failed.\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ talloc_steal(pr_ctx, pr_ctx->user_dom_sid_str);
+
+ pr_ctx->user_sid_str = talloc_asprintf(pr_ctx, "%s-%"PRIu32,
+ pr_ctx->user_dom_sid_str,
+ pr_ctx->logon_info->info3.base.rid);
+ if (pr_ctx->user_sid_str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = responder_get_domain_by_id(cctx->rctx, pr_ctx->user_dom_sid_str,
+ &pr_ctx->dom);
+ if (ret == EAGAIN || ret == ENOENT) {
+ req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true,
+ pr_ctx->domain_name);
+ if (req == NULL) {
+ ret = ENOMEM;
+ } else {
+ tevent_req_set_callback(req, pac_get_domains_done, pr_ctx);
+ ret = EAGAIN;
+ }
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "responder_get_domain_by_id failed.\n");
+ goto done;
+ }
+
+ ret = pac_resolve_user_sid_next(pr_ctx);
+
+done:
+ if (ret != EAGAIN) {
+ talloc_free(pr_ctx);
+ }
+ return pac_cmd_done(cctx, ret);
+}
+
+static void pac_get_domains_done(struct tevent_req *req)
+{
+ struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req,
+ struct pac_req_ctx);
+ struct cli_ctx *cctx = pr_ctx->cctx;
+ int ret;
+
+ ret = sss_dp_get_domains_recv(req);
+ talloc_free(req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = responder_get_domain_by_id(cctx->rctx, pr_ctx->user_dom_sid_str,
+ &pr_ctx->dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Corresponding domain [%s] has not been "
+ "found\n", pr_ctx->user_dom_sid_str);
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = pac_resolve_user_sid_next(pr_ctx);
+
+done:
+ if (ret != EAGAIN) {
+ talloc_free(pr_ctx);
+ }
+ pac_cmd_done(cctx, ret);
+}
+
+static errno_t pac_resolve_user_sid_next(struct pac_req_ctx *pr_ctx)
+{
+ int ret;
+ struct tevent_req *req;
+ const char *pw_attrs[] = SYSDB_PW_ATTRS;
+
+
+ req = cache_req_object_by_sid_send(pr_ctx, pr_ctx->cctx->ev,
+ pr_ctx->cctx->rctx,
+ pr_ctx->pac_ctx->rctx->ncache,
+ 0, pr_ctx->dom->name,
+ pr_ctx->user_sid_str,
+ pw_attrs);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "cache_req_object_by_sid_send failed.\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(req, pac_resolve_user_sid_done, pr_ctx);
+
+ ret = EAGAIN;
+
+
+ return ret;
+}
+
+static void pac_resolve_user_sid_done(struct tevent_req *req)
+{
+ struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req,
+ struct pac_req_ctx);
+ struct cli_ctx *cctx = pr_ctx->cctx;
+ errno_t ret;
+ struct cache_req_result *result;
+ struct sysdb_attrs *user_attrs;
+
+ ret = cache_req_object_by_sid_recv(pr_ctx, req, &result);
+ talloc_zfree(req);
+
+ if (ret != EOK) {
+ talloc_free(pr_ctx);
+ pac_cmd_done(cctx, ret);
+ return;
+ }
+
+ ret = check_upn_and_sid_from_user_and_pac(result->msgs[0],
+ pr_ctx->pac_ctx->idmap_ctx,
+ pr_ctx->upn_dns_info,
+ pr_ctx->pac_ctx->pac_check_opts);
+ if (ret != EOK) {
+ talloc_free(pr_ctx);
+ pac_cmd_done(cctx, ret);
+ return;
+ }
+
+ user_attrs = sysdb_new_attrs(pr_ctx);
+ if (user_attrs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_mem(user_attrs, SYSDB_PAC_BLOB, pr_ctx->blob,
+ pr_ctx->blen);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_mem failed");
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(user_attrs, SYSDB_PAC_BLOB_EXPIRE,
+ (time(NULL) + pr_ctx->pac_ctx->pac_lifetime));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_time_t failed");
+ goto done;
+ }
+
+ ret = sysdb_set_entry_attr(result->domain->sysdb,
+ result->msgs[0]->dn, user_attrs,
+ SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
+ goto done;
+ }
+
+done:
+ talloc_free(pr_ctx);
+ pac_cmd_done(cctx, ret);
+ return;
+}
+
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version pac_cli_protocol_version[] = {
+ {1, "2011-04-12", "initial version"},
+ {0, NULL, NULL}
+ };
+
+ return pac_cli_protocol_version;
+}
+
+static struct sss_cmd_table pac_cmds[] = {
+ {SSS_GET_VERSION, sss_cmd_get_version},
+ {SSS_PAC_ADD_PAC_USER, pac_add_pac_user},
+ {SSS_CLI_NULL, NULL}
+};
+
+struct sss_cmd_table *get_pac_cmds(void) {
+ return pac_cmds;
+}
diff --git a/src/responder/pam/pam_helpers.c b/src/responder/pam/pam_helpers.c
new file mode 100644
index 0000000..d0a79c5
--- /dev/null
+++ b/src/responder/pam/pam_helpers.c
@@ -0,0 +1,162 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 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 "src/responder/pam/pam_helpers.h"
+
+struct pam_initgr_table_ctx {
+ hash_table_t *id_table;
+ char *name;
+};
+
+static void pam_initgr_cache_remove(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *pvt);
+
+errno_t pam_initgr_cache_set(struct tevent_context *ev,
+ hash_table_t *id_table,
+ char *name,
+ long timeout)
+{
+ errno_t ret;
+ hash_key_t key;
+ hash_value_t val;
+ int hret;
+ struct tevent_timer *te;
+ struct timeval tv;
+ struct pam_initgr_table_ctx *table_ctx;
+
+ ret = pam_initgr_check_timeout(id_table, name);
+ if (ret == EOK) {
+ /* user is already in the cache */
+ goto done;
+ }
+
+ table_ctx = talloc_zero(id_table, struct pam_initgr_table_ctx);
+ if (!table_ctx) return ENOMEM;
+
+ table_ctx->id_table = id_table;
+ table_ctx->name = talloc_strdup(table_ctx, name);
+ if (!table_ctx->name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ key.type = HASH_KEY_STRING;
+ key.str = name;
+
+ /* The value isn't relevant, since we're using
+ * a timer to remove the entry.
+ */
+ val.type = HASH_VALUE_UNDEF;
+
+ hret = hash_enter(id_table, &key, &val);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not update initgr cache for [%s]: [%s]\n",
+ name, hash_error_string(hret));
+ ret = EIO;
+ goto done;
+ } else {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "[%s] added to PAM initgroup cache\n",
+ name);
+ }
+
+ /* Create a timer event to remove the entry from the cache */
+ tv = tevent_timeval_current_ofs(timeout, 0);
+ te = tevent_add_timer(ev, table_ctx, tv,
+ pam_initgr_cache_remove,
+ table_ctx);
+ if (!te) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(table_ctx);
+ }
+ return ret;
+}
+
+static void pam_initgr_cache_remove(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *pvt)
+{
+ int hret;
+ hash_key_t key;
+
+ struct pam_initgr_table_ctx *table_ctx =
+ talloc_get_type(pvt, struct pam_initgr_table_ctx);
+
+ key.type = HASH_KEY_STRING;
+ key.str = table_ctx->name;
+
+ hret = hash_delete(table_ctx->id_table, &key);
+ if (hret != HASH_SUCCESS
+ && hret != HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not clear [%s] from initgr cache: [%s]\n",
+ table_ctx->name,
+ hash_error_string(hret));
+ } else {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "[%s] removed from PAM initgroup cache\n",
+ table_ctx->name);
+ }
+
+ talloc_free(table_ctx);
+}
+
+errno_t pam_initgr_check_timeout(hash_table_t *id_table,
+ char *name)
+{
+ hash_key_t key;
+ hash_value_t val;
+ int hret;
+
+ key.type = HASH_KEY_STRING;
+ key.str = name;
+
+ hret = hash_lookup(id_table, &key, &val);
+ if (hret != HASH_SUCCESS
+ && hret != HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_TRACE_ALL, "Error searching user [%s] in PAM cache.\n",
+ name);
+ return EIO;
+ } else if (hret == HASH_ERROR_KEY_NOT_FOUND) {
+ DEBUG(SSSDBG_TRACE_ALL, "User [%s] not found in PAM cache.\n", name);
+ return ENOENT;
+ }
+
+ /* If there's a value here, then the cache
+ * entry is still valid.
+ */
+ DEBUG(SSSDBG_TRACE_INTERNAL, "User [%s] found in PAM cache.\n", name);
+ return EOK;
+}
+
diff --git a/src/responder/pam/pam_helpers.h b/src/responder/pam/pam_helpers.h
new file mode 100644
index 0000000..23fd308
--- /dev/null
+++ b/src/responder/pam/pam_helpers.h
@@ -0,0 +1,42 @@
+/*
+ SSSD
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2011 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 PAM_HELPERS_H_
+#define PAM_HELPERS_H_
+
+#include "util/util.h"
+
+#define CERT_AUTH_DEFAULT_MATCHING_RULE "KRB5:<EKU>clientAuth"
+
+errno_t pam_initgr_cache_set(struct tevent_context *ev,
+ hash_table_t *id_table,
+ char *name,
+ long timeout);
+
+/* Returns EOK if the cache is still valid
+ * Returns ENOENT if the user is not found or is expired
+ * May report other errors if the hash lookup fails.
+ */
+errno_t pam_initgr_check_timeout(hash_table_t *id_table,
+ char *name);
+
+#endif /* PAM_HELPERS_H_ */
diff --git a/src/responder/pam/pam_prompting_config.c b/src/responder/pam/pam_prompting_config.c
new file mode 100644
index 0000000..7d0362f
--- /dev/null
+++ b/src/responder/pam/pam_prompting_config.c
@@ -0,0 +1,309 @@
+/*
+ SSSD
+
+ PAM Responder - helpers for PAM prompting configuration
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2019
+
+ 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 "util/util.h"
+#include "util/sss_pam_data.h"
+#include "confdb/confdb.h"
+#include "sss_client/sss_cli.h"
+#include "responder/pam/pamsrv.h"
+
+#define DEFAULT_PASSKEY_PROMPT_INTERACTIVE _("Insert your Passkey device, then press ENTER.")
+#define DEFAULT_PASSKEY_PROMPT_TOUCH _("Please touch the device.")
+
+typedef errno_t (pam_set_prompting_fn_t)(TALLOC_CTX *, struct confdb_ctx *,
+ const char *,
+ struct prompt_config ***);
+
+
+static errno_t pam_set_password_prompting_options(TALLOC_CTX *tmp_ctx,
+ struct confdb_ctx *cdb,
+ const char *section,
+ struct prompt_config ***pc_list)
+{
+ int ret;
+ char *value = NULL;
+
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_PASSWORD_PROMPT,
+ NULL, &value);
+ if (ret == EOK && value != NULL) {
+ ret = pc_list_add_password(pc_list, value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_password failed.\n");
+ }
+ return ret;
+ }
+
+ return ENOENT;
+}
+
+static errno_t pam_set_2fa_prompting_options(TALLOC_CTX *tmp_ctx,
+ struct confdb_ctx *cdb,
+ const char *section,
+ struct prompt_config ***pc_list)
+{
+ bool single_2fa_prompt = false;
+ char *first_prompt = NULL;
+ char *second_prompt = NULL;
+ int ret;
+
+
+ ret = confdb_get_bool(cdb, section, CONFDB_PC_2FA_SINGLE_PROMPT, false,
+ &single_2fa_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_bool failed, using defaults");
+ }
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_2FA_1ST_PROMPT,
+ NULL, &first_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_string failed, using defaults");
+ }
+
+ if (single_2fa_prompt) {
+ ret = pc_list_add_2fa_single(pc_list, first_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_2fa_single failed.\n");
+ }
+ return ret;
+ } else {
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_2FA_2ND_PROMPT,
+ NULL, &second_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "confdb_get_string failed, using defaults");
+ }
+
+ ret = pc_list_add_2fa(pc_list, first_prompt, second_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_2fa failed.\n");
+ }
+ return ret;
+ }
+
+ return ENOENT;
+}
+
+static errno_t pam_set_passkey_prompting_options(TALLOC_CTX *tmp_ctx,
+ struct confdb_ctx *cdb,
+ const char *section,
+ struct prompt_config ***pc_list)
+{
+ bool passkey_interactive = false;
+ char *passkey_interactive_prompt = NULL;
+ bool passkey_touch = false;
+ char *passkey_touch_prompt = NULL;
+ int ret;
+
+
+ ret = confdb_get_bool(cdb, section, CONFDB_PC_PASSKEY_INTERACTIVE, false,
+ &passkey_interactive);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_bool failed, using defaults");
+ }
+
+ if (passkey_interactive) {
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_PASSKEY_INTERACTIVE_PROMPT,
+ DEFAULT_PASSKEY_PROMPT_INTERACTIVE, &passkey_interactive_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_string failed, using defaults");
+ }
+ }
+
+ ret = confdb_get_bool(cdb, section, CONFDB_PC_PASSKEY_TOUCH, false,
+ &passkey_touch);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_bool failed, using defaults");
+ }
+
+ if (passkey_touch) {
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_PASSKEY_TOUCH_PROMPT,
+ DEFAULT_PASSKEY_PROMPT_TOUCH, &passkey_touch_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_string failed, using defaults");
+ }
+ }
+
+ ret = pc_list_add_passkey(pc_list, passkey_interactive_prompt, passkey_touch_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_passkey_touch failed.\n");
+ }
+
+ return ret;
+}
+static errno_t pam_set_prompting_options(struct confdb_ctx *cdb,
+ const char *service_name,
+ char **sections,
+ int num_sections,
+ const char *section_path,
+ pam_set_prompting_fn_t *setter,
+ struct prompt_config ***pc_list)
+{
+ char *dummy;
+ size_t c;
+ bool global = false;
+ bool specific = false;
+ char *section = NULL;
+ int ret;
+ char *last;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ dummy = talloc_asprintf(tmp_ctx, "%s/%s", section_path,
+ service_name);
+ for (c = 0; c < num_sections; c++) {
+ if (strcmp(sections[c], section_path) == 0) {
+ global = true;
+ }
+ if (dummy != NULL && strcmp(sections[c], dummy) == 0) {
+ specific = true;
+ }
+ }
+
+ section = talloc_asprintf(tmp_ctx, "%s/%s", CONFDB_PC_CONF_ENTRY, dummy);
+ if (section == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ENOENT;
+ if (specific) {
+ ret = setter(tmp_ctx, cdb, section, pc_list);
+ }
+ if (global && ret == ENOENT) {
+ last = strrchr(section, '/');
+ if (last != NULL) {
+ *last = '\0';
+ ret = setter(tmp_ctx, cdb, section, pc_list);
+ }
+ }
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "setter failed.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd)
+{
+ int ret;
+ struct prompt_config **pc_list = NULL;
+ int resp_len;
+ uint8_t *resp_data = NULL;
+ struct pam_resp_auth_type types;
+
+ if (pctx->num_prompting_config_sections == 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "No prompting configuration found.\n");
+ return EOK;
+ }
+
+ ret = pam_get_auth_types(pd, &types);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get authentication types\n");
+ goto done;
+ }
+
+ if (types.passkey_auth) {
+ ret = pam_set_prompting_options(pctx->rctx->cdb, pd->service,
+ pctx->prompting_config_sections,
+ pctx->num_prompting_config_sections,
+ CONFDB_PC_TYPE_PASSKEY,
+ pam_set_passkey_prompting_options,
+ &pc_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_set_prompting_options failed.\n");
+ goto done;
+ }
+ }
+
+ if (types.cert_auth) {
+ /* If certificate based authentication is possilbe, i.e. a Smartcard
+ * or similar with the mapped certificate is available we currently
+ * prefer this authentication type unconditionally. If other types
+ * should be used the Smartcard can be removed during authentication.
+ * Since there currently are no specific options for cert_auth we are
+ * done. */
+ ret = EOK;
+ goto done;
+ }
+
+ /* If OTP and password auth are possible we currently prefer OTP. */
+ if (types.otp_auth) {
+ ret = pam_set_prompting_options(pctx->rctx->cdb, pd->service,
+ pctx->prompting_config_sections,
+ pctx->num_prompting_config_sections,
+ CONFDB_PC_TYPE_2FA,
+ pam_set_2fa_prompting_options,
+ &pc_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_set_prompting_options failed.\n");
+ goto done;
+ }
+ }
+
+ if (types.password_auth) {
+ ret = pam_set_prompting_options(pctx->rctx->cdb, pd->service,
+ pctx->prompting_config_sections,
+ pctx->num_prompting_config_sections,
+ CONFDB_PC_TYPE_PASSWORD,
+ pam_set_password_prompting_options,
+ &pc_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_set_prompting_options failed.\n");
+ goto done;
+ }
+ }
+
+ if (pc_list != NULL) {
+ ret = pam_get_response_prompt_config(pc_list, &resp_len, &resp_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_get_response_prompt_config failed.\n");
+ goto done;
+ }
+
+ ret = pam_add_response(pd, SSS_PAM_PROMPT_CONFIG, resp_len, resp_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_add_response failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ free(resp_data);
+ pc_list_free(pc_list);
+
+ return ret;
+}
diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
new file mode 100644
index 0000000..73ebb0a
--- /dev/null
+++ b/src/responder/pam/pamsrv.c
@@ -0,0 +1,529 @@
+/*
+ SSSD
+
+ PAM Responder
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2009
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <popt.h>
+#include <dbus/dbus.h>
+
+#include "config.h"
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder_packet.h"
+#include "providers/data_provider.h"
+#include "responder/pam/pamsrv.h"
+#include "responder/common/negcache.h"
+#include "sss_iface/sss_iface_async.h"
+
+#define DEFAULT_PAM_FD_LIMIT 8192
+#define ALL_UIDS_ALLOWED "all"
+#define ALL_DOMAINS_ARE_PUBLIC "all"
+#define NO_DOMAINS_ARE_PUBLIC "none"
+#define DEFAULT_ALLOWED_UIDS ALL_UIDS_ALLOWED
+#define DEFAULT_PAM_CERT_AUTH false
+#define DEFAULT_PAM_PASSKEY_AUTH true
+#define DEFAULT_PAM_CERT_DB_PATH SYSCONFDIR"/sssd/pki/sssd_auth_ca_db.pem"
+#define DEFAULT_PAM_INITGROUPS_SCHEME "no_session"
+
+static errno_t get_trusted_uids(struct pam_ctx *pctx)
+{
+ char *uid_str;
+ errno_t ret;
+
+ ret = confdb_get_string(pctx->rctx->cdb, pctx->rctx,
+ CONFDB_PAM_CONF_ENTRY, CONFDB_PAM_TRUSTED_USERS,
+ DEFAULT_ALLOWED_UIDS, &uid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get allowed UIDs.\n");
+ goto done;
+ }
+
+ if (strcmp(uid_str, ALL_UIDS_ALLOWED) == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "All UIDs are allowed.\n");
+ pctx->trusted_uids_count = 0;
+ } else {
+ ret = csv_string_to_uid_array(pctx->rctx, uid_str,
+ &pctx->trusted_uids_count,
+ &pctx->trusted_uids);
+ }
+
+ talloc_free(uid_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set allowed UIDs.\n");
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static errno_t get_public_domains(struct pam_ctx *pctx)
+{
+ char *domains_str = NULL;
+ errno_t ret;
+
+ ret = confdb_get_string(pctx->rctx->cdb, pctx->rctx,
+ CONFDB_PAM_CONF_ENTRY, CONFDB_PAM_PUBLIC_DOMAINS,
+ NO_DOMAINS_ARE_PUBLIC, &domains_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get allowed UIDs.\n");
+ goto done;
+ }
+
+ if (strcmp(domains_str, ALL_DOMAINS_ARE_PUBLIC) == 0) { /* all */
+ /* copy all domains */
+ ret = get_dom_names(pctx,
+ pctx->rctx->domains,
+ &pctx->public_domains,
+ &pctx->public_domains_count);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "get_dom_names failed.\n");
+ goto done;
+ }
+ } else if (strcmp(domains_str, NO_DOMAINS_ARE_PUBLIC) == 0) { /* none */
+ pctx->public_domains = NULL;
+ pctx->public_domains_count = 0;
+ } else {
+ ret = split_on_separator(pctx, domains_str, ',', true, false,
+ &pctx->public_domains,
+ &pctx->public_domains_count);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(domains_str);
+ return ret;
+}
+
+static errno_t get_app_services(struct pam_ctx *pctx)
+{
+ errno_t ret;
+
+ ret = confdb_get_string_as_list(pctx->rctx->cdb, pctx,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_APP_SERVICES,
+ &pctx->app_services);
+ if (ret == ENOENT) {
+ pctx->app_services = talloc_zero_array(pctx, char *, 1);
+ if (pctx->app_services == NULL) {
+ return ENOMEM;
+ }
+ /* Allocating an empty array makes it easier for the consumer
+ * to iterate over it
+ */
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot read "CONFDB_PAM_APP_SERVICES" [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static void pam_get_domains_callback(void *pvt)
+{
+ struct pam_ctx *pctx;
+ int ret;
+
+ pctx = talloc_get_type(pvt, struct pam_ctx);
+ ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "p11_refresh_certmap_ctx failed.\n");
+ }
+}
+
+static int pam_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ int pipe_fd, int priv_pipe_fd)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *pam_cmds;
+ struct pam_ctx *pctx;
+ int ret;
+ int id_timeout;
+ int fd_limit;
+ char *tmpstr = NULL;
+
+ pam_cmds = get_pam_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ pam_cmds,
+ SSS_PAM_SOCKET_NAME, pipe_fd,
+ SSS_PAM_PRIV_SOCKET_NAME, priv_pipe_fd,
+ CONFDB_PAM_CONF_ENTRY,
+ SSS_BUS_PAM, SSS_PAM_SBUS_SERVICE_NAME,
+ sss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ pctx = talloc_zero(rctx, struct pam_ctx);
+ if (!pctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ pctx->rctx = rctx;
+ pctx->rctx->pvt_ctx = pctx;
+
+ ret = get_trusted_uids(pctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "get_trusted_uids failed: %d:[%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = get_public_domains(pctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "get_public_domains failed: %d:[%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = get_app_services(pctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "get_app_services failed: %d:[%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Set up the PAM identity timeout */
+ ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_ID_TIMEOUT, 5,
+ &id_timeout);
+ if (ret != EOK) goto done;
+
+ pctx->id_timeout = (size_t)id_timeout;
+
+ ret = sss_ncache_prepopulate(pctx->rctx->ncache, cdb, pctx->rctx);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Create table for initgroup lookups */
+ ret = sss_hash_create(pctx, 0, &pctx->id_table);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Could not create initgroups hash table: [%s]\n",
+ strerror(ret));
+ goto done;
+ }
+
+ /* Set up file descriptor limits */
+ ret = confdb_get_int(pctx->rctx->cdb,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_SERVICE_FD_LIMIT,
+ DEFAULT_PAM_FD_LIMIT,
+ &fd_limit);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to set up file descriptor limit\n");
+ goto done;
+ }
+ responder_set_fd_limit(fd_limit);
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, pctx->rctx->ncache,
+ pam_get_domains_callback, pctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto done;
+ }
+
+ /* Check if there is a prompting configuration */
+ pctx->prompting_config_sections = NULL;
+ pctx->num_prompting_config_sections = 0;
+ ret = confdb_get_sub_sections(pctx, pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY,
+ &pctx->prompting_config_sections,
+ &pctx->num_prompting_config_sections);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_sub_sections failed, not fatal.\n");
+ }
+
+ /* Check if certificate based authentication is enabled */
+ ret = confdb_get_bool(pctx->rctx->cdb,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CERT_AUTH,
+ DEFAULT_PAM_CERT_AUTH,
+ &pctx->cert_auth);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to determine get cert db path.\n");
+ goto done;
+ }
+
+ if (pctx->cert_auth) {
+ ret = p11_child_init(pctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "p11_child_init failed.\n");
+ goto done;
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, pctx,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CERT_DB_PATH,
+ DEFAULT_PAM_CERT_DB_PATH,
+ &pctx->ca_db);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to determine if certificate based authentication is " \
+ "enabled or not.\n");
+ goto done;
+ }
+
+ }
+
+ /* Check if passkey authentication is enabled */
+ ret = confdb_get_bool(pctx->rctx->cdb,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_PASSKEY_AUTH,
+ DEFAULT_PAM_PASSKEY_AUTH,
+ &pctx->passkey_auth);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to check if passkey authentication is " \
+ "enabled.\n");
+ goto done;
+ }
+
+ if (pctx->cert_auth
+ || pctx->passkey_auth
+ || pctx->num_prompting_config_sections != 0) {
+ ret = create_preauth_indicator();
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to create pre-authentication indicator file, "
+ "Smartcard/passkey authentication or configured prompting might "
+ "not work as expected.\n");
+ }
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, pctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_INITGROUPS_SCHEME,
+ DEFAULT_PAM_INITGROUPS_SCHEME, &tmpstr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to determine initgroups scheme.\n");
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found value [%s] for option [%s].\n", tmpstr,
+ CONFDB_PAM_INITGROUPS_SCHEME);
+
+ if (tmpstr == NULL) {
+ pctx->initgroups_scheme = PAM_INITGR_NO_SESSION;
+ } else {
+ pctx->initgroups_scheme = pam_initgroups_string_to_enum(tmpstr);
+ if (pctx->initgroups_scheme == PAM_INITGR_INVALID) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unknown value [%s] for option %s.\n",
+ tmpstr, CONFDB_PAM_INITGROUPS_SCHEME);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, pctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_GSSAPI_SERVICES, "-", &tmpstr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to determine gssapi services.\n");
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found value [%s] for option [%s].\n", tmpstr,
+ CONFDB_PAM_GSSAPI_SERVICES);
+
+ if (tmpstr != NULL) {
+ ret = split_on_separator(pctx, tmpstr, ',', true, true,
+ &pctx->gssapi_services, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "split_on_separator() failed [%d]: [%s].\n", ret,
+ sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ ret = confdb_get_bool(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_GSSAPI_CHECK_UPN, true,
+ &pctx->gssapi_check_upn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to read %s [%d]: %s\n",
+ CONFDB_PAM_GSSAPI_CHECK_UPN, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, pctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_GSSAPI_INDICATORS_MAP, "-", &tmpstr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to determine gssapi services.\n");
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found value [%s] for option [%s].\n", tmpstr,
+ CONFDB_PAM_GSSAPI_INDICATORS_MAP);
+
+ if (tmpstr != NULL) {
+ ret = split_on_separator(pctx, tmpstr, ',', true, true,
+ &pctx->gssapi_indicators_map, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "split_on_separator() failed [%d]: [%s].\n", ret,
+ sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_PAM,
+ SSS_PAM_SBUS_SERVICE_NAME,
+ SSS_PAM_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto done;
+ }
+
+ ret = sss_resp_register_service_iface(rctx);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(rctx);
+ }
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ int pipe_fd = -1;
+ int priv_pipe_fd = -1;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_pam";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ if (!is_socket_activated()) {
+ /* Create pipe file descriptors here before privileges are dropped
+ * in server_setup() */
+ ret = create_pipe_fd(SSS_PAM_SOCKET_NAME, &pipe_fd, SCKT_RSP_UMASK);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "create_pipe_fd failed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ return 2;
+ }
+
+ ret = create_pipe_fd(SSS_PAM_PRIV_SOCKET_NAME, &priv_pipe_fd,
+ DFL_RSP_UMASK);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "create_pipe_fd failed (privileged pipe) [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ return 2;
+ }
+ }
+
+ /* server_setup() might switch to an unprivileged user, so the permissions
+ * for p11_child.log have to be fixed first. */
+ ret = chown_debug_file("p11_child", uid, gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot chown the p11_child debug file, "
+ "debugging might not work!\n");
+ }
+
+ ret = server_setup("pam", true, 0, uid, gid, CONFDB_PAM_CONF_ENTRY,
+ &main_ctx, false);
+ if (ret != EOK) return 2;
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not set up to exit when parent process does\n");
+ }
+
+ ret = pam_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx,
+ pipe_fd, priv_pipe_fd);
+ if (ret != EOK) return 3;
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
+
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
new file mode 100644
index 0000000..7013a8e
--- /dev/null
+++ b/src/responder/pam/pamsrv.h
@@ -0,0 +1,173 @@
+/*
+ Authors:
+ Simo Sorce <ssorce@redhat.com>
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2009 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 __PAMSRV_H__
+#define __PAMSRV_H__
+
+#include <security/pam_appl.h>
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "lib/certmap/sss_certmap.h"
+
+struct pam_auth_req;
+
+typedef void (pam_dp_callback_t)(struct pam_auth_req *preq);
+
+enum pam_initgroups_scheme {
+ PAM_INITGR_NEVER,
+ PAM_INITGR_NO_SESSION,
+ PAM_INITGR_ALWAYS,
+ PAM_INITGR_INVALID
+};
+
+struct pam_ctx {
+ struct resp_ctx *rctx;
+ time_t id_timeout;
+ hash_table_t *id_table;
+ size_t trusted_uids_count;
+ uid_t *trusted_uids;
+
+ /* List of domains that are accessible even for untrusted users. */
+ char **public_domains;
+ int public_domains_count;
+
+ /* What services are permitted to access application domains */
+ char **app_services;
+
+ bool cert_auth;
+ char *ca_db;
+ struct sss_certmap_ctx *sss_certmap_ctx;
+ char **smartcard_services;
+
+ /* parsed list of pam_response_filter option */
+ char **pam_filter_opts;
+
+ char **prompting_config_sections;
+ int num_prompting_config_sections;
+
+ enum pam_initgroups_scheme initgroups_scheme;
+
+ /* List of PAM services that are allowed to authenticate with GSSAPI. */
+ char **gssapi_services;
+ /* List of authentication indicators associated with a PAM service */
+ char **gssapi_indicators_map;
+ bool gssapi_check_upn;
+ bool passkey_auth;
+ struct pam_passkey_table_data *pk_table_data;
+};
+
+struct pam_auth_req {
+ struct cli_ctx *cctx;
+ struct sss_domain_info *domain;
+ enum cache_req_dom_type req_dom_type;
+
+ struct pam_data *pd;
+
+ pam_dp_callback_t *callback;
+
+ bool is_uid_trusted;
+ void *data;
+ bool use_cached_auth;
+ /* whether cached authentication was tried and failed */
+ bool cached_auth_failed;
+
+ struct ldb_message *user_obj;
+ struct cert_auth_info *cert_list;
+ struct cert_auth_info *current_cert;
+ bool cert_auth_local;
+
+ bool passkey_data_exists;
+ uint32_t client_id_num;
+};
+
+struct pam_resp_auth_type {
+ bool password_auth;
+ bool otp_auth;
+ bool cert_auth;
+ bool passkey_auth;
+};
+
+struct sss_cmd_table *get_pam_cmds(void);
+
+errno_t
+pam_dp_send_req(struct pam_auth_req *preq);
+
+int pam_check_user_search(struct pam_auth_req *preq);
+int pam_check_user_done(struct pam_auth_req *preq, int ret);
+void pam_reply(struct pam_auth_req *preq);
+
+errno_t p11_child_init(struct pam_ctx *pctx);
+
+struct cert_auth_info;
+const char *sss_cai_get_cert(struct cert_auth_info *i);
+const char *sss_cai_get_token_name(struct cert_auth_info *i);
+const char *sss_cai_get_module_name(struct cert_auth_info *i);
+const char *sss_cai_get_key_id(struct cert_auth_info *i);
+const char *sss_cai_get_label(struct cert_auth_info *i);
+struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i);
+struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i);
+void sss_cai_set_cert_user_objs(struct cert_auth_info *i,
+ struct ldb_result *cert_user_objs);
+void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
+ size_t *_cert_user_count);
+
+struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *ca_db,
+ time_t timeout,
+ const char *verify_opts,
+ struct sss_certmap_ctx *sss_certmap_ctx,
+ const char *uri,
+ struct pam_data *pd);
+errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct cert_auth_info **cert_list);
+
+errno_t add_pam_cert_response(struct pam_data *pd, struct sss_domain_info *dom,
+ const char *sysdb_username,
+ struct cert_auth_info *cert_info,
+ enum response_type type);
+
+bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd);
+
+errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+ struct sss_domain_info *domains);
+
+errno_t
+pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *username,
+ uint64_t value);
+
+errno_t filter_responses(struct pam_ctx *pctx,
+ struct response_data *resp_list,
+ struct pam_data *pd);
+
+errno_t pam_get_auth_types(struct pam_data *pd,
+ struct pam_resp_auth_type *_auth_types);
+errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd);
+
+enum pam_initgroups_scheme pam_initgroups_string_to_enum(const char *str);
+const char *pam_initgroup_enum_to_string(enum pam_initgroups_scheme scheme);
+
+int pam_cmd_gssapi_init(struct cli_ctx *cli_ctx);
+int pam_cmd_gssapi_sec_ctx(struct cli_ctx *cctx);
+
+#endif /* __PAMSRV_H__ */
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
new file mode 100644
index 0000000..c23ea7b
--- /dev/null
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -0,0 +1,3085 @@
+/*
+ SSSD
+
+ PAM Responder
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2009
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2009
+
+ 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/>.
+*/
+
+#define _GNU_SOURCE
+
+#include <time.h>
+#include <string.h>
+#include "util/util.h"
+#include "util/auth_utils.h"
+#include "util/find_uid.h"
+#include "util/sss_ptr_hash.h"
+#include "db/sysdb.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "responder/common/negcache.h"
+#include "providers/data_provider.h"
+#include "responder/pam/pamsrv.h"
+#include "responder/pam/pamsrv_passkey.h"
+#include "responder/pam/pam_helpers.h"
+#include "responder/common/cache_req/cache_req.h"
+
+enum pam_verbosity {
+ PAM_VERBOSITY_NO_MESSAGES = 0,
+ PAM_VERBOSITY_IMPORTANT,
+ PAM_VERBOSITY_INFO,
+ PAM_VERBOSITY_DEBUG
+};
+
+#define DEFAULT_PAM_VERBOSITY PAM_VERBOSITY_IMPORTANT
+
+struct pam_initgroup_enum_str {
+ enum pam_initgroups_scheme scheme;
+ const char *option;
+};
+
+struct pam_initgroup_enum_str pam_initgroup_enum_str[] = {
+ { PAM_INITGR_NEVER, "never" },
+ { PAM_INITGR_NO_SESSION, "no_session" },
+ { PAM_INITGR_ALWAYS, "always" },
+ { PAM_INITGR_INVALID, NULL }
+};
+
+enum pam_initgroups_scheme pam_initgroups_string_to_enum(const char *str)
+{
+ size_t c;
+
+ for (c = 0 ; pam_initgroup_enum_str[c].option != NULL; c++) {
+ if (strcasecmp(pam_initgroup_enum_str[c].option, str) == 0) {
+ return pam_initgroup_enum_str[c].scheme;
+ }
+ }
+
+ return PAM_INITGR_INVALID;
+}
+
+const char *pam_initgroup_enum_to_string(enum pam_initgroups_scheme scheme)
+{
+ size_t c;
+
+ for (c = 0 ; pam_initgroup_enum_str[c].option != NULL; c++) {
+ if (pam_initgroup_enum_str[c].scheme == scheme) {
+ return pam_initgroup_enum_str[c].option;
+ }
+ }
+
+ return "(NULL)";
+}
+
+static errno_t
+pam_null_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *username);
+static errno_t
+pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *name,
+ uint64_t *_value);
+
+void pam_reply(struct pam_auth_req *preq);
+
+static errno_t check_cert(TALLOC_CTX *mctx,
+ struct tevent_context *ev,
+ struct pam_ctx *pctx,
+ struct pam_auth_req *preq,
+ struct pam_data *pd);
+
+int pam_check_user_done(struct pam_auth_req *preq, int ret);
+
+static errno_t pack_user_info_msg(TALLOC_CTX *mem_ctx,
+ const char *user_error_message,
+ size_t *resp_len,
+ uint8_t **_resp)
+{
+ uint32_t resp_type = SSS_PAM_USER_INFO_ACCOUNT_EXPIRED;
+ size_t err_len;
+ uint8_t *resp;
+ size_t p;
+
+ err_len = strlen(user_error_message);
+ *resp_len = 2 * sizeof(uint32_t) + err_len;
+ resp = talloc_size(mem_ctx, *resp_len);
+ if (resp == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+
+ p = 0;
+ SAFEALIGN_SET_UINT32(&resp[p], resp_type, &p);
+ SAFEALIGN_SET_UINT32(&resp[p], err_len, &p);
+ safealign_memcpy(&resp[p], user_error_message, err_len, &p);
+ if (p != *resp_len) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Size mismatch\n");
+ }
+
+ *_resp = resp;
+ return EOK;
+}
+
+static void inform_user(struct pam_data* pd, const char *pam_message)
+{
+ size_t msg_len;
+ uint8_t *msg;
+ errno_t ret;
+
+ ret = pack_user_info_msg(pd, pam_message, &msg_len, &msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pack_user_info_msg failed.\n");
+ } else {
+ ret = pam_add_response(pd, SSS_PAM_USER_INFO, msg_len, msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+ }
+ }
+}
+
+static bool is_domain_requested(struct pam_data *pd, const char *domain_name)
+{
+ int i;
+
+ /* If none specific domains got requested via pam, all domains are allowed.
+ * Which mimics the default/original behaviour.
+ */
+ if (!pd->requested_domains) {
+ return true;
+ }
+
+ for (i = 0; pd->requested_domains[i]; i++) {
+ if (strcasecmp(domain_name, pd->requested_domains[i])) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+static int extract_authtok_v2(struct sss_auth_token *tok,
+ size_t data_size, uint8_t *body, size_t blen,
+ size_t *c)
+{
+ uint32_t auth_token_type;
+ uint32_t auth_token_length;
+ uint8_t *auth_token_data;
+ int ret = EOK;
+
+ if (data_size < sizeof(uint32_t) || *c+data_size > blen ||
+ SIZE_T_OVERFLOW(*c, data_size)) return EINVAL;
+
+ SAFEALIGN_COPY_UINT32_CHECK(&auth_token_type, &body[*c], blen, c);
+ auth_token_length = data_size - sizeof(uint32_t);
+ auth_token_data = body+(*c);
+
+ switch (auth_token_type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ sss_authtok_set_empty(tok);
+ break;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ if (auth_token_length == 0) {
+ sss_authtok_set_empty(tok);
+ } else {
+ ret = sss_authtok_set_password(tok, (const char *)auth_token_data,
+ auth_token_length);
+ }
+ break;
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_OAUTH2:
+ case SSS_AUTHTOK_TYPE_PASSKEY:
+ case SSS_AUTHTOK_TYPE_PASSKEY_KRB:
+ case SSS_AUTHTOK_TYPE_PASSKEY_REPLY:
+ ret = sss_authtok_set(tok, auth_token_type,
+ auth_token_data, auth_token_length);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ *c += auth_token_length;
+
+ return ret;
+}
+
+static int extract_string(char **var, size_t size, uint8_t *body, size_t blen,
+ size_t *c) {
+ uint8_t *str;
+
+ if (*c+size > blen || SIZE_T_OVERFLOW(*c, size)) return EINVAL;
+
+ str = body+(*c);
+
+ if (str[size-1]!='\0') return EINVAL;
+
+ /* If the string isn't valid UTF-8, fail */
+ if (!sss_utf8_check(str, size-1)) {
+ return EINVAL;
+ }
+
+ *c += size;
+
+ *var = (char *) str;
+
+ return EOK;
+}
+
+static int extract_uint32_t(uint32_t *var, size_t size, uint8_t *body,
+ size_t blen, size_t *c) {
+
+ if (size != sizeof(uint32_t) || *c+size > blen || SIZE_T_OVERFLOW(*c, size))
+ return EINVAL;
+
+ SAFEALIGN_COPY_UINT32_CHECK(var, &body[*c], blen, c);
+
+ return EOK;
+}
+
+static int pd_set_primary_name(const struct ldb_message *msg,struct pam_data *pd)
+{
+ const char *name;
+
+ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (!name) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no name?\n");
+ return EIO;
+ }
+
+ if (strcmp(pd->user, name)) {
+ DEBUG(SSSDBG_TRACE_FUNC, "User's primary name is %s\n", name);
+ talloc_free(pd->user);
+ pd->user = talloc_strdup(pd, name);
+ if (!pd->user) return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static int pam_parse_in_data_v2(struct pam_data *pd,
+ uint8_t *body, size_t blen)
+{
+ size_t c;
+ uint32_t type;
+ uint32_t size;
+ int ret;
+ uint32_t start;
+ uint32_t terminator;
+ char *requested_domains;
+
+ if (blen < 4*sizeof(uint32_t)+2) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Received data is invalid.\n");
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&start, body, NULL);
+ SAFEALIGN_COPY_UINT32(&terminator, body + blen - sizeof(uint32_t), NULL);
+
+ if (start != SSS_START_OF_PAM_REQUEST
+ || terminator != SSS_END_OF_PAM_REQUEST) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Received data is invalid.\n");
+ return EINVAL;
+ }
+
+ c = sizeof(uint32_t);
+ do {
+ SAFEALIGN_COPY_UINT32_CHECK(&type, &body[c], blen, &c);
+
+ if (type == SSS_END_OF_PAM_REQUEST) {
+ if (c != blen) return EINVAL;
+ } else {
+ SAFEALIGN_COPY_UINT32_CHECK(&size, &body[c], blen, &c);
+ /* the uint32_t end maker SSS_END_OF_PAM_REQUEST does not count to
+ * the remaining buffer */
+ if (size > (blen - c - sizeof(uint32_t))) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid data size.\n");
+ return EINVAL;
+ }
+
+ switch(type) {
+ case SSS_PAM_ITEM_USER:
+ ret = extract_string(&pd->logon_name, size, body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_SERVICE:
+ ret = extract_string(&pd->service, size, body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_TTY:
+ ret = extract_string(&pd->tty, size, body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_RUSER:
+ ret = extract_string(&pd->ruser, size, body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_RHOST:
+ ret = extract_string(&pd->rhost, size, body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_REQUESTED_DOMAINS:
+ ret = extract_string(&requested_domains, size, body, blen,
+ &c);
+ if (ret != EOK) return ret;
+
+ ret = split_on_separator(pd, requested_domains, ',', true,
+ true, &pd->requested_domains,
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to parse requested_domains list!\n");
+ return ret;
+ }
+ break;
+ case SSS_PAM_ITEM_CLI_PID:
+ ret = extract_uint32_t(&pd->cli_pid, size,
+ body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_CHILD_PID:
+ /* This is optional. */
+ ret = extract_uint32_t(&pd->child_pid, size,
+ body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_AUTHTOK:
+ ret = extract_authtok_v2(pd->authtok,
+ size, body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_NEWAUTHTOK:
+ ret = extract_authtok_v2(pd->newauthtok,
+ size, body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ case SSS_PAM_ITEM_FLAGS:
+ ret = extract_uint32_t(&pd->cli_flags, size,
+ body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Ignoring unknown data type [%d].\n", type);
+ c += size;
+ }
+ }
+
+ } while(c < blen);
+
+ return EOK;
+
+}
+
+static int pam_parse_in_data_v3(struct pam_data *pd,
+ uint8_t *body, size_t blen)
+{
+ int ret;
+
+ ret = pam_parse_in_data_v2(pd, body, blen);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_parse_in_data_v2 failed.\n");
+ return ret;
+ }
+
+ if (pd->cli_pid == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing client PID.\n");
+ return EINVAL;
+ }
+
+ return EOK;
+}
+
+static int extract_authtok_v1(struct sss_auth_token *tok,
+ uint8_t *body, size_t blen, size_t *c)
+{
+ uint32_t auth_token_type;
+ uint32_t auth_token_length;
+ uint8_t *auth_token_data;
+ int ret = EOK;
+
+ SAFEALIGN_COPY_UINT32_CHECK(&auth_token_type, &body[*c], blen, c);
+ SAFEALIGN_COPY_UINT32_CHECK(&auth_token_length, &body[*c], blen, c);
+ auth_token_data = body+(*c);
+
+ switch (auth_token_type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ sss_authtok_set_empty(tok);
+ break;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ ret = sss_authtok_set_password(tok, (const char *)auth_token_data,
+ auth_token_length);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ *c += auth_token_length;
+
+ return ret;
+}
+
+static int pam_parse_in_data(struct pam_data *pd,
+ uint8_t *body, size_t blen)
+{
+ size_t start;
+ size_t end;
+ size_t last;
+ int ret;
+
+ last = blen - 1;
+ end = 0;
+
+ /* user name */
+ for (start = end; end < last; end++) if (body[end] == '\0') break;
+ if (body[end++] != '\0') return EINVAL;
+ pd->logon_name = (char *) &body[start];
+
+ for (start = end; end < last; end++) if (body[end] == '\0') break;
+ if (body[end++] != '\0') return EINVAL;
+ pd->service = (char *) &body[start];
+
+ for (start = end; end < last; end++) if (body[end] == '\0') break;
+ if (body[end++] != '\0') return EINVAL;
+ pd->tty = (char *) &body[start];
+
+ for (start = end; end < last; end++) if (body[end] == '\0') break;
+ if (body[end++] != '\0') return EINVAL;
+ pd->ruser = (char *) &body[start];
+
+ for (start = end; end < last; end++) if (body[end] == '\0') break;
+ if (body[end++] != '\0') return EINVAL;
+ pd->rhost = (char *) &body[start];
+
+ ret = extract_authtok_v1(pd->authtok, body, blen, &end);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid auth token\n");
+ return ret;
+ }
+ ret = extract_authtok_v1(pd->newauthtok, body, blen, &end);
+ if (ret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid new auth token\n");
+ return ret;
+ }
+
+ DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd);
+
+ return EOK;
+}
+
+static errno_t
+pam_get_local_auth_policy(struct sss_domain_info *domain,
+ const char *name,
+ bool *_sc_allow,
+ bool *_passkey_allow)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *attrs[] = { SYSDB_LOCAL_SMARTCARD_AUTH, SYSDB_LOCAL_PASSKEY_AUTH, NULL };
+ struct ldb_message *ldb_msg;
+ bool sc_allow = false;
+ bool passkey_allow = false;
+ errno_t ret;
+
+ if (name == NULL || *name == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing user name.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (domain->sysdb == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing sysdb db context.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, name, attrs, &ldb_msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_search_user_by_name failed [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ sc_allow = ldb_msg_find_attr_as_bool(ldb_msg, SYSDB_LOCAL_SMARTCARD_AUTH,
+ false);
+
+ passkey_allow = ldb_msg_find_attr_as_bool(ldb_msg, SYSDB_LOCAL_PASSKEY_AUTH,
+ true);
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_sc_allow = sc_allow;
+ *_passkey_allow = passkey_allow;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+static errno_t set_local_auth_type(struct pam_auth_req *preq,
+ bool sc_allow,
+ bool passkey_allow)
+{
+ struct sysdb_attrs *attrs;
+ errno_t ret;
+
+ attrs = sysdb_new_attrs(preq);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_LOCAL_SMARTCARD_AUTH, sc_allow);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = sysdb_attrs_add_bool(attrs, SYSDB_LOCAL_PASSKEY_AUTH, passkey_allow);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = sysdb_set_user_attr(preq->domain, preq->pd->user, attrs,
+ SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "set_local_auth_type failed.\n");
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ goto fail;
+ }
+
+ return EOK;
+
+fail:
+ return ret;
+}
+/*=Save-Last-Login-State===================================================*/
+
+static errno_t set_last_login(struct pam_auth_req *preq)
+{
+ struct sysdb_attrs *attrs;
+ errno_t ret;
+
+ attrs = sysdb_new_attrs(preq);
+ if (!attrs) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_ONLINE_AUTH, time(NULL));
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs,
+ SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN,
+ time(NULL));
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_LOGIN, time(NULL));
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = sysdb_set_user_attr(preq->domain, preq->pd->user, attrs,
+ SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "set_last_login failed.\n");
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ goto fail;
+ } else {
+ preq->pd->last_auth_saved = true;
+ }
+ preq->callback(preq);
+
+ return EOK;
+
+fail:
+ return ret;
+}
+
+static errno_t filter_responses_env(struct response_data *resp,
+ struct pam_data *pd,
+ char * const *pam_filter_opts)
+{
+ size_t c;
+ const char *var_name;
+ size_t var_name_len;
+ const char *service;
+
+ if (pam_filter_opts == NULL) {
+ return EOK;
+ }
+
+ for (c = 0; pam_filter_opts[c] != NULL; c++) {
+ if (strncmp(pam_filter_opts[c], "ENV", 3) != 0) {
+ continue;
+ }
+
+ var_name = NULL;
+ var_name_len = 0;
+ service = NULL;
+ if (pam_filter_opts[c][3] != '\0') {
+ if (pam_filter_opts[c][3] != ':') {
+ /* Neither plain ENV nor ENV:, ignored */
+ continue;
+ }
+
+ var_name = pam_filter_opts[c] + 4;
+ /* check if there is a second ':' in the option and use the following
+ * data, if any, as service name. */
+ service = strchr(var_name, ':');
+ if (service == NULL) {
+ var_name_len = strlen(var_name);
+ } else {
+ var_name_len = service - var_name;
+
+ service++;
+ /* handle empty service name "ENV:var:" */
+ if (*service == '\0') {
+ service = NULL;
+ }
+ }
+ }
+ /* handle empty var name "ENV:" or "ENV::service" */
+ if (var_name_len == 0) {
+ var_name = NULL;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Found PAM ENV filter for variable [%.*s] and service [%s].\n",
+ (int) var_name_len,
+ (var_name ? var_name : "(NULL)"),
+ (service ? service : "(NULL)"));
+
+ if (service != NULL && pd->service != NULL
+ && strcmp(service, pd->service) != 0) {
+ /* current service does not match the filter */
+ continue;
+ }
+
+ if (var_name == NULL) {
+ /* All environment variables should be filtered */
+ resp->do_not_send_to_client = true;
+ continue;
+ }
+
+ if (resp->len > var_name_len && resp->data[var_name_len] == '='
+ && memcmp(resp->data, var_name, var_name_len) == 0) {
+ resp->do_not_send_to_client = true;
+ }
+ }
+
+ return EOK;
+}
+
+errno_t filter_responses(struct pam_ctx *pctx,
+ struct response_data *resp_list,
+ struct pam_data *pd)
+{
+ int ret;
+ struct response_data *resp;
+ uint32_t user_info_type;
+ int64_t expire_date = 0;
+ int pam_verbosity = DEFAULT_PAM_VERBOSITY;
+ char **new_opts;
+ size_t c;
+ const char *default_pam_response_filter[] = { "ENV:KRB5CCNAME:sudo",
+ "ENV:KRB5CCNAME:sudo-i",
+ NULL };
+
+ ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_VERBOSITY, DEFAULT_PAM_VERBOSITY,
+ &pam_verbosity);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read PAM verbosity, not fatal.\n");
+ pam_verbosity = DEFAULT_PAM_VERBOSITY;
+ }
+
+ if (pctx->pam_filter_opts == NULL) {
+ ret = confdb_get_string_as_list(pctx->rctx->cdb, pctx,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_RESPONSE_FILTER,
+ &pctx->pam_filter_opts);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read values of [%s], not fatal.\n",
+ CONFDB_PAM_RESPONSE_FILTER);
+ pctx->pam_filter_opts = NULL;
+ } else {
+ if (pctx->pam_filter_opts == NULL
+ || *pctx->pam_filter_opts[0] == '+'
+ || *pctx->pam_filter_opts[0] == '-') {
+ ret = mod_defaults_list(pctx, default_pam_response_filter,
+ pctx->pam_filter_opts, &new_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to modify [%s] defaults.\n",
+ CONFDB_PAM_RESPONSE_FILTER);
+ return ret;
+ }
+ talloc_free(pctx->pam_filter_opts);
+ pctx->pam_filter_opts = new_opts;
+ }
+ }
+
+ if (pctx->pam_filter_opts == NULL) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "No PAM response filter set.\n");
+ } else {
+ /* Make sure there are no '+' or '-' prefixes anymore */
+ for (c = 0; pctx->pam_filter_opts[c] != NULL; c++) {
+ if (*pctx->pam_filter_opts[0] == '+'
+ || *pctx->pam_filter_opts[0] == '-') {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unsupport mix of prefixed and not prefixed "
+ "values of [%s].\n", CONFDB_PAM_RESPONSE_FILTER);
+ return EINVAL;
+ }
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "PAM response filter: [%s].\n",
+ pctx->pam_filter_opts[c]);
+ }
+ }
+ }
+
+ resp = resp_list;
+ while(resp != NULL) {
+ if (resp->type == SSS_PAM_USER_INFO) {
+ if (resp->len < sizeof(uint32_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "User info entry is too short.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (pam_verbosity == PAM_VERBOSITY_NO_MESSAGES) {
+ resp->do_not_send_to_client = true;
+ resp = resp->next;
+ continue;
+ }
+
+ memcpy(&user_info_type, resp->data, sizeof(uint32_t));
+
+ resp->do_not_send_to_client = false;
+ switch (user_info_type) {
+ case SSS_PAM_USER_INFO_OFFLINE_AUTH:
+ if (resp->len != sizeof(uint32_t) + sizeof(int64_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "User info offline auth entry is "
+ "too short.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ memcpy(&expire_date, resp->data + sizeof(uint32_t),
+ sizeof(int64_t));
+ if ((expire_date == 0 &&
+ pam_verbosity < PAM_VERBOSITY_INFO) ||
+ (expire_date > 0 &&
+ pam_verbosity < PAM_VERBOSITY_IMPORTANT)) {
+ resp->do_not_send_to_client = true;
+ }
+
+ break;
+ default:
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "User info type [%d] not filtered.\n",
+ user_info_type);
+ }
+ } else if (resp->type == SSS_PAM_ENV_ITEM) {
+ resp->do_not_send_to_client = false;
+ ret = filter_responses_env(resp, pd, pctx->pam_filter_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "filter_responses_env failed.\n");
+ goto done;
+ }
+ } else if (resp->type & SSS_SERVER_INFO) {
+ resp->do_not_send_to_client = true;
+ }
+
+ resp = resp->next;
+ }
+
+ ret = EOK;
+done:
+
+ return ret;
+}
+
+static void do_not_send_cert_info(struct pam_data *pd)
+{
+ struct response_data *resp;
+
+ resp = pd->resp_list;
+ while (resp != NULL) {
+ switch (resp->type) {
+ case SSS_PAM_CERT_INFO:
+ case SSS_PAM_CERT_INFO_WITH_HINT:
+ resp->do_not_send_to_client = true;
+ break;
+ default:
+ break;
+ }
+ resp = resp->next;
+ }
+}
+
+static void evaluate_pam_resp_list(struct pam_data *pd,
+ struct pam_resp_auth_type *_auth_types,
+ bool *_found_cert_info)
+{
+ struct response_data *resp;
+ struct pam_resp_auth_type types = {0};
+ bool found_cert_info = false;
+
+ resp = pd->resp_list;
+ while (resp != NULL) {
+ switch (resp->type) {
+ case SSS_PAM_OTP_INFO:
+ types.otp_auth = true;
+ break;
+ case SSS_PAM_CERT_INFO:
+ case SSS_PAM_CERT_INFO_WITH_HINT:
+ found_cert_info = true;
+ break;
+ case SSS_PAM_PASSKEY_INFO:
+ case SSS_PAM_PASSKEY_KRB_INFO:
+ types.passkey_auth = true;
+ break;
+ case SSS_PASSWORD_PROMPTING:
+ types.password_auth = true;
+ break;
+ case SSS_CERT_AUTH_PROMPTING:
+ types.cert_auth = true;
+ break;
+ default:
+ break;
+ }
+ resp = resp->next;
+ }
+
+ if (_auth_types != NULL) {
+ *_auth_types = types;
+ }
+ if (_found_cert_info != NULL) {
+ *_found_cert_info = found_cert_info;
+ }
+}
+
+static void evalute_sending_cert_info(struct pam_data *pd)
+{
+ struct pam_resp_auth_type types = {0};
+ bool found_cert_info = false;
+
+ evaluate_pam_resp_list(pd, &types, &found_cert_info);
+
+ if (found_cert_info && !types.cert_auth) {
+ do_not_send_cert_info(pd);
+ }
+}
+
+errno_t pam_get_auth_types(struct pam_data *pd,
+ struct pam_resp_auth_type *_auth_types)
+{
+ int ret;
+ struct pam_resp_auth_type types = {0};
+
+ evaluate_pam_resp_list(pd, &types, NULL);
+
+ if (!types.password_auth && !types.otp_auth && !types.cert_auth && !types.passkey_auth) {
+ /* If the backend cannot determine which authentication types are
+ * available the default would be to prompt for a password. */
+ types.password_auth = true;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Authentication types for user [%s] and service "
+ "[%s]:%s%s%s%s\n", pd->user, pd->service,
+ types.password_auth ? " password": "",
+ types.otp_auth ? " two-factor" : "",
+ types.passkey_auth ? " passkey" : "",
+ types.cert_auth ? " smartcard" : "");
+
+ ret = EOK;
+
+ *_auth_types = types;
+
+ return ret;
+}
+
+static errno_t pam_eval_local_auth_policy(TALLOC_CTX *mem_ctx,
+ struct pam_ctx *pctx,
+ struct pam_data *pd,
+ struct pam_auth_req *preq,
+ bool *_sc_allow,
+ bool *_passkey_allow,
+ char **_local_policy) {
+
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ const char *domain_cdb;
+ char *local_policy = NULL;
+ bool sc_allow = false;
+ bool passkey_allow = false;
+ struct pam_resp_auth_type auth_types;
+ char **opts;
+ size_t c;
+
+#ifdef BUILD_FILES_PROVIDER
+ if (is_files_provider(preq->domain)) {
+ *_sc_allow = true;
+ *_passkey_allow = false;
+ *_local_policy = NULL;
+
+ return EOK;
+ }
+#endif
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Check local auth policy */
+ domain_cdb = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL, preq->domain->name);
+ if (domain_cdb == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, tmp_ctx, domain_cdb,
+ CONFDB_DOMAIN_LOCAL_AUTH_POLICY,
+ "match", &local_policy);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get the confdb local_auth_policy\n");
+ return ret;
+ }
+
+ /* "only" ignores online methods and allows all local ones */
+ if (strcasecmp(local_policy, "only") == 0) {
+ sc_allow = true;
+ passkey_allow = true;
+ /* Match what the KDC supports and provides */
+ } else if (strcasecmp(local_policy, "match") == 0) {
+ /* Don't overwrite the local auth type when offline */
+ if (pd->pam_status == PAM_SUCCESS && pd->cmd == SSS_PAM_PREAUTH &&
+ !is_domain_provider(preq->domain, "ldap")) {
+ ret = pam_get_auth_types(pd, &auth_types);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get authentication types\n");
+ goto done;
+ }
+
+ if (auth_types.cert_auth) {
+ sc_allow = true;
+ } else if (auth_types.passkey_auth) {
+ passkey_allow = true;
+ }
+
+ /* Store the local auth types, in case we go offline */
+ if (!auth_types.password_auth) {
+ ret = set_local_auth_type(preq, sc_allow, passkey_allow);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to evaluate local auth policy\n");
+ goto done;
+ }
+ }
+ }
+
+ /* Read the latest auth types */
+ ret = pam_get_local_auth_policy(preq->domain, preq->pd->user,
+ &sc_allow, &passkey_allow);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to get PAM local auth policy\n");
+ goto done;
+ }
+ /* Check for enable */
+ } else {
+ ret = split_on_separator(tmp_ctx, local_policy, ',', true, true, &opts,
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed [%d], %s.\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (c = 0; opts[c] != NULL; c++) {
+ if (strcasestr(opts[c], "passkey") != NULL) {
+ passkey_allow = strstr(opts[c], "enable") ? true : false;
+ } else if (strcasestr(opts[c], "smartcard") != NULL) {
+ sc_allow = strstr(opts[c], "enable") ? true : false;
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unexpected local auth policy option [%s], " \
+ "skipping.\n", opts[c]);
+ }
+ }
+ }
+
+ /* if passkey is enabled but local Smartcard authentication is not but
+ * possible, the cert info data has to be remove as well if only local
+ * Smartcard authentication is possible. If Smartcard authentication
+ * is possible on the server side we have to keep it because the
+ * 'enable' option should only add local methods but not reject remote
+ * ones. */
+ if (!sc_allow) {
+ evalute_sending_cert_info(pd);
+ }
+
+ *_sc_allow = sc_allow;
+ *_passkey_allow = passkey_allow;
+ *_local_policy = talloc_steal(mem_ctx, local_policy);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static void pam_reply_delay(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct pam_auth_req *preq;
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "pam_reply_delay get called.\n");
+
+ preq = talloc_get_type(pvt, struct pam_auth_req);
+
+ pam_reply(preq);
+}
+
+static errno_t get_password_for_cache_auth(struct sss_auth_token *authtok,
+ const char **password)
+{
+ int ret;
+ size_t pw_len;
+ const char *fa2;
+ size_t fa2_len;
+
+ switch (sss_authtok_get_type(authtok)) {
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ ret = sss_authtok_get_password(authtok, password, NULL);
+ break;
+ case SSS_AUTHTOK_TYPE_2FA:
+ ret = sss_authtok_get_2fa(authtok, password, &pw_len, &fa2, &fa2_len);
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unsupported auth token type [%d].\n",
+ sss_authtok_get_type(authtok));
+ ret = EINVAL;
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd);
+static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
+ time_t expire_date, time_t delayed_until, bool cached_auth);
+
+/*
+ * Add a request to add a variable to the PAM user environment, containing the
+ * actual (not overridden) user shell, in case session recording is enabled.
+ */
+static int pam_reply_sr_export_shell(struct pam_auth_req *preq,
+ const char *var_name)
+{
+ int ret;
+ TALLOC_CTX *ctx = NULL;
+ bool enabled;
+ const char *enabled_str;
+ const char *shell;
+ char *buf;
+
+ /* Create temporary talloc context */
+ ctx = talloc_new(NULL);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Check if session recording is enabled */
+ if (preq->cctx->rctx->sr_conf.scope ==
+ SESSION_RECORDING_SCOPE_NONE) {
+ enabled = false;
+ } else {
+ enabled_str = ldb_msg_find_attr_as_string(preq->user_obj,
+ SYSDB_SESSION_RECORDING, NULL);
+ if (enabled_str == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "%s attribute not found\n", SYSDB_SESSION_RECORDING);
+ ret = ENOENT;
+ goto done;
+ } else if (strcmp(enabled_str, "TRUE") == 0) {
+ enabled = true;
+ } else if (strcmp(enabled_str, "FALSE") == 0) {
+ enabled = false;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "invalid value of %s attribute: %s\n",
+ SYSDB_SESSION_RECORDING, enabled_str);
+ ret = ENOENT;
+ goto done;
+ }
+ }
+
+ /* Export original shell if recording is enabled and so it's overridden */
+ if (enabled) {
+ /* Extract the shell */
+ shell = sss_resp_get_shell_override(preq->user_obj,
+ preq->cctx->rctx, preq->domain);
+ if (shell == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "user has no shell\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* Format environment entry */
+ buf = talloc_asprintf(ctx, "%s=%s", var_name, shell);
+ if (buf == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add request to add the entry to user environment */
+ ret = pam_add_response(preq->pd, SSS_PAM_ENV_ITEM,
+ strlen(buf) + 1, (uint8_t *)buf);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+void pam_reply(struct pam_auth_req *preq)
+{
+ struct cli_ctx *cctx;
+ struct cli_protocol *prctx;
+ uint8_t *body;
+ size_t blen;
+ int ret;
+ int32_t resp_c;
+ int32_t resp_size;
+ struct response_data *resp;
+ int p;
+ struct timeval tv;
+ struct tevent_timer *te;
+ struct pam_data *pd;
+ char *local_policy = NULL;
+ struct pam_ctx *pctx;
+ uint32_t user_info_type;
+ time_t exp_date = -1;
+ time_t delay_until = -1;
+ char* pam_account_expired_message;
+ char* pam_account_locked_message;
+ int pam_verbosity;
+ bool local_sc_auth_allow = false;
+ bool local_passkey_auth_allow = false;
+#ifdef BUILD_PASSKEY
+ bool pk_preauth_done = false;
+#endif /* BUILD_PASSKEY */
+
+ pd = preq->pd;
+ cctx = preq->cctx;
+ pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+ prctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+
+ ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_VERBOSITY, DEFAULT_PAM_VERBOSITY,
+ &pam_verbosity);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read PAM verbosity, not fatal.\n");
+ pam_verbosity = DEFAULT_PAM_VERBOSITY;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "pam_reply initially called with result [%d]: %s. "
+ "this result might be changed during processing\n",
+ pd->pam_status, pam_strerror(NULL, pd->pam_status));
+
+ if (preq->domain != NULL && preq->domain->name != NULL) {
+ ret = pam_eval_local_auth_policy(cctx, pctx, pd, preq,
+ &local_sc_auth_allow,
+ &local_passkey_auth_allow,
+ &local_policy);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to evaluate local auth policy\n");
+ goto done;
+ }
+ }
+
+ /* Ignore local_auth_policy for the files provider, allow local
+ * smartcard auth (default behavior prior to local_auth_policy) */
+ if (is_domain_provider(preq->domain, "files")) {
+ local_sc_auth_allow = true;
+ /* For the ldap auth provider we currently only support
+ * password based authentication */
+ } else if (is_domain_provider(preq->domain, "ldap") && local_policy != NULL
+ && strcasecmp(local_policy, "match") == 0) {
+ local_passkey_auth_allow = false;
+ local_sc_auth_allow = false;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Local auth policy allowed: smartcard [%s], passkey [%s]\n",
+ local_sc_auth_allow ? "True" : "False",
+ local_passkey_auth_allow ? "True" : "False");
+
+ if (pd->cmd == SSS_PAM_AUTHENTICATE
+ && !preq->cert_auth_local
+ && (pd->pam_status == PAM_AUTHINFO_UNAVAIL
+ || pd->pam_status == PAM_NO_MODULE_DATA
+ || pd->pam_status == PAM_BAD_ITEM)
+ && may_do_cert_auth(pctx, pd)) {
+ /* We have Smartcard credentials and the backend indicates that it is
+ * offline (PAM_AUTHINFO_UNAVAIL) or cannot handle the credentials
+ * (PAM_BAD_ITEM), so let's try authentication against the Smartcard
+ * PAM_NO_MODULE_DATA is returned by the krb5 backend if no
+ * authentication method was found at all, this might happen if the
+ * user has a Smartcard assigned but the pkint plugin is not available
+ * on the client. */
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Backend cannot handle Smartcard authentication, "
+ "trying local Smartcard authentication.\n");
+ if (local_sc_auth_allow) {
+ preq->cert_auth_local = true;
+ ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
+ pam_check_user_done(preq, ret);
+ return;
+ } else {
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "Local smartcard auth not allowed by local_auth_policy");
+ }
+ }
+
+ if (pd->pam_status == PAM_AUTHINFO_UNAVAIL || preq->use_cached_auth) {
+
+ switch(pd->cmd) {
+ case SSS_PAM_AUTHENTICATE:
+ if ((preq->domain != NULL) &&
+ (preq->domain->cache_credentials == true) &&
+ (pd->offline_auth == false)) {
+ const char *password = NULL;
+ bool use_cached_auth;
+
+ /* backup value of preq->use_cached_auth*/
+ use_cached_auth = preq->use_cached_auth;
+ /* set to false to avoid entering this branch when pam_reply()
+ * is recursively called from pam_handle_cached_login() */
+ preq->use_cached_auth = false;
+
+ /* do auth with offline credentials */
+ pd->offline_auth = true;
+
+ if (preq->domain->sysdb == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Fatal: Sysdb CTX not found for domain"
+ " [%s]!\n", preq->domain->name);
+ goto done;
+ }
+
+ ret = get_password_for_cache_auth(pd->authtok, &password);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "get_password_and_type_for_cache_auth failed.\n");
+ goto done;
+ }
+
+ ret = sysdb_cache_auth(preq->domain,
+ pd->user, password,
+ pctx->rctx->cdb, false,
+ &exp_date, &delay_until);
+
+ pam_handle_cached_login(preq, ret, exp_date, delay_until,
+ use_cached_auth);
+ return;
+ }
+ break;
+ case SSS_PAM_CHAUTHTOK_PRELIM:
+ case SSS_PAM_CHAUTHTOK:
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Password change not possible while offline.\n");
+ pd->pam_status = PAM_AUTHTOK_ERR;
+ user_info_type = SSS_PAM_USER_INFO_OFFLINE_CHPASS;
+ ret = pam_add_response(pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
+ (const uint8_t *) &user_info_type);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+ goto done;
+ }
+ break;
+/* TODO: we need the pam session cookie here to make sure that cached
+ * authentication was successful */
+ case SSS_PAM_PREAUTH:
+ case SSS_PAM_SETCRED:
+ case SSS_PAM_ACCT_MGMT:
+ case SSS_PAM_OPEN_SESSION:
+ case SSS_PAM_CLOSE_SESSION:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Assuming offline authentication setting status for "
+ "pam call %d to PAM_SUCCESS.\n", pd->cmd);
+ pd->pam_status = PAM_SUCCESS;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown PAM call [%d].\n", pd->cmd);
+ pd->pam_status = PAM_MODULE_UNKNOWN;
+ }
+ }
+
+ if (pd->pam_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK) {
+ ret = pam_null_last_online_auth_with_curr_token(preq->domain,
+ pd->user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_null_last_online_auth_with_curr_token failed: "
+ "%s [%d].\n", sss_strerror(ret), ret);
+ goto done;
+ }
+ }
+
+ if (pd->response_delay > 0) {
+ ret = gettimeofday(&tv, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "gettimeofday failed [%d][%s].\n",
+ errno, strerror(errno));
+ goto done;
+ }
+ tv.tv_sec += pd->response_delay;
+ tv.tv_usec = 0;
+ pd->response_delay = 0;
+
+ te = tevent_add_timer(cctx->ev, cctx, tv, pam_reply_delay, preq);
+ if (te == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to add event pam_reply_delay.\n");
+ goto done;
+ }
+
+ return;
+ }
+
+ /* If this was a successful login, save the lastLogin time */
+ if (pd->cmd == SSS_PAM_AUTHENTICATE &&
+ pd->pam_status == PAM_SUCCESS &&
+ preq->domain &&
+ preq->domain->cache_credentials &&
+ !pd->offline_auth &&
+ !pd->last_auth_saved &&
+ !is_files_provider(preq->domain)) {
+ ret = set_last_login(preq);
+ if (ret != EOK) {
+ goto done;
+ }
+ return;
+ }
+
+ ret = sss_packet_new(prctx->creq, 0, sss_packet_get_cmd(prctx->creq->in),
+ &prctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Passkey auth user notification if no TGT is granted */
+ if (pd->cmd == SSS_PAM_AUTHENTICATE &&
+ pd->pam_status == PAM_SUCCESS &&
+ preq->pd->passkey_local_done) {
+ user_info_type = SSS_PAM_USER_INFO_NO_KRB_TGT;
+ pam_add_response(pd, SSS_PAM_USER_INFO,
+ sizeof(uint32_t), (const uint8_t *) &user_info_type);
+ DEBUG(SSSDBG_IMPORTANT_INFO,
+ "User [%s] logged in with local passkey authentication, single "
+ "sign on ticket is not obtained.\n", pd->user);
+ }
+
+ /* Account expiration warning is printed for sshd. If pam_verbosity
+ * is equal or above PAM_VERBOSITY_INFO then all services are informed
+ * about account expiration.
+ */
+ if (pd->pam_status == PAM_ACCT_EXPIRED &&
+ ((pd->service != NULL && strcasecmp(pd->service, "sshd") == 0) ||
+ pam_verbosity >= PAM_VERBOSITY_INFO)) {
+
+ ret = confdb_get_string(pctx->rctx->cdb, pd, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_ACCOUNT_EXPIRED_MESSAGE, "",
+ &pam_account_expired_message);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to get expiration message: %d:[%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ inform_user(pd, pam_account_expired_message);
+ }
+
+ if (pd->account_locked) {
+
+ ret = confdb_get_string(pctx->rctx->cdb, pd, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_ACCOUNT_LOCKED_MESSAGE, "",
+ &pam_account_locked_message);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to get expiration message: %d:[%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ inform_user(pd, pam_account_locked_message);
+ }
+
+ ret = filter_responses(pctx, pd->resp_list, pd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "filter_responses failed, not fatal.\n");
+ }
+
+ if (pd->domain != NULL) {
+ ret = pam_add_response(pd, SSS_PAM_DOMAIN_NAME, strlen(pd->domain)+1,
+ (uint8_t *) pd->domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+ goto done;
+ }
+ }
+
+ if (pd->cmd == SSS_PAM_PREAUTH) {
+ ret = pam_eval_prompting_config(pctx, pd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to add prompting information, "
+ "using defaults.\n");
+ }
+
+#ifdef BUILD_PASSKEY
+ ret = pam_eval_passkey_response(pctx, pd, preq, &pk_preauth_done);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to eval passkey response\n");
+ goto done;
+ }
+
+ if (may_do_passkey_auth(pctx, pd)
+ && !pk_preauth_done
+ && preq->passkey_data_exists
+ && local_passkey_auth_allow) {
+ ret = passkey_local(cctx, cctx->ev, pctx, preq, pd);
+ pam_check_user_done(preq, ret);
+ return;
+ }
+#endif /* BUILD_PASSKEY */
+ }
+
+ /*
+ * Export non-overridden shell to tlog-rec-session when opening the session
+ */
+ if (pd->cmd == SSS_PAM_OPEN_SESSION && pd->pam_status == PAM_SUCCESS) {
+ ret = pam_reply_sr_export_shell(preq, "TLOG_REC_SESSION_SHELL");
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "failed to export the shell to tlog-rec-session.\n");
+ goto done;
+ }
+ }
+
+ resp_c = 0;
+ resp_size = 0;
+ resp = pd->resp_list;
+ while(resp != NULL) {
+ if (!resp->do_not_send_to_client) {
+ resp_c++;
+ resp_size += resp->len;
+ }
+ resp = resp->next;
+ }
+
+ ret = sss_packet_grow(prctx->creq->out, sizeof(int32_t) +
+ sizeof(int32_t) +
+ resp_c * 2* sizeof(int32_t) +
+ resp_size);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(prctx->creq->out, &body, &blen);
+ DEBUG(SSSDBG_FUNC_DATA, "blen: %zu\n", blen);
+ p = 0;
+
+ memcpy(&body[p], &pd->pam_status, sizeof(int32_t));
+ p += sizeof(int32_t);
+
+ memcpy(&body[p], &resp_c, sizeof(int32_t));
+ p += sizeof(int32_t);
+
+ resp = pd->resp_list;
+ while(resp != NULL) {
+ if (!resp->do_not_send_to_client) {
+ memcpy(&body[p], &resp->type, sizeof(int32_t));
+ p += sizeof(int32_t);
+ memcpy(&body[p], &resp->len, sizeof(int32_t));
+ p += sizeof(int32_t);
+ memcpy(&body[p], resp->data, resp->len);
+ p += resp->len;
+ }
+
+ resp = resp->next;
+ }
+
+done:
+ DEBUG(SSSDBG_FUNC_DATA, "Returning [%d]: %s to the client\n",
+ pd->pam_status, pam_strerror(NULL, pd->pam_status));
+ sss_cmd_done(cctx, preq);
+}
+
+static void pam_dom_forwarder(struct pam_auth_req *preq);
+
+static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
+ time_t expire_date, time_t delayed_until,
+ bool use_cached_auth)
+{
+ uint32_t resp_type;
+ size_t resp_len;
+ uint8_t *resp;
+ int64_t dummy;
+
+ preq->pd->pam_status = cached_login_pam_status(ret);
+
+ switch (preq->pd->pam_status) {
+ case PAM_SUCCESS:
+ resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH;
+ resp_len = sizeof(uint32_t) + sizeof(int64_t);
+ resp = talloc_size(preq->pd, resp_len);
+ if (resp == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "talloc_size failed, cannot prepare user info.\n");
+ } else {
+ memcpy(resp, &resp_type, sizeof(uint32_t));
+ dummy = (int64_t) expire_date;
+ memcpy(resp+sizeof(uint32_t), &dummy, sizeof(int64_t));
+ ret = pam_add_response(preq->pd, SSS_PAM_USER_INFO, resp_len,
+ (const uint8_t *) resp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+ }
+ }
+ break;
+ case PAM_PERM_DENIED:
+ if (delayed_until >= 0) {
+ resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED;
+ resp_len = sizeof(uint32_t) + sizeof(int64_t);
+ resp = talloc_size(preq->pd, resp_len);
+ if (resp == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "talloc_size failed, cannot prepare user info.\n");
+ } else {
+ memcpy(resp, &resp_type, sizeof(uint32_t));
+ dummy = (int64_t) delayed_until;
+ memcpy(resp+sizeof(uint32_t), &dummy, sizeof(int64_t));
+ ret = pam_add_response(preq->pd, SSS_PAM_USER_INFO, resp_len,
+ (const uint8_t *) resp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pam_add_response failed.\n");
+ }
+ }
+ }
+ break;
+ case PAM_AUTH_ERR:
+ /* Was this attempt to authenticate from cache? */
+ if (use_cached_auth) {
+ /* Don't try cached authentication again, try online check. */
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Cached authentication failed for: %s\n",
+ preq->pd->user);
+ preq->cached_auth_failed = true;
+ pam_dom_forwarder(preq);
+ return;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "cached login returned: %d\n", preq->pd->pam_status);
+ }
+
+ pam_reply(preq);
+ return;
+}
+
+static void pam_forwarder_cb(struct tevent_req *req);
+static void pam_forwarder_cert_cb(struct tevent_req *req);
+int pam_check_user_search(struct pam_auth_req *preq);
+
+
+/* TODO: we should probably return some sort of cookie that is set in the
+ * PAM_ENVIRONMENT, so that we can save performing some calls and cache
+ * data. */
+
+static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *pd)
+{
+ struct cli_protocol *prctx;
+ uint8_t *body;
+ size_t blen;
+ errno_t ret;
+ uint32_t terminator;
+ const char *key_id;
+
+ prctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(prctx->creq->in, &body, &blen);
+ if (blen >= sizeof(uint32_t)) {
+ SAFEALIGN_COPY_UINT32(&terminator,
+ body + blen - sizeof(uint32_t),
+ NULL);
+ if (terminator != SSS_END_OF_PAM_REQUEST) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Received data not terminated.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ switch (prctx->cli_protocol_version->version) {
+ case 1:
+ ret = pam_parse_in_data(pd, body, blen);
+ break;
+ case 2:
+ ret = pam_parse_in_data_v2(pd, body, blen);
+ break;
+ case 3:
+ ret = pam_parse_in_data_v3(pd, body, blen);
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Illegal protocol version [%d].\n",
+ prctx->cli_protocol_version->version);
+ ret = EINVAL;
+ }
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (pd->logon_name != NULL) {
+ ret = sss_parse_name_for_domains(pd, cctx->rctx->domains,
+ cctx->rctx->default_domain,
+ pd->logon_name,
+ &pd->domain, &pd->user);
+ } else {
+ /* SSS_PAM_PREAUTH request may have a missing name, e.g. if the
+ * name is determined with the help of a certificate. During
+ * SSS_PAM_AUTHENTICATE at least a key ID is needed to identify the
+ * selected certificate. */
+ if (pd->cmd == SSS_PAM_AUTHENTICATE
+ && may_do_cert_auth(talloc_get_type(cctx->rctx->pvt_ctx,
+ struct pam_ctx), pd)
+ && (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
+ || sss_authtok_get_type(pd->authtok)
+ == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
+ ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, NULL, NULL, NULL,
+ NULL, &key_id, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+ goto done;
+ }
+
+ if (key_id == NULL || *key_id == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing logon and Smartcard key ID during "
+ "authentication.\n");
+ ret = ERR_NO_CREDS;
+ goto done;
+ }
+
+ ret = EOK;
+ } else if (pd->cmd == SSS_PAM_PREAUTH
+ && may_do_cert_auth(talloc_get_type(cctx->rctx->pvt_ctx,
+ struct pam_ctx), pd)) {
+ ret = EOK;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing logon name in PAM request.\n");
+ ret = ERR_NO_CREDS;
+ goto done;
+ }
+ }
+
+ DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd);
+
+done:
+ return ret;
+}
+
+static bool is_uid_trusted(struct cli_creds *creds,
+ size_t trusted_uids_count,
+ uid_t *trusted_uids)
+{
+ errno_t ret;
+
+ /* root is always trusted */
+ if (client_euid(creds) == 0) {
+ return true;
+ }
+
+ /* All uids are allowed */
+ if (trusted_uids_count == 0) {
+ return true;
+ }
+
+ ret = check_allowed_uids(client_euid(creds), trusted_uids_count, trusted_uids);
+ if (ret == EOK) return true;
+
+ return false;
+}
+
+static bool is_domain_public(char *name,
+ char **public_dom_names,
+ size_t public_dom_names_count)
+{
+ size_t i;
+
+ for(i=0; i < public_dom_names_count; i++) {
+ if (strcasecmp(name, public_dom_names[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static enum cache_req_dom_type
+get_domain_request_type(struct pam_auth_req *preq,
+ struct pam_ctx *pctx)
+{
+ enum cache_req_dom_type req_dom_type;
+
+ /* By default, only POSIX domains are to be contacted */
+ req_dom_type = CACHE_REQ_POSIX_DOM;
+
+ for (int i = 0; pctx->app_services[i]; i++) {
+ if (strcmp(pctx->app_services[i], preq->pd->service) == 0) {
+ req_dom_type = CACHE_REQ_APPLICATION_DOM;
+ break;
+ }
+ }
+
+ return req_dom_type;
+}
+
+static errno_t check_cert(TALLOC_CTX *mctx,
+ struct tevent_context *ev,
+ struct pam_ctx *pctx,
+ struct pam_auth_req *preq,
+ struct pam_data *pd)
+{
+ int p11_child_timeout;
+ int wait_for_card_timeout;
+ char *cert_verification_opts;
+ errno_t ret;
+ struct tevent_req *req;
+ char *uri = NULL;
+
+ ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_P11_CHILD_TIMEOUT,
+ P11_CHILD_TIMEOUT_DEFAULT,
+ &p11_child_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read p11_child_timeout from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+ if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) {
+ ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT,
+ P11_WAIT_FOR_CARD_TIMEOUT_DEFAULT,
+ &wait_for_card_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read [%s] from confdb: [%d]: %s\n",
+ CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT, ret, sss_strerror(ret));
+ return ret;
+ }
+
+ p11_child_timeout += wait_for_card_timeout;
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CERT_VERIFICATION,
+ NULL, &cert_verification_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read '"CONFDB_PAM_CERT_VERIFICATION"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (cert_verification_opts == NULL) {
+ ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_CERT_VERIFICATION, NULL,
+ &cert_verification_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read '"CONFDB_MONITOR_CERT_VERIFICATION"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_P11_URI, NULL, &uri);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read '"CONFDB_PAM_P11_URI"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ req = pam_check_cert_send(mctx, ev,
+ pctx->ca_db, p11_child_timeout,
+ cert_verification_opts, pctx->sss_certmap_ctx,
+ uri, pd);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_check_cert_send failed.\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(req, pam_forwarder_cert_cb, preq);
+ return EAGAIN;
+}
+
+
+static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd)
+{
+ struct pam_auth_req *preq;
+ struct pam_data *pd;
+ int ret;
+ struct pam_ctx *pctx =
+ talloc_get_type(cctx->rctx->pvt_ctx, struct pam_ctx);
+ struct tevent_req *req;
+
+ preq = talloc_zero(cctx, struct pam_auth_req);
+ if (!preq) {
+ return ENOMEM;
+ }
+ preq->cctx = cctx;
+ preq->cert_auth_local = false;
+ preq->client_id_num = cctx->client_id_num;
+
+ preq->pd = create_pam_data(preq);
+ if (!preq->pd) {
+ talloc_free(preq);
+ return ENOMEM;
+ }
+ pd = preq->pd;
+
+ preq->is_uid_trusted = is_uid_trusted(cctx->creds,
+ pctx->trusted_uids_count,
+ pctx->trusted_uids);
+
+ if (!preq->is_uid_trusted) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "uid %"SPRIuid" is not trusted.\n",
+ client_euid(cctx->creds));
+ }
+
+
+ pd->cmd = pam_cmd;
+ pd->priv = cctx->priv;
+ pd->client_id_num = cctx->client_id_num;
+
+ ret = pam_forwarder_parse_data(cctx, pd);
+ if (ret == EAGAIN) {
+ req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, pd->domain);
+ if (req == NULL) {
+ ret = ENOMEM;
+ } else {
+ tevent_req_set_callback(req, pam_forwarder_cb, preq);
+ ret = EAGAIN;
+ }
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ /* Determine what domain type to contact */
+ preq->req_dom_type = get_domain_request_type(preq, pctx);
+
+ if (pd->cmd == SSS_PAM_AUTHENTICATE
+ && (pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)
+ && !IS_SC_AUTHTOK(pd->authtok)) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Smartcard authentication required but authentication "
+ "token [%d][%s] is not suitable.\n",
+ sss_authtok_get_type(pd->authtok),
+ sss_authtok_type_to_str(sss_authtok_get_type(pd->authtok)));
+ ret = ERR_NO_CREDS;
+ goto done;
+ }
+
+ /* Try backend first for authentication before doing local Smartcard
+ * authentication if a logon name is available. Otherwise try to derive
+ * the logon name from the certificate first. */
+ if ((pd->cmd != SSS_PAM_AUTHENTICATE
+ || (pd->cmd == SSS_PAM_AUTHENTICATE && pd->logon_name == NULL))
+ && may_do_cert_auth(pctx, pd)) {
+ ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
+ /* Finish here */
+ goto done;
+ }
+
+ /* This is set to false inside passkey_local() if no passkey data is found.
+ * It is checked in pam_reply() to avoid an endless loop */
+ preq->passkey_data_exists = true;
+
+#ifdef BUILD_PASSKEY
+ if ((pd->cmd == SSS_PAM_AUTHENTICATE)) {
+ if (may_do_passkey_auth(pctx, pd)) {
+ if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_PASSKEY_KRB) {
+ ret = passkey_kerberos(pctx, preq->pd, preq);
+ goto done;
+ } else if ((sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_PASSKEY) ||
+ (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_EMPTY)) {
+ ret = passkey_local(cctx, cctx->ev, pctx, preq, pd);
+ goto done;
+ }
+ }
+ }
+#endif /* BUILD_PASSKEY */
+
+ ret = pam_check_user_search(preq);
+
+done:
+ return pam_check_user_done(preq, ret);
+}
+
+static errno_t pam_user_by_cert_step(struct pam_auth_req *preq);
+static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req);
+static void pam_forwarder_cert_cb(struct tevent_req *req)
+{
+ struct pam_auth_req *preq = tevent_req_callback_data(req,
+ struct pam_auth_req);
+ struct pam_data *pd;
+ errno_t ret = EOK;
+ const char *cert;
+
+ ret = pam_check_cert_recv(req, preq, &preq->cert_list);
+ talloc_free(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_cert request failed.\n");
+ goto done;
+ }
+
+ pd = preq->pd;
+
+ cert = sss_cai_get_cert(preq->cert_list);
+
+ if (cert == NULL) {
+ if (pd->logon_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "No certificate found and no logon name given, " \
+ "authentication not possible.\n");
+ ret = ENOENT;
+ } else if (pd->cmd == SSS_PAM_PREAUTH
+ && (pd->cli_flags & PAM_CLI_FLAGS_TRY_CERT_AUTH)) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "try_cert_auth flag set but no certificate available, "
+ "request finished.\n");
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ pam_reply(preq);
+ return;
+ } else {
+ if (pd->cmd == SSS_PAM_AUTHENTICATE) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "No certificate returned, authentication failed.\n");
+ preq->pd->pam_status = PAM_AUTH_ERR;
+ pam_reply(preq);
+ return;
+ } else {
+ ret = pam_check_user_search(preq);
+ }
+
+ }
+ goto done;
+ }
+
+ preq->current_cert = preq->cert_list;
+ ret = pam_user_by_cert_step(preq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n");
+ goto done;
+ }
+
+ return;
+
+done:
+ pam_check_user_done(preq, ret);
+}
+
+static errno_t pam_user_by_cert_step(struct pam_auth_req *preq)
+{
+ struct cli_ctx *cctx = preq->cctx;
+ struct tevent_req *req;
+ struct pam_ctx *pctx =
+ talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+
+ if (preq->current_cert == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate data.\n");
+ return EINVAL;
+ }
+
+ req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx,
+ pctx->rctx->ncache, 0,
+ preq->req_dom_type, NULL,
+ sss_cai_get_cert(preq->current_cert));
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(req, pam_forwarder_lookup_by_cert_done, preq);
+ return EOK;
+}
+
+static errno_t get_results_from_all_domains(TALLOC_CTX *mem_ctx,
+ struct cache_req_result **results,
+ struct ldb_result **ldb_results)
+{
+ int ret;
+ size_t count = 0;
+ size_t c;
+ size_t d;
+ size_t r = 0;
+ struct ldb_result *res;
+
+ for (d = 0; results != NULL && results[d] != NULL; d++) {
+ count += results[d]->count;
+ }
+
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ return ENOMEM;
+ }
+
+ if (count == 0) {
+ *ldb_results = res;
+ return EOK;
+ }
+
+ res->msgs = talloc_zero_array(res, struct ldb_message *, count);
+ if (res->msgs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ return ENOMEM;
+ }
+ res->count = count;
+
+ for (d = 0; results != NULL && results[d] != NULL; d++) {
+ for (c = 0; c < results[d]->count; c++) {
+ if (r >= count) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "More results found then counted before.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ res->msgs[r++] = talloc_steal(res->msgs, results[d]->msgs[c]);
+ }
+ }
+
+ *ldb_results = res;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(res);
+ }
+
+ return ret;
+}
+
+/* Return true if hint is set for at least one domain */
+static bool get_user_name_hint(struct sss_domain_info *domains)
+{
+ struct sss_domain_info *d;
+
+ DLIST_FOR_EACH(d, domains) {
+ if (d->user_name_hint == true) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
+{
+ int ret;
+ struct cache_req_result **results;
+ struct pam_auth_req *preq = tevent_req_callback_data(req,
+ struct pam_auth_req);
+ const char *cert_user = NULL;
+ size_t cert_count = 0;
+ size_t cert_user_count = 0;
+ struct ldb_result *cert_user_objs;
+
+ ret = cache_req_recv(preq, req, &results);
+ talloc_zfree(req);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert request failed.\n");
+ goto done;
+ }
+
+ if (ret == EOK) {
+ ret = get_results_from_all_domains(preq, results,
+ &cert_user_objs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_results_from_all_domains failed.\n");
+ goto done;
+ }
+
+ sss_cai_set_cert_user_objs(preq->current_cert, cert_user_objs);
+ }
+
+ preq->current_cert = sss_cai_get_next(preq->current_cert);
+ if (preq->current_cert != NULL) {
+ ret = pam_user_by_cert_step(preq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n");
+ goto done;
+ }
+ return;
+ }
+
+ sss_cai_check_users(&preq->cert_list, &cert_count, &cert_user_count);
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Found [%zu] certificates and [%zu] related users.\n",
+ cert_count, cert_user_count);
+
+ if (cert_user_count == 0) {
+ if (preq->pd->logon_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing logon name and no certificate user found.\n");
+ ret = ENOENT;
+ goto done;
+ }
+ } else {
+
+ if (preq->pd->logon_name == NULL) {
+ if (preq->pd->cmd != SSS_PAM_PREAUTH
+ && preq->pd->cmd != SSS_PAM_AUTHENTICATE) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Missing logon name only allowed during (pre-)auth.\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (cert_count > 1) {
+ for (preq->current_cert = preq->cert_list;
+ preq->current_cert != NULL;
+ preq->current_cert = sss_cai_get_next(preq->current_cert)) {
+
+ ret = add_pam_cert_response(preq->pd,
+ preq->cctx->rctx->domains, "",
+ preq->current_cert,
+ get_user_name_hint(preq->cctx->rctx->domains)
+ ? SSS_PAM_CERT_INFO_WITH_HINT
+ : SSS_PAM_CERT_INFO);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "add_pam_cert_response failed.\n");
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ }
+ }
+
+ ret = EOK;
+ preq->pd->pam_status = PAM_SUCCESS;
+ pam_reply(preq);
+ goto done;
+ }
+
+ if (cert_user_count == 1) {
+ cert_user_objs = sss_cai_get_cert_user_objs(preq->cert_list);
+ if (cert_user_objs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate user.\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ cert_user = ldb_msg_find_attr_as_string(
+ cert_user_objs->msgs[0],
+ SYSDB_NAME, NULL);
+ if (cert_user == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Certificate user object has not name.\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Found certificate user [%s].\n", cert_user);
+
+ ret = sss_parse_name_for_domains(preq->pd,
+ preq->cctx->rctx->domains,
+ preq->cctx->rctx->default_domain,
+ cert_user,
+ &preq->pd->domain,
+ &preq->pd->user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_parse_name_for_domains failed.\n");
+ goto done;
+ }
+ }
+
+ if (get_user_name_hint(preq->cctx->rctx->domains)
+ && preq->pd->cmd == SSS_PAM_PREAUTH) {
+ ret = add_pam_cert_response(preq->pd,
+ preq->cctx->rctx->domains, cert_user,
+ preq->cert_list,
+ SSS_PAM_CERT_INFO_WITH_HINT);
+ preq->pd->pam_status = PAM_SUCCESS;
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ }
+ ret = EOK;
+ pam_reply(preq);
+ goto done;
+ }
+
+ /* Without user name hints the certificate must map to single user
+ * if no login name was given */
+ if (cert_user == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "More than one user mapped to certificate.\n");
+ ret = ERR_NO_CREDS;
+ goto done;
+ }
+
+ /* If logon_name was not given during authentication add a
+ * SSS_PAM_CERT_INFO message to send the name to the caller. */
+ if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
+ && preq->pd->logon_name == NULL) {
+ ret = add_pam_cert_response(preq->pd,
+ preq->cctx->rctx->domains, cert_user,
+ preq->cert_list,
+ SSS_PAM_CERT_INFO);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ goto done;
+ }
+ }
+
+ /* cert_user will be returned to the PAM client as user name, so
+ * we can use it here already e.g. to set in initgroups timeout */
+ preq->pd->logon_name = talloc_strdup(preq->pd, cert_user);
+ if (preq->pd->logon_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ if (preq->user_obj == NULL) {
+ ret = pam_check_user_search(preq);
+ } else {
+ ret = EOK;
+ }
+
+ if (ret == EOK) {
+ pam_dom_forwarder(preq);
+ }
+
+done:
+ pam_check_user_done(preq, ret);
+}
+
+static void pam_forwarder_cb(struct tevent_req *req)
+{
+ struct pam_auth_req *preq = tevent_req_callback_data(req,
+ struct pam_auth_req);
+ struct cli_ctx *cctx = preq->cctx;
+ struct pam_data *pd;
+ errno_t ret = EOK;
+ struct pam_ctx *pctx =
+ talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+
+ ret = sss_dp_get_domains_recv(req);
+ talloc_free(req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "p11_refresh_certmap_ctx failed, "
+ "certificate matching might not work as expected");
+ }
+
+ pd = preq->pd;
+
+ ret = pam_forwarder_parse_data(cctx, pd);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Assuming %s is a UPN\n", pd->logon_name);
+ /* If not, cache_req will error out later */
+ pd->user = talloc_strdup(pd, pd->logon_name);
+ if (pd->user == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ pd->domain = NULL;
+ } else if (ret != EOK) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* try backend first for authentication before doing local Smartcard
+ * authentication */
+ if (pd->cmd != SSS_PAM_AUTHENTICATE && may_do_cert_auth(pctx, pd)) {
+ ret = check_cert(cctx, cctx->ev, pctx, preq, pd);
+ /* Finish here */
+ goto done;
+ }
+
+#ifdef BUILD_PASSKEY
+ /* This is set to false inside passkey_local() if no passkey data is found.
+ * It is checked in pam_reply() to avoid an endless loop */
+ preq->passkey_data_exists = true;
+
+ if ((pd->cmd == SSS_PAM_AUTHENTICATE)) {
+ if (may_do_passkey_auth(pctx, pd)) {
+ if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_PASSKEY_KRB) {
+ ret = passkey_kerberos(pctx, preq->pd, preq);
+ goto done;
+ } else if ((sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_PASSKEY) ||
+ (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_EMPTY)) {
+ ret = passkey_local(cctx, cctx->ev, pctx, preq, pd);
+ goto done;
+ }
+ }
+ }
+#endif /* BUILD_PASSKEY */
+
+ ret = pam_check_user_search(preq);
+
+done:
+ pam_check_user_done(preq, ret);
+}
+
+static void pam_check_user_search_next(struct tevent_req *req);
+static void pam_check_user_search_lookup(struct tevent_req *req);
+static void pam_check_user_search_done(struct pam_auth_req *preq, int ret,
+ struct cache_req_result *result);
+
+/* lookup the user uid from the cache first,
+ * then we'll refresh initgroups if needed */
+int pam_check_user_search(struct pam_auth_req *preq)
+{
+ struct tevent_req *dpreq;
+ struct cache_req_data *data;
+
+ data = cache_req_data_name(preq,
+ CACHE_REQ_INITGROUPS,
+ preq->pd->logon_name);
+ if (data == NULL) {
+ return ENOMEM;
+ }
+
+ cache_req_data_set_bypass_cache(data, false);
+ cache_req_data_set_bypass_dp(data, true);
+ cache_req_data_set_requested_domains(data, preq->pd->requested_domains);
+
+ dpreq = cache_req_send(preq,
+ preq->cctx->rctx->ev,
+ preq->cctx->rctx,
+ preq->cctx->rctx->ncache,
+ 0,
+ preq->req_dom_type,
+ NULL,
+ data);
+ if (!dpreq) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Out of memory sending data provider request\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(dpreq, pam_check_user_search_next, preq);
+
+ /* tell caller we are in an async call */
+ return EAGAIN;
+}
+
+static void pam_check_user_search_next(struct tevent_req *req)
+{
+ struct pam_auth_req *preq;
+ struct pam_ctx *pctx;
+ struct cache_req_result *result = NULL;
+ struct cache_req_data *data;
+ struct tevent_req *dpreq;
+ int ret;
+
+ preq = tevent_req_callback_data(req, struct pam_auth_req);
+ pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+
+ ret = cache_req_single_domain_recv(preq, req, &result);
+ talloc_zfree(req);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cache lookup failed, trying to get fresh "
+ "data from the backend.\n");
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "PAM initgroups scheme [%s].\n",
+ pam_initgroup_enum_to_string(pctx->initgroups_scheme));
+
+ if (ret == EOK) {
+ bool user_has_session = false;
+
+ if (pctx->initgroups_scheme == PAM_INITGR_NO_SESSION) {
+ uid_t uid = ldb_msg_find_attr_as_uint64(result->msgs[0],
+ SYSDB_UIDNUM, 0);
+ if (!uid) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A user with no UID?\n");
+ talloc_zfree(preq->cctx);
+ return;
+ }
+
+ /* If a user already has a session on the system, we take the
+ * cache for granted and do not force an online lookup. This is
+ * because in most cases the user is just trying to authenticate
+ * but not create a new session (sudo, lockscreen, polkit, etc.)
+ * An online refresh in this situation would just delay operations
+ * without providing any useful additional information.
+ */
+ (void)check_if_uid_is_active(uid, &user_has_session);
+
+ DEBUG(SSSDBG_TRACE_ALL, "Found %s session for uid %"SPRIuid".\n",
+ user_has_session ? "a" : "no", uid);
+ }
+
+ /* The initgr cache is used to make sure that during a single PAM
+ * session (auth, acct_mgtm, ....) the backend is contacted only
+ * once. logon_name is the name provided by the PAM client and
+ * will not be modified during the request, so it makes sense to
+ * use it here instead od the pd->user.
+ */
+ ret = pam_initgr_check_timeout(pctx->id_table, preq->pd->logon_name);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not look up initgroup timeout\n");
+ }
+
+ if ((ret == EOK) || user_has_session
+ || pctx->initgroups_scheme == PAM_INITGR_NEVER) {
+ DEBUG(SSSDBG_TRACE_ALL, "No new initgroups needed because:\n");
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_ALL, "PAM initgr cache still valid.\n");
+ } else if (user_has_session) {
+ DEBUG(SSSDBG_TRACE_ALL, "there is a active session for "
+ "user [%s].\n", preq->pd->logon_name);
+ } else if (pctx->initgroups_scheme == PAM_INITGR_NEVER) {
+ DEBUG(SSSDBG_TRACE_ALL, "initgroups scheme is 'never'.\n");
+ }
+ pam_check_user_search_done(preq, EOK, result);
+ return;
+ }
+ }
+
+ /* If we get here it means the user was not found or does not have a
+ * session, or initgr has not been cached before, so we force a new
+ * online lookup */
+ data = cache_req_data_name(preq,
+ CACHE_REQ_INITGROUPS,
+ preq->pd->logon_name);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
+ talloc_zfree(preq->cctx);
+ return;
+ }
+ cache_req_data_set_bypass_cache(data, true);
+ cache_req_data_set_bypass_dp(data, false);
+ cache_req_data_set_requested_domains(data, preq->pd->requested_domains);
+
+ dpreq = cache_req_send(preq,
+ preq->cctx->rctx->ev,
+ preq->cctx->rctx,
+ preq->cctx->rctx->ncache,
+ 0,
+ preq->req_dom_type,
+ NULL,
+ data);
+ if (!dpreq) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Out of memory sending data provider request\n");
+ talloc_zfree(preq->cctx);
+ return;
+ }
+
+ tevent_req_set_callback(dpreq, pam_check_user_search_lookup, preq);
+}
+
+static void pam_check_user_search_lookup(struct tevent_req *req)
+{
+ struct cache_req_result *result;
+ struct pam_auth_req *preq;
+ int ret;
+
+ preq = tevent_req_callback_data(req, struct pam_auth_req);
+
+ ret = cache_req_single_domain_recv(preq, req, &result);
+ talloc_zfree(req);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Fatal error, killing connection!\n");
+ talloc_zfree(preq->cctx);
+ return;
+ }
+
+ pam_check_user_search_done(preq, ret, result);
+}
+
+static void pam_check_user_search_done(struct pam_auth_req *preq, int ret,
+ struct cache_req_result *result)
+{
+ struct pam_ctx *pctx;
+
+ pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+
+ if (ret == EOK) {
+ preq->user_obj = result->msgs[0];
+ pd_set_primary_name(preq->user_obj, preq->pd);
+ preq->domain = result->domain;
+
+ ret = pam_initgr_cache_set(pctx->rctx->ev,
+ pctx->id_table,
+ preq->pd->logon_name,
+ pctx->id_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not save initgr timestamp."
+ "Proceeding with PAM actions\n");
+ }
+
+ pam_dom_forwarder(preq);
+ }
+
+ ret = pam_check_user_done(preq, ret);
+ if (ret != EOK) {
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ }
+}
+
+int pam_check_user_done(struct pam_auth_req *preq, int ret)
+{
+ switch (ret) {
+ case EOK:
+ break;
+
+ case EAGAIN:
+ /* performing async request, just return */
+ break;
+
+ case ENOENT:
+ preq->pd->pam_status = PAM_USER_UNKNOWN;
+ pam_reply(preq);
+ break;
+
+ case ERR_P11_PIN_LOCKED:
+ preq->pd->pam_status = PAM_AUTH_ERR;
+ pam_reply(preq);
+ break;
+
+ case ERR_NO_CREDS:
+ preq->pd->pam_status = PAM_CRED_INSUFFICIENT;
+ pam_reply(preq);
+ break;
+
+ default:
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ break;
+ }
+
+ return EOK;
+}
+
+static errno_t pam_is_last_online_login_fresh(struct sss_domain_info *domain,
+ const char* user,
+ int cached_auth_timeout,
+ bool *_result)
+{
+ errno_t ret;
+ bool result = true;
+ uint64_t last_login;
+
+ ret = pam_get_last_online_auth_with_curr_token(domain, user, &last_login);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sysdb_get_last_online_auth_with_curr_token failed: %s:[%d]\n",
+ sss_strerror(ret), ret);
+ goto done;
+ }
+
+ result = time(NULL) < (last_login + cached_auth_timeout);
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_result = result;
+ }
+ return ret;
+}
+
+static bool pam_is_authtok_cachable(struct sss_auth_token *authtok)
+{
+ enum sss_authtok_type type;
+ bool cachable = false;
+
+ type = sss_authtok_get_type(authtok);
+ if (type == SSS_AUTHTOK_TYPE_PASSWORD) {
+ cachable = true;
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS, "Authentication token can't be cached\n");
+ }
+
+ return cachable;
+}
+
+static bool pam_can_user_cache_auth(struct sss_domain_info *domain,
+ int pam_cmd,
+ struct sss_auth_token *authtok,
+ const char* user,
+ bool cached_auth_failed)
+{
+ errno_t ret;
+ bool result = false;
+
+ if (cached_auth_failed) {
+ /* Do not retry indefinitely */
+ return false;
+ }
+
+ if (!domain->cache_credentials || domain->cached_auth_timeout <= 0) {
+ return false;
+ }
+
+ if (pam_cmd == SSS_PAM_PREAUTH
+ || (pam_cmd == SSS_PAM_AUTHENTICATE
+ && pam_is_authtok_cachable(authtok))) {
+
+ ret = pam_is_last_online_login_fresh(domain, user,
+ domain->cached_auth_timeout,
+ &result);
+ if (ret != EOK) {
+ /* non-critical, consider fail as 'non-fresh value' */
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "pam_is_last_online_login_fresh failed: %s:[%d]\n",
+ sss_strerror(ret), ret);
+ }
+ }
+
+ return result;
+}
+
+static void pam_dom_forwarder(struct pam_auth_req *preq)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ int ret;
+ struct pam_ctx *pctx =
+ talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+ const char *cert_user;
+ struct ldb_result *cert_user_objs;
+ bool sc_auth;
+ bool passkey_auth;
+ size_t c;
+ char *local_policy = NULL;
+ bool found = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ if (!preq->pd->domain) {
+ preq->pd->domain = preq->domain->name;
+ }
+
+ /* Untrusted users can access only public domains. */
+ if (!preq->is_uid_trusted &&
+ !is_domain_public(preq->pd->domain, pctx->public_domains,
+ pctx->public_domains_count)) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Untrusted user %"SPRIuid" cannot access non-public domain %s.\n",
+ client_euid(preq->cctx->creds), preq->pd->domain);
+ preq->pd->pam_status = PAM_PERM_DENIED;
+ pam_reply(preq);
+ return;
+ }
+
+ /* skip this domain if not requested and the user is trusted
+ * as untrusted users can't request a domain */
+ if (preq->is_uid_trusted &&
+ !is_domain_requested(preq->pd, preq->pd->domain)) {
+ preq->pd->pam_status = PAM_USER_UNKNOWN;
+ pam_reply(preq);
+ return;
+ }
+
+ if (pam_can_user_cache_auth(preq->domain,
+ preq->pd->cmd,
+ preq->pd->authtok,
+ preq->pd->user,
+ preq->cached_auth_failed)) {
+ preq->use_cached_auth = true;
+ pam_reply(preq);
+ return;
+ }
+
+ /* Skip online auth when local auth policy = only */
+#ifdef BUILD_PASSKEY
+ if (may_do_cert_auth(pctx, preq->pd) || may_do_passkey_auth(pctx, preq->pd)) {
+#else
+ if (may_do_cert_auth(pctx, preq->pd)) {
+#endif /* BUILD_PASSKEY */
+ if (preq->domain->name != NULL) {
+ ret = pam_eval_local_auth_policy(preq->cctx, pctx, preq->pd, preq,
+ &sc_auth,
+ &passkey_auth,
+ &local_policy);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to evaluate local auth policy\n");
+ preq->pd->pam_status = PAM_AUTH_ERR;
+ pam_reply(preq);
+ return;
+ }
+ }
+ }
+
+ if (may_do_cert_auth(pctx, preq->pd) && preq->cert_list != NULL) {
+ /* Check if user matches certificate user */
+ found = false;
+ for (preq->current_cert = preq->cert_list;
+ preq->current_cert != NULL;
+ preq->current_cert = sss_cai_get_next(preq->current_cert)) {
+
+ cert_user_objs = sss_cai_get_cert_user_objs(preq->current_cert);
+ if (cert_user_objs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unexpected missing certificate user, "
+ "trying next certificate.\n");
+ continue;
+ }
+
+ for (c = 0; c < cert_user_objs->count; c++) {
+ cert_user = ldb_msg_find_attr_as_string(cert_user_objs->msgs[c],
+ SYSDB_NAME, NULL);
+ if (cert_user == NULL) {
+ /* Even if there might be other users mapped to the
+ * certificate a missing SYSDB_NAME indicates some critical
+ * condition which justifies that the whole request is aborted
+ * */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Certificate user object has no name.\n");
+ preq->pd->pam_status = PAM_USER_UNKNOWN;
+ pam_reply(preq);
+ return;
+ }
+
+ if (ldb_dn_compare(cert_user_objs->msgs[c]->dn,
+ preq->user_obj->dn) == 0) {
+ found = true;
+ if (preq->pd->cmd == SSS_PAM_PREAUTH) {
+ ret = sss_authtok_set_sc(preq->pd->authtok,
+ SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0,
+ sss_cai_get_token_name(preq->current_cert), 0,
+ sss_cai_get_module_name(preq->current_cert), 0,
+ sss_cai_get_key_id(preq->current_cert), 0,
+ sss_cai_get_label(preq->current_cert), 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_authtok_set_sc failed, Smartcard "
+ "authentication detection might fail in "
+ "the backend.\n");
+ }
+
+ ret = add_pam_cert_response(preq->pd,
+ preq->cctx->rctx->domains,
+ cert_user,
+ preq->current_cert,
+ SSS_PAM_CERT_INFO);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ }
+ }
+
+ }
+ }
+ }
+
+ if (found) {
+ if (local_policy != NULL && strcasecmp(local_policy, "only") == 0) {
+ talloc_free(tmp_ctx);
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Local auth only set, skipping online auth\n");
+ if (preq->pd->cmd == SSS_PAM_PREAUTH) {
+ preq->pd->pam_status = PAM_SUCCESS;
+ } else if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
+ && IS_SC_AUTHTOK(preq->pd->authtok)
+ && preq->cert_auth_local) {
+ preq->pd->pam_status = PAM_SUCCESS;
+ preq->callback = pam_reply;
+ }
+
+ pam_reply(preq);
+ return;
+ }
+
+ /* We are done if we do not have to call the backend */
+ if (preq->pd->cmd == SSS_PAM_AUTHENTICATE
+ && preq->cert_auth_local) {
+ preq->pd->pam_status = PAM_SUCCESS;
+ preq->callback = pam_reply;
+ pam_reply(preq);
+ return;
+ }
+ } else {
+ if (preq->pd->cmd == SSS_PAM_PREAUTH) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "User and certificate user do not match, "
+ "continue with other authentication methods.\n");
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "User and certificate user do not match.\n");
+ preq->pd->pam_status = PAM_AUTH_ERR;
+ pam_reply(preq);
+ return;
+ }
+ }
+ }
+
+ if (local_policy != NULL && strcasecmp(local_policy, "only") == 0) {
+ talloc_free(tmp_ctx);
+ DEBUG(SSSDBG_IMPORTANT_INFO, "Local auth only set, skipping online auth\n");
+ if (preq->pd->cmd == SSS_PAM_PREAUTH) {
+ preq->pd->pam_status = PAM_SUCCESS;
+ } else if (preq->pd->cmd == SSS_PAM_AUTHENTICATE && IS_SC_AUTHTOK(preq->pd->authtok)) {
+ /* Trigger offline smartcardcard autheitcation */
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ }
+
+ pam_reply(preq);
+ return;
+ }
+
+ preq->callback = pam_reply;
+ ret = pam_dp_send_req(preq);
+ DEBUG(SSSDBG_CONF_SETTINGS, "pam_dp_send_req returned %d\n", ret);
+
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ pam_reply(preq);
+ }
+}
+
+static int pam_cmd_authenticate(struct cli_ctx *cctx) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_authenticate\n");
+ return pam_forwarder(cctx, SSS_PAM_AUTHENTICATE);
+}
+
+static int pam_cmd_setcred(struct cli_ctx *cctx) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_setcred\n");
+ return pam_forwarder(cctx, SSS_PAM_SETCRED);
+}
+
+static int pam_cmd_acct_mgmt(struct cli_ctx *cctx) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_acct_mgmt\n");
+ return pam_forwarder(cctx, SSS_PAM_ACCT_MGMT);
+}
+
+static int pam_cmd_open_session(struct cli_ctx *cctx) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_open_session\n");
+ return pam_forwarder(cctx, SSS_PAM_OPEN_SESSION);
+}
+
+static int pam_cmd_close_session(struct cli_ctx *cctx) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_close_session\n");
+ return pam_forwarder(cctx, SSS_PAM_CLOSE_SESSION);
+}
+
+static int pam_cmd_chauthtok(struct cli_ctx *cctx) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_chauthtok\n");
+ return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK);
+}
+
+static int pam_cmd_chauthtok_prelim(struct cli_ctx *cctx) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_chauthtok_prelim\n");
+ return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK_PRELIM);
+}
+
+static int pam_cmd_preauth(struct cli_ctx *cctx)
+{
+ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_preauth\n");
+ return pam_forwarder(cctx, SSS_PAM_PREAUTH);
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version pam_cli_protocol_version[] = {
+ {3, "2009-09-14", "make cli_pid mandatory"},
+ {2, "2009-05-12", "new format <type><size><data>"},
+ {1, "2008-09-05", "initial version, \\0 terminated strings"},
+ {0, NULL, NULL}
+ };
+
+ return pam_cli_protocol_version;
+}
+
+struct sss_cmd_table *get_pam_cmds(void)
+{
+ static struct sss_cmd_table sss_cmds[] = {
+ {SSS_GET_VERSION, sss_cmd_get_version},
+ {SSS_PAM_AUTHENTICATE, pam_cmd_authenticate},
+ {SSS_PAM_SETCRED, pam_cmd_setcred},
+ {SSS_PAM_ACCT_MGMT, pam_cmd_acct_mgmt},
+ {SSS_PAM_OPEN_SESSION, pam_cmd_open_session},
+ {SSS_PAM_CLOSE_SESSION, pam_cmd_close_session},
+ {SSS_PAM_CHAUTHTOK, pam_cmd_chauthtok},
+ {SSS_PAM_CHAUTHTOK_PRELIM, pam_cmd_chauthtok_prelim},
+ {SSS_PAM_PREAUTH, pam_cmd_preauth},
+ {SSS_GSSAPI_INIT, pam_cmd_gssapi_init},
+ {SSS_GSSAPI_SEC_CTX, pam_cmd_gssapi_sec_ctx},
+ {SSS_CLI_NULL, NULL}
+ };
+
+ return sss_cmds;
+}
+
+errno_t
+pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *username,
+ uint64_t value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs *attrs;
+ int ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ attrs = sysdb_new_attrs(tmp_ctx);
+ if (attrs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_time_t(attrs,
+ SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN,
+ value);
+ if (ret != EOK) { goto done; }
+
+ ret = sysdb_set_user_attr(domain, username, attrs, SYSDB_MOD_REP);
+ if (ret != EOK) { goto done; }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Error: %d (%s)\n", ret, sss_strerror(ret));
+ }
+
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+pam_null_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *username)
+{
+ return pam_set_last_online_auth_with_curr_token(domain, username, 0);
+}
+
+static errno_t
+pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *name,
+ uint64_t *_value)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *attrs[] = { SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN, NULL };
+ struct ldb_message *ldb_msg;
+ uint64_t value = 0;
+ errno_t ret;
+
+ if (name == NULL || *name == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing user name.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (domain->sysdb == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing sysdb db context.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, name, attrs, &ldb_msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_search_user_by_name failed [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ /* Check offline_auth_cache_timeout */
+ value = ldb_msg_find_attr_as_uint64(ldb_msg,
+ SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN,
+ 0);
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_value = value;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
diff --git a/src/responder/pam/pamsrv_dp.c b/src/responder/pam/pamsrv_dp.c
new file mode 100644
index 0000000..881352e
--- /dev/null
+++ b/src/responder/pam/pamsrv_dp.c
@@ -0,0 +1,106 @@
+/*
+ SSSD
+
+ NSS Responder - Data Provider Interfaces
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+#include <security/pam_modules.h>
+
+#include "util/util.h"
+#include "util/sss_pam_data.h"
+#include "responder/pam/pamsrv.h"
+#include "sss_iface/sss_iface_async.h"
+
+static void
+pam_dp_send_req_done(struct tevent_req *subreq);
+
+errno_t
+pam_dp_send_req(struct pam_auth_req *preq)
+{
+ struct tevent_req *subreq;
+ struct be_conn *be_conn;
+ errno_t ret;
+
+ ret = sss_dp_get_domain_conn(preq->cctx->rctx, preq->domain->conn_name,
+ &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "The Data Provider connection for %s is not "
+ "available! This maybe a bug, it shouldn't happen!\n",
+ preq->domain->conn_name);
+ return EIO;
+ }
+
+ DEBUG(SSSDBG_CONF_SETTINGS, "Sending request with the following data:\n");
+ DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, preq->pd);
+
+ subreq = sbus_call_dp_dp_pamHandler_send(preq, be_conn->conn,
+ be_conn->bus_name, SSS_BUS_PATH, preq->pd);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, pam_dp_send_req_done, preq);
+
+ return EOK;
+}
+
+static void
+pam_dp_send_req_done(struct tevent_req *subreq)
+{
+ struct pam_data *pam_response;
+ struct response_data *resp;
+ struct pam_auth_req *preq;
+ errno_t ret;
+
+ preq = tevent_req_callback_data(subreq, struct pam_auth_req);
+
+ ret = sbus_call_dp_dp_pamHandler_recv(preq, subreq, &pam_response);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "PAM handler failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ preq->pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+
+ preq->pd->pam_status = pam_response->pam_status;
+ preq->pd->account_locked = pam_response->account_locked;
+
+ DEBUG(SSSDBG_FUNC_DATA, "received: [%d (%s)][%s]\n",
+ pam_response->pam_status,
+ pam_strerror(NULL, pam_response->pam_status),
+ preq->pd->domain);
+
+ for (resp = pam_response->resp_list; resp != NULL; resp = resp->next) {
+ talloc_steal(preq->pd, resp);
+
+ if (resp->next == NULL) {
+ resp->next = preq->pd->resp_list;
+ preq->pd->resp_list = pam_response->resp_list;
+ break;
+ }
+ }
+
+ talloc_zfree(pam_response);
+
+done:
+ preq->callback(preq);
+}
diff --git a/src/responder/pam/pamsrv_gssapi.c b/src/responder/pam/pamsrv_gssapi.c
new file mode 100644
index 0000000..e4da4c4
--- /dev/null
+++ b/src/responder/pam/pamsrv_gssapi.c
@@ -0,0 +1,1043 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2020 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 <errno.h>
+#include <gssapi.h>
+#include <gssapi/gssapi_ext.h>
+#include <gssapi/gssapi_krb5.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+#include <ldb.h>
+
+#include "confdb/confdb.h"
+#include "db/sysdb.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/pam/pamsrv.h"
+#include "sss_client/sss_cli.h"
+#include "util/util.h"
+#include "util/sss_utf8.h"
+
+static errno_t read_str(size_t body_len,
+ uint8_t *body,
+ size_t *pctr,
+ const char **_str)
+{
+ size_t i;
+
+ for (i = *pctr; i < body_len && body[i] != 0; i++) {
+ /* counting */
+ }
+
+ if (i >= body_len) {
+ return EINVAL;
+ }
+
+ if (!sss_utf8_check(&body[*pctr], i - *pctr)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not UTF-8 string!\n");
+ return EINVAL;
+ }
+
+ *_str = (const char *)&body[*pctr];
+ *pctr = i + 1;
+
+ return EOK;
+}
+
+static bool pam_gssapi_should_check_upn(struct pam_ctx *pam_ctx,
+ struct sss_domain_info *domain)
+{
+ if (domain->gssapi_check_upn != NULL) {
+ if (strcasecmp(domain->gssapi_check_upn, "true") == 0) {
+ return true;
+ }
+
+ if (strcasecmp(domain->gssapi_check_upn, "false") == 0) {
+ return false;
+ }
+
+ DEBUG(SSSDBG_MINOR_FAILURE, "Invalid value for %s: %s\n",
+ CONFDB_PAM_GSSAPI_CHECK_UPN, domain->gssapi_check_upn);
+ return false;
+ }
+
+ return pam_ctx->gssapi_check_upn;
+}
+
+static int pam_gssapi_check_indicators(TALLOC_CTX *mem_ctx,
+ const char *pam_service,
+ char **gssapi_indicators_map,
+ char **indicators)
+{
+ char *authind = NULL;
+ size_t pam_len = strlen(pam_service);
+ char **map = gssapi_indicators_map;
+ char **result = NULL;
+ int res;
+
+ authind = talloc_strdup(mem_ctx, "");
+ if (authind == NULL) {
+ return ENOMEM;
+ }
+
+ for (int i = 0; map[i]; i++) {
+ if (map[i][0] == '-') {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Indicators aren't used for [%s]\n",
+ pam_service);
+ talloc_free(authind);
+ return EOK;
+ }
+ if (!strchr(map[i], ':')) {
+ authind = talloc_asprintf_append(authind, "%s ", map[i]);
+ if (authind == NULL) {
+ /* Since we allocate on pam_ctx, caller will free it */
+ return ENOMEM;
+ }
+ continue;
+ }
+
+ res = strncmp(map[i], pam_service, pam_len);
+ if (res == 0) {
+ if (strlen(map[i]) > pam_len) {
+ if (map[i][pam_len] != ':') {
+ /* different PAM service, skip it */
+ continue;
+ }
+
+ if (map[i][pam_len + 1] == '-') {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Indicators aren't used for [%s]\n",
+ pam_service);
+ talloc_free(authind);
+ return EOK;
+ }
+
+ authind = talloc_asprintf_append(authind, "%s ",
+ map[i] + (pam_len + 1));
+ if (authind == NULL) {
+ /* Since we allocate on pam_ctx, caller will free it */
+ return ENOMEM;
+ }
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Invalid value for %s: [%s]\n",
+ CONFDB_PAM_GSSAPI_INDICATORS_MAP, map[i]);
+ talloc_free(authind);
+ return EINVAL;
+ }
+ }
+ }
+
+ res = ENOENT;
+ map = NULL;
+
+ if (authind[0] == '\0') {
+ /* empty list of per-service indicators -> skip */
+ goto done;
+ }
+
+ /* trim a space after the final indicator
+ * to prevent split_on_separator() to fail */
+ authind[strlen(authind) - 1] = '\0';
+
+ res = split_on_separator(mem_ctx, authind, ' ', true, true,
+ &map, NULL);
+ if (res != 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Cannot parse list of indicators: [%s]\n", authind);
+ res = EINVAL;
+ goto done;
+ }
+
+ res = diff_string_lists(mem_ctx, indicators, map, NULL, NULL, &result);
+ if (res != 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE,"Cannot diff lists of indicators\n");
+ res = EINVAL;
+ goto done;
+ }
+
+ if (result && result[0] != NULL) {
+ for (int i = 0; result[i]; i++) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "indicator [%s] is allowed for PAM service [%s]\n",
+ result[i], pam_service);
+ }
+ res = EOK;
+ goto done;
+ }
+
+ res = EPERM;
+
+done:
+ talloc_free(result);
+ talloc_free(authind);
+ talloc_free(map);
+ return res;
+}
+
+static bool pam_gssapi_allowed(struct pam_ctx *pam_ctx,
+ struct sss_domain_info *domain,
+ const char *service)
+{
+ char **list = pam_ctx->gssapi_services;
+
+ if (domain->gssapi_services != NULL) {
+ list = domain->gssapi_services;
+ }
+
+ if (strcmp(service, "-") == 0) {
+ /* Dash is used as a "not set" value to allow to explicitly disable
+ * gssapi auth for specific domain. Disallow this service to be safe.
+ */
+ DEBUG(SSSDBG_TRACE_FUNC, "Dash - was used as a PAM service name. "
+ "GSSAPI authentication is not allowed.\n");
+ return false;
+ }
+
+ return string_in_list(service, list, true);
+}
+
+static char *pam_gssapi_target(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain)
+{
+ return talloc_asprintf(mem_ctx, "host@%s", domain->hostname);
+}
+
+static const char *pam_gssapi_get_upn(struct cache_req_result *result)
+{
+ if (result->count == 0) {
+ return NULL;
+ }
+
+ /* Canonical UPN should be available if the user has kinited through SSSD.
+ * Use it as a hint for GSSAPI. Default to empty string so it may be
+ * more easily transffered over the wire. */
+ return ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_CANONICAL_UPN, "");
+}
+
+static const char *pam_gssapi_get_name(struct cache_req_result *result)
+{
+ if (result->count == 0) {
+ return NULL;
+ }
+
+ /* Return username known to SSSD to make sure we authenticated as the same
+ * user after GSSAPI handshake. */
+ return ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
+}
+
+static errno_t pam_gssapi_init_parse(struct cli_protocol *pctx,
+ const char **_service,
+ const char **_username)
+{
+ size_t body_len;
+ size_t pctr = 0;
+ uint8_t *body;
+ errno_t ret;
+
+ sss_packet_get_body(pctx->creq->in, &body, &body_len);
+ if (body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input\n");
+ return EINVAL;
+ }
+
+ ret = read_str(body_len, body, &pctr, _service);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = read_str(body_len, body, &pctr, _username);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t pam_gssapi_init_reply(struct cli_protocol *pctx,
+ const char *domain,
+ const char *target,
+ const char *upn,
+ const char *username)
+{
+ size_t reply_len;
+ size_t body_len;
+ size_t pctr;
+ uint8_t *body;
+ errno_t ret;
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create a new packet [%d]; %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ reply_len = strlen(username) + 1;
+ reply_len += strlen(domain) + 1;
+ reply_len += strlen(target) + 1;
+ reply_len += strlen(upn) + 1;
+
+ ret = sss_packet_grow(pctx->creq->out, reply_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create response: %s\n",
+ sss_strerror(ret));
+ return ret;
+ }
+
+ sss_packet_get_body(pctx->creq->out, &body, &body_len);
+
+ pctr = 0;
+ SAFEALIGN_SETMEM_STRING(&body[pctr], username, strlen(username) + 1, &pctr);
+ SAFEALIGN_SETMEM_STRING(&body[pctr], domain, strlen(domain) + 1, &pctr);
+ SAFEALIGN_SETMEM_STRING(&body[pctr], target, strlen(target) + 1, &pctr);
+ SAFEALIGN_SETMEM_STRING(&body[pctr], upn, strlen(upn) + 1, &pctr);
+
+ return EOK;
+}
+
+struct gssapi_init_state {
+ struct cli_ctx *cli_ctx;
+ const char *username;
+ const char *service;
+};
+
+static void pam_cmd_gssapi_init_done(struct tevent_req *req);
+
+int pam_cmd_gssapi_init(struct cli_ctx *cli_ctx)
+{
+ struct gssapi_init_state *state;
+ struct cli_protocol *pctx;
+ struct tevent_req *req;
+ const char *username;
+ const char *service;
+ const char *attrs[] = { SYSDB_NAME, SYSDB_CANONICAL_UPN, NULL };
+ errno_t ret;
+
+ state = talloc_zero(cli_ctx, struct gssapi_init_state);
+ if (state == NULL) {
+ return ENOMEM;
+ }
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = pam_gssapi_init_parse(pctx, &service, &username);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse input [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ state->cli_ctx = cli_ctx;
+ state->service = service;
+ state->username = username;
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Requesting GSSAPI authentication of [%s] in service [%s]\n",
+ username, service);
+
+ req = cache_req_user_by_name_attrs_send(cli_ctx, cli_ctx->ev, cli_ctx->rctx,
+ cli_ctx->rctx->ncache, 0,
+ NULL, username, attrs);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, pam_cmd_gssapi_init_done, state);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ sss_cmd_send_error(cli_ctx, ret);
+ sss_cmd_done(cli_ctx, NULL);
+ }
+
+ return EOK;
+}
+
+static void pam_cmd_gssapi_init_done(struct tevent_req *req)
+{
+ struct gssapi_init_state *state;
+ struct cache_req_result *result;
+ struct cli_protocol *pctx;
+ struct pam_ctx *pam_ctx;
+ const char *username;
+ const char *upn;
+ char *target;
+ errno_t ret;
+
+ state = tevent_req_callback_data(req, struct gssapi_init_state);
+ pctx = talloc_get_type(state->cli_ctx->protocol_ctx, struct cli_protocol);
+ pam_ctx = talloc_get_type(state->cli_ctx->rctx->pvt_ctx, struct pam_ctx);
+
+ ret = cache_req_user_by_name_attrs_recv(state, req, &result);
+ talloc_zfree(req);
+ if (ret == ENOENT || ret == ERR_DOMAIN_NOT_FOUND) {
+ ret = ENOENT;
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ if (!pam_gssapi_allowed(pam_ctx, result->domain, state->service)) {
+ ret = ENOTSUP;
+ goto done;
+ }
+
+ username = pam_gssapi_get_name(result);
+ if (username == NULL) {
+ /* User with no name? */
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ upn = pam_gssapi_get_upn(result);
+ if (upn == NULL) {
+ /* UPN hint may be an empty string, but not NULL. */
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ target = pam_gssapi_target(state, result->domain);
+ if (target == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Trying GSSAPI auth: User[%s], Domain[%s], UPN[%s], Target[%s]\n",
+ username, result->domain->name, upn, target);
+
+ ret = pam_gssapi_init_reply(pctx, result->domain->name, target, upn,
+ username);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to construct reply [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+done:
+ DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
+
+ if (ret == EOK) {
+ sss_packet_set_error(pctx->creq->out, EOK);
+ } else {
+ sss_cmd_send_error(state->cli_ctx, ret);
+ }
+
+ sss_cmd_done(state->cli_ctx, state);
+}
+
+static void gssapi_log_status(int type, OM_uint32 status_code)
+{
+ OM_uint32 message_context = 0;
+ gss_buffer_desc buf;
+ OM_uint32 minor;
+
+ do {
+ gss_display_status(&minor, status_code, type, GSS_C_NO_OID,
+ &message_context, &buf);
+ DEBUG(SSSDBG_OP_FAILURE, "GSSAPI: %.*s\n", (int)buf.length,
+ (char *)buf.value);
+ gss_release_buffer(&minor, &buf);
+ } while (message_context != 0);
+}
+
+static void gssapi_log_error(OM_uint32 major, OM_uint32 minor)
+{
+ gssapi_log_status(GSS_C_GSS_CODE, major);
+ gssapi_log_status(GSS_C_MECH_CODE, minor);
+}
+
+static char *gssapi_get_name(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
+{
+ gss_buffer_desc buf;
+ OM_uint32 major;
+ OM_uint32 minor;
+ char *exported;
+
+ major = gss_display_name(&minor, gss_name, &buf, NULL);
+ if (major != GSS_S_COMPLETE) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export name\n");
+ return NULL;
+ }
+
+ exported = talloc_strndup(mem_ctx, buf.value, buf.length);
+ gss_release_buffer(&minor, &buf);
+
+ if (exported == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ return exported;
+}
+
+#define AUTH_INDICATORS_TAG "auth-indicators"
+
+static char **gssapi_get_indicators(TALLOC_CTX *mem_ctx, gss_name_t gss_name)
+{
+ gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
+ int is_mechname;
+ OM_uint32 major;
+ OM_uint32 minor;
+ gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc display_value = GSS_C_EMPTY_BUFFER;
+ char *exported = NULL;
+ char **map = NULL;
+ int res;
+
+ major = gss_inquire_name(&minor, gss_name, &is_mechname, NULL, &attrs);
+ if (major != GSS_S_COMPLETE) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to inquire name\n");
+ return NULL;
+ }
+
+ if (attrs == GSS_C_NO_BUFFER_SET) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No krb5 attributes in the ticket\n");
+ return NULL;
+ }
+
+ exported = talloc_strdup(mem_ctx, "");
+ if (exported == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to pre-allocate indicators\n");
+ goto done;
+ }
+
+ for (int i = 0; i < attrs->count; i++) {
+ int authenticated = 0;
+ int complete = 0;
+ int more = -1;
+
+ /* skip anything but auth-indicators */
+ if (strncmp(AUTH_INDICATORS_TAG, attrs->elements[i].value,
+ sizeof(AUTH_INDICATORS_TAG) - 1) != 0)
+ continue;
+
+ /* retrieve all indicators */
+ while (more != 0) {
+ value.value = NULL;
+ display_value.value = NULL;
+
+ major = gss_get_name_attribute(&minor, gss_name,
+ &attrs->elements[i],
+ &authenticated,
+ &complete, &value,
+ &display_value,
+ &more);
+ if (major != GSS_S_COMPLETE) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to retrieve an attribute\n");
+ goto done;
+ }
+
+ if ((value.value != NULL) && authenticated) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "attribute's [%.*s] value [%.*s] authenticated\n",
+ (int) attrs->elements[i].length,
+ (char*) attrs->elements[i].value,
+ (int) value.length,
+ (char*) value.value);
+ exported = talloc_asprintf_append(exported, "%.*s ",
+ (int) value.length,
+ (char*) value.value);
+ }
+
+ if (exported == NULL) {
+ /* Since we allocate on mem_ctx, caller will free
+ * the previous version of 'exported' */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to collect an attribute value\n");
+ goto done;
+ }
+ (void) gss_release_buffer(&minor, &value);
+ (void) gss_release_buffer(&minor, &display_value);
+ }
+ }
+
+ if (exported[0] != '\0') {
+ /* trim a space after the final indicator
+ * to prevent split_on_separator() to fail */
+ exported[strlen(exported) - 1] = '\0';
+ } else {
+ /* empty list */
+ goto done;
+ }
+
+ res = split_on_separator(mem_ctx, exported, ' ', true, true,
+ &map, NULL);
+ if (res != 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Cannot parse list of indicators: [%s]\n", exported);
+ goto done;
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "authentication indicators: [%s]\n",
+ exported);
+ }
+
+done:
+ (void) gss_release_buffer(&minor, &value);
+ (void) gss_release_buffer(&minor, &display_value);
+ (void) gss_release_buffer_set(&minor, &attrs);
+
+ talloc_free(exported);
+ return map;
+}
+
+
+struct gssapi_state {
+ struct cli_ctx *cli_ctx;
+ struct sss_domain_info *domain;
+ const char *username;
+
+ char *authenticated_upn;
+ char **auth_indicators;
+ bool established;
+ gss_ctx_id_t ctx;
+};
+
+int gssapi_state_destructor(struct gssapi_state *state)
+{
+ OM_uint32 minor;
+
+ gss_delete_sec_context(&minor, &state->ctx, NULL);
+
+ return 0;
+}
+
+static struct gssapi_state *gssapi_get_state(struct cli_ctx *cli_ctx,
+ const char *username,
+ struct sss_domain_info *domain)
+{
+ struct gssapi_state *state;
+
+ state = talloc_get_type(cli_ctx->state_ctx, struct gssapi_state);
+ if (state != NULL) {
+ return state;
+ }
+
+ state = talloc_zero(cli_ctx, struct gssapi_state);
+ if (state == NULL) {
+ return NULL;
+ }
+
+ state->username = talloc_strdup(state, username);
+ if (state == NULL) {
+ talloc_free(state);
+ return NULL;
+ }
+
+ state->domain = domain;
+ state->cli_ctx = cli_ctx;
+ state->ctx = GSS_C_NO_CONTEXT;
+ talloc_set_destructor(state, gssapi_state_destructor);
+
+ cli_ctx->state_ctx = state;
+
+ return state;
+}
+
+static errno_t gssapi_get_creds(const char *keytab,
+ const char *target,
+ gss_cred_id_t *_creds)
+{
+ gss_key_value_set_desc cstore = {0, NULL};
+ gss_key_value_element_desc el;
+ gss_buffer_desc name_buf;
+ gss_name_t name = GSS_C_NO_NAME;
+ OM_uint32 major;
+ OM_uint32 minor;
+ errno_t ret;
+
+ if (keytab != NULL) {
+ el.key = "keytab";
+ el.value = keytab;
+ cstore.count = 1;
+ cstore.elements = &el;
+ }
+
+ if (target != NULL) {
+ name_buf.value = discard_const(target);
+ name_buf.length = strlen(target);
+
+ major = gss_import_name(&minor, &name_buf, GSS_C_NT_HOSTBASED_SERVICE,
+ &name);
+ if (GSS_ERROR(major)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not import name [%s] "
+ "[maj:0x%x, min:0x%x]\n", target, major, minor);
+
+ gssapi_log_error(major, minor);
+
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ major = gss_acquire_cred_from(&minor, name, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cstore,
+ _creds, NULL, NULL);
+ if (GSS_ERROR(major)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to read credentials from [%s] "
+ "[maj:0x%x, min:0x%x]\n", keytab ? keytab : "default",
+ major, minor);
+
+ gssapi_log_error(major, minor);
+
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ gss_release_name(&minor, &name);
+
+ return ret;
+}
+
+static errno_t
+gssapi_handshake(struct gssapi_state *state,
+ struct cli_protocol *pctx,
+ const char *keytab,
+ const char *target,
+ uint8_t *gss_data,
+ size_t gss_data_len)
+{
+ OM_uint32 flags = GSS_C_MUTUAL_FLAG;
+ gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc input;
+ gss_name_t client_name;
+ gss_cred_id_t creds;
+ OM_uint32 ret_flags;
+ gss_OID mech_type;
+ OM_uint32 major;
+ OM_uint32 minor;
+ errno_t ret;
+
+ input.value = gss_data;
+ input.length = gss_data_len;
+
+ ret = gssapi_get_creds(keytab, target, &creds);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ major = gss_accept_sec_context(&minor, &state->ctx, creds,
+ &input, NULL, &client_name, &mech_type,
+ &output, &ret_flags, NULL, NULL);
+ if (major == GSS_S_CONTINUE_NEEDED || output.length > 0) {
+ ret = sss_packet_set_body(pctx->creq->out, output.value, output.length);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (GSS_ERROR(major)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to establish GSS context "
+ "[maj:0x%x, min:0x%x]\n", major, minor);
+
+ gssapi_log_error(major, minor);
+ ret = EIO;
+ goto done;
+ }
+
+ if (major == GSS_S_CONTINUE_NEEDED) {
+ ret = EOK;
+ goto done;
+ } else if (major != GSS_S_COMPLETE) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to establish GSS context, unexpected "
+ "value: 0x%x\n", major);
+ ret = EIO;
+ goto done;
+ }
+
+ if ((ret_flags & flags) != flags) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Negotiated context does not support requested flags\n");
+ state->established = false;
+ ret = EIO;
+ goto done;
+ }
+
+ state->authenticated_upn = gssapi_get_name(state, client_name);
+ if (state->authenticated_upn == NULL) {
+ state->established = false;
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Security context established with [%s]\n",
+ state->authenticated_upn);
+
+ state->auth_indicators = gssapi_get_indicators(state, client_name);
+
+ state->established = true;
+ ret = EOK;
+
+done:
+ gss_release_cred(&minor, &creds);
+ gss_release_buffer(&minor, &output);
+
+ return ret;
+}
+
+static errno_t pam_cmd_gssapi_sec_ctx_parse(struct cli_protocol *pctx,
+ const char **_pam_service,
+ const char **_username,
+ const char **_domain,
+ uint8_t **_gss_data,
+ size_t *_gss_data_len)
+{
+ size_t body_len;
+ uint8_t *body;
+ size_t pctr;
+ errno_t ret;
+
+ sss_packet_get_body(pctx->creq->in, &body, &body_len);
+ if (body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input\n");
+ return EINVAL;
+ }
+
+ pctr = 0;
+ ret = read_str(body_len, body, &pctr, _pam_service);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = read_str(body_len, body, &pctr, _username);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = read_str(body_len, body, &pctr, _domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_gss_data = (pctr == body_len) ? NULL : body + pctr;
+ *_gss_data_len = body_len - pctr;
+
+ return EOK;
+}
+
+static void pam_cmd_gssapi_sec_ctx_done(struct tevent_req *req);
+
+int
+pam_cmd_gssapi_sec_ctx(struct cli_ctx *cli_ctx)
+{
+ struct sss_domain_info *domain;
+ struct gssapi_state *state;
+ struct cli_protocol *pctx;
+ struct pam_ctx *pam_ctx;
+ struct tevent_req *req;
+ const char *pam_service;
+ const char *domain_name;
+ const char *username;
+ char *target;
+ char **indicators_map = NULL;
+ size_t gss_data_len;
+ uint8_t *gss_data;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+ pam_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct pam_ctx);
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create a new packet [%d]; %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = pam_cmd_gssapi_sec_ctx_parse(pctx, &pam_service, &username,
+ &domain_name, &gss_data, &gss_data_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to parse input data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ domain = find_domain_by_name(cli_ctx->rctx->domains, domain_name, false);
+ if (domain == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (!pam_gssapi_allowed(pam_ctx, domain, pam_service)) {
+ ret = ENOTSUP;
+ goto done;
+ }
+
+ target = pam_gssapi_target(cli_ctx, domain);
+ if (target == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state = gssapi_get_state(cli_ctx, username, domain);
+ if (state == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (strcmp(username, state->username) != 0 || state->domain != domain) {
+ /* This should not happen, but be paranoid. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Different input user then who initiated "
+ "the request!\n");
+ ret = EPERM;
+ goto done;
+ }
+
+ if (state->established) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Security context is already established\n");
+ ret = EPERM;
+ goto done;
+ }
+
+ ret = gssapi_handshake(state, pctx, domain->krb5_keytab, target, gss_data,
+ gss_data_len);
+ if (ret != EOK || !state->established) {
+ goto done;
+ }
+
+ /* Use map for auth-indicators from the domain, if defined and
+ * fallback to the [pam] section otherwise */
+ indicators_map = domain->gssapi_indicators_map ?
+ domain->gssapi_indicators_map :
+ (pam_ctx->gssapi_indicators_map ?
+ pam_ctx->gssapi_indicators_map : NULL);
+ if (indicators_map != NULL) {
+ ret = pam_gssapi_check_indicators(state,
+ pam_service,
+ indicators_map,
+ state->auth_indicators);
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Check if acquired service ticket has req. indicators: %d\n",
+ ret);
+ if ((ret == EPERM) || (ret == ENOMEM) || (ret == EINVAL)) {
+ /* skip further checks if denied or no memory,
+ * ENOENT means the check is not applicable */
+ goto done;
+ }
+ }
+
+ if (!pam_gssapi_should_check_upn(pam_ctx, domain)) {
+ /* We are done. */
+ goto done;
+ }
+
+ /* We have established the security context. Now check the the principal
+ * used for authorization can be associated with the user. We have
+ * already done initgroups before so we could just search the sysdb
+ * directly, but use cache req to avoid looking up a possible expired
+ * object if the handshake took longer. */
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Checking that target user matches UPN\n");
+
+ req = cache_req_user_by_upn_send(cli_ctx, cli_ctx->ev, cli_ctx->rctx,
+ cli_ctx->rctx->ncache, 0,
+ CACHE_REQ_POSIX_DOM,
+ domain->name, state->authenticated_upn);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, pam_cmd_gssapi_sec_ctx_done, state);
+
+ return EOK;
+
+done:
+ DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
+
+ if (ret == EOK) {
+ sss_packet_set_error(pctx->creq->out, EOK);
+ } else {
+ sss_cmd_send_error(cli_ctx, ret);
+ }
+
+ sss_cmd_done(cli_ctx, NULL);
+ return EOK;
+}
+
+static void pam_cmd_gssapi_sec_ctx_done(struct tevent_req *req)
+{
+ struct gssapi_state *state;
+ struct cache_req_result *result;
+ struct cli_protocol *pctx;
+ const char *name;
+ errno_t ret;
+
+ state = tevent_req_callback_data(req, struct gssapi_state);
+ pctx = talloc_get_type(state->cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = cache_req_user_by_upn_recv(state, req, &result);
+ talloc_zfree(req);
+ if (ret == ENOENT || ret == ERR_DOMAIN_NOT_FOUND) {
+ /* We have no match. Return failure. */
+ DEBUG(SSSDBG_TRACE_FUNC, "User with UPN [%s] was not found. "
+ "Authentication failed.\n", state->authenticated_upn);
+ ret = EACCES;
+ goto done;
+ } else if (ret != EOK) {
+ /* Generic error. Return failure. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup user by UPN [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Check that username match. */
+ name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL || strcmp(name, state->username) != 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "UPN [%s] does not match target user [%s]. "
+ "Authentication failed.\n", state->authenticated_upn,
+ state->username);
+ ret = EACCES;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "User [%s] match UPN [%s]. Authentication was "
+ "successful.\n", state->username, state->authenticated_upn);
+
+ ret = EOK;
+
+done:
+ DEBUG(SSSDBG_TRACE_FUNC, "Returning [%d]: %s\n", ret, sss_strerror(ret));
+
+ if (ret == EOK) {
+ sss_packet_set_error(pctx->creq->out, EOK);
+ } else {
+ sss_cmd_send_error(state->cli_ctx, ret);
+ }
+
+ sss_cmd_done(state->cli_ctx, state);
+}
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
new file mode 100644
index 0000000..2f973d8
--- /dev/null
+++ b/src/responder/pam/pamsrv_p11.c
@@ -0,0 +1,1312 @@
+/*
+ SSSD
+
+ PAM Responder - certificate related requests
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2015
+
+ 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 <time.h>
+
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "util/child_common.h"
+#include "util/strtonum.h"
+#include "responder/pam/pamsrv.h"
+#include "responder/pam/pam_helpers.h"
+#include "lib/certmap/sss_certmap.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/sss_chain_id.h"
+#include "db/sysdb.h"
+
+
+struct cert_auth_info {
+ char *cert;
+ char *token_name;
+ char *module_name;
+ char *key_id;
+ char *label;
+ struct ldb_result *cert_user_objs;
+ struct cert_auth_info *prev;
+ struct cert_auth_info *next;
+};
+
+const char *sss_cai_get_cert(struct cert_auth_info *i)
+{
+ return i != NULL ? i->cert : NULL;
+}
+
+const char *sss_cai_get_token_name(struct cert_auth_info *i)
+{
+ return i != NULL ? i->token_name : NULL;
+}
+
+const char *sss_cai_get_module_name(struct cert_auth_info *i)
+{
+ return i != NULL ? i->module_name : NULL;
+}
+
+const char *sss_cai_get_key_id(struct cert_auth_info *i)
+{
+ return i != NULL ? i->key_id : NULL;
+}
+
+const char *sss_cai_get_label(struct cert_auth_info *i)
+{
+ return i != NULL ? i->label : NULL;
+}
+
+struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i)
+{
+ return i != NULL ? i->next : NULL;
+}
+
+struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i)
+{
+ return i != NULL ? i->cert_user_objs : NULL;
+}
+
+void sss_cai_set_cert_user_objs(struct cert_auth_info *i,
+ struct ldb_result *cert_user_objs)
+{
+ if (i->cert_user_objs != NULL) {
+ talloc_free(i->cert_user_objs);
+ }
+ i->cert_user_objs = talloc_steal(i, cert_user_objs);
+}
+
+void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count,
+ size_t *_cert_user_count)
+{
+ struct cert_auth_info *c;
+ struct cert_auth_info *tmp;
+ size_t cert_count = 0;
+ size_t cert_user_count = 0;
+ struct ldb_result *user_objs;
+
+ DLIST_FOR_EACH_SAFE(c, tmp, *list) {
+ user_objs = sss_cai_get_cert_user_objs(c);
+ if (user_objs != NULL) {
+ cert_count++;
+ cert_user_count += user_objs->count;
+ } else {
+ DLIST_REMOVE(*list, c);
+ }
+ }
+
+ if (_cert_count != NULL) {
+ *_cert_count = cert_count;
+ }
+
+ if (_cert_user_count != NULL) {
+ *_cert_user_count = cert_user_count;
+ }
+
+ return;
+}
+
+struct priv_sss_debug {
+ int level;
+};
+
+static void ext_debug(void *private, const char *file, long line,
+ const char *function, const char *format, ...)
+{
+ va_list ap;
+ struct priv_sss_debug *data = private;
+ int level = SSSDBG_OP_FAILURE;
+
+ if (data != NULL) {
+ level = data->level;
+ }
+
+ va_start(ap, format);
+ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
+ format, ap);
+ va_end(ap);
+}
+
+errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
+ struct sss_domain_info *domains)
+{
+ int ret;
+ struct sss_certmap_ctx *sss_certmap_ctx = NULL;
+ size_t c;
+ struct sss_domain_info *dom;
+ bool certmap_found = false;
+ struct certmap_info **certmap_list;
+
+ ret = sss_certmap_init(pctx, ext_debug, NULL, &sss_certmap_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
+ goto done;
+ }
+
+ DLIST_FOR_EACH(dom, domains) {
+ certmap_list = dom->certmaps;
+ if (certmap_list != NULL && *certmap_list != NULL) {
+ certmap_found = true;
+ break;
+ }
+ }
+
+ if (!certmap_found) {
+ /* Try to add default matching rule */
+ ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO,
+ CERT_AUTH_DEFAULT_MATCHING_RULE, NULL, NULL);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to add default matching rule.\n");
+ }
+
+ goto done;
+ }
+
+ DLIST_FOR_EACH(dom, domains) {
+ certmap_list = dom->certmaps;
+ if (certmap_list == NULL || *certmap_list == NULL) {
+ continue;
+ }
+
+ for (c = 0; certmap_list[c] != NULL; c++) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Trying to add rule [%s][%d][%s][%s].\n",
+ certmap_list[c]->name, certmap_list[c]->priority,
+ certmap_list[c]->match_rule, certmap_list[c]->map_rule);
+
+ ret = sss_certmap_add_rule(sss_certmap_ctx,
+ certmap_list[c]->priority,
+ certmap_list[c]->match_rule,
+ certmap_list[c]->map_rule,
+ certmap_list[c]->domains);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_certmap_add_rule failed for rule [%s] "
+ "with error [%d][%s], skipping. "
+ "Please check for typos and if rule syntax is supported.\n",
+ certmap_list[c]->name, ret, sss_strerror(ret));
+ continue;
+ }
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ sss_certmap_free_ctx(pctx->sss_certmap_ctx);
+ pctx->sss_certmap_ctx = sss_certmap_ctx;
+ } else {
+ sss_certmap_free_ctx(sss_certmap_ctx);
+ }
+
+ return ret;
+}
+
+errno_t p11_child_init(struct pam_ctx *pctx)
+{
+ int ret;
+ struct certmap_info **certmaps;
+ bool user_name_hint;
+ struct sss_domain_info *dom;
+
+ DLIST_FOR_EACH(dom, pctx->rctx->domains) {
+ ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n");
+ return ret;
+ }
+
+ dom->user_name_hint = user_name_hint;
+ talloc_free(dom->certmaps);
+ dom->certmaps = certmaps;
+ }
+
+ ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "p11_refresh_certmap_ctx failed.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static inline bool
+service_in_list(char **list, size_t nlist, const char *str)
+{
+ size_t i;
+
+ for (i = 0; i < nlist; i++) {
+ if (strcasecmp(list[i], str) == 0) {
+ break;
+ }
+ }
+
+ return (i < nlist) ? true : false;
+}
+
+static errno_t get_sc_services(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx,
+ char ***_sc_list)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *conf_str;
+ char **conf_list;
+ int conf_list_size;
+ char **add_list;
+ char **remove_list;
+ int ai = 0;
+ int ri = 0;
+ int j = 0;
+ char **sc_list;
+ int expected_sc_list_size;
+
+ const char *default_sc_services[] = {
+ "login", "su", "su-l", "gdm-smartcard", "gdm-password", "kdm", "sudo",
+ "sudo-i", "gnome-screensaver", "polkit-1", NULL,
+ };
+ const int default_sc_services_size =
+ sizeof(default_sc_services) / sizeof(default_sc_services[0]);
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, tmp_ctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_P11_ALLOWED_SERVICES, NULL,
+ &conf_str);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "confdb_get_string failed %d [%s]\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (conf_str != NULL) {
+ ret = split_on_separator(tmp_ctx, conf_str, ',', true, true,
+ &conf_list, &conf_list_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot parse list of service names '%s': %d [%s]\n",
+ conf_str, ret, sss_strerror(ret));
+ goto done;
+ }
+ } else {
+ conf_list = talloc_zero_array(tmp_ctx, char *, 1);
+ conf_list_size = 0;
+ }
+
+ add_list = talloc_zero_array(tmp_ctx, char *, conf_list_size + 1);
+ remove_list = talloc_zero_array(tmp_ctx, char *, conf_list_size + 1);
+
+ if (add_list == NULL || remove_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; conf_list[i] != NULL; ++i) {
+ switch (conf_list[i][0]) {
+ case '+':
+ add_list[ai] = conf_list[i] + 1;
+ ++ai;
+ break;
+ case '-':
+ remove_list[ri] = conf_list[i] + 1;
+ ++ri;
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "The option "CONFDB_PAM_P11_ALLOWED_SERVICES" must start"
+ "with either '+' (for adding service) or '-' (for "
+ "removing service) got '%s'\n", conf_list[i]);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ expected_sc_list_size = default_sc_services_size + ai + 1;
+
+ sc_list = talloc_zero_array(tmp_ctx, char *, expected_sc_list_size);
+ if (sc_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; add_list[i] != NULL; ++i) {
+ if (service_in_list(remove_list, ri, add_list[i])) {
+ continue;
+ }
+
+ sc_list[j] = talloc_strdup(sc_list, add_list[i]);
+ if (sc_list[j] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ++j;
+ }
+
+ for (int i = 0; default_sc_services[i] != NULL; ++i) {
+ if (service_in_list(remove_list, ri, default_sc_services[i])) {
+ continue;
+ }
+
+ sc_list[j] = talloc_strdup(sc_list, default_sc_services[i]);
+ if (sc_list[j] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ++j;
+ }
+
+ if (_sc_list != NULL) {
+ *_sc_list = talloc_steal(mem_ctx, sc_list);
+ }
+
+done:
+ talloc_zfree(tmp_ctx);
+
+ return ret;
+}
+
+bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd)
+{
+ size_t c;
+ errno_t ret;
+
+ if (!pctx->cert_auth) {
+ return false;
+ }
+
+ if (pd->cmd != SSS_PAM_PREAUTH && pd->cmd != SSS_PAM_AUTHENTICATE) {
+ return false;
+ }
+
+ if (pd->cmd == SSS_PAM_AUTHENTICATE
+ && sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN
+ && sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+ return false;
+ }
+
+ if (pd->service == NULL || *pd->service == '\0') {
+ return false;
+ }
+
+ /* Initialize smartcard allowed services just once */
+ if (pctx->smartcard_services == NULL) {
+ ret = get_sc_services(pctx, pctx, &pctx->smartcard_services);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to get p11 allowed services %d[%s]",
+ ret, sss_strerror(ret));
+ sss_log(SSS_LOG_ERR,
+ "Failed to evaluate pam_p11_allowed_services option, "
+ "please check for typos in the SSSD configuration");
+ return false;
+ }
+ }
+
+ for (c = 0; pctx->smartcard_services[c] != NULL; c++) {
+ if (strcmp(pd->service, pctx->smartcard_services[c]) == 0) {
+ break;
+ }
+ }
+ if (pctx->smartcard_services[c] == NULL) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Smartcard authentication for service [%s] not supported.\n",
+ pd->service);
+ return false;
+ }
+
+ return true;
+}
+
+static errno_t get_p11_child_write_buffer(TALLOC_CTX *mem_ctx,
+ struct pam_data *pd,
+ uint8_t **_buf, size_t *_len)
+{
+ int ret;
+ uint8_t *buf;
+ size_t len;
+ const char *pin = NULL;
+
+ if (pd == NULL || pd->authtok == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing authtok.\n");
+ return EINVAL;
+ }
+
+ switch (sss_authtok_get_type(pd->authtok)) {
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ ret = sss_authtok_get_sc_pin(pd->authtok, &pin, &len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc_pin failed.\n");
+ return ret;
+ }
+ if (pin == NULL || len == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing PIN.\n");
+ return EINVAL;
+ }
+
+ buf = talloc_size(mem_ctx, len);
+ if (buf == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+
+ safealign_memcpy(buf, pin, len, NULL);
+
+ break;
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ /* Nothing to send */
+ len = 0;
+ buf = NULL;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported authtok type [%d].\n",
+ sss_authtok_get_type(pd->authtok));
+ return EINVAL;
+ }
+
+ *_len = len;
+ *_buf = buf;
+
+ return EOK;
+}
+
+static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
+ ssize_t buf_len,
+ struct sss_certmap_ctx *sss_certmap_ctx,
+ struct cert_auth_info **_cert_list)
+{
+ int ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint8_t *p;
+ uint8_t *pn;
+ struct cert_auth_info *cert_list = NULL;
+ struct cert_auth_info *cert_auth_info;
+ unsigned char *der = NULL;
+ size_t der_size;
+
+ if (buf_len <= 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error occurred while reading data from p11_child (len = %ld)\n",
+ buf_len);
+ return EIO;
+ }
+
+ p = memchr(buf, '\n', buf_len);
+ if ((p == NULL) || (p == buf)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing status in p11_child response.\n");
+ return EINVAL;
+ }
+ errno = 0;
+ ret = strtol((char *)buf, (char **)&pn, 10);
+ if ((errno != 0) || (pn == buf)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid status in p11_child response.\n");
+ return EINVAL;
+ }
+
+ if (ret != 0) {
+ if (ret == ERR_P11_PIN_LOCKED) {
+ DEBUG(SSSDBG_OP_FAILURE, "PIN locked\n");
+ return ret;
+ } else {
+ *_cert_list = NULL;
+ return EOK; /* other errors are treated as "no cert found" */
+ }
+ }
+
+ if ((p - buf + 1) == buf_len) {
+ DEBUG(SSSDBG_TRACE_LIBS, "No certificate found.\n");
+ *_cert_list = NULL;
+ return EOK;
+ }
+
+ p++;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ do {
+ cert_auth_info = talloc_zero(tmp_ctx, struct cert_auth_info);
+ if (cert_auth_info == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing token name in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ cert_auth_info->token_name = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->token_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found token name [%s].\n",
+ cert_auth_info->token_name);
+
+ p = ++pn;
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing module name in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ cert_auth_info->module_name = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->module_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found module name [%s].\n",
+ cert_auth_info->module_name);
+
+ p = ++pn;
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing key id in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ cert_auth_info->key_id = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->key_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found key id [%s].\n", cert_auth_info->key_id);
+
+ p = ++pn;
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing label in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ cert_auth_info->label = talloc_strndup(cert_auth_info, (char *) p,
+ (pn - p));
+ if (cert_auth_info->label == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found label [%s].\n", cert_auth_info->label);
+
+ p = ++pn;
+ pn = memchr(p, '\n', buf_len - (p - buf));
+ if (pn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Missing new-line in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (pn == p) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ cert_auth_info->cert = talloc_strndup(cert_auth_info, (char *)p,
+ (pn - p));
+ if (cert_auth_info->cert == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert_auth_info->cert);
+
+ der = sss_base64_decode(tmp_ctx, cert_auth_info->cert, &der_size);
+ if (der == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sss_certmap_match_cert(sss_certmap_ctx, der, der_size);
+ if (ret == 0) {
+ DLIST_ADD(cert_list, cert_auth_info);
+ } else {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "Cert [%s] does not match matching rules and is ignored.\n",
+ cert_auth_info->cert);
+ talloc_free(cert_auth_info);
+ }
+
+ p = ++pn;
+ } while ((pn - buf) < buf_len);
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ DLIST_FOR_EACH(cert_auth_info, cert_list) {
+ talloc_steal(mem_ctx, cert_auth_info);
+ }
+
+ *_cert_list = cert_list;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct pam_check_cert_state {
+ int child_status;
+ struct sss_child_ctx_old *child_ctx;
+ struct tevent_timer *timeout_handler;
+ struct tevent_context *ev;
+ struct sss_certmap_ctx *sss_certmap_ctx;
+
+ struct child_io_fds *io;
+
+ struct cert_auth_info *cert_list;
+ struct pam_data *pam_data;
+};
+
+static void p11_child_write_done(struct tevent_req *subreq);
+static void p11_child_done(struct tevent_req *subreq);
+static void p11_child_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt);
+
+struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *ca_db,
+ time_t timeout,
+ const char *verify_opts,
+ struct sss_certmap_ctx *sss_certmap_ctx,
+ const char *uri,
+ struct pam_data *pd)
+{
+ errno_t ret;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct pam_check_cert_state *state;
+ pid_t child_pid;
+ struct timeval tv;
+ int pipefd_to_child[2] = PIPE_INIT;
+ int pipefd_from_child[2] = PIPE_INIT;
+ const char *extra_args[20] = { NULL };
+ uint8_t *write_buf = NULL;
+ size_t write_buf_len = 0;
+ size_t arg_c;
+ uint64_t chain_id;
+ const char *module_name = NULL;
+ const char *token_name = NULL;
+ const char *key_id = NULL;
+ const char *label = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct pam_check_cert_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (ca_db == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing CA DB path.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (sss_certmap_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate matching context.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->pam_data = pd;
+
+ /* extra_args are added in revers order */
+ arg_c = 0;
+
+ chain_id = sss_chain_id_get();
+
+ extra_args[arg_c++] = talloc_asprintf(mem_ctx, "%lu", chain_id);
+ extra_args[arg_c++] = "--chain-id";
+
+ if (uri != NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Adding PKCS#11 URI [%s].\n", uri);
+ extra_args[arg_c++] = uri;
+ extra_args[arg_c++] = "--uri";
+ }
+
+ if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) {
+ extra_args[arg_c++] = "--wait_for_card";
+ }
+ extra_args[arg_c++] = ca_db;
+ extra_args[arg_c++] = "--ca_db";
+ if (verify_opts != NULL) {
+ extra_args[arg_c++] = verify_opts;
+ extra_args[arg_c++] = "--verify";
+ }
+
+ if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
+ || sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_KEYPAD) {
+ ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, &token_name, NULL,
+ &module_name, NULL, &key_id, NULL,
+ &label, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+ goto done;
+ }
+
+ if (module_name != NULL && *module_name != '\0') {
+ extra_args[arg_c++] = module_name;
+ extra_args[arg_c++] = "--module_name";
+ }
+ if (token_name != NULL && *token_name != '\0') {
+ extra_args[arg_c++] = token_name;
+ extra_args[arg_c++] = "--token_name";
+ }
+ if (key_id != NULL && *key_id != '\0') {
+ extra_args[arg_c++] = key_id;
+ extra_args[arg_c++] = "--key_id";
+ }
+ if (label != NULL && *label != '\0') {
+ extra_args[arg_c++] = label;
+ extra_args[arg_c++] = "--label";
+ }
+ }
+
+ if (pd->cmd == SSS_PAM_AUTHENTICATE) {
+ extra_args[arg_c++] = "--auth";
+ switch (sss_authtok_get_type(pd->authtok)) {
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ extra_args[arg_c++] = "--pin";
+ break;
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ extra_args[arg_c++] = "--keypad";
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Unsupported authtok type.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ } else if (pd->cmd == SSS_PAM_PREAUTH) {
+ extra_args[arg_c++] = "--pre";
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected PAM command [%d].\n", pd->cmd);
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->ev = ev;
+ state->sss_certmap_ctx = sss_certmap_ctx;
+ state->child_status = EFAULT;
+ state->io = talloc(state, struct child_io_fds);
+ if (state->io == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->io->write_to_child_fd = -1;
+ state->io->read_from_child_fd = -1;
+ talloc_set_destructor((void *) state->io, child_io_destructor);
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ child_pid = fork();
+ if (child_pid == 0) { /* child */
+ exec_child_ex(state, pipefd_to_child, pipefd_from_child,
+ P11_CHILD_PATH, P11_CHILD_LOG_FILE, extra_args, false,
+ STDIN_FILENO, STDOUT_FILENO);
+
+ /* We should never get here */
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec p11 child\n");
+ } else if (child_pid > 0) { /* parent */
+
+ state->io->read_from_child_fd = pipefd_from_child[0];
+ PIPE_FD_CLOSE(pipefd_from_child[1]);
+ sss_fd_nonblocking(state->io->read_from_child_fd);
+
+ state->io->write_to_child_fd = pipefd_to_child[1];
+ PIPE_FD_CLOSE(pipefd_to_child[0]);
+ sss_fd_nonblocking(state->io->write_to_child_fd);
+
+ /* Set up SIGCHLD handler */
+ ret = child_handler_setup(ev, child_pid, NULL, NULL, &state->child_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+
+ /* Set up timeout handler */
+ tv = sss_tevent_timeval_current_ofs_time_t(timeout);
+ state->timeout_handler = tevent_add_timer(ev, req, tv,
+ p11_child_timeout, req);
+ if(state->timeout_handler == NULL) {
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+
+ if (pd->cmd == SSS_PAM_AUTHENTICATE) {
+ ret = get_p11_child_write_buffer(state, pd, &write_buf,
+ &write_buf_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "get_p11_child_write_buffer failed.\n");
+ goto done;
+ }
+ }
+
+ if (write_buf_len != 0) {
+ subreq = write_pipe_send(state, ev, write_buf, write_buf_len,
+ state->io->write_to_child_fd);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "write_pipe_send failed.\n");
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, p11_child_write_done, req);
+ } else {
+ subreq = read_pipe_send(state, ev, state->io->read_from_child_fd);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "read_pipe_send failed.\n");
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, p11_child_done, req);
+ }
+
+ /* Now either wait for the timeout to fire or the child
+ * to finish
+ */
+ } else { /* error */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ PIPE_CLOSE(pipefd_from_child);
+ PIPE_CLOSE(pipefd_to_child);
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void p11_child_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct pam_check_cert_state *state = tevent_req_data(req,
+ struct pam_check_cert_state);
+ int ret;
+
+ ret = write_pipe_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ PIPE_FD_CLOSE(state->io->write_to_child_fd);
+
+ subreq = read_pipe_send(state, state->ev, state->io->read_from_child_fd);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, p11_child_done, req);
+}
+
+static void p11_child_done(struct tevent_req *subreq)
+{
+ uint8_t *buf;
+ ssize_t buf_len;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct pam_check_cert_state *state = tevent_req_data(req,
+ struct pam_check_cert_state);
+ uint32_t user_info_type;
+ int ret;
+
+ talloc_zfree(state->timeout_handler);
+
+ ret = read_pipe_recv(subreq, state, &buf, &buf_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ PIPE_FD_CLOSE(state->io->read_from_child_fd);
+
+ ret = parse_p11_child_response(state, buf, buf_len, state->sss_certmap_ctx,
+ &state->cert_list);
+ if (ret != EOK) {
+ if (ret == ERR_P11_PIN_LOCKED) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "PIN locked\n");
+ user_info_type = SSS_PAM_USER_INFO_PIN_LOCKED;
+ pam_add_response(state->pam_data, SSS_PAM_USER_INFO,
+ sizeof(uint32_t), (const uint8_t *) &user_info_type);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_response failed.\n");
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static void p11_child_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct pam_check_cert_state *state =
+ tevent_req_data(req, struct pam_check_cert_state);
+
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Timeout reached for p11_child, "
+ "consider increasing p11_child_timeout.\n");
+ child_handler_destroy(state->child_ctx);
+ state->child_ctx = NULL;
+ state->child_status = ETIMEDOUT;
+ tevent_req_error(req, ERR_P11_CHILD_TIMEOUT);
+}
+
+errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct cert_auth_info **cert_list)
+{
+ struct cert_auth_info *tmp_cert_auth_info;
+ struct pam_check_cert_state *state =
+ tevent_req_data(req, struct pam_check_cert_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (cert_list != NULL) {
+ DLIST_FOR_EACH(tmp_cert_auth_info, state->cert_list) {
+ talloc_steal(mem_ctx, tmp_cert_auth_info);
+ }
+
+ *cert_list = state->cert_list;
+ }
+
+ return EOK;
+}
+
+static char *get_cert_prompt(TALLOC_CTX *mem_ctx,
+ struct cert_auth_info *cert_info)
+{
+ int ret;
+ struct sss_certmap_ctx *ctx = NULL;
+ unsigned char *der = NULL;
+ size_t der_size;
+ char *prompt = NULL;
+ char *filter = NULL;
+ char **domains = NULL;
+
+ ret = sss_certmap_init(mem_ctx, NULL, NULL, &ctx);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
+ return NULL;
+ }
+
+ ret = sss_certmap_add_rule(ctx, 10, "KRB5:<ISSUER>.*",
+ "LDAP:{subject_dn!nss}", NULL);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_add_rule failed.\n");
+ goto done;
+ }
+
+ der = sss_base64_decode(mem_ctx, sss_cai_get_cert(cert_info), &der_size);
+ if (der == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ goto done;
+ }
+
+ ret = sss_certmap_expand_mapping_rule(ctx, der, der_size,
+ &filter, &domains);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_expand_mapping_rule failed.\n");
+ goto done;
+ }
+
+ prompt = talloc_asprintf(mem_ctx, "%s\n%s", sss_cai_get_label(cert_info),
+ filter);
+ if (prompt == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ }
+
+done:
+ sss_certmap_free_filter_and_domains(filter, domains);
+ sss_certmap_free_ctx(ctx);
+ talloc_free(der);
+
+ return prompt;
+}
+
+static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username,
+ struct cert_auth_info *cert_info,
+ const char *nss_name,
+ uint8_t **_msg, size_t *_msg_len)
+{
+ uint8_t *msg = NULL;
+ size_t msg_len;
+ const char *token_name;
+ const char *module_name;
+ const char *key_id;
+ const char *label;
+ char *prompt;
+ size_t user_len;
+ size_t token_len;
+ size_t module_len;
+ size_t key_id_len;
+ size_t label_len;
+ size_t prompt_len;
+ size_t nss_name_len;
+ const char *username = "";
+ const char *nss_username = "";
+
+ if (sysdb_username != NULL) {
+ username = sysdb_username;
+ }
+
+ if (nss_name != NULL) {
+ nss_username = nss_name;
+ }
+
+ prompt = get_cert_prompt(mem_ctx, cert_info);
+ if (prompt == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_cert_prompt failed.\n");
+ return EIO;
+ }
+
+ token_name = sss_cai_get_token_name(cert_info);
+ module_name = sss_cai_get_module_name(cert_info);
+ key_id = sss_cai_get_key_id(cert_info);
+ label = sss_cai_get_label(cert_info);
+
+ user_len = strlen(username) + 1;
+ token_len = strlen(token_name) + 1;
+ module_len = strlen(module_name) + 1;
+ key_id_len = strlen(key_id) + 1;
+ label_len = strlen(label) + 1;
+ prompt_len = strlen(prompt) + 1;
+ nss_name_len = strlen(nss_username) +1;
+
+ msg_len = user_len + token_len + module_len + key_id_len + label_len
+ + prompt_len + nss_name_len;
+
+ msg = talloc_zero_size(mem_ctx, msg_len);
+ if (msg == NULL) {
+ talloc_free(prompt);
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
+ return ENOMEM;
+ }
+
+ memcpy(msg, username, user_len);
+ memcpy(msg + user_len, token_name, token_len);
+ memcpy(msg + user_len + token_len, module_name, module_len);
+ memcpy(msg + user_len + token_len + module_len, key_id, key_id_len);
+ memcpy(msg + user_len + token_len + module_len + key_id_len,
+ label, label_len);
+ memcpy(msg + user_len + token_len + module_len + key_id_len + label_len,
+ prompt, prompt_len);
+ memcpy(msg + user_len + token_len + module_len + key_id_len + label_len
+ + prompt_len,
+ nss_username, nss_name_len);
+ talloc_free(prompt);
+
+ if (_msg != NULL) {
+ *_msg = msg;
+ }
+
+ if (_msg_len != NULL) {
+ *_msg_len = msg_len;
+ }
+
+ return EOK;
+}
+
+/* The PKCS11_LOGIN_TOKEN_NAME environment variable is e.g. used by the Gnome
+ * Settings Daemon to determine the name of the token used for login but it
+ * should be only set if SSSD is called by gdm-smartcard. Otherwise desktop
+ * components might assume that gdm-smartcard PAM stack is configured
+ * correctly which might not be the case e.g. if Smartcard authentication was
+ * used when running gdm-password. */
+#define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME"
+
+errno_t add_pam_cert_response(struct pam_data *pd, struct sss_domain_info *dom,
+ const char *sysdb_username,
+ struct cert_auth_info *cert_info,
+ enum response_type type)
+{
+ uint8_t *msg = NULL;
+ char *env = NULL;
+ size_t msg_len;
+ int ret;
+ char *short_name = NULL;
+ char *domain_name = NULL;
+ const char *cert_info_name = sysdb_username;
+ struct sss_domain_info *user_dom;
+ char *nss_name = NULL;
+
+
+ if (type != SSS_PAM_CERT_INFO && type != SSS_PAM_CERT_INFO_WITH_HINT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid response type [%d].\n", type);
+ return EINVAL;
+ }
+
+ if ((type == SSS_PAM_CERT_INFO && sysdb_username == NULL)
+ || cert_info == NULL
+ || sss_cai_get_token_name(cert_info) == NULL
+ || sss_cai_get_module_name(cert_info) == NULL
+ || sss_cai_get_key_id(cert_info) == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n");
+ return EINVAL;
+ }
+
+ /* sysdb_username is a fully-qualified name which is used by pam_sss when
+ * prompting the user for the PIN and as login name if it wasn't set by
+ * the PAM caller but has to be determined based on the inserted
+ * Smartcard. If this type of name is irritating at the PIN prompt or the
+ * re_expression config option was set in a way that user@domain cannot be
+ * handled anymore some more logic has to be added here. But for the time
+ * being I think using sysdb_username is fine.
+ */
+
+ if (sysdb_username != NULL) {
+ ret = sss_parse_internal_fqname(pd, sysdb_username,
+ &short_name, &domain_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name '%s' [%d]: %s, "
+ "using full name.\n",
+ sysdb_username, ret, sss_strerror(ret));
+ } else {
+ if (domain_name != NULL) {
+ user_dom = find_domain_by_name(dom, domain_name, false);
+
+ if (user_dom != NULL) {
+ ret = sss_output_fqname(short_name, user_dom,
+ sysdb_username, false, &nss_name);
+ if (ret != EOK) {
+ nss_name = NULL;
+ }
+ }
+ }
+
+ }
+ }
+
+ ret = pack_cert_data(pd, cert_info_name, cert_info,
+ nss_name != NULL ? nss_name : sysdb_username,
+ &msg, &msg_len);
+ talloc_free(short_name);
+ talloc_free(domain_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pack_cert_data failed.\n");
+ return ret;
+ }
+
+ ret = pam_add_response(pd, type, msg_len, msg);
+ talloc_free(msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_add_response failed to add certificate info.\n");
+ return ret;
+ }
+
+ if (strcmp(pd->service, "gdm-smartcard") == 0) {
+ env = talloc_asprintf(pd, "%s=%s", PKCS11_LOGIN_TOKEN_ENV_NAME,
+ sss_cai_get_token_name(cert_info));
+ if (env == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ return ENOMEM;
+ }
+
+ ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(env) + 1,
+ (uint8_t *)env);
+ talloc_free(env);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_add_response failed to add environment variable.\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
diff --git a/src/responder/pam/pamsrv_passkey.c b/src/responder/pam/pamsrv_passkey.c
new file mode 100644
index 0000000..1125840
--- /dev/null
+++ b/src/responder/pam/pamsrv_passkey.c
@@ -0,0 +1,1438 @@
+/*
+ SSSD
+
+ PAM Responder - passkey related requests
+
+ Copyright (C) Justin Stephenson <jstephen@redhat.com> 2022
+
+ 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 "util/child_common.h"
+#include "util/authtok.h"
+#include "db/sysdb.h"
+#include "db/sysdb_passkey_user_verification.h"
+#include "responder/pam/pamsrv.h"
+
+#include "responder/pam/pamsrv_passkey.h"
+
+struct pam_passkey_verification_enum_str {
+ enum passkey_user_verification verification;
+ const char *option;
+};
+
+struct pam_passkey_table_data {
+ hash_table_t *table;
+ char *key;
+ struct pk_child_user_data *data;
+};
+
+struct pam_passkey_verification_enum_str pam_passkey_verification_enum_str[] = {
+ { PAM_PASSKEY_VERIFICATION_ON, "on" },
+ { PAM_PASSKEY_VERIFICATION_OFF, "off" },
+ { PAM_PASSKEY_VERIFICATION_OMIT, "unset" },
+ { PAM_PASSKEY_VERIFICATION_INVALID, NULL }
+};
+
+#define PASSKEY_PREFIX "passkey:"
+#define USER_VERIFICATION "user_verification="
+#define USER_VERIFICATION_LEN (sizeof(USER_VERIFICATION) -1)
+
+const char *pam_passkey_verification_enum_to_string(enum passkey_user_verification verification)
+{
+ size_t c;
+
+ for (c = 0 ; pam_passkey_verification_enum_str[c].option != NULL; c++) {
+ if (pam_passkey_verification_enum_str[c].verification == verification) {
+ return pam_passkey_verification_enum_str[c].option;
+ }
+ }
+
+ return "(NULL)";
+}
+
+struct passkey_ctx {
+ struct pam_ctx *pam_ctx;
+ struct tevent_context *ev;
+ struct pam_data *pd;
+ struct pam_auth_req *preq;
+};
+
+void pam_forwarder_passkey_cb(struct tevent_req *req);
+
+errno_t pam_passkey_concatenate_keys(TALLOC_CTX *mem_ctx,
+ struct pk_child_user_data *pk_data,
+ bool kerberos_pa,
+ char **_result_kh,
+ char **_result_ph);
+
+struct tevent_req *pam_passkey_get_mapping_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct passkey_ctx *pctx);
+void pam_passkey_get_user_done(struct tevent_req *req);
+void pam_passkey_get_mapping_done(struct tevent_req *req);
+errno_t pam_passkey_get_mapping_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result **_result);
+
+struct passkey_get_mapping_state {
+ struct pam_data *pd;
+ struct cache_req_result *result;
+};
+
+void passkey_kerberos_cb(struct tevent_req *req)
+{
+ struct pam_auth_req *preq = tevent_req_callback_data(req,
+ struct pam_auth_req);
+ errno_t ret = EOK;
+ int child_status;
+
+ ret = pam_passkey_auth_recv(req, &child_status);
+ talloc_free(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "PAM passkey auth failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "passkey child finished with status [%d]\n", child_status);
+
+ pam_check_user_search(preq);
+
+done:
+ pam_check_user_done(preq, ret);
+}
+
+errno_t passkey_kerberos(struct pam_ctx *pctx,
+ struct pam_data *pd,
+ struct pam_auth_req *preq)
+{
+ errno_t ret;
+ const char *prompt;
+ const char *key;
+ const char *pin;
+ size_t pin_len;
+ struct pk_child_user_data *data;
+ struct tevent_req *req;
+ int timeout;
+ char *verify_opts;
+ bool debug_libfido2;
+ enum passkey_user_verification verification;
+
+ ret = sss_authtok_get_passkey(preq, preq->pd->authtok,
+ &prompt, &key, &pin, &pin_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failure to get passkey authtok\n");
+ return EIO;
+ }
+
+ if (prompt == NULL || key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Passkey prompt and key are missing or invalid.\n");
+ return EIO;
+ }
+
+ data = sss_ptr_hash_lookup(pctx->pk_table_data->table, key,
+ struct pk_child_user_data);
+ if (data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to lookup passkey authtok\n");
+ return EIO;
+ }
+
+ ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_PASSKEY_CHILD_TIMEOUT, PASSKEY_CHILD_TIMEOUT_DEFAULT,
+ &timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read passkey_child_timeout from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_string(pctx->rctx->cdb, preq, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_PASSKEY_VERIFICATION, NULL,
+ &verify_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read '"CONFDB_MONITOR_PASSKEY_VERIFICATION"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Always use verification sent from passkey krb5 plugin */
+ if (strcasecmp(data->user_verification, "false") == 0) {
+ verification = PAM_PASSKEY_VERIFICATION_OFF;
+ } else {
+ verification = PAM_PASSKEY_VERIFICATION_ON;
+ }
+
+ ret = confdb_get_bool(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_PASSKEY_DEBUG_LIBFIDO2, false,
+ &debug_libfido2);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read '"CONFDB_PAM_PASSKEY_DEBUG_LIBFIDO2"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ req = pam_passkey_auth_send(preq->cctx, preq->cctx->ev, timeout, debug_libfido2,
+ verification, pd, data, true);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "passkey auth send failed [%d]: [%s]\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ tevent_req_set_callback(req, passkey_kerberos_cb, preq);
+
+ ret = EAGAIN;
+
+done:
+
+ return ret;
+
+}
+
+
+errno_t passkey_local(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct pam_ctx *pam_ctx,
+ struct pam_auth_req *preq,
+ struct pam_data *pd)
+{
+ struct tevent_req *req;
+ struct passkey_ctx *pctx;
+ errno_t ret;
+
+ pctx = talloc_zero(mem_ctx, struct passkey_ctx);
+ if (pctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "pctx == NULL\n");
+ return ENOMEM;
+ }
+
+ pctx->pd = pd;
+ pctx->pam_ctx = pam_ctx;
+ pctx->ev = ev;
+ pctx->preq = preq;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Checking for passkey authentication data\n");
+
+ req = pam_passkey_get_mapping_send(mem_ctx, pctx->ev, pctx);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_passkey_get_mapping_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, pam_passkey_get_user_done, pctx);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ talloc_free(pctx);
+ }
+
+ return ret;
+}
+
+struct tevent_req *pam_passkey_get_mapping_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct passkey_ctx *pk_ctx)
+{
+
+ struct passkey_get_mapping_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ int ret;
+ static const char *attrs[] = { SYSDB_NAME, SYSDB_USER_PASSKEY, NULL };
+
+ req = tevent_req_create(mem_ctx, &state, struct passkey_get_mapping_state);
+ if (req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = cache_req_user_by_name_attrs_send(state, pk_ctx->ev,
+ pk_ctx->pam_ctx->rctx,
+ pk_ctx->pam_ctx->rctx->ncache, 0,
+ pk_ctx->pd->domain,
+ pk_ctx->pd->user, attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, pam_passkey_get_mapping_done, req);
+
+ return req;
+
+done:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+void pam_passkey_get_mapping_done(struct tevent_req *subreq)
+{
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ struct passkey_get_mapping_state *state;
+
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct passkey_get_mapping_state);
+
+ ret = cache_req_user_by_name_attrs_recv(state, subreq, &result);
+ state->result = result;
+
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t pam_passkey_get_mapping_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct cache_req_result **_result)
+{
+ struct passkey_get_mapping_state *state = NULL;
+
+ state = tevent_req_data(req, struct passkey_get_mapping_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_result = talloc_steal(mem_ctx, state->result);
+ return EOK;
+}
+
+errno_t read_passkey_conf_verification(TALLOC_CTX *mem_ctx,
+ const char *verify_opts,
+ enum passkey_user_verification *_user_verification)
+{
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ char **opts;
+ size_t c;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ if (verify_opts == NULL) {
+ ret = EOK;
+ goto done;
+ }
+
+ ret = split_on_separator(tmp_ctx, verify_opts, ',', true, true, &opts,
+ NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed [%d], %s.\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (c = 0; opts[c] != NULL; c++) {
+ if (strncasecmp(opts[c], USER_VERIFICATION, USER_VERIFICATION_LEN) == 0) {
+ if (strcasecmp("true", &opts[c][USER_VERIFICATION_LEN]) == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "user_verification set to true.\n");
+ *_user_verification = PAM_PASSKEY_VERIFICATION_ON;
+ } else if (strcasecmp("false", &opts[c][USER_VERIFICATION_LEN]) == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "user_verification set to false.\n");
+ *_user_verification = PAM_PASSKEY_VERIFICATION_OFF;
+ }
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unsupported passkey verification option [%s], " \
+ "skipping.\n", opts[c]);
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t passkey_local_verification(TALLOC_CTX *mem_ctx,
+ struct passkey_ctx *pctx,
+ struct confdb_ctx *cdb,
+ struct sysdb_ctx *sysdb,
+ const char *domain_name,
+ struct pk_child_user_data *pk_data,
+ enum passkey_user_verification *_user_verification,
+ bool *_debug_libfido2)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ const char *verification_from_ldap;
+ char *verify_opts = NULL;
+ bool debug_libfido2 = false;
+ enum passkey_user_verification verification = PAM_PASSKEY_VERIFICATION_OMIT;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_domain_get_passkey_user_verification(tmp_ctx, sysdb, domain_name,
+ &verification_from_ldap);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read passkeyUserVerification from sysdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ /* This is expected for AD and LDAP */
+ ret = EOK;
+ goto done;
+ }
+
+ ret = confdb_get_bool(pctx->pam_ctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_PASSKEY_DEBUG_LIBFIDO2, false,
+ &debug_libfido2);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read '"CONFDB_PAM_PASSKEY_DEBUG_LIBFIDO2"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* If require user verification setting is set in LDAP, use it */
+ if (verification_from_ldap != NULL) {
+ if (strcasecmp(verification_from_ldap, "true") == 0) {
+ verification = PAM_PASSKEY_VERIFICATION_ON;
+ } else if (strcasecmp(verification_from_ldap, "false") == 0) {
+ verification = PAM_PASSKEY_VERIFICATION_OFF;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Passkey verification is being enforced from LDAP\n");
+ } else {
+ /* No verification set in LDAP, fallback to local sssd.conf setting */
+ ret = confdb_get_string(pctx->pam_ctx->rctx->cdb, tmp_ctx, CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_PASSKEY_VERIFICATION, NULL,
+ &verify_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read '"CONFDB_MONITOR_PASSKEY_VERIFICATION"' from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+
+ ret = read_passkey_conf_verification(tmp_ctx, verify_opts, &verification);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to parse passkey verificaton options.\n");
+ /* Continue anyway */
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Passkey verification is being enforced from local configuration\n");
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Passkey verification setting [%s]\n",
+ pam_passkey_verification_enum_to_string(verification));
+
+ *_user_verification = verification;
+ *_debug_libfido2 = debug_libfido2;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t process_passkey_data(TALLOC_CTX *mem_ctx,
+ struct ldb_message *user_mesg,
+ const char *domain,
+ struct pk_child_user_data *_data)
+{
+ struct ldb_message_element *el;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ char **mappings;
+ const char **kh_mappings;
+ const char **public_keys;
+ const char *domain_name;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ERROR("talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ el = ldb_msg_find_element(user_mesg, SYSDB_USER_PASSKEY);
+ if (el == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No passkey data found\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ /* This attribute may contain other mapping data unrelated to passkey. In that case
+ * let's omit it. For example, AD user altSecurityIdentities may store ssh public key
+ * or smart card mapping data */
+ ret = split_on_separator(tmp_ctx, (const char *) el->values[0].data, ':', true, true,
+ &mappings, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Incorrectly formatted passkey data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ENOENT;
+ goto done;
+ } else if (strcasecmp(mappings[0], "passkey") != 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Mapping data found is not passkey related\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ kh_mappings = talloc_zero_array(tmp_ctx, const char *, el->num_values + 1);
+ if (kh_mappings == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ public_keys = talloc_zero_array(tmp_ctx, const char *, el->num_values + 1);
+ if (public_keys == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < el->num_values; i++) {
+ ret = split_on_separator(tmp_ctx, (const char *) el->values[i].data, ',', true, true,
+ &mappings, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Incorrectly formatted passkey data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ kh_mappings[i] = talloc_strdup(kh_mappings, mappings[0] + strlen(PASSKEY_PREFIX));
+ if (kh_mappings[i] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup key handle failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ public_keys[i] = talloc_strdup(public_keys, mappings[1]);
+ if (public_keys[i] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup public key failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ domain_name = talloc_strdup(tmp_ctx, domain);
+ if (domain_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup domain failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ _data->domain = talloc_steal(mem_ctx, domain_name);
+ _data->key_handles = talloc_steal(mem_ctx, kh_mappings);
+ _data->public_keys = talloc_steal(mem_ctx, public_keys);
+ _data->num_credentials = el->num_values;
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+void pam_forwarder_passkey_cb(struct tevent_req *req)
+{
+ struct pam_auth_req *preq = tevent_req_callback_data(req,
+ struct pam_auth_req);
+ errno_t ret = EOK;
+ int child_status;
+
+ ret = pam_passkey_auth_recv(req, &child_status);
+ talloc_free(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "PAM passkey auth failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ preq->pd->passkey_local_done = true;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "passkey child finished with status [%d]\n", child_status);
+ preq->pd->pam_status = PAM_SUCCESS;
+ pam_reply(preq);
+
+ return;
+
+done:
+ pam_check_user_done(preq, ret);
+}
+
+void pam_passkey_get_user_done(struct tevent_req *req)
+{
+ int ret;
+ struct passkey_ctx *pctx;
+ bool debug_libfido2 = false;
+ char *domain_name = NULL;
+ int timeout;
+ struct cache_req_result *result = NULL;
+ struct pk_child_user_data *pk_data = NULL;
+ enum passkey_user_verification verification = PAM_PASSKEY_VERIFICATION_OMIT;
+
+ pctx = tevent_req_callback_data(req, struct passkey_ctx);
+
+ ret = pam_passkey_get_mapping_recv(pctx, req, &result);
+ talloc_zfree(req);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_name_attrs_recv failed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (result == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "cache req result == NULL\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ pk_data = talloc_zero(pctx, struct pk_child_user_data);
+ if (!pk_data) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pk_data == NULL\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Use dns_name for AD/IPA - for LDAP fallback to domain->name */
+ if (result->domain != NULL) {
+ domain_name = result->domain->dns_name;
+ if (domain_name == NULL) {
+ domain_name = result->domain->name;
+ }
+ }
+
+ if (domain_name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid or missing domain name\n");
+ ret = EIO;
+ goto done;
+ }
+
+ /* Get passkey data */
+ DEBUG(SSSDBG_TRACE_ALL, "Processing passkey data\n");
+ ret = process_passkey_data(pk_data, result->msgs[0], domain_name, pk_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "process_passkey_data failed: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* timeout */
+ ret = confdb_get_int(pctx->pam_ctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_PASSKEY_CHILD_TIMEOUT, PASSKEY_CHILD_TIMEOUT_DEFAULT,
+ &timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to read passkey_child_timeout from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = passkey_local_verification(pctx, pctx, pctx->pam_ctx->rctx->cdb,
+ result->domain->sysdb, result->domain->dns_name,
+ pk_data, &verification, &debug_libfido2);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to check passkey verification [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Preauth respond with prompt_pin true or false based on user verification */
+ if (pctx->pd->cmd == SSS_PAM_PREAUTH) {
+ const char *prompt_pin = verification == PAM_PASSKEY_VERIFICATION_OFF ? "false" : "true";
+
+ ret = pam_add_response(pctx->pd, SSS_PAM_PASSKEY_INFO, strlen(prompt_pin) + 1,
+ (const uint8_t *) prompt_pin);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed. [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ pctx->pd->pam_status = PAM_SUCCESS;
+ pam_reply(pctx->preq);
+ talloc_free(pk_data);
+ return;
+ }
+
+ req = pam_passkey_auth_send(pctx, pctx->ev, timeout, debug_libfido2,
+ verification, pctx->pd, pk_data, false);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_passkey_auth_send failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ tevent_req_set_callback(req, pam_forwarder_passkey_cb, pctx->preq);
+
+done:
+ if (pk_data != NULL) {
+ talloc_free(pk_data);
+ }
+
+ if (ret == ENOENT) {
+ /* No passkey data, continue through to typical auth flow */
+ DEBUG(SSSDBG_TRACE_FUNC, "No passkey data found, skipping passkey auth\n");
+ pctx->preq->passkey_data_exists = false;
+ pam_check_user_search(pctx->preq);
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unexpected passkey error [%d]: %s."
+ " Skipping passkey auth\n",
+ ret, sss_strerror(ret));
+ pam_check_user_search(pctx->preq);
+ }
+
+ return;
+}
+
+
+struct pam_passkey_auth_send_state {
+ struct pam_data *pd;
+ struct tevent_context *ev;
+ struct tevent_timer *timeout_handler;
+ struct sss_child_ctx_old *child_ctx;
+ struct child_io_fds *io;
+ const char *logfile;
+ const char **extra_args;
+ char *verify_opts;
+ int timeout;
+ int child_status;
+ bool kerberos_pa;
+};
+
+static errno_t passkey_child_exec(struct tevent_req *req);
+static void pam_passkey_auth_done(int child_status,
+ struct tevent_signal *sige,
+ void *pvt);
+
+static int pin_destructor(void *ptr)
+{
+ uint8_t *pin = talloc_get_type(ptr, uint8_t);
+ if (pin == NULL) return EOK;
+
+ sss_erase_talloc_mem_securely(pin);
+
+ return EOK;
+}
+
+errno_t get_passkey_child_write_buffer(TALLOC_CTX *mem_ctx,
+ struct pam_data *pd,
+ uint8_t **_buf, size_t *_len)
+{
+ int ret;
+ uint8_t *buf;
+ size_t len;
+ const char *pin = NULL;
+
+ if (pd == NULL || pd->authtok == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing authtok.\n");
+ return EINVAL;
+ }
+
+ if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_PASSKEY ||
+ sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_PASSKEY_KRB) {
+ ret = sss_authtok_get_passkey_pin(pd->authtok, &pin, &len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_passkey_pin failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (pin == NULL || len == 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing PIN.\n");
+ return EINVAL;
+ }
+
+ buf = talloc_size(mem_ctx, len);
+ if (buf == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
+ return ENOMEM;
+ }
+
+ talloc_set_destructor((void *) buf, pin_destructor);
+
+ safealign_memcpy(buf, pin, len, NULL);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported authtok type [%d].\n",
+ sss_authtok_get_type(pd->authtok));
+ return EINVAL;
+ }
+
+ *_len = len;
+ *_buf = buf;
+
+ return EOK;
+}
+
+static void pam_passkey_child_read_data(struct tevent_req *subreq)
+{
+ uint8_t *buf;
+ ssize_t buf_len;
+ char *str;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct pam_passkey_auth_send_state *state = tevent_req_data(req, struct pam_passkey_auth_send_state);
+ int ret;
+
+ ret = read_pipe_recv(subreq, state, &buf, &buf_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ str = malloc(sizeof(char) * buf_len);
+ if (str == NULL) {
+ return;
+ }
+
+ snprintf(str, buf_len, "%s", buf);
+
+ sss_authtok_set_passkey_reply(state->pd->authtok, str, 0);
+
+ free(str);
+
+ tevent_req_done(req);
+ return;
+}
+
+static void passkey_child_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct pam_passkey_auth_send_state *state = tevent_req_data(req, struct pam_passkey_auth_send_state);
+
+ int ret;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Sending passkey data complete\n");
+
+ ret = write_pipe_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ PIPE_FD_CLOSE(state->io->write_to_child_fd);
+
+ if (state->kerberos_pa) {
+ /* Read data back from passkey child */
+ subreq = read_pipe_send(state, state->ev, state->io->read_from_child_fd);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "read_pipe_send failed.\n");
+ return;
+ }
+
+ tevent_req_set_callback(subreq, pam_passkey_child_read_data, req);
+ }
+}
+
+errno_t pam_passkey_concatenate_keys(TALLOC_CTX *mem_ctx,
+ struct pk_child_user_data *pk_data,
+ bool kerberos_pa,
+ char **_result_kh,
+ char **_result_pk)
+{
+ errno_t ret;
+ char *result_kh = NULL;
+ char *result_pk = NULL;
+
+ result_kh = talloc_strdup(mem_ctx, pk_data->key_handles[0]);
+ if (!kerberos_pa) {
+ result_pk = talloc_strdup(mem_ctx, pk_data->public_keys[0]);
+ }
+
+ for (int i = 1; i < pk_data->num_credentials; i++) {
+ result_kh = talloc_strdup_append(result_kh, ",");
+ if (result_kh == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ result_kh = talloc_strdup_append(result_kh, pk_data->key_handles[i]);
+ if (result_kh == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!kerberos_pa) {
+ result_pk = talloc_strdup_append(result_pk, ",");
+ if (result_pk == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ result_pk = talloc_strdup_append(result_pk, pk_data->public_keys[i]);
+ if (result_kh == NULL || result_pk == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ *_result_kh = result_kh;
+ *_result_pk = result_pk;
+
+ ret = EOK;
+done:
+ return ret;
+}
+
+struct tevent_req *
+pam_passkey_auth_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int timeout,
+ bool debug_libfido2,
+ enum passkey_user_verification verification,
+ struct pam_data *pd,
+ struct pk_child_user_data *pk_data,
+ bool kerberos_pa)
+{
+ struct tevent_req *req;
+ struct pam_passkey_auth_send_state *state;
+ size_t arg_c = 0;
+ char *result_kh;
+ char *result_pk;
+ int num_args;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct pam_passkey_auth_send_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->pd = pd;
+ state->ev = ev;
+
+ state->timeout = timeout;
+ state->kerberos_pa = kerberos_pa;
+ state->logfile = PASSKEY_CHILD_LOG_FILE;
+ state->io = talloc(state, struct child_io_fds);
+ if (state->io == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc child fds failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->io->write_to_child_fd = -1;
+ state->io->read_from_child_fd = -1;
+ talloc_set_destructor((void *) state->io, child_io_destructor);
+
+ num_args = 11;
+ state->extra_args = talloc_zero_array(state, const char *, num_args + 1);
+ if (state->extra_args == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch (verification) {
+ case PAM_PASSKEY_VERIFICATION_ON:
+ state->extra_args[arg_c++] = "--user-verification=true";
+ DEBUG(SSSDBG_TRACE_FUNC, "Calling child with user-verification true\n");
+ break;
+ case PAM_PASSKEY_VERIFICATION_OFF:
+ state->extra_args[arg_c++] = "--user-verification=false";
+ DEBUG(SSSDBG_TRACE_FUNC, "Calling child with user-verification false\n");
+ break;
+ default:
+ DEBUG(SSSDBG_TRACE_FUNC, "Calling child with user-verification unset\n");
+ break;
+ }
+
+ ret = pam_passkey_concatenate_keys(state, pk_data, state->kerberos_pa,
+ &result_kh, &result_pk);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_passkey_concatenate keys failed - [%d]: [%s]\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+
+ if (state->kerberos_pa) {
+ state->extra_args[arg_c++] = pk_data->crypto_challenge;
+ state->extra_args[arg_c++] = "--cryptographic-challenge";
+ state->extra_args[arg_c++] = result_kh;
+ state->extra_args[arg_c++] = "--key-handle";
+ state->extra_args[arg_c++] = pk_data->domain;
+ state->extra_args[arg_c++] = "--domain";
+ state->extra_args[arg_c++] = "--get-assert";
+ } else {
+ state->extra_args[arg_c++] = result_pk;
+ state->extra_args[arg_c++] = "--public-key";
+ state->extra_args[arg_c++] = result_kh;
+ state->extra_args[arg_c++] = "--key-handle";
+ state->extra_args[arg_c++] = pk_data->domain;
+ state->extra_args[arg_c++] = "--domain";
+ state->extra_args[arg_c++] = state->pd->user;
+ state->extra_args[arg_c++] = "--username";
+ state->extra_args[arg_c++] = "--authenticate";
+ }
+
+ ret = passkey_child_exec(req);
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void
+passkey_child_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req =
+ talloc_get_type(pvt, struct tevent_req);
+ struct pam_passkey_auth_send_state *state =
+ tevent_req_data(req, struct pam_passkey_auth_send_state);
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "Timeout reached for passkey child, "
+ "consider increasing passkey_child_timeout\n");
+ child_handler_destroy(state->child_ctx);
+ state->child_ctx = NULL;
+ state->child_status = ETIMEDOUT;
+ tevent_req_error(req, ERR_PASSKEY_CHILD_TIMEOUT);
+}
+
+static errno_t passkey_child_exec(struct tevent_req *req)
+{
+ struct pam_passkey_auth_send_state *state;
+ struct tevent_req *subreq;
+ int pipefd_from_child[2] = PIPE_INIT;
+ int pipefd_to_child[2] = PIPE_INIT;
+ pid_t child_pid;
+ uint8_t *write_buf = NULL;
+ size_t write_buf_len = 0;
+ struct timeval tv;
+ int ret;
+
+ state = tevent_req_data(req, struct pam_passkey_auth_send_state);
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ child_pid = fork();
+ if (child_pid == 0) { /* child */
+ exec_child_ex(state, pipefd_to_child, pipefd_from_child,
+ PASSKEY_CHILD_PATH, state->logfile, state->extra_args,
+ false, STDIN_FILENO, STDOUT_FILENO);
+ /* We should never get here */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec passkey child\n");
+ goto done;
+ } else if (child_pid > 0) { /* parent */
+ state->io->read_from_child_fd = pipefd_from_child[0];
+ PIPE_FD_CLOSE(pipefd_from_child[1]);
+ sss_fd_nonblocking(state->io->read_from_child_fd);
+
+ state->io->write_to_child_fd = pipefd_to_child[1];
+ PIPE_FD_CLOSE(pipefd_to_child[0]);
+ sss_fd_nonblocking(state->io->write_to_child_fd);
+
+ /* Set up SIGCHLD handler */
+ if (state->kerberos_pa) {
+ ret = child_handler_setup(state->ev, child_pid, NULL, req, &state->child_ctx);
+ } else {
+ ret = child_handler_setup(state->ev, child_pid,
+ pam_passkey_auth_done, req,
+ &state->child_ctx);
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_PASSKEY_CHILD;
+ goto done;
+ }
+
+ /* Set up timeout handler */
+ tv = tevent_timeval_current_ofs(state->timeout, 0);
+ state->timeout_handler = tevent_add_timer(state->ev, req, tv,
+ passkey_child_timeout, req);
+ if (state->timeout_handler == NULL) {
+ ret = ERR_PASSKEY_CHILD;
+ goto done;
+ }
+
+ /* PIN is needed */
+ if (sss_authtok_get_type(state->pd->authtok) != SSS_AUTHTOK_TYPE_EMPTY) {
+ ret = get_passkey_child_write_buffer(state, state->pd, &write_buf,
+ &write_buf_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "get_passkey_child_write_buffer failed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ if (write_buf_len != 0) {
+ subreq = write_pipe_send(state, state->ev, write_buf, write_buf_len,
+ state->io->write_to_child_fd);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "write_pipe_send failed.\n");
+ ret = ERR_PASSKEY_CHILD;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, passkey_child_write_done, req);
+ }
+ /* Now either wait for the timeout to fire or the child to finish */
+ } else { /* error */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ return EAGAIN;
+
+done:
+ if (ret != EOK) {
+ PIPE_CLOSE(pipefd_from_child);
+ PIPE_CLOSE(pipefd_to_child);
+ }
+
+ return ret;
+}
+
+errno_t pam_passkey_auth_recv(struct tevent_req *req,
+ int *child_status)
+{
+ struct pam_passkey_auth_send_state *state =
+ tevent_req_data(req, struct pam_passkey_auth_send_state);
+
+ *child_status = state->child_status;
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+errno_t decode_pam_passkey_msg(TALLOC_CTX *mem_ctx,
+ uint8_t *buf,
+ size_t len,
+ struct pk_child_user_data **_data)
+{
+
+ size_t p = 0;
+ size_t pctr = 0;
+ errno_t ret;
+ size_t offset;
+ struct pk_child_user_data *data = NULL;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ data = talloc_zero(tmp_ctx, struct pk_child_user_data);
+ if (data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to talloc passkey data.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ data->user_verification = talloc_strdup(data, (char *) &buf[p]);
+ if (data->user_verification == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to strdup passkey prompt.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ offset = strlen(data->user_verification) + 1;
+ if (offset >= len) {
+ DEBUG(SSSDBG_OP_FAILURE, "passkey prompt offset failure.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ data->crypto_challenge = talloc_strdup(data, (char *) &buf[p + offset]);
+ if (data->crypto_challenge == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to strdup passkey challenge.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ offset += strlen(data->crypto_challenge) + 1;
+ if (offset >= len) {
+ DEBUG(SSSDBG_OP_FAILURE, "passkey challenge offset failure.\n");
+ ret = EIO;
+ goto done;
+ }
+
+
+ data->domain = talloc_strdup(data, (char *) &buf[p] + offset);
+ if (data->domain == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to strdup passkey domain.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ offset += strlen(data->domain) + 1;
+ if (offset >= len) {
+ DEBUG(SSSDBG_OP_FAILURE, "passkey domain offset failure.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ SAFEALIGN_COPY_UINT32(&data->num_credentials, &buf[p + offset], &pctr);
+ size_t list_sz = (size_t) data->num_credentials;
+
+ offset += sizeof(uint32_t);
+
+ data->key_handles = talloc_zero_array(data, const char *, list_sz);
+
+ for (int i = 0; i < list_sz; i++) {
+ data->key_handles[i] = talloc_strdup(data->key_handles, (char *) &buf[p + offset]);
+ if (data->key_handles[i] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to strdup passkey list.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ offset += strlen(data->key_handles[i]) + 1;
+ }
+
+ *_data = talloc_steal(mem_ctx, data);
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t save_passkey_data(TALLOC_CTX *mem_ctx,
+ struct pam_ctx *pctx,
+ struct pk_child_user_data *data,
+ struct pam_auth_req *preq)
+{
+ char *pk_key;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Passkey data (pk_table_data) is stolen onto client ctx, it will
+ * be freed when the client closes, and the sss_ptr_hash interface
+ * takes care of automatically removing it from the hash table then */
+ pctx->pk_table_data = talloc_zero(tmp_ctx, struct pam_passkey_table_data);
+ if (pctx->pk_table_data == NULL) {
+ return ENOMEM;
+ }
+
+ if (pctx->pk_table_data->table == NULL) {
+ pctx->pk_table_data->table = sss_ptr_hash_create(pctx->pk_table_data,
+ NULL, NULL);
+ if (pctx->pk_table_data->table == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ pk_key = talloc_asprintf(tmp_ctx, "%s", data->crypto_challenge);
+ if (pk_key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ pctx->pk_table_data->key = talloc_strdup(pctx->pk_table_data, pk_key);
+ if (pctx->pk_table_data->key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_ptr_hash_add(pctx->pk_table_data->table, pk_key, data,
+ struct pk_child_user_data);
+ if (ret == EEXIST) {
+ DEBUG(SSSDBG_TRACE_FUNC, "pk_table key [%s] already exists\n",
+ pk_key);
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to add pk data to hash table "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ talloc_steal(mem_ctx, pctx->pk_table_data);
+ pctx->pk_table_data->data = talloc_steal(mem_ctx, data);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t pam_eval_passkey_response(struct pam_ctx *pctx,
+ struct pam_data *pd,
+ struct pam_auth_req *preq,
+ bool *_pk_preauth_done)
+{
+ struct response_data *pk_resp;
+ struct pk_child_user_data *pk_data;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ pk_resp = pd->resp_list;
+
+ while (pk_resp != NULL) {
+ switch (pk_resp->type) {
+ case SSS_PAM_PASSKEY_KRB_INFO:
+ if (!pctx->passkey_auth) {
+ /* Passkey auth is disabled. To avoid passkey prompts appearing,
+ * don't send SSS_PAM_PASSKEY_KRB_INFO to the client and
+ * add a dummy response to fallback to normal auth */
+ pk_resp->do_not_send_to_client = true;
+ ret = pam_add_response(pd, SSS_OTP, 0, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+ goto done;
+ }
+ break;
+ }
+ ret = decode_pam_passkey_msg(tmp_ctx, pk_resp->data, pk_resp->len, &pk_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to decode passkey msg\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = save_passkey_data(preq->cctx, pctx, pk_data, preq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to save passkey msg\n");
+ ret = EIO;
+ goto done;
+ }
+ break;
+ /* Passkey non-kerberos preauth has already run */
+ case SSS_PAM_PASSKEY_INFO:
+ *_pk_preauth_done = true;
+ default:
+ break;
+ }
+ pk_resp = pk_resp->next;
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+
+static void
+pam_passkey_auth_done(int child_status,
+ struct tevent_signal *sige,
+ void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+
+ struct pam_passkey_auth_send_state *state =
+ tevent_req_data(req, struct pam_passkey_auth_send_state);
+ state->child_status = WEXITSTATUS(child_status);
+ if (WIFEXITED(child_status)) {
+ if (WEXITSTATUS(child_status) != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ PASSKEY_CHILD_PATH " failed with status [%d]. Check passkey_child"
+ " logs for more information.\n",
+ WEXITSTATUS(child_status));
+ tevent_req_error(req, ERR_PASSKEY_CHILD);
+ return;
+ }
+ } else if (WIFSIGNALED(child_status)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ PASSKEY_CHILD_PATH " was terminated by signal [%d]. Check passkey_child"
+ " logs for more information.\n",
+ WTERMSIG(child_status));
+ tevent_req_error(req, ECHILD);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "passkey data is valid. Mark done\n");
+
+ tevent_req_done(req);
+ return;
+}
+
+bool may_do_passkey_auth(struct pam_ctx *pctx,
+ struct pam_data *pd)
+{
+#ifndef BUILD_PASSKEY
+ DEBUG(SSSDBG_TRACE_FUNC, "Passkey auth not possible, SSSD built without passkey support!\n");
+ return false;
+#else
+ if (!pctx->passkey_auth) {
+ return false;
+ }
+
+ if (pd->cmd != SSS_PAM_PREAUTH && pd->cmd != SSS_PAM_AUTHENTICATE) {
+ return false;
+ }
+
+ if (pd->service == NULL || *pd->service == '\0') {
+ return false;
+ }
+
+ return true;
+#endif /* BUILD_PASSKEY */
+}
diff --git a/src/responder/pam/pamsrv_passkey.h b/src/responder/pam/pamsrv_passkey.h
new file mode 100644
index 0000000..7c5a532
--- /dev/null
+++ b/src/responder/pam/pamsrv_passkey.h
@@ -0,0 +1,83 @@
+/*
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Copyright (C) 2022 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 __PAMSRV_PASSKEY_H__
+#define __PAMSRV_PASSKEY_H__
+
+#include <security/pam_appl.h>
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/pam/pamsrv.h"
+#include "lib/certmap/sss_certmap.h"
+
+enum passkey_user_verification {
+ PAM_PASSKEY_VERIFICATION_ON,
+ PAM_PASSKEY_VERIFICATION_OFF,
+ PAM_PASSKEY_VERIFICATION_OMIT,
+ PAM_PASSKEY_VERIFICATION_INVALID
+};
+
+errno_t passkey_local(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct pam_ctx *pam_ctx,
+ struct pam_auth_req *preq,
+ struct pam_data *pd);
+errno_t passkey_kerberos(struct pam_ctx *pctx,
+ struct pam_data *pd,
+ struct pam_auth_req *preq);
+
+struct pk_child_user_data {
+ /* Both Kerberos and non-kerberos */
+ const char *domain;
+ size_t num_credentials;
+ const char *user_verification;
+ const char **key_handles;
+ /* Kerberos PA only */
+ const char *crypto_challenge;
+ /* Non-kerberos only */
+ const char *user;
+ const char **public_keys;
+};
+
+errno_t read_passkey_conf_verification(TALLOC_CTX *mem_ctx,
+ const char *verify_opts,
+ enum passkey_user_verification *_user_verification);
+
+void pam_forwarder_passkey_cb(struct tevent_req *req);
+struct tevent_req *pam_passkey_auth_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int timeout,
+ bool debug_libfido2,
+ enum passkey_user_verification verification,
+ struct pam_data *pd,
+ struct pk_child_user_data *pk_data,
+ bool kerberos_pa);
+errno_t pam_passkey_auth_recv(struct tevent_req *req,
+ int *child_status);
+errno_t pam_eval_passkey_response(struct pam_ctx *pctx,
+ struct pam_data *pd,
+ struct pam_auth_req *preq,
+ bool *_pk_preauth_done);
+bool may_do_passkey_auth(struct pam_ctx *pctx,
+ struct pam_data *pd);
+
+#endif /* __PAMSRV_PASSKEY_H__ */
diff --git a/src/responder/ssh/ssh_cert_to_ssh_key.c b/src/responder/ssh/ssh_cert_to_ssh_key.c
new file mode 100644
index 0000000..b8bc8b7
--- /dev/null
+++ b/src/responder/ssh/ssh_cert_to_ssh_key.c
@@ -0,0 +1,342 @@
+/*
+ SSSD - certificate handling utils
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2018
+
+ 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 "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/child_common.h"
+#include "lib/certmap/sss_certmap.h"
+
+struct cert_to_ssh_key_state {
+ struct tevent_context *ev;
+ const char *logfile;
+ time_t timeout;
+ const char **extra_args;
+ const char **certs;
+ struct ldb_val *keys;
+ size_t cert_count;
+ size_t iter;
+ size_t valid_keys;
+
+ struct sss_child_ctx_old *child_ctx;
+ struct tevent_timer *timeout_handler;
+ struct child_io_fds *io;
+};
+
+static errno_t cert_to_ssh_key_step(struct tevent_req *req);
+static void cert_to_ssh_key_done(int child_status,
+ struct tevent_signal *sige,
+ void *pvt);
+
+struct tevent_req *cert_to_ssh_key_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *logfile, time_t timeout,
+ const char *ca_db,
+ struct sss_certmap_ctx *sss_certmap_ctx,
+ size_t cert_count,
+ struct ldb_val *bin_certs,
+ const char *verify_opts)
+{
+ struct tevent_req *req;
+ struct cert_to_ssh_key_state *state;
+ size_t arg_c;
+ size_t c;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct cert_to_ssh_key_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (ca_db == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing CA DB path.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->ev = ev;
+ state->logfile = logfile;
+ state->timeout = timeout;
+ state->io = talloc(state, struct child_io_fds);
+ if (state->io == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->io->write_to_child_fd = -1;
+ state->io->read_from_child_fd = -1;
+ talloc_set_destructor((void *) state->io, child_io_destructor);
+
+ state->keys = talloc_zero_array(state, struct ldb_val, cert_count);
+ if (state->keys == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->valid_keys = 0;
+
+ state->extra_args = talloc_zero_array(state, const char *, 8);
+ if (state->extra_args == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ /* extra_args are added in revers order, base64 encoded certificate is
+ * added at 0 */
+ arg_c = 1;
+ state->extra_args[arg_c++] = "--certificate";
+ state->extra_args[arg_c++] = ca_db;
+ state->extra_args[arg_c++] = "--ca_db";
+ if (verify_opts != NULL) {
+ state->extra_args[arg_c++] = verify_opts;
+ state->extra_args[arg_c++] = "--verify";
+ }
+ state->extra_args[arg_c++] = "--verification";
+
+ state->certs = talloc_zero_array(state, const char *, cert_count);
+ if (state->certs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->cert_count = 0;
+ for (c = 0; c < cert_count; c++) {
+
+ if (sss_certmap_ctx != NULL) {
+ ret = sss_certmap_match_cert(sss_certmap_ctx, bin_certs[c].data,
+ bin_certs[c].length);
+ if (ret != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Certificate does not match matching "
+ "rules and is ignored.\n");
+ continue;
+ }
+ }
+ state->certs[state->cert_count] = sss_base64_encode(state->certs,
+ bin_certs[c].data,
+ bin_certs[c].length);
+ if (state->certs[state->cert_count] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->cert_count++;
+ }
+
+ state->iter = 0;
+
+ ret = cert_to_ssh_key_step(req);
+
+done:
+ if (ret != EAGAIN) {
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void p11_child_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct cert_to_ssh_key_state *state =
+ tevent_req_data(req, struct cert_to_ssh_key_state);
+
+ DEBUG(SSSDBG_MINOR_FAILURE, "Timeout reached for p11_child.\n");
+ child_handler_destroy(state->child_ctx);
+ state->child_ctx = NULL;
+ tevent_req_error(req, ERR_P11_CHILD_TIMEOUT);
+}
+
+static errno_t cert_to_ssh_key_step(struct tevent_req *req)
+{
+ struct cert_to_ssh_key_state *state = tevent_req_data(req,
+ struct cert_to_ssh_key_state);
+ int ret;
+ int pipefd_from_child[2] = PIPE_INIT;
+ int pipefd_to_child[2] = PIPE_INIT;
+ pid_t child_pid;
+ struct timeval tv;
+
+ if (state->iter >= state->cert_count) {
+ return EOK;
+ }
+
+ state->extra_args[0] = state->certs[state->iter];
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ child_pid = fork();
+ if (child_pid == 0) { /* child */
+ exec_child_ex(state, pipefd_to_child, pipefd_from_child, P11_CHILD_PATH,
+ state->logfile, state->extra_args, false,
+ STDIN_FILENO, STDOUT_FILENO);
+ /* We should never get here */
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec p11 child\n");
+ } else if (child_pid > 0) { /* parent */
+
+ state->io->read_from_child_fd = pipefd_from_child[0];
+ PIPE_FD_CLOSE(pipefd_from_child[1]);
+ sss_fd_nonblocking(state->io->read_from_child_fd);
+
+ state->io->write_to_child_fd = pipefd_to_child[1];
+ PIPE_FD_CLOSE(pipefd_to_child[0]);
+ sss_fd_nonblocking(state->io->write_to_child_fd);
+
+ /* Set up SIGCHLD handler */
+ ret = child_handler_setup(state->ev, child_pid, cert_to_ssh_key_done,
+ req, &state->child_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+
+ /* Set up timeout handler */
+ tv = sss_tevent_timeval_current_ofs_time_t(state->timeout);
+ state->timeout_handler = tevent_add_timer(state->ev, req, tv,
+ p11_child_timeout,
+ req);
+ if (state->timeout_handler == NULL) {
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+ /* Now either wait for the timeout to fire or the child to finish */
+ } else { /* error */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ return EAGAIN;
+
+done:
+ if (ret != EOK) {
+ PIPE_CLOSE(pipefd_from_child);
+ PIPE_CLOSE(pipefd_to_child);
+ }
+
+ return ret;
+}
+
+static void cert_to_ssh_key_done(int child_status,
+ struct tevent_signal *sige,
+ void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct cert_to_ssh_key_state *state = tevent_req_data(req,
+ struct cert_to_ssh_key_state);
+ int ret;
+ bool valid = false;
+
+ PIPE_FD_CLOSE(state->io->read_from_child_fd);
+ PIPE_FD_CLOSE(state->io->write_to_child_fd);
+
+ if (WIFEXITED(child_status)) {
+ if (WEXITSTATUS(child_status) != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ P11_CHILD_PATH " failed with status [%d]\n", child_status);
+ } else {
+ valid = true;
+ }
+ }
+
+ if (WIFSIGNALED(child_status)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ P11_CHILD_PATH " was terminated by signal [%d]\n",
+ WTERMSIG(child_status));
+ }
+
+ if (valid) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Certificate [%s] is valid.\n",
+ state->certs[state->iter]);
+ ret = get_ssh_key_from_derb64(state->keys,
+ state->certs[state->iter],
+ &state->keys[state->iter].data,
+ &state->keys[state->iter].length);
+ if (ret == EOK) {
+ state->valid_keys++;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "get_ssh_key_from_cert failed, "
+ "skipping certificate [%s].\n",
+ state->certs[state->iter]);
+ state->keys[state->iter].data = NULL;
+ state->keys[state->iter].length = 0;
+ }
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Certificate [%s] is not valid.\n",
+ state->certs[state->iter]);
+ state->keys[state->iter].data = NULL;
+ state->keys[state->iter].length = 0;
+ }
+
+ state->iter++;
+ ret = cert_to_ssh_key_step(req);
+
+ if (ret != EAGAIN) {
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ }
+
+ return;
+}
+
+errno_t cert_to_ssh_key_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct ldb_val **keys, size_t *valid_keys)
+{
+ struct cert_to_ssh_key_state *state = tevent_req_data(req,
+ struct cert_to_ssh_key_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (keys != NULL) {
+ *keys = talloc_steal(mem_ctx, state->keys);
+ }
+
+ if (valid_keys != NULL) {
+ *valid_keys = state->valid_keys;
+ }
+
+ return EOK;
+}
diff --git a/src/responder/ssh/ssh_cmd.c b/src/responder/ssh/ssh_cmd.c
new file mode 100644
index 0000000..45ab57b
--- /dev/null
+++ b/src/responder/ssh/ssh_cmd.c
@@ -0,0 +1,409 @@
+/*
+ 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 "config.h"
+
+#include <talloc.h>
+#include <string.h>
+#include <pwd.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ssh/ssh_private.h"
+#include "responder/pam/pam_helpers.h"
+#include "lib/certmap/sss_certmap.h"
+
+struct ssh_cmd_ctx {
+ struct cli_ctx *cli_ctx;
+ const char *name;
+ const char *alias;
+ const char *domain;
+};
+
+static errno_t
+ssh_check_non_sssd_user(const char *username)
+{
+ struct passwd *pwd;
+
+ pwd = getpwnam(username);
+ if (pwd != NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "%s is a non-SSSD user\n", username);
+ return ERR_NON_SSSD_USER;
+ }
+
+ return ENOENT;
+}
+
+
+static struct sss_domain_info *
+ssh_get_result_domain(struct resp_ctx *rctx,
+ struct cache_req_result *result,
+ const char *name)
+{
+ if (result != NULL) {
+ return result->domain;
+ }
+
+ return find_domain_by_name(rctx->domains, name, true);
+}
+
+static void ssh_cmd_get_user_pubkeys_done(struct tevent_req *subreq);
+
+static errno_t ssh_cmd_get_user_pubkeys(struct cli_ctx *cli_ctx)
+{
+ struct ssh_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ static const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY,
+ SYSDB_USER_CERT, NULL };
+
+ cmd_ctx = talloc_zero(cli_ctx, struct ssh_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+
+ ret = ssh_protocol_parse_user(cli_ctx, cli_ctx->rctx->default_domain,
+ &cmd_ctx->name, &cmd_ctx->domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Requesting SSH user public keys for [%s] from [%s]\n",
+ cmd_ctx->name, cmd_ctx->domain ? cmd_ctx->domain : "<ALL>");
+
+ if (strcmp(cmd_ctx->name, "root") == 0) {
+ ret = ERR_NON_SSSD_USER;
+ goto done;
+ }
+
+ subreq = cache_req_user_by_name_attrs_send(cmd_ctx, cli_ctx->ev,
+ cli_ctx->rctx,
+ cli_ctx->rctx->ncache, 0,
+ cmd_ctx->domain,
+ cmd_ctx->name, attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ssh_cmd_get_user_pubkeys_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return ssh_protocol_done(cli_ctx, ret);
+ }
+
+ return ret;
+}
+
+struct priv_sss_debug {
+ int level;
+};
+
+static void ssh_ext_debug(void *private, const char *file, long line,
+ const char *function, const char *format, ...)
+{
+ va_list ap;
+ struct priv_sss_debug *data = private;
+ int level = SSSDBG_OP_FAILURE;
+
+ if (data != NULL) {
+ level = data->level;
+ }
+
+ va_start(ap, format);
+ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
+ format, ap);
+ va_end(ap);
+}
+
+static errno_t ssh_cmd_refresh_certmap_ctx(struct ssh_ctx *ssh_ctx,
+ struct sss_domain_info *domains)
+{
+
+ struct sss_certmap_ctx *sss_certmap_ctx = NULL;
+ struct sss_domain_info *dom;
+ struct certmap_info **certmap_list;
+ size_t c;
+ int ret;
+ bool rule_added;
+ bool all_rules = false;
+ bool no_rules = false;
+ bool rules_present = false;
+
+ ssh_ctx->cert_rules_error = false;
+
+ if (ssh_ctx->cert_rules == NULL || ssh_ctx->cert_rules[0] == NULL) {
+ all_rules = true;
+ } else if (ssh_ctx->cert_rules[0] != NULL
+ && ssh_ctx->cert_rules[1] == NULL) {
+ if (strcmp(ssh_ctx->cert_rules[0], "all_rules") == 0) {
+ all_rules = true;
+ } else if (strcmp(ssh_ctx->cert_rules[0], "no_rules") == 0) {
+ no_rules = true;
+ }
+ }
+
+ if (!ssh_ctx->use_cert_keys
+ || ssh_ctx->certmap_last_read
+ >= ssh_ctx->rctx->get_domains_last_call.tv_sec
+ || no_rules) {
+ DEBUG(SSSDBG_TRACE_ALL, "No certmap update needed.\n");
+ return EOK;
+ }
+
+ ret = sss_certmap_init(ssh_ctx, ssh_ext_debug, NULL, &sss_certmap_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
+ goto done;
+ }
+
+ rule_added = false;
+ DLIST_FOR_EACH(dom, domains) {
+ certmap_list = dom->certmaps;
+ if (certmap_list == NULL || *certmap_list == NULL) {
+ continue;
+ }
+
+ for (c = 0; certmap_list[c] != NULL; c++) {
+ rules_present = true;
+
+ if (!all_rules && !string_in_list(certmap_list[c]->name,
+ ssh_ctx->cert_rules, true)) {
+ DEBUG(SSSDBG_TRACE_ALL, "Skipping matching rule [%s], it is "
+ "not listed in the ssh_use_certificate_matching_rules "
+ "option.\n", certmap_list[c]->name);
+ continue;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Trying to add rule [%s][%d][%s][%s].\n",
+ certmap_list[c]->name, certmap_list[c]->priority,
+ certmap_list[c]->match_rule, certmap_list[c]->map_rule);
+
+ ret = sss_certmap_add_rule(sss_certmap_ctx,
+ certmap_list[c]->priority,
+ certmap_list[c]->match_rule,
+ certmap_list[c]->map_rule,
+ certmap_list[c]->domains);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_certmap_add_rule failed for rule [%s] "
+ "with error [%d][%s], skipping. "
+ "Please check for typos and if rule syntax is supported.\n",
+ certmap_list[c]->name, ret, sss_strerror(ret));
+ continue;
+ }
+ rule_added = true;
+ }
+ }
+
+ if (!rule_added) {
+ if (!rules_present) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "No rules available, trying to add default matching rule.\n");
+ ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO,
+ CERT_AUTH_DEFAULT_MATCHING_RULE,
+ NULL, NULL);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to add default matching rule [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "No matching rule added, please check "
+ "ssh_use_certificate_matching_rules option values for "
+ "typos.\n");
+
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ sss_certmap_free_ctx(ssh_ctx->sss_certmap_ctx);
+ ssh_ctx->sss_certmap_ctx = sss_certmap_ctx;
+ ssh_ctx->certmap_last_read = ssh_ctx->rctx->get_domains_last_call.tv_sec;
+ } else {
+ sss_certmap_free_ctx(sss_certmap_ctx);
+ ssh_ctx->cert_rules_error = true;
+ }
+
+ return ret;
+}
+
+static void ssh_cmd_get_user_pubkeys_done(struct tevent_req *subreq)
+{
+ struct cache_req_result *result;
+ struct ssh_cmd_ctx *cmd_ctx;
+ errno_t ret;
+ struct ssh_ctx *ssh_ctx;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct ssh_cmd_ctx);
+
+ ret = cache_req_user_by_name_attrs_recv(cmd_ctx, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ /* Check if it is a non SSSD user. */
+ ret = ssh_check_non_sssd_user(cmd_ctx->name);
+ }
+
+ ssh_protocol_done(cmd_ctx->cli_ctx, ret);
+ goto done;
+ }
+
+ ssh_ctx = talloc_get_type(cmd_ctx->cli_ctx->rctx->pvt_ctx, struct ssh_ctx);
+ ret = ssh_cmd_refresh_certmap_ctx(ssh_ctx, cmd_ctx->cli_ctx->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ssh_cmd_refresh_certmap_ctx failed, "
+ "certificate matching might not work as expected.\n");
+ }
+
+ ssh_protocol_reply(cmd_ctx->cli_ctx, result);
+
+done:
+ talloc_free(cmd_ctx);
+}
+
+static void ssh_cmd_get_host_pubkeys_done(struct tevent_req *subreq);
+
+static errno_t ssh_cmd_get_host_pubkeys(struct cli_ctx *cli_ctx)
+{
+ struct ssh_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ static const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, NULL };
+
+ cmd_ctx = talloc_zero(cli_ctx, struct ssh_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+
+ ret = ssh_protocol_parse_host(cli_ctx, &cmd_ctx->name, &cmd_ctx->alias,
+ &cmd_ctx->domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Requesting SSH host public keys for [%s] from [%s]\n",
+ cmd_ctx->name, cmd_ctx->domain ? cmd_ctx->domain : "<ALL>");
+
+ subreq = cache_req_ssh_host_id_by_name_send(cmd_ctx, cli_ctx->ev,
+ cli_ctx->rctx,
+ cli_ctx->rctx->ncache, 0,
+ cmd_ctx->domain,
+ cmd_ctx->name,
+ cmd_ctx->alias, attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ssh_cmd_get_host_pubkeys_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return ssh_protocol_done(cli_ctx, ret);
+ }
+
+ return ret;
+}
+
+static void ssh_cmd_get_host_pubkeys_done(struct tevent_req *subreq)
+{
+ struct cache_req_result *result = NULL;
+ struct sss_domain_info *domain;
+ struct ssh_cmd_ctx *cmd_ctx;
+ struct ssh_ctx *ssh_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct ssh_cmd_ctx);
+ ssh_ctx = talloc_get_type(cmd_ctx->cli_ctx->rctx->pvt_ctx, struct ssh_ctx);
+
+ ret = cache_req_ssh_host_id_by_name_recv(cmd_ctx, subreq, &result);
+ talloc_zfree(subreq);
+
+ if (ret == EOK || ret == ENOENT) {
+ domain = ssh_get_result_domain(ssh_ctx->rctx, result, cmd_ctx->domain);
+
+ ssh_update_known_hosts_file(ssh_ctx->rctx->domains, domain,
+ cmd_ctx->name, ssh_ctx->hash_known_hosts,
+ ssh_ctx->known_hosts_timeout);
+ }
+
+ if (ret != EOK) {
+ ssh_protocol_done(cmd_ctx->cli_ctx, ret);
+ goto done;
+ }
+
+ ssh_protocol_reply(cmd_ctx->cli_ctx, result);
+
+done:
+ talloc_free(cmd_ctx);
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version ssh_cli_protocol_version[] = {
+ {0, NULL, NULL}
+ };
+
+ return ssh_cli_protocol_version;
+}
+
+struct sss_cmd_table *get_ssh_cmds(void) {
+ static struct sss_cmd_table ssh_cmds[] = {
+ {SSS_GET_VERSION, sss_cmd_get_version},
+ {SSS_SSH_GET_USER_PUBKEYS, ssh_cmd_get_user_pubkeys},
+ {SSS_SSH_GET_HOST_PUBKEYS, ssh_cmd_get_host_pubkeys},
+ {SSS_CLI_NULL, NULL}
+ };
+
+ return ssh_cmds;
+}
diff --git a/src/responder/ssh/ssh_known_hosts.c b/src/responder/ssh/ssh_known_hosts.c
new file mode 100644
index 0000000..2be240b
--- /dev/null
+++ b/src/responder/ssh/ssh_known_hosts.c
@@ -0,0 +1,333 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 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 "config.h"
+
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/sss_ssh.h"
+#include "db/sysdb.h"
+#include "db/sysdb_ssh.h"
+#include "responder/ssh/ssh_private.h"
+
+static char *
+ssh_host_pubkeys_format_known_host_plain(TALLOC_CTX *mem_ctx,
+ struct sss_ssh_ent *ent)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *name, *pubkey;
+ char *result = NULL;
+ size_t i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ name = talloc_strdup(tmp_ctx, ent->name);
+ if (!name) {
+ goto done;
+ }
+
+ for (i = 0; i < ent->num_aliases; i++) {
+ name = talloc_asprintf_append(name, ",%s", ent->aliases[i]);
+ if (!name) {
+ goto done;
+ }
+ }
+
+ result = talloc_strdup(tmp_ctx, "");
+ if (!result) {
+ goto done;
+ }
+
+ for (i = 0; i < ent->num_pubkeys; i++) {
+ ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey);
+ if (ret != EOK) {
+ result = NULL;
+ goto done;
+ }
+
+ result = talloc_asprintf_append(result, "%s %s\n", name, pubkey);
+ if (!result) {
+ goto done;
+ }
+
+ talloc_free(pubkey);
+ }
+
+ talloc_steal(mem_ctx, result);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return result;
+}
+
+static char *
+ssh_host_pubkeys_format_known_host_hashed(TALLOC_CTX *mem_ctx,
+ struct sss_ssh_ent *ent)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *name, *pubkey, *saltstr, *hashstr, *result;
+ unsigned char salt[SSS_SHA1_LENGTH], hash[SSS_SHA1_LENGTH];
+ size_t i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ result = talloc_strdup(tmp_ctx, "");
+ if (!result) {
+ goto done;
+ }
+
+ for (i = 0; i < ent->num_pubkeys; i++) {
+ ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey);
+ if (ret != EOK) {
+ result = NULL;
+ goto done;
+ }
+
+ for (j = 0; j <= ent->num_aliases; j++) {
+ name = (j == 0 ? ent->name : ent->aliases[j-1]);
+
+ ret = sss_generate_csprng_buffer((uint8_t *)salt, SSS_SHA1_LENGTH);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_generate_csprng_buffer() failed (%d)\n", ret);
+ result = NULL;
+ goto done;
+ }
+
+ ret = sss_hmac_sha1(salt, SSS_SHA1_LENGTH,
+ (unsigned char *)name, strlen(name),
+ hash);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_hmac_sha1() failed (%d): %s\n",
+ ret, strerror(ret));
+ result = NULL;
+ goto done;
+ }
+
+ saltstr = sss_base64_encode(tmp_ctx, salt, SSS_SHA1_LENGTH);
+ if (!saltstr) {
+ result = NULL;
+ goto done;
+ }
+
+ hashstr = sss_base64_encode(tmp_ctx, hash, SSS_SHA1_LENGTH);
+ if (!hashstr) {
+ result = NULL;
+ goto done;
+ }
+
+ result = talloc_asprintf_append(result, "|1|%s|%s %s\n",
+ saltstr, hashstr, pubkey);
+ if (!result) {
+ goto done;
+ }
+
+ talloc_free(saltstr);
+ talloc_free(hashstr);
+ }
+
+ talloc_free(pubkey);
+ }
+
+ talloc_steal(mem_ctx, result);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return result;
+}
+
+static errno_t
+ssh_write_known_hosts(struct sss_domain_info *domains,
+ bool hash_known_hosts,
+ time_t now,
+ int fd)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *dom;
+ struct ldb_message **hosts;
+ struct sysdb_ctx *sysdb;
+ struct sss_ssh_ent *ent;
+ char *entstr;
+ size_t num_hosts;
+ size_t i;
+ ssize_t wret;
+ errno_t ret;
+
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_SSH_PUBKEY,
+ NULL
+ };
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ for (dom = domains; dom != NULL; dom = get_next_domain(dom, false)) {
+ sysdb = dom->sysdb;
+ if (sysdb == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Fatal: Sysdb CTX not found for this domain!\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = sysdb_get_ssh_known_hosts(tmp_ctx, dom, now, attrs,
+ &hosts, &num_hosts);
+ if (ret == ENOENT) {
+ continue;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Host search failed for domain "
+ "%s [%d]: %s\n", dom->name, ret, sss_strerror(ret));
+ continue;
+ }
+
+ for (i = 0; i < num_hosts; i++) {
+ ret = sss_ssh_make_ent(tmp_ctx, hosts[i], &ent);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get SSH host public keys\n");
+ continue;
+ }
+
+ if (hash_known_hosts) {
+ entstr = ssh_host_pubkeys_format_known_host_hashed(ent, ent);
+ } else {
+ entstr = ssh_host_pubkeys_format_known_host_plain(ent, ent);
+ }
+
+ if (entstr == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to format known_hosts data "
+ "for [%s]\n", ent->name);
+ continue;
+ }
+
+ wret = sss_atomic_write_s(fd, entstr, strlen(entstr));
+ if (wret == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ talloc_free(ent);
+ }
+
+ talloc_free(hosts);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+ssh_update_known_hosts_file(struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ const char *name,
+ bool hash_known_hosts,
+ int known_hosts_timeout)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *filename;
+ errno_t ret;
+ time_t now;
+ int fd = -1;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ now = time(NULL);
+
+ /* Update host's expiration time. */
+ if (domain != NULL) {
+ ret = sysdb_update_ssh_known_host_expire(domain, name, now,
+ known_hosts_timeout);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+ }
+
+ /* Create temporary known hosts file. */
+ filename = talloc_strdup(tmp_ctx, SSS_SSH_KNOWN_HOSTS_TEMP_TMPL);
+ if (filename == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ fd = sss_unique_file_ex(tmp_ctx, filename, 0133, &ret);
+ if (fd == -1) {
+ filename = NULL;
+ goto done;
+ }
+
+ /* Write contents. */
+ ret = ssh_write_known_hosts(domains, hash_known_hosts, now, fd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write known hosts file "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+
+ /* Rename to SSH known hosts file. */
+ ret = fchmod(fd, 0644);
+ if (ret == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ ret = rename(filename, SSS_SSH_KNOWN_HOSTS_PATH);
+ if (ret == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return ret;
+}
diff --git a/src/responder/ssh/ssh_private.h b/src/responder/ssh/ssh_private.h
new file mode 100644
index 0000000..0e4ed10
--- /dev/null
+++ b/src/responder/ssh/ssh_private.h
@@ -0,0 +1,100 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 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 _SSHSRV_PRIVATE_H_
+#define _SSHSRV_PRIVATE_H_
+
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+
+#define SSS_SSH_KNOWN_HOSTS_PATH PUBCONF_PATH"/known_hosts"
+#define SSS_SSH_KNOWN_HOSTS_TEMP_TMPL PUBCONF_PATH"/.known_hosts.XXXXXX"
+
+struct ssh_ctx {
+ struct resp_ctx *rctx;
+ struct sss_names_ctx *snctx;
+
+ bool hash_known_hosts;
+ int known_hosts_timeout;
+ char *ca_db;
+ bool use_cert_keys;
+
+ time_t certmap_last_read;
+ struct sss_certmap_ctx *sss_certmap_ctx;
+ char **cert_rules;
+ bool cert_rules_error;
+};
+
+struct sss_cmd_table *get_ssh_cmds(void);
+
+errno_t
+ssh_protocol_parse_user(struct cli_ctx *cli_ctx,
+ const char *default_domain,
+ const char **_name,
+ const char **_domain);
+
+errno_t
+ssh_protocol_parse_host(struct cli_ctx *cli_ctx,
+ const char **_name,
+ const char **_alias,
+ const char **_domain);
+
+void ssh_protocol_reply(struct cli_ctx *cli_ctx,
+ struct cache_req_result *result);
+
+errno_t
+ssh_protocol_done(struct cli_ctx *cli_ctx, errno_t error);
+
+struct tevent_req * ssh_get_output_keys_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg);
+
+errno_t ssh_get_output_keys_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sized_string *name,
+ struct ldb_message_element ***elements,
+ uint32_t *num_keys);
+
+errno_t
+ssh_protocol_build_reply(struct sss_packet *packet,
+ struct sized_string name,
+ struct ldb_message_element **elements,
+ uint32_t num_keys);
+
+errno_t
+ssh_update_known_hosts_file(struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ const char *name,
+ bool hash_known_hosts,
+ int known_hosts_timeout);
+
+struct tevent_req *cert_to_ssh_key_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *logfile, time_t timeout,
+ const char *ca_db,
+ struct sss_certmap_ctx *sss_certmap_ctx,
+ size_t cert_count,
+ struct ldb_val *bin_certs,
+ const char *verify_opts);
+
+errno_t cert_to_ssh_key_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct ldb_val **keys, size_t *valid_keys);
+#endif /* _SSHSRV_PRIVATE_H_ */
diff --git a/src/responder/ssh/ssh_protocol.c b/src/responder/ssh/ssh_protocol.c
new file mode 100644
index 0000000..5a9081b
--- /dev/null
+++ b/src/responder/ssh/ssh_protocol.c
@@ -0,0 +1,252 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 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 "config.h"
+
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/sss_ssh.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ssh/ssh_private.h"
+
+errno_t
+ssh_protocol_done(struct cli_ctx *cli_ctx, errno_t error)
+{
+ struct cli_protocol *pctx;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ switch (error) {
+ case EOK:
+ /* Create empty packet if none was provided. */
+ if (pctx->creq->out == NULL) {
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: success\n");
+ ret = EOK;
+ goto done;
+ default:
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: error [%d]: %s\n",
+ error, sss_strerror(error));
+ ret = sss_cmd_send_error(cli_ctx, error);
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send reply [%d]: %s!\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ sss_cmd_done(cli_ctx, NULL);
+ return EOK;
+}
+
+static void got_ssh_keys(struct tevent_req *req);
+void ssh_protocol_reply(struct cli_ctx *cli_ctx,
+ struct cache_req_result *result)
+{
+ errno_t ret;
+ struct tevent_req *req;
+
+ /* Make sure we have the results around until the end of the request. To
+ * avoid copying and memory allocation the keys and certificates from the
+ * result will be referenced during the next requests, so they should not
+ * be freed too early. */
+ result = talloc_steal(cli_ctx, result);
+
+ req = ssh_get_output_keys_send(cli_ctx, cli_ctx->ev, cli_ctx,
+ result->domain, result->msgs[0]);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_get_output_keys_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(req, got_ssh_keys, cli_ctx);
+
+ return;
+
+done:
+ ssh_protocol_done(cli_ctx, ret);
+}
+
+static void got_ssh_keys(struct tevent_req *req)
+{
+ errno_t ret;
+ struct cli_ctx *cli_ctx = tevent_req_callback_data(req, struct cli_ctx);
+ struct cli_protocol *pctx;
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+ struct ldb_message_element **elements;
+ uint32_t num_keys;
+ struct sized_string name;
+
+ ret = ssh_get_output_keys_recv(req, cli_ctx, &name, &elements, &num_keys);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_get_output_keys_revc failed");
+ goto done;
+ }
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ssh_protocol_build_reply(pctx->creq->out, name, elements, num_keys);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+
+done:
+ ssh_protocol_done(cli_ctx, ret);
+}
+
+static errno_t
+ssh_protocol_parse_request(struct cli_ctx *cli_ctx,
+ const char *default_domain,
+ const char **_name,
+ const char **_alias,
+ const char **_domain)
+{
+ struct cli_protocol *pctx;
+ const char *name = NULL;
+ const char *alias = NULL;
+ const char *domain = NULL;
+ uint32_t flags;
+ uint32_t name_len;
+ uint32_t alias_len;
+ uint32_t domain_len;
+ size_t body_len;
+ uint8_t *body;
+ size_t c = 0;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &body_len);
+
+ SAFEALIGN_COPY_UINT32_CHECK(&flags, body + c, body_len, &c);
+ if (flags & ~(uint32_t)SSS_SSH_REQ_MASK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid flags received [0x%x]\n", flags);
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32_CHECK(&name_len, body + c, body_len, &c);
+ if (name_len == 0 || name_len > body_len - c) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid name length\n");
+ return EINVAL;
+ }
+
+ name = (const char *)(body + c);
+ if (!sss_utf8_check((const uint8_t *)name, name_len-1) ||
+ name[name_len - 1] != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Name is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+ c += name_len;
+
+ if (flags & SSS_SSH_REQ_ALIAS) {
+ SAFEALIGN_COPY_UINT32_CHECK(&alias_len, body + c, body_len, &c);
+ if (alias_len == 0 || alias_len > body_len - c) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid alias length\n");
+ return EINVAL;
+ }
+
+ alias = (const char *)(body+c);
+ if (!sss_utf8_check((const uint8_t *)alias, alias_len - 1) ||
+ alias[alias_len - 1] != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Alias is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+ c += alias_len;
+ }
+
+ if (flags & SSS_SSH_REQ_DOMAIN) {
+ SAFEALIGN_COPY_UINT32_CHECK(&domain_len, body + c, body_len, &c);
+ if (domain_len > 0) {
+ if (domain_len > body_len - c) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid domain length\n");
+ return EINVAL;
+ }
+
+ domain = (const char *)(body + c);
+ if (!sss_utf8_check((const uint8_t *)domain, domain_len - 1) ||
+ domain[domain_len - 1] != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Domain is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+ c += domain_len;
+ } else {
+ domain = default_domain;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Requested domain [%s]\n", domain ? domain : "<ALL>");
+ }
+
+ if (_name != NULL) {
+ *_name = name;
+ }
+
+ if (_alias != NULL) {
+ *_alias = alias;
+ }
+
+ if (_domain != NULL) {
+ *_domain = domain;
+ }
+
+ return EOK;
+}
+
+errno_t
+ssh_protocol_parse_user(struct cli_ctx *cli_ctx,
+ const char *default_domain,
+ const char **_name,
+ const char **_domain)
+{
+ return ssh_protocol_parse_request(cli_ctx, default_domain,
+ _name, NULL, _domain);
+}
+
+errno_t
+ssh_protocol_parse_host(struct cli_ctx *cli_ctx,
+ const char **_name,
+ const char **_alias,
+ const char **_domain)
+{
+ return ssh_protocol_parse_request(cli_ctx, NULL, _name, _alias, _domain);
+}
diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c
new file mode 100644
index 0000000..edeb287
--- /dev/null
+++ b/src/responder/ssh/ssh_reply.c
@@ -0,0 +1,429 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 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 "config.h"
+
+#include <talloc.h>
+#include <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/sss_ssh.h"
+#include "util/cert.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ssh/ssh_private.h"
+
+/* Locally used flag for libldb's ldb_message_element structure to indicate
+ * binary data. Since the related data is only used in memory it is safe. If
+ * should be used with care if libldb's I/O operations are involved. */
+#define SSS_EL_FLAG_BIN_DATA (1<<4)
+
+static errno_t decode_and_add_base64_data(struct sss_packet *packet,
+ struct ldb_message_element *el,
+ bool skip_base64_decode,
+ size_t fqname_len,
+ const char *fqname,
+ size_t *c)
+{
+ uint8_t *key;
+ size_t key_len;
+ uint8_t *body;
+ size_t body_len;
+ int ret;
+ size_t d;
+ TALLOC_CTX *tmp_ctx;
+
+ if (el == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Mssing element, nothing to do.\n");
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ for (d = 0; d < el->num_values; d++) {
+ if (el->values[d].length == 0 && el->values[d].data == NULL) {
+ /* skip empty keys, e.g. due to invalid certificate */
+ continue;
+ }
+ if (skip_base64_decode || (el->flags & SSS_EL_FLAG_BIN_DATA)) {
+ key = el->values[d].data;
+ key_len = el->values[d].length;
+ } else {
+ key = sss_base64_decode(tmp_ctx, (const char *) el->values[d].data,
+ &key_len);
+ if (key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sss_packet_grow(packet,
+ 3*sizeof(uint32_t) + key_len + fqname_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
+ goto done;
+ }
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(body+(*c), 0, c);
+ SAFEALIGN_SET_UINT32(body+(*c), fqname_len, c);
+ safealign_memcpy(body+(*c), fqname, fqname_len, c);
+ SAFEALIGN_SET_UINT32(body+(*c), key_len, c);
+ safealign_memcpy(body+(*c), key, key_len, c);
+
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct ssh_get_output_keys_state {
+ struct tevent_context *ev;
+ struct cli_ctx *cli_ctx;
+ struct ldb_message *msg;
+ char *cert_verification_opts;
+ int p11_child_timeout;
+ struct ssh_ctx *ssh_ctx;
+ struct ldb_message_element *user_cert;
+ struct ldb_message_element *user_cert_override;
+ struct ldb_message_element *current_cert;
+
+ const char *name;
+ struct ldb_message_element **elements;
+ uint32_t num_keys;
+ size_t iter;
+};
+
+void ssh_get_output_keys_done(struct tevent_req *subreq);
+
+struct tevent_req *ssh_get_output_keys_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ errno_t ret;
+ struct ssh_get_output_keys_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct ssh_get_output_keys_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->cli_ctx = cli_ctx;
+ state->msg = msg;
+ state->num_keys = 0;
+ state->iter = 0;
+ state->ssh_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct ssh_ctx);
+ if (state->ssh_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing ssh responder context.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->name = ldb_msg_find_attr_as_string(state->msg, SYSDB_NAME, NULL);
+ if (state->name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing name.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->elements = talloc_zero_array(state, struct ldb_message_element *, 6);
+ if (state->elements == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->elements[state->iter] = ldb_msg_find_element(state->msg,
+ SYSDB_SSH_PUBKEY);
+ if (state->elements[state->iter] != NULL) {
+ state->num_keys += state->elements[state->iter]->num_values;
+ state->iter++;
+ }
+
+ state->elements[state->iter] = ldb_msg_find_element(state->msg,
+ ORIGINALAD_PREFIX SYSDB_SSH_PUBKEY);
+ if (state->elements[state->iter] != NULL) {
+ state->num_keys += state->elements[state->iter]->num_values;
+ state->iter++;
+ }
+
+ if (DOM_HAS_VIEWS(domain)) {
+ state->elements[state->iter] = ldb_msg_find_element(state->msg,
+ OVERRIDE_PREFIX SYSDB_SSH_PUBKEY);
+ if (state->elements[state->iter] != NULL) {
+ state->num_keys += state->elements[state->iter]->num_values;
+ state->iter++;
+ }
+ }
+
+ if (!state->ssh_ctx->use_cert_keys) {
+ DEBUG(SSSDBG_TRACE_ALL, "Skipping keys from certificates.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ if (state->ssh_ctx->cert_rules_error) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Skipping keys from certificates because there was an error "
+ "while processing matching rules.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ ret = confdb_get_string(cli_ctx->rctx->cdb, state,
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_CERT_VERIFICATION, NULL,
+ &state->cert_verification_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read verification options from confdb: [%d] %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ state->p11_child_timeout = -1;
+ ret = confdb_get_int(cli_ctx->rctx->cdb, CONFDB_SSH_CONF_ENTRY,
+ CONFDB_PAM_P11_CHILD_TIMEOUT, -1,
+ &state->p11_child_timeout);
+ if (ret != EOK || state->p11_child_timeout == -1) {
+ /* check pam configuration as well or use default */
+ ret = confdb_get_int(cli_ctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_P11_CHILD_TIMEOUT,
+ P11_CHILD_TIMEOUT_DEFAULT,
+ &state->p11_child_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read p11_child_timeout from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ state->user_cert = ldb_msg_find_element(state->msg, SYSDB_USER_CERT);
+ if (DOM_HAS_VIEWS(domain)) {
+ state->user_cert_override = ldb_msg_find_element(state->msg,
+ OVERRIDE_PREFIX SYSDB_USER_CERT);
+ }
+
+ if (state->user_cert == NULL && state->user_cert_override == NULL) {
+ /* no certificates to convert, we are done */
+ ret = EOK;
+ goto done;
+ }
+
+ state->current_cert = state->user_cert != NULL ? state->user_cert
+ : state->user_cert_override;
+
+ subreq = cert_to_ssh_key_send(state, state->ev,
+ P11_CHILD_LOG_FILE,
+ state->p11_child_timeout,
+ state->ssh_ctx->ca_db,
+ state->ssh_ctx->sss_certmap_ctx,
+ state->current_cert->num_values,
+ state->current_cert->values,
+ state->cert_verification_opts);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "cert_to_ssh_key_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ssh_get_output_keys_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+void ssh_get_output_keys_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ssh_get_output_keys_state *state = tevent_req_data(req,
+ struct ssh_get_output_keys_state);
+ int ret;
+ struct ldb_val *keys;
+ size_t valid_keys;
+
+ ret = cert_to_ssh_key_recv(subreq, state, &keys, &valid_keys);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ if (ret == ERR_P11_CHILD_TIMEOUT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "cert_to_ssh_key request timeout, "
+ "consider increasing p11_child_timeout.\n");
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "cert_to_ssh_key request failed, ssh keys derived "
+ "from certificates will be skipped.\n");
+ }
+ /* Ignore ssh keys from certificates and return what we already have */
+ tevent_req_done(req);
+ return;
+ }
+
+ state->elements[state->iter] = talloc_zero(state->elements,
+ struct ldb_message_element);
+ if (state->elements[state->iter] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->elements[state->iter]->values = talloc_steal(
+ state->elements[state->iter],
+ keys);
+ state->elements[state->iter]->num_values = state->current_cert->num_values;
+ state->elements[state->iter]->flags |= SSS_EL_FLAG_BIN_DATA;
+ state->num_keys += valid_keys;
+
+ if (state->current_cert == state->user_cert) {
+ state->current_cert = state->user_cert_override;
+ } else if (state->current_cert == state->user_cert_override) {
+ state->current_cert = NULL;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected certificate pointer.\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ if (state->current_cert == NULL) {
+ /* done */
+ ret = EOK;
+ goto done;
+ }
+
+ subreq = cert_to_ssh_key_send(state, state->ev, NULL,
+ state->p11_child_timeout,
+ state->ssh_ctx->ca_db,
+ state->ssh_ctx->sss_certmap_ctx,
+ state->current_cert->num_values,
+ state->current_cert->values,
+ state->cert_verification_opts);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "cert_to_ssh_key_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ssh_get_output_keys_done, req);
+ return;
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+
+ return;
+}
+
+errno_t ssh_get_output_keys_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sized_string *name,
+ struct ldb_message_element ***elements,
+ uint32_t *num_keys)
+{
+ struct ssh_get_output_keys_state *state = tevent_req_data(req,
+ struct ssh_get_output_keys_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (name != NULL) {
+ name->str = talloc_strdup(mem_ctx, state->name);
+ name->len = strlen(name->str) + 1;
+ }
+
+ if (elements != NULL) {
+ *elements = talloc_steal(mem_ctx, state->elements);
+ }
+
+ if (num_keys != NULL) {
+ *num_keys = state->num_keys;
+ }
+
+ return EOK;
+}
+
+errno_t
+ssh_protocol_build_reply(struct sss_packet *packet,
+ struct sized_string name,
+ struct ldb_message_element **elements,
+ uint32_t num_keys)
+{
+ size_t body_len;
+ uint8_t *body;
+ size_t c = 0;
+ errno_t ret;
+ int i;
+
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[c], num_keys, &c);
+ SAFEALIGN_SET_UINT32(&body[c], 0, &c);
+
+ if (num_keys == 0) {
+ ret = EOK;
+ goto done;
+ }
+
+ for (i = 0; elements[i] != NULL; i++) {
+ ret = decode_and_add_base64_data(packet, elements[i], false,
+ name.len, name.str, &c);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+
+ return ret;
+}
diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c
new file mode 100644
index 0000000..91fb77b
--- /dev/null
+++ b/src/responder/ssh/sshsrv.c
@@ -0,0 +1,235 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 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 <popt.h>
+
+#include "util/util.h"
+#include "util/child_common.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/ssh/ssh_private.h"
+#include "providers/data_provider.h"
+#include "sss_iface/sss_iface_async.h"
+
+int ssh_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *ssh_cmds;
+ struct ssh_ctx *ssh_ctx;
+ int ret;
+
+ ssh_cmds = get_ssh_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ ssh_cmds,
+ SSS_SSH_SOCKET_NAME, -1, NULL, -1,
+ CONFDB_SSH_CONF_ENTRY,
+ SSS_BUS_SSH, SSS_SSH_SBUS_SERVICE_NAME,
+ sss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ ssh_ctx = talloc_zero(rctx, struct ssh_ctx);
+ if (!ssh_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing ssh_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ssh_ctx->rctx = rctx;
+ ssh_ctx->rctx->pvt_ctx = ssh_ctx;
+
+ ret = sss_names_init_from_args(ssh_ctx,
+ SSS_DEFAULT_RE,
+ "%1$s@%2$s", &ssh_ctx->snctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing regex data\n");
+ goto fail;
+ }
+
+ /* Get responder options */
+
+ /* Get ssh_hash_known_hosts option */
+ ret = confdb_get_bool(ssh_ctx->rctx->cdb,
+ CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_HASH_KNOWN_HOSTS,
+ CONFDB_DEFAULT_SSH_HASH_KNOWN_HOSTS,
+ &ssh_ctx->hash_known_hosts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Get ssh_known_hosts_timeout option */
+ ret = confdb_get_int(ssh_ctx->rctx->cdb,
+ CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_KNOWN_HOSTS_TIMEOUT,
+ CONFDB_DEFAULT_SSH_KNOWN_HOSTS_TIMEOUT,
+ &ssh_ctx->known_hosts_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = confdb_get_string(ssh_ctx->rctx->cdb, ssh_ctx,
+ CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_CA_DB,
+ CONFDB_DEFAULT_SSH_CA_DB, &ssh_ctx->ca_db);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading CA DB from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = confdb_get_bool(ssh_ctx->rctx->cdb, CONFDB_SSH_CONF_ENTRY,
+ CONFDB_SSH_USE_CERT_KEYS,
+ CONFDB_DEFAULT_SSH_USE_CERT_KEYS,
+ &ssh_ctx->use_cert_keys);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,"Error reading option "
+ CONFDB_SSH_USE_CERT_KEYS
+ "from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = confdb_get_string_as_list(ssh_ctx->rctx->cdb, ssh_ctx,
+ CONFDB_SSH_CONF_ENTRY,
+ CONFDB_SSH_USE_CERT_RULES,
+ &ssh_ctx->cert_rules);
+ if (ret == ENOENT) {
+ ssh_ctx->cert_rules = NULL;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading " CONFDB_SSH_USE_CERT_RULES
+ " from confdb (%d) [%s].\n", ret,
+ sss_strerror(ret));
+ goto fail;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_SSH,
+ SSS_SSH_SBUS_SERVICE_NAME,
+ SSS_SSH_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = sss_resp_register_service_iface(rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "SSH Initialization complete\n");
+
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_ssh";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ /* server_setup() might switch to an unprivileged user, so the permissions
+ * for p11_child.log have to be fixed first. We might call p11_child to
+ * validate certificates. */
+ ret = chown_debug_file("p11_child", uid, gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot chown the p11_child debug file, "
+ "debugging might not work!\n");
+ }
+
+ ret = server_setup("ssh", true, 0, uid, gid,
+ CONFDB_SSH_CONF_ENTRY, &main_ctx, true);
+ if (ret != EOK) {
+ return 2;
+ }
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit "
+ "when parent process does\n");
+ }
+
+ ret = ssh_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) {
+ return 3;
+ }
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
new file mode 100644
index 0000000..8568e6e
--- /dev/null
+++ b/src/responder/sudo/sudosrv.c
@@ -0,0 +1,223 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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 <popt.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "providers/data_provider.h"
+#include "responder/common/negcache.h"
+#include "sss_iface/sss_iface_async.h"
+
+int sudo_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb,
+ int pipe_fd)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *sudo_cmds;
+ struct sudo_ctx *sudo_ctx;
+ int ret;
+
+ sudo_cmds = get_sudo_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ sudo_cmds,
+ SSS_SUDO_SOCKET_NAME, pipe_fd, /* custom permissions on socket */
+ NULL, -1, /* No private socket */
+ CONFDB_SUDO_CONF_ENTRY,
+ SSS_BUS_SUDO, SSS_SUDO_SBUS_SERVICE_NAME,
+ sss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ sudo_ctx = talloc_zero(rctx, struct sudo_ctx);
+ if (!sudo_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing sudo_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ sudo_ctx->rctx = rctx;
+ sudo_ctx->rctx->pvt_ctx = sudo_ctx;
+
+ sss_ncache_prepopulate(sudo_ctx->rctx->ncache, sudo_ctx->rctx->cdb, rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "failed to set ncache for sudo's filter_users\n");
+ goto fail;
+ }
+
+ /* Get sudo_timed option */
+ ret = confdb_get_bool(sudo_ctx->rctx->cdb,
+ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_TIMED,
+ CONFDB_DEFAULT_SUDO_TIMED,
+ &sudo_ctx->timed);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Get sudo_inverse_order option */
+ ret = confdb_get_bool(sudo_ctx->rctx->cdb,
+ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_INVERSE_ORDER,
+ CONFDB_DEFAULT_SUDO_INVERSE_ORDER,
+ &sudo_ctx->inverse_order);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Get sudo_inverse_order option */
+ ret = confdb_get_int(sudo_ctx->rctx->cdb,
+ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_THRESHOLD,
+ CONFDB_DEFAULT_SUDO_THRESHOLD,
+ &sudo_ctx->threshold);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_SUDO,
+ SSS_SUDO_SBUS_SERVICE_NAME,
+ SSS_SUDO_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = sss_resp_register_service_iface(rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "SUDO Initialization complete\n");
+
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ int pipe_fd = -1;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_sudo";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ if (!is_socket_activated()) {
+ /* Create pipe file descriptors here with right ownerschip */
+ ret = create_pipe_fd(SSS_SUDO_SOCKET_NAME, &pipe_fd, SSS_DFL_UMASK);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "create_pipe_fd failed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ return 4;
+ }
+
+ ret = chown(SSS_SUDO_SOCKET_NAME, uid, 0);
+ if (ret != 0) {
+ ret = errno;
+ close(pipe_fd);
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "create_pipe_fd failed [%d]: %s.\n",
+ ret, sss_strerror(ret));
+ return 5;
+ }
+ }
+
+ ret = server_setup("sudo", true, 0, uid, gid, CONFDB_SUDO_CONF_ENTRY,
+ &main_ctx, true);
+ if (ret != EOK) {
+ return 2;
+ }
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit "
+ "when parent process does\n");
+ }
+
+ ret = sudo_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx, pipe_fd);
+ if (ret != EOK) {
+ return 3;
+ }
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
new file mode 100644
index 0000000..63b548f
--- /dev/null
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -0,0 +1,303 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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 <stdint.h>
+#include <errno.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "db/sysdb_sudo.h"
+#include "sss_client/sss_cli.h"
+#include "responder/common/negcache.h"
+
+static errno_t sudosrv_cmd_send_reply(struct sudo_cmd_ctx *cmd_ctx,
+ uint8_t *response_body,
+ size_t response_len)
+{
+ errno_t ret;
+ uint8_t *packet_body = NULL;
+ size_t packet_len = 0;
+ struct cli_ctx *cli_ctx = cmd_ctx->cli_ctx;
+ struct cli_protocol *pctx;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return ENOMEM;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to create a new packet [%d]; %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = sss_packet_grow(pctx->creq->out, response_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to create response: %s\n", strerror(ret));
+ goto done;
+ }
+ sss_packet_get_body(pctx->creq->out, &packet_body, &packet_len);
+ memcpy(packet_body, response_body, response_len);
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+ sss_cmd_done(cmd_ctx->cli_ctx, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_cmd_send_error(TALLOC_CTX *mem_ctx,
+ struct sudo_cmd_ctx *cmd_ctx,
+ uint32_t error)
+{
+ uint8_t *response_body = NULL;
+ size_t response_len = 0;
+ int ret = EOK;
+
+ if (error == EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Everything is fine but we are "
+ "returning error?\n");
+ return EFAULT;
+ }
+
+ ret = sudosrv_build_response(mem_ctx, error, 0, NULL,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sudosrv_cmd_send_reply(cmd_ctx, response_body, response_len);
+}
+
+errno_t sudosrv_cmd_reply(struct sudo_cmd_ctx *cmd_ctx, int ret)
+{
+ uint8_t *response_body = NULL;
+ size_t response_len = 0;
+ uint32_t num_rules = cmd_ctx->num_rules;
+ struct sysdb_attrs **rules = cmd_ctx->rules;
+
+ switch (ret) {
+ case EOK:
+ /*
+ * Parent of cmd_ctx->rules is in-memory cache, we must not talloc_free it!
+ */
+ if (cmd_ctx->sudo_ctx->timed) {
+ /* filter rules by time */
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Applying time restrictions on"
+ "%u rules\n", cmd_ctx->num_rules);
+
+ ret = sysdb_sudo_filter_rules_by_time(cmd_ctx, cmd_ctx->num_rules,
+ cmd_ctx->rules, 0,
+ &num_rules, &rules);
+ if (ret != EOK) {
+ return EFAULT;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Got %u rules after time filter\n",
+ num_rules);
+ }
+
+ /* send result */
+ ret = sudosrv_build_response(cmd_ctx, SSS_SUDO_ERROR_OK,
+ num_rules, rules,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ return EFAULT;
+ }
+
+ ret = sudosrv_cmd_send_reply(cmd_ctx, response_body, response_len);
+ break;
+
+ case EAGAIN:
+ /* async processing, just return here */
+ return EOK;
+
+ case EFAULT:
+ /* very bad error */
+ return EFAULT;
+
+
+ /* case ENOENT:
+ * - means user not found
+ * - send error ENOENT
+ */
+
+ default:
+ /* send error */
+ ret = sudosrv_cmd_send_error(cmd_ctx, cmd_ctx, ret);
+ break;
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Fatal error, killing connection!\n");
+ talloc_free(cmd_ctx->cli_ctx);
+ return EFAULT;
+ }
+
+ return EOK;
+}
+
+static void sudosrv_cmd_done(struct tevent_req *req);
+
+static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct sudo_cmd_ctx *cmd_ctx = NULL;
+ uint8_t *query_body = NULL;
+ size_t query_len = 0;
+ struct cli_protocol *pctx;
+ uint32_t protocol;
+ errno_t ret;
+
+ /* create cmd_ctx */
+
+ cmd_ctx = talloc_zero(cli_ctx, struct sudo_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ /* kill the connection here as we have no context for reply */
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n");
+ return ENOMEM;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+ cmd_ctx->type = type;
+ cmd_ctx->sudo_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sudo_ctx);
+ if (cmd_ctx->sudo_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sudo_ctx not set, killing connection!\n");
+ return EFAULT;
+ }
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+ protocol = pctx->cli_protocol_version->version;
+
+ /* if protocol is invalid return */
+ switch (protocol) {
+ case 0:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Protocol [%d] is not secure. "
+ "SSSD does not allow to use this protocol.\n", protocol);
+ ret = EFAULT;
+ goto done;
+ break;
+ case SSS_SUDO_PROTOCOL_VERSION:
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Using protocol version [%d]\n",
+ protocol);
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE, "Invalid protocol version [%d]!\n",
+ protocol);
+ ret = EFAULT;
+ goto done;
+ }
+
+ /* parse query */
+ sss_packet_get_body(pctx->creq->in, &query_body, &query_len);
+ if (query_len <= 0 || query_body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Query is empty\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sudosrv_parse_query(cmd_ctx, query_body, query_len,
+ &cmd_ctx->rawname, &cmd_ctx->uid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse sudo query [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ req = sudosrv_get_rules_send(cmd_ctx, cli_ctx->ev, cmd_ctx->sudo_ctx,
+ cmd_ctx->type, cmd_ctx->uid,
+ cmd_ctx->rawname);
+ if (req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(req, sudosrv_cmd_done, cmd_ctx);
+
+ ret = EAGAIN;
+
+done:
+ return sudosrv_cmd_reply(cmd_ctx, ret);
+}
+
+static void sudosrv_cmd_done(struct tevent_req *req)
+{
+ struct sudo_cmd_ctx *cmd_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(req, struct sudo_cmd_ctx);
+
+ ret = sudosrv_get_rules_recv(cmd_ctx, req, &cmd_ctx->rules,
+ &cmd_ctx->num_rules);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ DEBUG((ret == ENOENT) ? SSSDBG_MINOR_FAILURE : SSSDBG_OP_FAILURE,
+ "Unable to obtain cached rules [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+done:
+ sudosrv_cmd_reply(cmd_ctx, ret);
+}
+
+static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
+{
+ return sudosrv_cmd(SSS_SUDO_USER, cli_ctx);
+}
+
+static int sudosrv_cmd_get_defaults(struct cli_ctx *cli_ctx)
+{
+ return sudosrv_cmd(SSS_SUDO_DEFAULTS, cli_ctx);
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version sudo_cli_protocol_version[] = {
+ {1, "2012-05-14", "require uid and domain"},
+ {0, NULL, NULL}
+ };
+
+ return sudo_cli_protocol_version;
+}
+
+struct sss_cmd_table *get_sudo_cmds(void) {
+ static struct sss_cmd_table sudo_cmds[] = {
+ {SSS_GET_VERSION, sss_cmd_get_version},
+ {SSS_SUDO_GET_SUDORULES, sudosrv_cmd_get_sudorules},
+ {SSS_SUDO_GET_DEFAULTS, sudosrv_cmd_get_defaults},
+ {SSS_CLI_NULL, NULL}
+ };
+
+ return sudo_cmds;
+}
diff --git a/src/responder/sudo/sudosrv_dp.c b/src/responder/sudo/sudosrv_dp.c
new file mode 100644
index 0000000..465c0f2
--- /dev/null
+++ b/src/responder/sudo/sudosrv_dp.c
@@ -0,0 +1,265 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2011 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 "util/util.h"
+#include "providers/data_provider.h"
+#include "providers/data_provider_req.h"
+#include "responder/common/responder.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "db/sysdb.h"
+#include "sss_iface/sss_iface_async.h"
+
+static DBusMessage *
+sss_dp_get_sudoers_msg(TALLOC_CTX *mem_ctx,
+ const char *bus_name,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_sudo_type type,
+ const char *name,
+ uint32_t num_rules,
+ struct sysdb_attrs **rules)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ dbus_bool_t dbret;
+ errno_t ret;
+ uint32_t be_type = 0;
+ uint32_t dp_flags = 0;
+ const char *rule_name = NULL;
+ uint32_t i;
+
+ switch (type) {
+ case SSS_DP_SUDO_REFRESH_RULES:
+ be_type = BE_REQ_SUDO_RULES;
+ break;
+ case SSS_DP_SUDO_FULL_REFRESH:
+ be_type = BE_REQ_SUDO_FULL;
+ break;
+ }
+
+ if (fast_reply) {
+ dp_flags |= DP_FAST_REPLY;
+ }
+
+ msg = dbus_message_new_method_call(bus_name,
+ SSS_BUS_PATH,
+ "sssd.dataprovider",
+ "sudoHandler");
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
+ return NULL;
+ }
+
+ /* create the message */
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Creating SUDOers request for [%s][%u][%s][%u]\n",
+ dom->name, be_type, name, num_rules);
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &dp_flags);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ /* BE TYPE */
+ dbret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &be_type);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ /* BE TYPE SPECIFIC */
+ if (be_type & BE_REQ_SUDO_RULES) {
+ dbret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32,
+ &num_rules);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ dbret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array_iter);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+
+ for (i = 0; i < num_rules; i++) {
+ ret = sysdb_attrs_get_string(rules[i], SYSDB_NAME, &rule_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not get rule name [%d]: %s\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ dbret = dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_STRING,
+ &rule_name);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+ }
+
+ dbret = dbus_message_iter_close_container(&iter, &array_iter);
+ if (dbret == FALSE) {
+ goto fail;
+ }
+ }
+
+ return msg;
+
+fail:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
+ dbus_message_unref(msg);
+ return NULL;
+}
+
+struct sss_dp_get_sudoers_state {
+ uint16_t dp_error;
+ uint32_t error;
+ const char *error_message;
+};
+
+static void sss_dp_get_sudoers_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sss_dp_get_sudoers_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_sudo_type type,
+ const char *name,
+ uint32_t num_rules,
+ struct sysdb_attrs **rules)
+{
+ struct sss_dp_get_sudoers_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct be_conn *be_conn;
+ DBusMessage *msg;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sss_dp_get_sudoers_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ if (is_files_provider(dom)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Domain %s does not check DP\n",
+ dom->name);
+ state->dp_error = DP_ERR_OK;
+ state->error = EOK;
+ state->error_message = talloc_strdup(state, "Success");
+ if (state->error_message == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "BUG: The Data Provider connection for %s is not available!\n",
+ dom->name);
+ ret = EIO;
+ goto done;
+ }
+
+ msg = sss_dp_get_sudoers_msg(state, be_conn->bus_name, dom, fast_reply,
+ type, name, num_rules, rules);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sbus_call_dp_dp_sudoHandler_send(state, be_conn->conn, msg);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sss_dp_get_sudoers_done, req);
+
+ ret = EAGAIN;
+
+done:
+#ifdef BUILD_FILES_PROVIDER
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, rctx->ev);
+ } else
+#endif
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, rctx->ev);
+ }
+
+ return req;
+}
+
+static void sss_dp_get_sudoers_done(struct tevent_req *subreq)
+{
+ struct sss_dp_get_sudoers_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sss_dp_get_sudoers_state);
+
+ ret = sbus_call_dp_dp_sudoHandler_recv(state, subreq, &state->dp_error,
+ &state->error,
+ &state->error_message);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+sss_dp_get_sudoers_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char ** _error_message)
+{
+ struct sss_dp_get_sudoers_state *state;
+ state = tevent_req_data(req, struct sss_dp_get_sudoers_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_dp_error = state->dp_error;
+ *_error = state->error;
+ *_error_message = talloc_steal(mem_ctx, state->error_message);
+
+ return EOK;
+}
diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
new file mode 100644
index 0000000..1bcfc37
--- /dev/null
+++ b/src/responder/sudo/sudosrv_get_sudorules.c
@@ -0,0 +1,887 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2011 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 "config.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "db/sysdb_sudo.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/sudo/sudosrv_private.h"
+#include "providers/data_provider.h"
+
+static int
+sudo_order_cmp(const void *a, const void *b, bool lower_wins)
+{
+ struct sysdb_attrs *r1, *r2;
+ uint32_t o1, o2;
+ int ret;
+
+ r1 = * (struct sysdb_attrs * const *) a;
+ r2 = * (struct sysdb_attrs * const *) b;
+ if (!r1 || !r2) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Wrong data?\n");
+ return 0;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(r1, SYSDB_SUDO_CACHE_AT_ORDER, &o1);
+ if (ret == ENOENT) {
+ /* man sudoers-ldap: If the sudoOrder attribute is not present,
+ * a value of 0 is assumed */
+ o1 = 0;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get sudoOrder value\n");
+ return 0;
+ }
+
+ ret = sysdb_attrs_get_uint32_t(r2, SYSDB_SUDO_CACHE_AT_ORDER, &o2);
+ if (ret == ENOENT) {
+ /* man sudoers-ldap: If the sudoOrder attribute is not present,
+ * a value of 0 is assumed */
+ o2 = 0;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get sudoOrder value\n");
+ return 0;
+ }
+
+ if (lower_wins) {
+ /* The lowest value takes priority. Original wrong SSSD behaviour. */
+ if (o1 > o2) {
+ return 1;
+ } else if (o1 < o2) {
+ return -1;
+ }
+ } else {
+ /* The higher value takes priority. Standard LDAP behaviour. */
+ if (o1 < o2) {
+ return 1;
+ } else if (o1 > o2) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+sudo_order_low_cmp_fn(const void *a, const void *b)
+{
+ return sudo_order_cmp(a, b, true);
+}
+
+static int
+sudo_order_high_cmp_fn(const void *a, const void *b)
+{
+ return sudo_order_cmp(a, b, false);
+}
+
+static errno_t
+sort_sudo_rules(struct sysdb_attrs **rules, size_t count, bool lower_wins)
+{
+ if (lower_wins) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Sorting rules with lower-wins logic\n");
+ qsort(rules, count, sizeof(struct sysdb_attrs *),
+ sudo_order_low_cmp_fn);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Sorting rules with higher-wins logic\n");
+ qsort(rules, count, sizeof(struct sysdb_attrs *),
+ sudo_order_high_cmp_fn);
+ }
+
+ return EOK;
+}
+
+static errno_t sudosrv_format_runas(struct resp_ctx *rctx,
+ struct sysdb_attrs *rule,
+ const char *attr)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message_element *el;
+ struct sss_domain_info *dom;
+ const char *value;
+ char *fqname;
+ unsigned int i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_get_el_ext(rule, attr, false, &el);
+ if (ret == ENOENT) {
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get %s attribute "
+ "[%d]: %s\n", attr, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ value = (const char *)el->values[i].data;
+ if (value == NULL) {
+ continue;
+ }
+
+ dom = find_domain_by_object_name_ex(rctx->domains, value, true,
+ SSS_GND_DESCEND);
+ if (dom == NULL) {
+ continue;
+ }
+
+ ret = sss_output_fqname(tmp_ctx, dom, value,
+ rctx->override_space, &fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to convert %s to output fqname "
+ "[%d]: %s\n", value, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ talloc_free(el->values[i].data);
+ el->values[i].data = (uint8_t*)talloc_steal(el->values, fqname);
+ el->values[i].length = strlen(fqname);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t sudosrv_format_rules(struct resp_ctx *rctx,
+ struct sysdb_attrs **rules,
+ uint32_t num_rules)
+{
+ uint32_t i;
+ errno_t ret = EOK;
+
+
+ for (i = 0; i < num_rules; i++) {
+ ret = sudosrv_format_runas(rctx, rules[i],
+ SYSDB_SUDO_CACHE_AT_RUNAS);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sudosrv_format_runas(rctx, rules[i],
+ SYSDB_SUDO_CACHE_AT_RUNASUSER);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sudosrv_format_runas(rctx, rules[i],
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static errno_t sudosrv_query_cache(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char **attrs,
+ const char *filter,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_count)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ size_t count;
+ struct sysdb_attrs **rules;
+ struct ldb_message **msgs;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_FUNC_DATA, "Searching sysdb with [%s]\n", filter);
+
+ if (IS_SUBDOMAIN(domain)) {
+ /* rules are stored inside parent domain tree */
+ domain = domain->parent;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter, SUDORULE_SUBDIR,
+ attrs, &count, &msgs);
+ if (ret == ENOENT) {
+ *_rules = NULL;
+ *_count = 0;
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up SUDO rules\n");
+ goto done;
+ }
+
+ ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &rules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not convert ldb message to sysdb_attrs\n");
+ goto done;
+ }
+
+ *_rules = talloc_steal(mem_ctx, rules);
+ *_count = (uint32_t)count;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_expired_rules(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ const char *username,
+ char **groups,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ const char *attrs[] = { SYSDB_NAME, NULL };
+ char *filter;
+ errno_t ret;
+
+ filter = sysdb_sudo_filter_expired(NULL, username, groups, uid);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_query_cache(mem_ctx, domain, attrs, filter,
+ _rules, _num_rules);
+ talloc_free(filter);
+
+ return ret;
+}
+
+static errno_t sudosrv_cached_rules_by_user(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t cli_uid,
+ uid_t orig_uid,
+ const char *username,
+ char **groupnames,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+ uint32_t i;
+ const char *filter;
+ const char *val;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_SUDO_CACHE_AT_CN,
+ SYSDB_SUDO_CACHE_AT_HOST,
+ SYSDB_SUDO_CACHE_AT_COMMAND,
+ SYSDB_SUDO_CACHE_AT_OPTION,
+ SYSDB_SUDO_CACHE_AT_RUNAS,
+ SYSDB_SUDO_CACHE_AT_RUNASUSER,
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP,
+ SYSDB_SUDO_CACHE_AT_NOTBEFORE,
+ SYSDB_SUDO_CACHE_AT_NOTAFTER,
+ SYSDB_SUDO_CACHE_AT_ORDER,
+ NULL };
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ filter = sysdb_sudo_filter_user(tmp_ctx, username, groupnames, orig_uid);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sudosrv_query_cache(tmp_ctx, domain, attrs, filter,
+ &rules, &num_rules);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ val = talloc_asprintf(tmp_ctx, "#%"SPRIuid, cli_uid);
+ if (val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Add sudoUser: #uid to prevent conflicts with fqnames. */
+ DEBUG(SSSDBG_TRACE_FUNC, "Replacing sudoUser attribute with "
+ "sudoUser: %s\n", val);
+ for (i = 0; i < num_rules; i++) {
+ ret = sysdb_attrs_add_string(rules[i], SYSDB_SUDO_CACHE_AT_USER, val);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to alter sudoUser attribute "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+ }
+
+ *_rules = talloc_steal(mem_ctx, rules);
+ *_num_rules = num_rules;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_cached_rules_by_ng(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ uid_t uid,
+ const char *username,
+ char **groupnames,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ char *filter;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_SUDO_CACHE_AT_CN,
+ SYSDB_SUDO_CACHE_AT_USER,
+ SYSDB_SUDO_CACHE_AT_HOST,
+ SYSDB_SUDO_CACHE_AT_COMMAND,
+ SYSDB_SUDO_CACHE_AT_OPTION,
+ SYSDB_SUDO_CACHE_AT_RUNAS,
+ SYSDB_SUDO_CACHE_AT_RUNASUSER,
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP,
+ SYSDB_SUDO_CACHE_AT_NOTBEFORE,
+ SYSDB_SUDO_CACHE_AT_NOTAFTER,
+ SYSDB_SUDO_CACHE_AT_ORDER,
+ NULL };
+
+ filter = sysdb_sudo_filter_netgroups(NULL, username, groupnames, uid);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_query_cache(mem_ctx, domain, attrs, filter,
+ _rules, _num_rules);
+ talloc_free(filter);
+
+ return ret;
+}
+
+static errno_t sudosrv_cached_rules(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ uid_t cli_uid,
+ uid_t orig_uid,
+ const char *username,
+ char **groups,
+ bool inverse_order,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs **user_rules;
+ struct sysdb_attrs **ng_rules;
+ struct sysdb_attrs **rules;
+ uint32_t num_user_rules;
+ uint32_t num_ng_rules;
+ uint32_t num_rules;
+ uint32_t rule_iter, i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_cached_rules_by_user(tmp_ctx, domain,
+ cli_uid, orig_uid, username, groups,
+ &user_rules, &num_user_rules);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sudosrv_cached_rules_by_ng(tmp_ctx, domain,
+ orig_uid, username, groups,
+ &ng_rules, &num_ng_rules);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ num_rules = num_user_rules + num_ng_rules;
+ if (num_rules == 0) {
+ *_rules = NULL;
+ *_num_rules = 0;
+ ret = EOK;
+ goto done;
+ }
+
+ rules = talloc_array(tmp_ctx, struct sysdb_attrs *, num_rules);
+ if (rules == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ rule_iter = 0;
+ for (i = 0; i < num_user_rules; rule_iter++, i++) {
+ rules[rule_iter] = talloc_steal(rules, user_rules[i]);
+ }
+
+ for (i = 0; i < num_ng_rules; rule_iter++, i++) {
+ rules[rule_iter] = talloc_steal(rules, ng_rules[i]);
+ }
+
+ ret = sort_sudo_rules(rules, num_rules, inverse_order);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not sort rules by sudoOrder\n");
+ goto done;
+ }
+
+ ret = sudosrv_format_rules(rctx, rules, num_rules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not format sudo rules\n");
+ goto done;
+ }
+
+ *_rules = talloc_steal(mem_ctx, rules);
+ *_num_rules = num_rules;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sudosrv_cached_defaults(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ char *filter;
+ errno_t ret;
+ const char *attrs[] = { SYSDB_OBJECTCLASS,
+ SYSDB_SUDO_CACHE_AT_CN,
+ SYSDB_SUDO_CACHE_AT_USER,
+ SYSDB_SUDO_CACHE_AT_HOST,
+ SYSDB_SUDO_CACHE_AT_COMMAND,
+ SYSDB_SUDO_CACHE_AT_OPTION,
+ SYSDB_SUDO_CACHE_AT_RUNAS,
+ SYSDB_SUDO_CACHE_AT_RUNASUSER,
+ SYSDB_SUDO_CACHE_AT_RUNASGROUP,
+ SYSDB_SUDO_CACHE_AT_NOTBEFORE,
+ SYSDB_SUDO_CACHE_AT_NOTAFTER,
+ SYSDB_SUDO_CACHE_AT_ORDER,
+ NULL };
+
+ filter = sysdb_sudo_filter_defaults(NULL);
+ if (filter == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sudosrv_query_cache(mem_ctx, domain, attrs, filter,
+ _rules, _num_rules);
+ talloc_free(filter);
+
+ return ret;
+}
+
+static errno_t sudosrv_fetch_rules(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ enum sss_sudo_type type,
+ struct sss_domain_info *domain,
+ uid_t cli_uid,
+ uid_t orig_uid,
+ const char *username,
+ char **groups,
+ bool inverse_order,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ struct sysdb_attrs **rules = NULL;
+ const char *debug_name = "unknown";
+ uint32_t num_rules;
+ errno_t ret;
+
+ switch (type) {
+ case SSS_SUDO_USER:
+ DEBUG(SSSDBG_TRACE_FUNC, "Retrieving rules for [%s@%s]\n",
+ username, domain->name);
+ debug_name = "rules";
+
+ ret = sudosrv_cached_rules(mem_ctx, rctx, domain,
+ cli_uid, orig_uid, username, groups,
+ inverse_order, &rules, &num_rules);
+
+ break;
+ case SSS_SUDO_DEFAULTS:
+ debug_name = "default options";
+ DEBUG(SSSDBG_TRACE_FUNC, "Retrieving default options for [%s@%s]\n",
+ username, domain->name);
+
+ ret = sudosrv_cached_defaults(mem_ctx, domain, &rules, &num_rules);
+
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve %s [%d]: %s\n",
+ debug_name, ret, sss_strerror(ret));
+ return ret;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Returning %u %s for [%s@%s]\n",
+ num_rules, debug_name, username, domain->name);
+
+ *_rules = rules;
+ *_num_rules = num_rules;
+
+ return EOK;
+}
+
+static void
+sudosrv_dp_oob_req_done(struct tevent_req *req)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Out of band refresh finished\n");
+ talloc_free(req);
+}
+
+struct sudosrv_refresh_rules_state {
+ struct resp_ctx *rctx;
+ struct sss_domain_info *domain;
+ const char *username;
+};
+
+static void sudosrv_refresh_rules_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sudosrv_refresh_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *domain,
+ int threshold,
+ uid_t uid,
+ const char *username,
+ char **groups)
+{
+ struct sudosrv_refresh_rules_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sudosrv_refresh_rules_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->rctx = rctx;
+ state->domain = domain;
+ state->username = username;
+
+ ret = sudosrv_expired_rules(state, domain, uid, username, groups,
+ &rules, &num_rules);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to retrieve expired sudo rules [%d]: %s\n",
+ ret, strerror(ret));
+ goto immediately;
+ }
+
+ if (num_rules == 0) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No expired rules were found for [%s@%s].\n",
+ username, domain->name);
+ ret = EOK;
+ goto immediately;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Refreshing %d expired rules of [%s@%s]\n",
+ num_rules, username, domain->name);
+
+ if (num_rules > threshold) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Rules threshold [%d] is reached, performing full refresh "
+ "instead.\n", threshold);
+
+ subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
+ SSS_DP_SUDO_FULL_REFRESH,
+ username, 0, NULL);
+ } else {
+ subreq = sss_dp_get_sudoers_send(state, rctx, domain, false,
+ SSS_DP_SUDO_REFRESH_RULES,
+ username, num_rules, rules);
+ }
+
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+
+ tevent_req_set_callback(subreq, sudosrv_refresh_rules_done, req);
+
+ return req;
+
+immediately:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+
+ return req;
+}
+
+static void sudosrv_refresh_rules_done(struct tevent_req *subreq)
+{
+ struct sudosrv_refresh_rules_state *state;
+ struct tevent_req *req;
+ dbus_uint16_t err_maj;
+ dbus_uint32_t err_min;
+ const char *err_msg;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sudosrv_refresh_rules_state);
+
+ ret = sss_dp_get_sudoers_recv(state, subreq, &err_maj, &err_min, &err_msg);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh rules [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ } else if (err_maj != 0 || err_min != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to get information from Data Provider, "
+ "Error: %u, %u, %s\n",
+ (unsigned int)err_maj, (unsigned int)err_min,
+ (err_msg == NULL ? "(null)" : err_msg));
+ goto done;
+ }
+
+ if (err_min == ENOENT) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Some expired rules were removed from the server, scheduling "
+ "full refresh out of band\n");
+ subreq = sss_dp_get_sudoers_send(state->rctx, state->rctx,
+ state->domain, false,
+ SSS_DP_SUDO_FULL_REFRESH,
+ state->username, 0, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot issue DP request.\n");
+ ret = EOK; /* We don't care. */
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sudosrv_dp_oob_req_done, NULL);
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t sudosrv_refresh_rules_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct sudosrv_get_rules_state {
+ struct tevent_context *ev;
+ struct resp_ctx *rctx;
+ enum sss_sudo_type type;
+ uid_t cli_uid;
+ const char *username;
+ struct sss_domain_info *domain;
+ char **groups;
+ bool inverse_order;
+ int threshold;
+
+ uid_t orig_uid;
+ const char *orig_username;
+
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+};
+
+static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq);
+static void sudosrv_get_rules_done(struct tevent_req *subreq);
+
+struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sudo_ctx *sudo_ctx,
+ enum sss_sudo_type type,
+ uid_t cli_uid,
+ const char *username)
+{
+ struct sudosrv_get_rules_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sudosrv_get_rules_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->rctx = sudo_ctx->rctx;
+ state->type = type;
+ state->cli_uid = cli_uid;
+ state->inverse_order = sudo_ctx->inverse_order;
+ state->threshold = sudo_ctx->threshold;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Running initgroups for [%s]\n", username);
+
+ subreq = cache_req_initgr_by_name_send(state, ev, sudo_ctx->rctx,
+ sudo_ctx->rctx->ncache, 0,
+ CACHE_REQ_POSIX_DOM, NULL,
+ username);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ } else {
+ tevent_req_set_callback(subreq, sudosrv_get_rules_initgr_done, req);
+ }
+
+ return req;
+}
+
+static void sudosrv_get_rules_initgr_done(struct tevent_req *subreq)
+{
+ struct sudosrv_get_rules_state *state;
+ struct cache_req_result *result;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sudosrv_get_rules_state);
+
+ ret = cache_req_initgr_by_name_recv(state, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ state->domain = result->domain;
+ state->username = talloc_steal(state, result->lookup_name);
+ talloc_zfree(result);
+
+ ret = sysdb_get_sudo_user_info(state, state->domain, state->username,
+ &state->orig_username,
+ &state->orig_uid,
+ &state->groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain user groups [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ subreq = sudosrv_refresh_rules_send(state, state->ev, state->rctx,
+ state->domain, state->threshold,
+ state->orig_uid,
+ state->orig_username,
+ state->groups);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sudosrv_get_rules_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EOK && ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ } else if (ret != EAGAIN) {
+ tevent_req_done(req);
+ }
+}
+
+static void sudosrv_get_rules_done(struct tevent_req *subreq)
+{
+ struct sudosrv_get_rules_state *state = NULL;
+ struct tevent_req *req = NULL;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sudosrv_get_rules_state);
+
+ ret = sudosrv_refresh_rules_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to refresh expired rules, we will return what is "
+ "in cache.\n");
+ }
+
+ ret = sudosrv_fetch_rules(state, state->rctx, state->type, state->domain,
+ state->cli_uid,
+ state->orig_uid,
+ state->orig_username,
+ state->groups,
+ state->inverse_order,
+ &state->rules, &state->num_rules);
+
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t sudosrv_get_rules_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules)
+{
+ struct sudosrv_get_rules_state *state = NULL;
+ state = tevent_req_data(req, struct sudosrv_get_rules_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_rules = talloc_steal(mem_ctx, state->rules);
+ *_num_rules = state->num_rules;
+
+ return EOK;
+}
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
new file mode 100644
index 0000000..157afaa
--- /dev/null
+++ b/src/responder/sudo/sudosrv_private.h
@@ -0,0 +1,112 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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 _SUDOSRV_PRIVATE_H_
+#define _SUDOSRV_PRIVATE_H_
+
+#include <stdint.h>
+#include <talloc.h>
+#include <sys/types.h>
+
+#include "src/db/sysdb.h"
+#include "responder/common/responder.h"
+
+#define SSS_SUDO_ERROR_OK 0
+
+enum sss_dp_sudo_type {
+ SSS_DP_SUDO_REFRESH_RULES,
+ SSS_DP_SUDO_FULL_REFRESH
+};
+
+enum sss_sudo_type {
+ SSS_SUDO_DEFAULTS,
+ SSS_SUDO_USER
+};
+
+struct sudo_ctx {
+ struct resp_ctx *rctx;
+
+ /*
+ * options
+ */
+ bool timed;
+ bool inverse_order;
+ int threshold;
+};
+
+struct sudo_cmd_ctx {
+ struct cli_ctx *cli_ctx;
+ struct sudo_ctx *sudo_ctx;
+ enum sss_sudo_type type;
+
+ /* input data */
+ uid_t uid;
+ char *rawname;
+
+ /* output data */
+ struct sysdb_attrs **rules;
+ uint32_t num_rules;
+};
+
+struct sss_cmd_table *get_sudo_cmds(void);
+
+struct tevent_req *sudosrv_get_rules_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sudo_ctx *sudo_ctx,
+ enum sss_sudo_type type,
+ uid_t cli_uid,
+ const char *username);
+
+errno_t sudosrv_get_rules_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sysdb_attrs ***_rules,
+ uint32_t *_num_rules);
+
+errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
+ uint8_t *query_body,
+ size_t query_len,
+ char **_rawname,
+ uid_t *_uid);
+
+errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
+ uint32_t error,
+ uint32_t rules_num,
+ struct sysdb_attrs **rules,
+ uint8_t **_response_body,
+ size_t *_response_len);
+
+struct tevent_req *
+sss_dp_get_sudoers_send(TALLOC_CTX *mem_ctx,
+ struct resp_ctx *rctx,
+ struct sss_domain_info *dom,
+ bool fast_reply,
+ enum sss_dp_sudo_type type,
+ const char *name,
+ uint32_t num_rules,
+ struct sysdb_attrs **rules);
+
+errno_t
+sss_dp_get_sudoers_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ uint16_t *_dp_error,
+ uint32_t *_error,
+ const char ** _error_message);
+
+#endif /* _SUDOSRV_PRIVATE_H_ */
diff --git a/src/responder/sudo/sudosrv_query.c b/src/responder/sudo/sudosrv_query.c
new file mode 100644
index 0000000..a868ebe
--- /dev/null
+++ b/src/responder/sudo/sudosrv_query.c
@@ -0,0 +1,307 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 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 <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "responder/sudo/sudosrv_private.h"
+
+static int sudosrv_response_append_string(TALLOC_CTX *mem_ctx,
+ const char *str,
+ size_t str_len,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ size_t response_len = *_response_len;
+ uint8_t *response_body = *_response_body;
+
+ response_body = talloc_realloc(mem_ctx, response_body, uint8_t,
+ response_len + (str_len * sizeof(char)));
+ if (response_body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+ return ENOMEM;
+ }
+ memcpy(response_body + response_len, str, str_len);
+ response_len += str_len;
+
+ *_response_body = response_body;
+ *_response_len = response_len;
+
+ return EOK;
+}
+
+static int sudosrv_response_append_uint32(TALLOC_CTX *mem_ctx,
+ uint32_t number,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ size_t response_len = *_response_len;
+ uint8_t *response_body = *_response_body;
+
+ response_body = talloc_realloc(mem_ctx, response_body, uint8_t,
+ response_len + sizeof(uint32_t));
+ if (response_body == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc() failed\n");
+ return ENOMEM;
+ }
+ SAFEALIGN_SET_UINT32(response_body + response_len, number, &response_len);
+
+ *_response_body = response_body;
+ *_response_len = response_len;
+
+ return EOK;
+}
+
+static int sudosrv_response_append_attr(TALLOC_CTX *mem_ctx,
+ const char *name,
+ unsigned int values_num,
+ struct ldb_val *values,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ uint8_t *response_body = *_response_body;
+ size_t response_len = *_response_len;
+ TALLOC_CTX *tmp_ctx = NULL;
+ unsigned int i = 0;
+ int ret = EOK;
+ const char *strval;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* attr name */
+ ret = sudosrv_response_append_string(tmp_ctx, name, strlen(name) + 1,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* values count */
+ ret = sudosrv_response_append_uint32(tmp_ctx, values_num,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* values */
+ for (i = 0; i < values_num; i++) {
+ strval = (const char *) values[i].data;
+
+ if (strlen((strval)) != values[i].length) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "value is not a string\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sudosrv_response_append_string(tmp_ctx,
+ strval,
+ values[i].length + 1,
+ &response_body, &response_len);
+ DEBUG(SSSDBG_TRACE_INTERNAL, "%s:%s\n", name, strval);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ *_response_body = talloc_steal(mem_ctx, response_body);
+ *_response_len = response_len;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int sudosrv_response_append_rule(TALLOC_CTX *mem_ctx,
+ int attrs_num,
+ struct ldb_message_element *attrs,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ uint8_t *response_body = *_response_body;
+ size_t response_len = *_response_len;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int i = 0;
+ int ret = EOK;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* attrs count */
+ ret = sudosrv_response_append_uint32(tmp_ctx, attrs_num,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* attrs */
+ for (i = 0; i < attrs_num; i++) {
+ ret = sudosrv_response_append_attr(tmp_ctx, attrs[i].name,
+ attrs[i].num_values, attrs[i].values,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ *_response_body = talloc_steal(mem_ctx, response_body);
+ *_response_len = response_len;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ * Response format:
+ * <error_code(uint32_t)><domain(char*)>\0<num_entries(uint32_t)><rule1><rule2>...
+ * <ruleN> = <num_attrs(uint32_t)><attr1><attr2>...
+ * <attrN> = <name(char*)>\0<num_values(uint32_t)><value1(char*)>\0<value2(char*)>\0...
+ *
+ * if <error_code> is not SSS_SUDO_ERROR_OK, the rest of the data is skipped.
+ */
+errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx,
+ uint32_t error,
+ uint32_t rules_num,
+ struct sysdb_attrs **rules,
+ uint8_t **_response_body,
+ size_t *_response_len)
+{
+ uint8_t *response_body = NULL;
+ size_t response_len = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint32_t i = 0;
+ errno_t ret = EOK;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* error code */
+ ret = sudosrv_response_append_uint32(tmp_ctx, error,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "error: [%"PRIu32"]\n", error);
+
+ if (error != SSS_SUDO_ERROR_OK) {
+ goto done;
+ }
+
+ /* domain name - deprecated
+ * TODO: when possible change the protocol */
+ ret = sudosrv_response_append_string(tmp_ctx, "\0", 1,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ /* rules count */
+ ret = sudosrv_response_append_uint32(tmp_ctx, rules_num,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "rules_num: [%"PRIu32"]\n", rules_num);
+
+ /* rules */
+ for (i = 0; i < rules_num; i++) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "rule [%"PRIu32"]/[%"PRIu32"]\n", i+1, rules_num);
+ ret = sudosrv_response_append_rule(tmp_ctx, rules[i]->num, rules[i]->a,
+ &response_body, &response_len);
+ if (ret != EOK) {
+ goto fail;
+ }
+ }
+
+done:
+ *_response_body = talloc_steal(mem_ctx, response_body);
+ *_response_len = response_len;
+
+ ret = EOK;
+
+fail:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx,
+ uint8_t *query_body,
+ size_t query_len,
+ char **_rawname,
+ uid_t *_uid)
+{
+ size_t offset = 0;
+ size_t rawname_len;
+ char *rawname;
+ uid_t uid;
+
+ /* uid */
+ if (query_len < sizeof(uid_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Query is too small\n");
+ return EINVAL;
+ }
+ safealign_memcpy(&uid, query_body, sizeof(uid_t), &offset);
+
+ /* username[@domain] */
+ rawname = (char*)(query_body + offset);
+ rawname_len = query_len - offset; /* strlen + zero */
+
+ if (rawname[rawname_len - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Username is not zero terminated\n");
+ return EINVAL;
+ }
+
+ if (rawname_len < 2) { /* at least one character and zero */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Query does not contain username\n");
+ return EINVAL;
+ }
+
+ if (!sss_utf8_check((uint8_t*)rawname, rawname_len - 1)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Supplied data is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+
+ rawname = talloc_strdup(mem_ctx, rawname);
+ if (rawname == NULL) {
+ return ENOMEM;
+ }
+
+ *_uid = uid;
+ *_rawname = rawname;
+
+ return EOK;
+}