Hi everyone, Attached is the third version of work-in-progress SSSD/tlog integration patches. I'm sending them in the hope that somebody takes a look and perhaps points out some wrong bits I can fix before I'm too dependent on them.
The changes from the last version is some refactoring of the NSS and the common parts, plus start of the PAM part of the implementation. Also, at this point, I think I could contribute some general fixes and prerequisite refactoring patches separately. Nick
>From 6170aa31578f7717ee96c8a424a90eb39ef70371 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Fri, 15 Jul 2016 13:13:52 +0300 Subject: [PATCH 01/15] Add and correct some code documentation --- src/responder/common/responder.h | 23 ++++++++++++++--- src/responder/common/responder_packet.c | 45 +++++++++++++++++++-------------- src/responder/nss/nsssrv_cmd.c | 32 ++++++++++++++++++++++- src/responder/nss/nsssrv_private.h | 26 +++++++++++-------- 4 files changed, 92 insertions(+), 34 deletions(-) diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h index 9e3b2fd..a1dd56b 100644 --- a/src/responder/common/responder.h +++ b/src/responder/common/responder.h @@ -88,6 +88,7 @@ struct be_conn { struct sbus_connection *conn; }; +/** Responder context */ struct resp_ctx { struct tevent_context *ev; struct tevent_fd *lfde; @@ -123,16 +124,19 @@ struct resp_ctx { uint32_t cache_req_num; - void *pvt_ctx; + void *pvt_ctx; /**< Specific responder's private context. + E.g. NSS client info, etc. for NSS + responder */ bool shutting_down; }; struct cli_creds; +/** Client context */ struct cli_ctx { - struct tevent_context *ev; - struct resp_ctx *rctx; + struct tevent_context *ev; /**< The tevent context to add events to */ + struct resp_ctx *rctx; /**< Responder context */ int cfd; struct tevent_fd *cfde; tevent_fd_handler_t cfd_handler; @@ -141,7 +145,7 @@ struct cli_ctx { struct cli_creds *creds; - void *protocol_ctx; + void *protocol_ctx; /**< Protocol context */ void *state_ctx; struct tevent_timer *idle; @@ -323,6 +327,17 @@ void idle_handler(struct tevent_context *ev, #define GET_DOMAINS_DEFAULT_TIMEOUT 60 +/** + * Discover trusted domains and store them as sssd's subdomains. + * + * @param mem_ctx The memory context to allocate request with. + * @param rctx Responder context to work with. + * @param force Ignore timeouts and do request now, if true, + * observe timeouts, if false. + * @param hint + * + * @return Created event, or NULL if event creation failed. + */ struct tevent_req *sss_dp_get_domains_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx, bool force, diff --git a/src/responder/common/responder_packet.c b/src/responder/common/responder_packet.c index 4f5e110..97f93b0 100644 --- a/src/responder/common/responder_packet.c +++ b/src/responder/common/responder_packet.c @@ -31,21 +31,22 @@ #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; + size_t memsize; /**< Packet buffer size */ + + uint8_t *buffer; /**< Packet buffer with the following structure: + * <pre> + * 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 + * </pre> + */ + + size_t iop; /**< Current I/O pointer */ }; /* Offsets to data in sss_packet's buffer */ @@ -59,11 +60,17 @@ 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 +/** + * Allocate and initialize an empty packet. + * + * @param mem_ctx Context to allocate the packet with. + * @param size Packet body size. + * If zero, SSSSRV_PACKET_MEM_SIZE is used instead. + * @param rpacket Location for the created packet pointer, cannot be NULL. * - * - if size is defined use it otherwise the default packet will be - * SSSSRV_PACKET_MEM_SIZE bytes. + * @return Error code: + * ENOMEM - memory allocation failed, + * EOK - packet created successfully. */ int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size, enum sss_cli_command cmd, diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index b64cea2..61df283 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -174,6 +174,18 @@ static const char *get_homedir_override(TALLOC_CTX *mem_ctx, dom->case_preserve, homedir_ctx); } +/** + * Retrieve a user's overriden shell value from an LDB message, according to + * global and user's domain-specific configuration. + * + * @param mem_ctx Context to allocate returned value with. + * @param msg The LDB message to retrieve user info from. + * @param nctx NSS context to operate within. + * @param dom Domain the user belongs to. + * + * @return User shell allocated with mem_ctx, or NULL on allocation error, + * or unspecified shell. + */ static const char *get_shell_override(TALLOC_CTX *mem_ctx, struct ldb_message *msg, struct nss_ctx *nctx, @@ -340,6 +352,24 @@ done: return ret; } +/** + * Transfer user information from LDB messages to a packet and NSS memory + * cache. + * + * @param packet The packet to transfer user information to. + * @param dom Domain the users belong to. + * @param nctx NSS context the operation is carried within. + * @param filter_users True if users present in negative cache should be + * skipped, false otherwise. + * @param pw_mmap_cache True if unskipped users should be stored in mmap + * cache referenced by nctx, if it's present. + * @param msgs Array of LDB messages containing user information to + * be transferred. + * @param count Number of LDB messages in msgs array to transfer user + * information from. + * + * @return Status code. + */ static int fill_pwent(struct sss_packet *packet, struct sss_domain_info *dom, struct nss_ctx *nctx, @@ -721,7 +751,7 @@ errno_t check_cache(struct nss_dom_ctx *dctx, return ENOENT; } - /* In case of local view we have to always contant DP with the original + /* In case of local view we have to always contact DP with the original * name or id. */ get_dp_name_and_id(dctx->cmdctx, dctx->domain, req_type, opt_name, opt_id, &name, &id); diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h index 4720222..90a00ce 100644 --- a/src/responder/nss/nsssrv_private.h +++ b/src/responder/nss/nsssrv_private.h @@ -40,17 +40,21 @@ struct nss_state_ctx { int netgrent_cur; }; +/** NSS command execution context */ struct nss_cmd_ctx { - struct cli_ctx *cctx; - enum sss_cli_command cmd; - char *name; - const char *normalized_name; - bool name_is_upn; - uint32_t id; + struct cli_ctx *cctx; /**< Client context */ + enum sss_cli_command cmd; /**< Command being executed */ + char *name; /**< Name of the entry being requested */ + const char *normalized_name; /**< Normalized name of the entry being requested */ + bool name_is_upn; /**< Names above are Kerberos UPNs */ + uint32_t id; /**< ID of the entry being looked up */ char *secid; bool immediate; - bool check_next; + bool check_next; /**< False if only the domain pointed to + by the domain context should be + looked up, true if following linked + domains should also be looked up */ bool enum_cached; int saved_dom_idx; @@ -77,13 +81,15 @@ struct getent_ctx { }; struct nss_dom_ctx { - struct nss_cmd_ctx *cmdctx; - struct sss_domain_info *domain; + struct nss_cmd_ctx *cmdctx; /**< Parent command context */ + struct sss_domain_info *domain; /**< Domain to start lookup from */ /* For a case when we are discovering subdomains */ const char *rawname; - bool check_provider; + bool check_provider; /**< True if the current domain's + cache needs refresh from the + provider, false otherwise */ /* cache results */ struct ldb_result *res; -- 2.8.1
>From 36ed862a207ed1964948e5223ee5a0db3222f864 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Tue, 16 Aug 2016 17:31:13 +0300 Subject: [PATCH 02/15] Fix comment in sss_parse_name_for_domains --- src/util/usertools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/usertools.c b/src/util/usertools.c index e0d520a..b80a235 100644 --- a/src/util/usertools.c +++ b/src/util/usertools.c @@ -476,7 +476,7 @@ int sss_parse_name_for_domains(TALLOC_CTX *memctx, rname = candidate_name; } else if (candidate_domain) { /* This branch is taken when the input matches the configured - * regular expression, but the domain is now known. Normally, this + * regular expression, but the domain is not known. Normally, this * is the case with a FQDN of a user from subdomain that was not * yet discovered */ -- 2.8.1
>From 581d108c18638d7570088e6975ba966d261fcb80 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Tue, 9 Aug 2016 15:42:38 +0300 Subject: [PATCH 03/15] SYSDB: Fix debug message --- src/db/sysdb_search.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c index cfee578..1830e95 100644 --- a/src/db/sysdb_search.c +++ b/src/db/sysdb_search.c @@ -657,7 +657,7 @@ int sysdb_enumpwent_filter_with_views(TALLOC_CTX *mem_ctx, ret = sysdb_enumpwent_filter(tmp_ctx, domain, name_filter, addtl_filter, &res); if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sysdb_enumpwent failed.\n"); + DEBUG(SSSDBG_OP_FAILURE, "sysdb_enumpwent_filter failed.\n"); goto done; } -- 2.8.1
>From 48f4460a2ef98aee5164d7a8cc3f410d8d976531 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Thu, 11 Aug 2016 13:43:49 +0300 Subject: [PATCH 04/15] NSS: Add missing headers to nsssrv_private.h --- src/responder/nss/nsssrv_private.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h index 90a00ce..810d5f8 100644 --- a/src/responder/nss/nsssrv_private.h +++ b/src/responder/nss/nsssrv_private.h @@ -25,7 +25,11 @@ #ifndef NSSSRV_PRIVATE_H_ #define NSSSRV_PRIVATE_H_ +#include "sss_client/sss_cli.h" +#include "responder/common/responder.h" #include <dhash.h> +#include <talloc.h> +#include <tevent.h> struct nss_state_ent { int dom_idx; -- 2.8.1
>From fdd5db0775b362a9169ad364d63dec23e0237043 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Thu, 11 Aug 2016 13:12:50 +0300 Subject: [PATCH 05/15] NSS: Clarify comments on resuming getgrent/getpwent --- src/responder/nss/nsssrv_cmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index 61df283..380038f 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -2528,8 +2528,8 @@ static int nss_cmd_getpwent(struct cli_ctx *cctx) /* Save the current index and cursor locations * If we end up calling setpwent implicitly, because the response object - * expired and has to be recreated, we want to resume from the same - * location. + * expired and has to be recreated, we want to resume returning entries + * from the same location. */ cmdctx->saved_dom_idx = state_ctx->pwent.dom_idx; cmdctx->saved_cur = state_ctx->pwent.cur; @@ -3788,8 +3788,8 @@ static int nss_cmd_getgrent(struct cli_ctx *cctx) /* Save the current index and cursor locations * If we end up calling setgrent implicitly, because the response object - * expired and has to be recreated, we want to resume from the same - * location. + * expired and has to be recreated, we want to resume returning entries + * from the same location. */ state_ctx = talloc_get_type(cctx->state_ctx, struct nss_state_ctx); cmdctx->saved_dom_idx = state_ctx->grent.dom_idx; -- 2.8.1
>From 37542ee9bf271c650797e2d85a3ee402e8250e82 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Wed, 20 Jul 2016 18:08:40 +0300 Subject: [PATCH 06/15] NSS: Extract getbynam/id/sid search and reply parts Extract the duplicate getbynam/id/sid search and reply parts into functions to avoid maintenance cost and to prepare for insertion of extra async steps. --- src/responder/nss/nsssrv_cmd.c | 299 +++++++++++++++++------------------------ 1 file changed, 120 insertions(+), 179 deletions(-) diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index 380038f..1b8f9cc 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -1484,6 +1484,65 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx) return nss_cmd_getbynam(SSS_NSS_GETPWNAM, cctx); } +static int nss_cmd_getbynam_search_and_reply(struct nss_dom_ctx *dctx) +{ + errno_t ret; + const char *domname = dctx->domain->name; + + DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n", + dctx->cmdctx->name, domname?domname:"<ALL>"); + + dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); + + /* ok, find it ! */ + switch (dctx->cmdctx->cmd) { + case SSS_NSS_GETPWNAM: + ret = nss_cmd_getpwnam_search(dctx); + if (ret == EOK) { + /* we have results to return */ + ret = nss_cmd_getpw_send_reply(dctx, false); + } else if (ret == ENOENT + && dctx->rawname != NULL + && strchr(dctx->rawname, '@') != NULL) { + /* assume Kerberos principal */ + ret = nss_cmd_assume_upn(dctx); + } + break; + case SSS_NSS_GETGRNAM: + ret = nss_cmd_getgrnam_search(dctx); + if (ret == EOK) { + /* we have results to return */ + ret = nss_cmd_getgr_send_reply(dctx, false); + } + break; + case SSS_NSS_INITGR: + ret = nss_cmd_initgroups_search(dctx); + if (ret == EOK) { + /* we have results to return */ + ret = nss_cmd_initgr_send_reply(dctx); + } else if (ret == ENOENT + && dctx->rawname != NULL + && strchr(dctx->rawname, '@') != NULL) { + /* assume Kerberos principal */ + ret = nss_cmd_assume_upn(dctx); + } + break; + case SSS_NSS_GETSIDBYNAME: + case SSS_NSS_GETORIGBYNAME: + ret = nss_cmd_getsidby_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n", + dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd)); + ret = EINVAL; + } + + return ret; +} + static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) { @@ -1594,9 +1653,6 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) goto done; } - DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n", - cmdctx->name, domname?domname:"<ALL>"); - if (domname) { dctx->domain = responder_get_domain(cctx->rctx, domname); if (!dctx->domain) { @@ -1619,53 +1675,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) } } - dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); - - /* ok, find it ! */ - switch (dctx->cmdctx->cmd) { - case SSS_NSS_GETPWNAM: - ret = nss_cmd_getpwnam_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getpw_send_reply(dctx, false); - } else if (ret == ENOENT - && dctx->rawname != NULL - && strchr(dctx->rawname, '@') != NULL) { - /* assume Kerberos principal */ - ret = nss_cmd_assume_upn(dctx); - } - break; - case SSS_NSS_GETGRNAM: - ret = nss_cmd_getgrnam_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getgr_send_reply(dctx, false); - } - break; - case SSS_NSS_INITGR: - ret = nss_cmd_initgroups_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_initgr_send_reply(dctx); - } else if (ret == ENOENT - && dctx->rawname != NULL - && strchr(dctx->rawname, '@') != NULL) { - /* assume Kerberos principal */ - ret = nss_cmd_assume_upn(dctx); - } - break; - case SSS_NSS_GETSIDBYNAME: - case SSS_NSS_GETORIGBYNAME: - ret = nss_cmd_getsidby_search(dctx); - if (ret == EOK) { - ret = nss_cmd_getbysid_send_reply(dctx); - } - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n", - dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd)); - ret = EINVAL; - } + ret = nss_cmd_getbynam_search_and_reply(dctx); done: return nss_cmd_done(cmdctx, ret); @@ -1706,9 +1716,6 @@ static void nss_cmd_getbynam_done(struct tevent_req *req) /* Not fatal */ } - DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%s] from [%s]\n", - cmdctx->name, domname?domname:"<ALL>"); - if (domname) { dctx->domain = responder_get_domain(cctx->rctx, domname); if (dctx->domain == NULL) { @@ -1721,53 +1728,7 @@ static void nss_cmd_getbynam_done(struct tevent_req *req) cmdctx->check_next = true; } - dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); - - /* ok, find it ! */ - switch (dctx->cmdctx->cmd) { - case SSS_NSS_GETPWNAM: - ret = nss_cmd_getpwnam_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getpw_send_reply(dctx, false); - } else if (ret == ENOENT - && dctx->rawname != NULL - && strchr(dctx->rawname, '@') != NULL) { - /* assume Kerberos principal */ - ret = nss_cmd_assume_upn(dctx); - } - break; - case SSS_NSS_GETGRNAM: - ret = nss_cmd_getgrnam_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getgr_send_reply(dctx, false); - } - break; - case SSS_NSS_INITGR: - ret = nss_cmd_initgroups_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_initgr_send_reply(dctx); - } else if (ret == ENOENT - && dctx->rawname != NULL - && strchr(dctx->rawname, '@') != NULL) { - /* assume Kerberos principal */ - ret = nss_cmd_assume_upn(dctx); - } - break; - case SSS_NSS_GETSIDBYNAME: - case SSS_NSS_GETORIGBYNAME: - ret = nss_cmd_getsidby_search(dctx); - if (ret == EOK) { - ret = nss_cmd_getbysid_send_reply(dctx); - } - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n", - dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd)); - ret = EINVAL; - } + ret = nss_cmd_getbynam_search_and_reply(dctx); done: nss_cmd_done(cmdctx, ret); @@ -1917,6 +1878,62 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) return nss_cmd_getbyid(SSS_NSS_GETPWUID, cctx); } +static int nss_cmd_getbyid_search_and_reply(struct nss_dom_ctx *dctx) +{ + int ret; + + /* ok, find it ! */ + switch(dctx->cmdctx->cmd) { + case SSS_NSS_GETPWUID: + ret = nss_cmd_getpwuid_search(dctx); + if (ret == EOK) { + /* we have results to return */ + ret = nss_cmd_getpw_send_reply(dctx, true); + } + break; + case SSS_NSS_GETGRGID: + ret = nss_cmd_getgrgid_search(dctx); + if (ret == EOK) { + /* we have results to return */ + ret = nss_cmd_getgr_send_reply(dctx, true); + } + break; + case SSS_NSS_GETNAMEBYSID: + case SSS_NSS_GETIDBYSID: + if (dctx->domain == NULL) { + ret = responder_get_domain_by_id(dctx->cmdctx->cctx->rctx, + dctx->cmdctx->secid, + &dctx->domain); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot find domain for SID [%s].\n", + dctx->cmdctx->secid); + ret = ENOENT; + break; + } + } + + dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); + + ret = nss_cmd_getbysid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; + case SSS_NSS_GETSIDBYID: + ret = nss_cmd_getsidby_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n", + dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd)); + ret = EINVAL; + } + + return ret; +} + static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx) { struct cli_protocol *pctx; @@ -2027,33 +2044,7 @@ static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx) goto done; } - /* ok, find it ! */ - switch(dctx->cmdctx->cmd) { - case SSS_NSS_GETPWUID: - ret = nss_cmd_getpwuid_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getpw_send_reply(dctx, true); - } - break; - case SSS_NSS_GETGRGID: - ret = nss_cmd_getgrgid_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getgr_send_reply(dctx, true); - } - break; - case SSS_NSS_GETSIDBYID: - ret = nss_cmd_getsidby_search(dctx); - if (ret == EOK) { - ret = nss_cmd_getbysid_send_reply(dctx); - } - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n", - dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd)); - ret = EINVAL; - } + ret = nss_cmd_getbyid_search_and_reply(dctx); done: return nss_cmd_done(cmdctx, ret); @@ -2077,51 +2068,7 @@ static void nss_cmd_getbyid_done(struct tevent_req *req) /* Not fatal */ } - /* ok, find it ! */ - switch(dctx->cmdctx->cmd) { - case SSS_NSS_GETPWUID: - ret = nss_cmd_getpwuid_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getpw_send_reply(dctx, true); - } - break; - case SSS_NSS_GETGRGID: - ret = nss_cmd_getgrgid_search(dctx); - if (ret == EOK) { - /* we have results to return */ - ret = nss_cmd_getgr_send_reply(dctx, true); - } - break; - case SSS_NSS_GETNAMEBYSID: - case SSS_NSS_GETIDBYSID: - ret = responder_get_domain_by_id(cmdctx->cctx->rctx, cmdctx->secid, - &dctx->domain); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "Cannot find domain for SID [%s].\n", - cmdctx->secid); - ret = ENOENT; - goto done; - } - - dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); - - ret = nss_cmd_getbysid_search(dctx); - if (ret == EOK) { - ret = nss_cmd_getbysid_send_reply(dctx); - } - break; - case SSS_NSS_GETSIDBYID: - ret = nss_cmd_getsidby_search(dctx); - if (ret == EOK) { - ret = nss_cmd_getbysid_send_reply(dctx); - } - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n", - dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd)); - ret = EINVAL; - } + ret = nss_cmd_getbyid_search_and_reply(dctx); done: nss_cmd_done(cmdctx, ret); @@ -5317,13 +5264,7 @@ static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx) DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n", cmdctx->secid, dctx->domain->name); - dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); - - /* ok, find it ! */ - ret = nss_cmd_getbysid_search(dctx); - if (ret == EOK) { - ret = nss_cmd_getbysid_send_reply(dctx); - } + ret = nss_cmd_getbyid_search_and_reply(dctx); done: return nss_cmd_done(cmdctx, ret); -- 2.8.1
>From 11e8df09491d1a296fad5de0d6746a7583417525 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Wed, 20 Jul 2016 18:16:27 +0300 Subject: [PATCH 07/15] NSS: Reset negcache after domain stuff for getbynam Move nss_reset_negcache after code dealing with figuring out domains in nss_cmd_getbynam_done. This keeps the domain code together and makes it correspond more closely to the code doing the same in nss_cmd_getbynam, which it mimics and supports. --- src/responder/nss/nsssrv_cmd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index 1b8f9cc..8fd6f6a 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -1710,12 +1710,6 @@ static void nss_cmd_getbynam_done(struct tevent_req *req) goto done; } - ret = nss_reset_negcache(cctx->rctx); - if (ret != EOK) { - DEBUG(SSSDBG_MINOR_FAILURE, "Cannot reset negcache records\n"); - /* Not fatal */ - } - if (domname) { dctx->domain = responder_get_domain(cctx->rctx, domname); if (dctx->domain == NULL) { @@ -1728,6 +1722,12 @@ static void nss_cmd_getbynam_done(struct tevent_req *req) cmdctx->check_next = true; } + ret = nss_reset_negcache(cctx->rctx); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot reset negcache records\n"); + /* Not fatal */ + } + ret = nss_cmd_getbynam_search_and_reply(dctx); done: -- 2.8.1
>From d0a643a310ac8b608ffcb8efce697115ba554c8a Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Wed, 20 Jul 2016 18:24:52 +0300 Subject: [PATCH 08/15] NSS: Rename nss_cmd_getbynam/id_done Rename nss_cmd_getby(nam|id)_done to nss_cmd_getby(nam|id)_get_domains_done to prepare for addition of another async request to nss_cmd_getby(nam|id) processing. --- src/responder/nss/nsssrv_cmd.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index 8fd6f6a..7c6ed5d 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -1478,7 +1478,7 @@ static int nss_check_name_of_well_known_sid(struct nss_cmd_ctx *cmdctx, } static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx); -static void nss_cmd_getbynam_done(struct tevent_req *req); +static void nss_cmd_getbynam_get_domains_done(struct tevent_req *req); static int nss_cmd_getpwnam(struct cli_ctx *cctx) { return nss_cmd_getbynam(SSS_NSS_GETPWNAM, cctx); @@ -1628,7 +1628,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) if (req == NULL) { ret = ENOMEM; } else { - tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx); + tevent_req_set_callback(req, nss_cmd_getbynam_get_domains_done, dctx); ret = EAGAIN; } goto done; @@ -1643,7 +1643,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) if (req == NULL) { ret = ENOMEM; } else { - tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx); + tevent_req_set_callback(req, nss_cmd_getbynam_get_domains_done, dctx); ret = EAGAIN; } goto done; @@ -1668,7 +1668,8 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) if (req == NULL) { ret = ENOMEM; } else { - tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx); + tevent_req_set_callback(req, + nss_cmd_getbynam_get_domains_done, dctx); ret = EAGAIN; } goto done; @@ -1681,7 +1682,7 @@ done: return nss_cmd_done(cmdctx, ret); } -static void nss_cmd_getbynam_done(struct tevent_req *req) +static void nss_cmd_getbynam_get_domains_done(struct tevent_req *req) { struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx); struct nss_cmd_ctx *cmdctx = dctx->cmdctx; @@ -1872,7 +1873,7 @@ done: static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx); static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx); -static void nss_cmd_getbyid_done(struct tevent_req *req); +static void nss_cmd_getbyid_get_domains_done(struct tevent_req *req); static int nss_cmd_getpwuid(struct cli_ctx *cctx) { return nss_cmd_getbyid(SSS_NSS_GETPWUID, cctx); @@ -2038,7 +2039,8 @@ static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx) if (req == NULL) { ret = ENOMEM; } else { - tevent_req_set_callback(req, nss_cmd_getbyid_done, dctx); + tevent_req_set_callback(req, + nss_cmd_getbyid_get_domains_done, dctx); ret = EAGAIN; } goto done; @@ -2050,7 +2052,7 @@ done: return nss_cmd_done(cmdctx, ret); } -static void nss_cmd_getbyid_done(struct tevent_req *req) +static void nss_cmd_getbyid_get_domains_done(struct tevent_req *req) { struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx); struct nss_cmd_ctx *cmdctx = dctx->cmdctx; @@ -5252,7 +5254,8 @@ static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx) ret = ENOMEM; } else { dctx->rawname = sid_str; - tevent_req_set_callback(req, nss_cmd_getbyid_done, dctx); + tevent_req_set_callback(req, + nss_cmd_getbyid_get_domains_done, dctx); ret = EAGAIN; } goto done; -- 2.8.1
>From 9f0e10931624dbdb26bb89fa5825fec62bd52428 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Wed, 20 Jul 2016 18:53:57 +0300 Subject: [PATCH 09/15] NSS: Make fill_pwent take cmdctx, not nctx Make fill_pwent in nsssrv_cmd.c accept more specific command context, instead of just NSS context to prepare for addition of command-specific session-recording code. --- src/responder/nss/nsssrv_cmd.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index 7c6ed5d..9eccf78 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -358,7 +358,7 @@ done: * * @param packet The packet to transfer user information to. * @param dom Domain the users belong to. - * @param nctx NSS context the operation is carried within. + * @param cmdctx The command context the operation is carried within. * @param filter_users True if users present in negative cache should be * skipped, false otherwise. * @param pw_mmap_cache True if unskipped users should be stored in mmap @@ -372,11 +372,12 @@ done: */ static int fill_pwent(struct sss_packet *packet, struct sss_domain_info *dom, - struct nss_ctx *nctx, + struct nss_cmd_ctx *cmdctx, bool filter_users, bool pw_mmap_cache, struct ldb_message **msgs, int *count) { + struct nss_ctx *nctx; struct ldb_message *msg; uint8_t *body; const char *upn; @@ -397,6 +398,8 @@ static int fill_pwent(struct sss_packet *packet, TALLOC_CTX *tmp_ctx = NULL; struct sss_nss_homedir_ctx homedir_ctx; + nctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx); + to_sized_string(&pwfield, nctx->pwfield); rp = 2*sizeof(uint32_t); @@ -554,12 +557,10 @@ static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter) struct nss_cmd_ctx *cmdctx = dctx->cmdctx; struct cli_ctx *cctx = cmdctx->cctx; struct cli_protocol *pctx; - struct nss_ctx *nctx; int ret; int i; pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); - nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), @@ -571,7 +572,7 @@ static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter) ret = fill_pwent(pctx->creq->out, dctx->domain, - nctx, filter, true, + cmdctx, filter, true, dctx->res->msgs, &i); if (ret) { return ret; @@ -2499,7 +2500,7 @@ static int nss_cmd_getpwent(struct cli_ctx *cctx) return nss_cmd_getpwent_immediate(cmdctx); } -static int nss_cmd_retpwent(struct cli_ctx *cctx, int num); +static int nss_cmd_retpwent(struct nss_cmd_ctx *cmdctx, int num); static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx) { struct cli_protocol *pctx; @@ -2525,7 +2526,7 @@ static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx) return ret; } - ret = nss_cmd_retpwent(cmdctx->cctx, num); + ret = nss_cmd_retpwent(cmdctx, num); sss_packet_set_error(pctx->creq->out, ret); sss_cmd_done(cmdctx->cctx, cmdctx); @@ -2533,7 +2534,7 @@ static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx) return EOK; } -static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) +static int nss_cmd_retpwent(struct nss_cmd_ctx *cmdctx, int num) { struct cli_protocol *pctx; struct nss_state_ctx *state_ctx; @@ -2544,9 +2545,9 @@ static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) int n = 0; int ret = ENOENT; - pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); - state_ctx = talloc_get_type(cctx->state_ctx, struct nss_state_ctx); - nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + pctx = talloc_get_type(cmdctx->cctx->protocol_ctx, struct cli_protocol); + state_ctx = talloc_get_type(cmdctx->cctx->state_ctx, struct nss_state_ctx); + nctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx); if (!nctx->pctx) goto none; gctx = nctx->pctx; @@ -2579,7 +2580,7 @@ static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) msgs = &(pdom->res->msgs[state_ctx->pwent.cur]); - ret = fill_pwent(pctx->creq->out, pdom->domain, nctx, + ret = fill_pwent(pctx->creq->out, pdom->domain, cmdctx, true, false, msgs, &n); state_ctx->pwent.cur += n; -- 2.8.1
>From d40f2c835b95f2aecc45b1eb0749d7f19b449f22 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Thu, 11 Aug 2016 14:10:44 +0300 Subject: [PATCH 10/15] config: Add session_recording section Add information on "session_recording" config section, having three options: "scope", "users", and "groups". The section is intended for disabling session recording ("scope = none", default), enabling session recording for all users ("scope = all"), and enabling it for some specific users and/or groups ("scope = some", "users = <users>", "groups = <groups>"). --- src/confdb/confdb.h | 6 ++++++ src/config/SSSDConfigTest.py | 6 ++++-- src/config/etc/sssd.api.conf | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index 72adbd8..cbdfe6e 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -157,6 +157,12 @@ #define CONFDB_IFP_USER_ATTR_LIST "user_attributes" #define CONFDB_IFP_WILDCARD_LIMIT "wildcard_limit" +/* Session Recording */ +#define CONFDB_SESSION_RECORDING_CONF_ENTRY "config/session_recording" +#define CONFDB_SESSION_RECORDING_SCOPE "scope" +#define CONFDB_SESSION_RECORDING_USERS "users" +#define CONFDB_SESSION_RECORDING_GROUPS "groups" + /* Domains */ #define CONFDB_DOMAIN_PATH_TMPL "config/domain/%s" #define CONFDB_DOMAIN_BASEDN "cn=domain,cn=config" diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py index 8fcd1a5..d0ec9f4 100755 --- a/src/config/SSSDConfigTest.py +++ b/src/config/SSSDConfigTest.py @@ -1355,7 +1355,8 @@ class SSSDConfigTestSSSDConfig(unittest.TestCase): 'autofs', 'ssh', 'pac', - 'ifp'] + 'ifp', + 'session_recording'] for section in control_list: self.assertTrue(sssdconfig.has_section(section), "Section [%s] missing" % @@ -1448,7 +1449,8 @@ class SSSDConfigTestSSSDConfig(unittest.TestCase): 'autofs', 'ssh', 'pac', - 'ifp'] + 'ifp', + 'session_recording'] service_list = sssdconfig.list_services() for service in control_list: self.assertTrue(service in service_list, diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index 5e69414..d228e1f 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -95,6 +95,12 @@ pac_lifetime = int, None, false allowed_uids = str, None, false user_attributes = str, None, false +[session_recording] +# Session recording service +scope = str, None, false +users = list, str, false +groups = list, str, false + [provider] #Available provider types id_provider = str, None, true -- 2.8.1
>From 349c733dcda75e3e44c78007f32b239c249e71ce Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Thu, 11 Aug 2016 14:15:55 +0300 Subject: [PATCH 11/15] BUILD: Support configuring session recording shell Add support for specifying the shell used for recording user sessions, at configure time. --- configure.ac | 1 + src/conf_macros.m4 | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/configure.ac b/configure.ac index 8760a85..7d276fb 100644 --- a/configure.ac +++ b/configure.ac @@ -134,6 +134,7 @@ WITH_SEMANAGE WITH_AD_GPO_DEFAULT WITH_GPO_CACHE_PATH WITH_NOLOGIN_SHELL +WITH_SESSION_RECORDING_SHELL WITH_APP_LIBS WITH_SUDO WITH_SUDO_LIB_PATH diff --git a/src/conf_macros.m4 b/src/conf_macros.m4 index bc295c5..16542fb 100644 --- a/src/conf_macros.m4 +++ b/src/conf_macros.m4 @@ -592,6 +592,22 @@ AC_DEFUN([WITH_NOLOGIN_SHELL], AC_DEFINE_UNQUOTED(NOLOGIN_SHELL, "$nologin_shell", [The shell used to deny access to users]) ]) +AC_DEFUN([WITH_SESSION_RECORDING_SHELL], + [ AC_ARG_WITH([session-recording-shell], + [AC_HELP_STRING([--with-session-recording-shell=PATH], + [The shell used to record user sessions [/usr/bin/tlog-rec]] + ) + ] + ) + session_recording_shell="/usr/bin/tlog-rec" + if test x"$with_session_recording_shell" != x; then + nologin_shell=$with_session_recording_shell + fi + AC_SUBST(session_recording_shell) + AC_DEFINE_UNQUOTED(SESSION_RECORDING_SHELL, "$session_recording_shell", + [The shell used to record user sessions]) + ]) + AC_ARG_ENABLE([all-experimental-features], [AS_HELP_STRING([--enable-all-experimental-features], [build all experimental features])], -- 2.8.1
>From dc0ffbc8f637adbf6432132aece38cd39ed733c0 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Fri, 15 Jul 2016 13:16:39 +0300 Subject: [PATCH 12/15] NSS: Add session recording shell substitution Add support for substituting user shell with a session recording shell for users and groups with session recording enabled, for getpwnam, getpwuid, and getpwent calls. --- Makefile.am | 6 +- src/responder/common/session_recording.c | 305 +++++++++++++++++++++++++++++++ src/responder/common/session_recording.h | 143 +++++++++++++++ src/responder/nss/nsssrv.c | 22 +++ src/responder/nss/nsssrv.h | 9 + src/responder/nss/nsssrv_cmd.c | 203 +++++++++++++++++++- src/responder/nss/nsssrv_private.h | 18 ++ 7 files changed, 695 insertions(+), 11 deletions(-) create mode 100644 src/responder/common/session_recording.c create mode 100644 src/responder/common/session_recording.h diff --git a/Makefile.am b/Makefile.am index 8b9240f..99ffbc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -490,6 +490,7 @@ SSSD_RESPONDER_OBJ = \ src/responder/common/responder_get_domains.c \ src/responder/common/responder_utils.c \ src/responder/common/responder_cache_req.c \ + src/responder/common/session_recording.c \ src/responder/common/data_provider/rdp_message.c \ src/responder/common/data_provider/rdp_client.c \ src/monitor/monitor_iface_generated.c \ @@ -602,6 +603,7 @@ dist_noinst_HEADERS = \ src/responder/common/responder_packet.h \ src/responder/common/responder_sbus.h \ src/responder/common/responder_cache_req.h \ + src/responder/common/session_recording.h \ src/responder/common/data_provider/rdp.h \ src/responder/pam/pamsrv.h \ src/responder/pam/pam_helpers.h \ @@ -2091,6 +2093,7 @@ TEST_MOCK_RESP_OBJ = \ src/responder/common/negcache_files.c \ src/responder/common/negcache.c \ src/responder/common/responder_common.c \ + src/responder/common/session_recording.c \ src/responder/common/data_provider/rdp_message.c \ src/responder/common/data_provider/rdp_client.c \ src/responder/common/responder_utils.c \ @@ -2117,7 +2120,8 @@ nss_srv_tests_SOURCES = \ src/responder/nss/nsssrv_cmd.c \ src/responder/nss/nsssrv_netgroup.c \ src/responder/nss/nsssrv_services.c \ - src/responder/nss/nsssrv_mmap_cache.c + src/responder/nss/nsssrv_mmap_cache.c \ + $(NULL) nss_srv_tests_CFLAGS = \ $(AM_CFLAGS) nss_srv_tests_LDFLAGS = \ diff --git a/src/responder/common/session_recording.c b/src/responder/common/session_recording.c new file mode 100644 index 0000000..618791f --- /dev/null +++ b/src/responder/common/session_recording.c @@ -0,0 +1,305 @@ +/* + SSSD + + Responder session recording functions + + Authors: + Nikolai Kondrashov <[email protected]> + + 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/common/session_recording.h" +#include "responder/common/responder_cache_req.h" + +int session_recording_conf_load(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + struct session_recording_conf *pconf) +{ + int ret; + char *str; + + /* Read session_recording/scope option */ + ret = confdb_get_string(cdb, mem_ctx, CONFDB_SESSION_RECORDING_CONF_ENTRY, + CONFDB_SESSION_RECORDING_SCOPE, "none", &str); + if (ret != EOK) goto done; + if (strcasecmp(str, "none") == 0) { + pconf->scope = SESSION_RECORDING_SCOPE_NONE; + } else if (strcasecmp(str, "some") == 0) { + pconf->scope = SESSION_RECORDING_SCOPE_SOME; + } else if (strcasecmp(str, "all") == 0) { + pconf->scope = SESSION_RECORDING_SCOPE_ALL; + } else { + DEBUG(SSSDBG_OP_FAILURE, + "Unknown value for session recording scope: %s\n", + str); + ret = EINVAL; + goto done; + } + + /* Read session_recording/users option */ + ret = confdb_get_string_as_list(cdb, mem_ctx, + CONFDB_SESSION_RECORDING_CONF_ENTRY, + CONFDB_SESSION_RECORDING_USERS, + &pconf->users); + if (ret != EOK && ret != ENOENT) goto done; + + /* Read session_recording/groups option */ + ret = confdb_get_string_as_list(cdb, mem_ctx, + CONFDB_SESSION_RECORDING_CONF_ENTRY, + CONFDB_SESSION_RECORDING_GROUPS, + &pconf->groups); + if (ret != EOK && ret != ENOENT) goto done; + + ret = EOK; +done: + return ret; +} + +/** + * Internal state of the asynchronous request to retrieve information on + * groups for which session recording is enabled. + */ +struct session_recording_get_groups_state { + TALLOC_CTX *mem_ctx; /**< Memory context to allocate results + with */ + struct resp_ctx *rctx; /**< Responder context to work within */ + char **pname; /**< Pointer to name of the next group to + retrieve info about */ + struct ldb_result **presult; /**< Next location to store retrieved + result pointer in */ +}; + +/** + * Handle a result of a group info request and submit another one, if there's + * any. + * + * @param subreq The completed group info request. + */ +static void session_recording_get_groups_step(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct session_recording_get_groups_state *state = + tevent_req_data(req, struct session_recording_get_groups_state); + struct resp_ctx *rctx = state->rctx; + + /* Get the lookup result, move onto next array element if we got one */ + ret = cache_req_group_by_name_recv(state->mem_ctx, subreq, + state->presult, NULL, NULL); + talloc_zfree(subreq); + if (ret == EOK) { + state->presult++; + } else if (ret != ENOENT) { + goto done; + } + + /* Move onto the next group name */ + state->pname++; + + /* If there are no more names */ + if (*state->pname == NULL) { + tevent_req_done(req); + ret = EOK; + goto done; + } + + /* Lookup next name */ + /* TODO: Figure out proper caching parameters */ + subreq = cache_req_group_by_name_send(state->mem_ctx, rctx->ev, rctx, + rctx->ncache, 0, NULL, + *state->pname); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + tevent_req_set_callback(subreq, + session_recording_get_groups_step, req); + return; + +done: + tevent_req_error(req, ret); +} + +struct tevent_req *session_recording_get_groups_send( + TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + char **name_list, + struct ldb_result ***presult_list) +{ + errno_t ret; + struct tevent_req *req; + struct tevent_req *subreq; + struct session_recording_get_groups_state *state; + char **pname; + size_t num; + + /* Create the tracking "request" */ + req = tevent_req_create(mem_ctx, &state, + struct session_recording_get_groups_state); + if (req == NULL) { + return NULL; + } + + /* Count groups to request */ + num = 0; + pname = name_list; + if (pname != NULL) { + for (; *pname != NULL; pname++, num++); + } + + /* Allocate array for lookup results, include terminating NULL */ + *presult_list = talloc_zero_array(mem_ctx, struct ldb_result *, num + 1); + if (*presult_list == NULL) { + ret = ENOMEM; + goto error; + } + + /* TODO Handle zero groups */ + + /* Fill state */ + state->mem_ctx = mem_ctx; + state->rctx = rctx; + state->pname = name_list; + state->presult = *presult_list; + + /* Start first group lookup */ + subreq = cache_req_group_by_name_send(mem_ctx, rctx->ev, rctx, + rctx->ncache, 0, NULL, + *state->pname); + if (subreq == NULL) { + ret = ENOMEM; + goto error; + } + tevent_req_set_callback(subreq, + session_recording_get_groups_step, req); + + return req; + +error: + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + return req; +} + +errno_t session_recording_get_groups_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +errno_t session_recording_is_enabled(enum session_recording_scope scope, + char **users, + struct ldb_result **group_results, + struct sss_domain_info *dom, + const char *orig_name, + const char *name, + bool *penabled) +{ + errno_t ret; + bool enabled; + char **pname; + struct ldb_result **pgroup_result; + TALLOC_CTX *tmp_ctx = NULL; + + if (dom == NULL || orig_name == NULL || + name == NULL || penabled == NULL) { + ret = EINVAL; + goto done; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + switch (scope) { + case SESSION_RECORDING_SCOPE_NONE: + enabled = false; + ret = EOK; + goto done; + case SESSION_RECORDING_SCOPE_ALL: + enabled = true; + ret = EOK; + goto done; + case SESSION_RECORDING_SCOPE_SOME: + /* + * Check if user name matches recorded names + */ + if (users != NULL) { + for (pname = users; *pname != NULL; pname++) { + if (strcmp(name, *pname) == 0) { + enabled = true; + ret = EOK; + goto done; + } + } + } + + /* + * Check if user is in a group with session recording enabled + */ + pgroup_result = group_results; + if (pgroup_result != NULL && *pgroup_result != NULL) { + struct ldb_result *user_result; + struct ldb_result *group_result; + size_t i; + size_t j; + + /* Retrieve groups the user is member of */ + ret = sysdb_initgroups_with_views(tmp_ctx, dom, orig_name, &user_result); + if (ret != EOK) { + goto done; + } + /* + * For each result of a session recording-enabled group lookup + */ + do { + group_result = *pgroup_result; + /* For each message (match) in the result */ + for (i = 0; i < group_result->count; i++) { + /* For each user's group */ + for (j = 1; j < user_result->count; j++) { + /* If their DN's match */ + if (ldb_dn_compare( + group_result->msgs[i]->dn, + user_result->msgs[j]->dn) == 0) { + enabled = true; + ret = EOK; + goto done; + } + } + } + } while (*++pgroup_result != NULL); + } + enabled = false; + ret = EOK; + break; + default: + ret = EINVAL; + goto done; + } + +done: + talloc_zfree(tmp_ctx); + if (ret == EOK) { + *penabled = enabled; + } + return ret; +} diff --git a/src/responder/common/session_recording.h b/src/responder/common/session_recording.h new file mode 100644 index 0000000..b2a8cc7 --- /dev/null +++ b/src/responder/common/session_recording.h @@ -0,0 +1,143 @@ +/* + SSSD + + Responder session recording functions + + Authors: + Nikolai Kondrashov <[email protected]> + + 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 __SSS_RESPONDER_SESSION_RECORDING_H__ +#define __SSS_RESPONDER_SESSION_RECORDING_H__ + +#include "confdb/confdb.h" +#include "responder/common/responder.h" + +/** Scope of users whose session should be recorded */ +enum session_recording_scope { + SESSION_RECORDING_SCOPE_NONE, /**< None, no users */ + SESSION_RECORDING_SCOPE_SOME, /**< Some users specified elsewhere */ + SESSION_RECORDING_SCOPE_ALL /**< All users */ +}; + +/** Session recording configuration (from "session_recording" section) */ +struct session_recording_conf { + enum session_recording_scope scope; /**< Session recording scope: + whether to record nobody, + everyone, or some + users/groups */ + char **users; /**< NULL-terminated list of users + whose session should be + recorded. Can be NULL, + meaning empty list. Only + applicable if scope is "some" + */ + char **groups; /**< NULL-terminated list of + groups, members of which + should have their sessions + recorded. Can be NULL, + meaning empty list. Only + applicable if scope is "some" + */ +}; + +/** + * Load session recording configuration from configuration database. + * + * @param mem_ctx Memory context to allocate data with. + * @param cdb The configuration database connection object to retrieve + * data from. + * @param pconf Location for the loaded session configuration. + * + * @return Status code: + * ENOMEM - memory allocation failed, + * EINVAL - configuration was invalid, + * EIO - an I/O error occurred while communicating with the ConfDB. + */ +extern int session_recording_conf_load(TALLOC_CTX *mem_ctx, + struct confdb_ctx *cdb, + struct session_recording_conf *pconf); + +/** + * Start an asynchronous request to retrieve groups for which session + * recording is enabled, for an abstract responder context. + * + * @param mem_ctx The memory context to allocate results and request + * state with. + * @param rctx Abstract responder context to operate within. + * @param name_list NULL-terminated list of group names to retrieve info + * for. Can be NULL. Must be valid during the whole + * request lifetime. + * @param presult_list Location for the pointer to a NULL-terminated array of + * pointers to retrieved group results, allocated with + * mem_ctx. + * + * @return The created asynchronous request, or NULL, if failed to allocate + * memory for it. + */ +extern struct tevent_req *session_recording_get_groups_send( + TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + char **name_list, + struct ldb_result ***presult_list); + +/** + * Retrieve status code for a finished request to retrieve groups for which + * session recording is enabled. + * + * @return Status code. + */ +extern errno_t session_recording_get_groups_recv(struct tevent_req *req); + +/** + * Check if session recording is enabled for a user, according to session + * recording configuration. + * + * @param scope Session recording scope. + * @param users A NULL-terminated list of names of users (as returned + * to NSS client) with session recording enabled. Only + * valid if scope is "some". Can be NULL. + * @param group_results A NULL-terminated list of ldb_results for groups with + * session recording enabled. Only valid if scope is + * "some". Can be NULL. + * @param dom Domain the user belongs to. Used to retrieve the + * groups the user belongs to. + * @param orig_name User's name. Used to retrieve the groups the user + * belongs to. + * @param name User's name as returned to NSS client. Used to match + * against the list of users with enabled session + * recording. + * @param penabled Location for the "enabled" flag: true if session + * recording is enabled, false otherwise. Not changed in + * case of error. + * + * @return Status code: + * EOK - check completed successfully, + * EINVAL - invalid arguments supplied, + * ENOMEM - memory allocation failed, + * other - failed retrieving user info. + */ +extern int session_recording_is_enabled(enum session_recording_scope scope, + char **users, + struct ldb_result **group_results, + struct sss_domain_info *dom, + const char *orig_name, + const char *name, + bool *penabled); + +#endif /* __SSS_RESPONDER_SESSION_RECORDING_H__ */ diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c index 06d58f2..e32d1b3 100644 --- a/src/responder/nss/nsssrv.c +++ b/src/responder/nss/nsssrv.c @@ -323,6 +323,28 @@ static int nss_get_config(struct nss_ctx *nctx, } } + /* Read session_recording section */ + ret = session_recording_conf_load(nctx, cdb, + &nctx->session_recording_conf); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed loading session recording configuration: %s\n", + strerror(ret)); + goto done; + } + + /* If configured to enable session recording for specific groups */ + if (nctx->session_recording_conf.scope == + SESSION_RECORDING_SCOPE_SOME && + nctx->session_recording_conf.groups != NULL && + nctx->session_recording_conf.groups[0] != NULL) { + /* Always request user's groups when requesting user info */ + nctx->user_req_type = SSS_DP_INITGROUPS; + } else { + /* Request normal user info only */ + nctx->user_req_type = SSS_DP_USER; + } + ret = 0; done: return ret; diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h index d4a80f7..6ac057c 100644 --- a/src/responder/nss/nsssrv.h +++ b/src/responder/nss/nsssrv.h @@ -34,6 +34,7 @@ #include "sbus/sssd_dbus.h" #include "responder/common/responder_packet.h" #include "responder/common/responder.h" +#include "responder/common/session_recording.h" #include "lib/idmap/sss_idmap.h" #define NSS_PACKET_MAX_RECV_SIZE 1024 @@ -75,6 +76,14 @@ struct nss_ctx { struct sss_names_ctx *global_names; const char **extra_attributes; + + struct session_recording_conf + session_recording_conf; /**< Session recording + configuration */ + enum sss_dp_acct_type user_req_type; /**< Request type to use when + refreshing cached user + information from the + provider */ }; struct nss_packet; diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index 9eccf78..f14a303 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -29,6 +29,7 @@ #include "responder/nss/nsssrv_netgroup.h" #include "responder/nss/nsssrv_services.h" #include "responder/nss/nsssrv_mmap_cache.h" +#include "responder/common/session_recording.h" #include "responder/common/negcache.h" #include "responder/common/responder_cache_req.h" #include "providers/data_provider.h" @@ -394,6 +395,7 @@ static int fill_pwent(struct sss_packet *packet, int i, ret, num; const char *domain = dom->name; bool packet_initialized = false; + bool session_recording_enabled; int ncret; TALLOC_CTX *tmp_ctx = NULL; struct sss_nss_homedir_ctx homedir_ctx; @@ -491,12 +493,27 @@ static int fill_pwent(struct sss_packet *packet, to_sized_string(&homedir, tmpstr); } - tmpstr = get_shell_override(tmp_ctx, msg, nctx, dom); - if (!tmpstr) { - to_sized_string(&shell, ""); + ret = session_recording_is_enabled(nctx->session_recording_conf.scope, + nctx->session_recording_conf.users, + cmdctx->session_recording_groups, + dom, orig_name, name->str, + &session_recording_enabled); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed checking if session recording is enabled for user " + "[%s] in [%s]: %s! Skipping user.\n", + name->str, domain, strerror(ret)); + continue; + } + if (session_recording_enabled) { + tmpstr = SESSION_RECORDING_SHELL; } else { - to_sized_string(&shell, tmpstr); + tmpstr = get_shell_override(tmp_ctx, msg, nctx, dom); + } + if (!tmpstr) { + tmpstr = ""; } + to_sized_string(&shell, tmpstr); rsize = 2 * sizeof(uint32_t) + name->len + gecos.len + homedir.len + shell.len + pwfield.len; @@ -1162,7 +1179,8 @@ static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx) extra_flag = NULL; } - ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, name, 0, + ret = check_cache(dctx, nctx, dctx->res, nctx->user_req_type, + name, 0, extra_flag, nss_cmd_getby_dp_callback, dctx); if (ret != EOK) { /* Anything but EOK means we should reenter the mainloop @@ -1480,6 +1498,9 @@ static int nss_check_name_of_well_known_sid(struct nss_cmd_ctx *cmdctx, static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx); static void nss_cmd_getbynam_get_domains_done(struct tevent_req *req); +static int nss_cmd_getbynam_session_recording_get_groups(struct nss_dom_ctx *dctx); +static void nss_cmd_getbynam_session_recording_get_groups_done(struct tevent_req *req); + static int nss_cmd_getpwnam(struct cli_ctx *cctx) { return nss_cmd_getbynam(SSS_NSS_GETPWNAM, cctx); @@ -1677,7 +1698,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) } } - ret = nss_cmd_getbynam_search_and_reply(dctx); + ret = nss_cmd_getbynam_session_recording_get_groups(dctx); done: return nss_cmd_done(cmdctx, ret); @@ -1730,6 +1751,57 @@ static void nss_cmd_getbynam_get_domains_done(struct tevent_req *req) /* Not fatal */ } + ret = nss_cmd_getbynam_session_recording_get_groups(dctx); + +done: + nss_cmd_done(cmdctx, ret); +} + +static int nss_cmd_getbynam_session_recording_get_groups(struct nss_dom_ctx *dctx) +{ + struct nss_ctx *nctx; + struct tevent_req *req; + + nctx = talloc_get_type(dctx->cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx); + + /* If we need to match a user against session recording groups */ + if (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM && + nctx->session_recording_conf.scope == + SESSION_RECORDING_SCOPE_SOME && + nctx->session_recording_conf.groups != NULL && + nctx->session_recording_conf.groups[0] != NULL) { + /* + * Do an async request for groups with session recording enabled. + * Store them in the command context. + */ + req = session_recording_get_groups_send( + dctx->cmdctx, + dctx->cmdctx->cctx->rctx, + nctx->session_recording_conf.groups, + &dctx->cmdctx->session_recording_groups); + if (req == NULL) { + return ENOMEM; + } else { + tevent_req_set_callback(req, nss_cmd_getbynam_session_recording_get_groups_done, dctx); + return EAGAIN; + } + } + + return nss_cmd_getbynam_search_and_reply(dctx); +} + +static void nss_cmd_getbynam_session_recording_get_groups_done(struct tevent_req *req) +{ + struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + errno_t ret; + + ret = session_recording_get_groups_recv(req); + talloc_free(req); + if (ret != EOK) { + goto done; + } + ret = nss_cmd_getbynam_search_and_reply(dctx); done: @@ -1836,7 +1908,7 @@ static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx) extra_flag = NULL; } - ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, NULL, + ret = check_cache(dctx, nctx, dctx->res, nctx->user_req_type, NULL, cmdctx->id, extra_flag, nss_cmd_getby_dp_callback, dctx); if (ret != EOK) { @@ -1875,6 +1947,9 @@ done: static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx); static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx); static void nss_cmd_getbyid_get_domains_done(struct tevent_req *req); +static int nss_cmd_getbyid_session_recording_get_groups(struct nss_dom_ctx *dctx); +static void nss_cmd_getbyid_session_recording_get_groups_done(struct tevent_req *req); + static int nss_cmd_getpwuid(struct cli_ctx *cctx) { return nss_cmd_getbyid(SSS_NSS_GETPWUID, cctx); @@ -2047,7 +2122,7 @@ static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx) goto done; } - ret = nss_cmd_getbyid_search_and_reply(dctx); + ret = nss_cmd_getbyid_session_recording_get_groups(dctx); done: return nss_cmd_done(cmdctx, ret); @@ -2071,6 +2146,59 @@ static void nss_cmd_getbyid_get_domains_done(struct tevent_req *req) /* Not fatal */ } + ret = nss_cmd_getbyid_session_recording_get_groups(dctx); + +done: + nss_cmd_done(cmdctx, ret); +} + +static int nss_cmd_getbyid_session_recording_get_groups(struct nss_dom_ctx *dctx) +{ + struct nss_ctx *nctx; + struct tevent_req *req; + + nctx = talloc_get_type(dctx->cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx); + + /* If we need to match a user against session recording groups */ + if (dctx->cmdctx->cmd == SSS_NSS_GETPWUID && + nctx->session_recording_conf.scope == + SESSION_RECORDING_SCOPE_SOME && + nctx->session_recording_conf.groups != NULL && + nctx->session_recording_conf.groups[0] != NULL) { + /* + * Do an async request for groups with session recording enabled. + * Store them in the command context. + */ + req = session_recording_get_groups_send( + dctx->cmdctx, + dctx->cmdctx->cctx->rctx, + nctx->session_recording_conf.groups, + &dctx->cmdctx->session_recording_groups); + if (req == NULL) { + return ENOMEM; + } else { + tevent_req_set_callback(req, + nss_cmd_getbyid_session_recording_get_groups_done, + dctx); + return EAGAIN; + } + } + + return nss_cmd_getbyid_search_and_reply(dctx); +} + +static void nss_cmd_getbyid_session_recording_get_groups_done(struct tevent_req *req) +{ + struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + errno_t ret; + + ret = session_recording_get_groups_recv(req); + talloc_free(req); + if (ret != EOK) { + goto done; + } + ret = nss_cmd_getbyid_search_and_reply(dctx); done: @@ -2230,6 +2358,11 @@ struct tevent_req *nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx, step_ctx->rctx = client->rctx; step_ctx->cctx = client; step_ctx->returned_to_mainloop = false; + step_ctx->need_session_recording_groups = + state->nctx->session_recording_conf.scope == + SESSION_RECORDING_SCOPE_SOME && + state->nctx->session_recording_conf.groups != NULL && + state->nctx->session_recording_conf.groups[0] != NULL; ret = nss_cmd_setpwent_step(step_ctx); if (ret != EOK && ret != EAGAIN) goto error; @@ -2252,6 +2385,8 @@ static void setpwent_result_timeout(struct tevent_context *ev, struct tevent_timer *te, struct timeval current_time, void *pvt); +static void nss_cmd_setpwent_session_recording_get_groups_done( + struct tevent_req *req); /* nss_cmd_setpwent_step returns * EOK if everything is done and the request needs to be posted explicitly @@ -2271,6 +2406,23 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx) struct tevent_req *dpreq; struct dp_callback_ctx *cb_ctx; + if (step_ctx->need_session_recording_groups) { + struct tevent_req *req; + req = session_recording_get_groups_send( + pctx, rctx, + nctx->session_recording_conf.groups, + &pctx->session_recording_groups); + if (req == NULL) { + return ENOMEM; + } else { + tevent_req_set_callback( + req, + nss_cmd_setpwent_session_recording_get_groups_done, + step_ctx); + return EAGAIN; + } + } + while (dom) { while (dom && dom->enumerate == false) { dom = get_next_domain(dom, SSS_GND_DESCEND); @@ -2304,7 +2456,7 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx) dctx->check_provider = false; dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true, - SSS_DP_USER, NULL, 0, NULL); + nctx->user_req_type, NULL, 0, NULL); if (!dpreq) { DEBUG(SSSDBG_MINOR_FAILURE, "Enum Cache refresh for domain [%s] failed." @@ -2406,6 +2558,31 @@ static void setpwent_result_timeout(struct tevent_context *ev, talloc_zfree(nctx->pctx); } +static void nss_cmd_setpwent_session_recording_get_groups_done(struct tevent_req *req) +{ + struct setent_step_ctx *step_ctx = + tevent_req_callback_data(req, struct setent_step_ctx); + errno_t ret; + + /* We're done, don't try again */ + step_ctx->need_session_recording_groups = false; + + ret = session_recording_get_groups_recv(req); + talloc_free(req); + if (ret != EOK) { + /* Notify any waiting processes of failure */ + nss_setent_notify_error(step_ctx->nctx->pctx, ret); + return; + } + + ret = nss_cmd_setpwent_step(step_ctx); + if (ret != EOK && ret != EAGAIN) { + /* Notify any waiting processes of failure */ + nss_setent_notify_error(step_ctx->nctx->pctx, ret); + return; + } +} + static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min, const char *err_msg, void *ptr) { @@ -2497,6 +2674,12 @@ static int nss_cmd_getpwent(struct cli_ctx *cctx) return EOK; } + /* + * Use the groups setpwent retrieved as groups for which session recording + * is enabled. + */ + cmdctx->session_recording_groups = nctx->pctx->session_recording_groups; + return nss_cmd_getpwent_immediate(cmdctx); } @@ -5268,7 +5451,7 @@ static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx) DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n", cmdctx->secid, dctx->domain->name); - ret = nss_cmd_getbyid_search_and_reply(dctx); + ret = nss_cmd_getbyid_session_recording_get_groups(dctx); done: return nss_cmd_done(cmdctx, ret); diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h index 810d5f8..005df47 100644 --- a/src/responder/nss/nsssrv_private.h +++ b/src/responder/nss/nsssrv_private.h @@ -63,6 +63,12 @@ struct nss_cmd_ctx { int saved_dom_idx; int saved_cur; + + struct ldb_result **session_recording_groups; /**< NULL-terminated list of + results of looking up + groups for which + session recording is + enabled */ }; struct dom_ctx { @@ -82,6 +88,13 @@ struct getent_ctx { char *name; char *domain; bool found; + + /* Passwd-specific */ + struct ldb_result **session_recording_groups; /**< NULL-terminated list of + results of looking up + groups for which + session recording is + enabled */ }; struct nss_dom_ctx { @@ -119,6 +132,11 @@ struct setent_step_ctx { /* Netgroup-specific */ char *name; + + /* Passwd-specific */ + bool need_session_recording_groups; /**< True if groups for which session + recording is enabled need to be + retrieved on the next step */ }; #define NSS_CMD_FATAL_ERROR(cctx) do { \ -- 2.8.1
>From 8c0decf5c79097a94afd06b7ec0ebe6184532424 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Wed, 17 Aug 2016 16:18:19 +0300 Subject: [PATCH 13/15] PAM: Load session recording conf --- src/responder/pam/pamsrv.c | 10 ++++++++++ src/responder/pam/pamsrv.h | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c index 9374de4..ce84d23 100644 --- a/src/responder/pam/pamsrv.c +++ b/src/responder/pam/pamsrv.c @@ -311,6 +311,16 @@ static int pam_process_init(TALLOC_CTX *mem_ctx, } } + /* Read session_recording section */ + ret = session_recording_conf_load(pctx, pctx->rctx->cdb, + &pctx->session_recording_conf); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed loading session recording configuration: %s\n", + strerror(ret)); + goto done; + } + ret = EOK; done: diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h index e686d03..6703a2c 100644 --- a/src/responder/pam/pamsrv.h +++ b/src/responder/pam/pamsrv.h @@ -26,6 +26,7 @@ #include "util/util.h" #include "sbus/sssd_dbus.h" #include "responder/common/responder.h" +#include "responder/common/session_recording.h" struct pam_auth_req; @@ -45,6 +46,10 @@ struct pam_ctx { bool cert_auth; int p11_child_debug_fd; char *nss_db; + + struct session_recording_conf + session_recording_conf; /**< Session recording + configuration */ }; struct pam_auth_dp_req { -- 2.8.1
>From 23d316ab2b1aba400aa7f5917c02d62f5fcbb0bc Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Fri, 19 Aug 2016 19:15:06 +0300 Subject: [PATCH 14/15] PAM: Add session recording group fetching --- src/responder/pam/pamsrv.h | 2 ++ src/responder/pam/pamsrv_cmd.c | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h index 6703a2c..8bfdfff 100644 --- a/src/responder/pam/pamsrv.h +++ b/src/responder/pam/pamsrv.h @@ -75,6 +75,8 @@ struct pam_auth_req { struct ldb_message *cert_user_obj; char *token_name; + + struct ldb_result **session_recording_groups; }; struct sss_cmd_table *get_pam_cmds(void); diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index be54fbf..143c334 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -52,6 +52,7 @@ pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain, uint64_t *_value); static void pam_reply(struct pam_auth_req *preq); +static void pam_reply_session_recording_get_groups_done(struct tevent_req *req); static errno_t pack_user_info_msg(TALLOC_CTX *mem_ctx, const char *user_error_message, @@ -726,6 +727,32 @@ static void pam_reply(struct pam_auth_req *preq) return; } + /* If this was a successfull session opening */ + if (pd->cmd == SSS_PAM_OPEN_SESSION && + pd->pam_status == PAM_SUCCESS) { + /* If we need to fetch groups with session recording enabled */ + if (pctx->session_recording_conf.scope == + SESSION_RECORDING_SCOPE_SOME && + pctx->session_recording_conf.groups != NULL && + pctx->session_recording_conf.groups[0] != NULL && + preq->session_recording_groups == NULL) { + struct tevent_req *req; + + req = session_recording_get_groups_send( + preq, preq->cctx->rctx, + pctx->session_recording_conf.groups, + &preq->session_recording_groups); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + tevent_req_set_callback(req, + pam_reply_session_recording_get_groups_done, + preq); + return; + } + } + /* If this was a successful login, save the lastLogin time */ if (pd->cmd == SSS_PAM_AUTHENTICATE && pd->pam_status == PAM_SUCCESS && @@ -843,6 +870,23 @@ done: sss_cmd_done(cctx, preq); } +static void pam_reply_session_recording_get_groups_done(struct tevent_req *req) +{ + struct pam_auth_req *preq = tevent_req_callback_data(req, struct pam_auth_req); + errno_t ret; + + ret = session_recording_get_groups_recv(req); + talloc_free(req); + if (ret != EOK) { + goto done; + } + + pam_reply(preq); + +done: + sss_cmd_done(preq->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, -- 2.8.1
>From f761ee3af4f928bf01a9fa846b2978a17541c416 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov <[email protected]> Date: Thu, 11 Aug 2016 14:18:38 +0300 Subject: [PATCH 15/15] intg: Add session recording tests --- src/tests/intg/Makefile.am | 1 + src/tests/intg/config.py.m4 | 21 ++-- src/tests/intg/test_enumeration.py | 239 +++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 10 deletions(-) diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am index d73e421..8a0e68b 100644 --- a/src/tests/intg/Makefile.am +++ b/src/tests/intg/Makefile.am @@ -24,6 +24,7 @@ config.py: config.py.m4 -D "pidpath=\`$(pidpath)'" \ -D "logpath=\`$(logpath)'" \ -D "mcpath=\`$(mcpath)'" \ + -D "session_recording_shell=\`$(session_recording_shell)'" \ $< > $@ root: diff --git a/src/tests/intg/config.py.m4 b/src/tests/intg/config.py.m4 index 77aa47b..c2922f9 100644 --- a/src/tests/intg/config.py.m4 +++ b/src/tests/intg/config.py.m4 @@ -2,13 +2,14 @@ Build configuration variables. """ -PREFIX = "prefix" -SYSCONFDIR = "sysconfdir" -NSS_MODULE_DIR = PREFIX + "/lib" -SSSDCONFDIR = SYSCONFDIR + "/sssd" -CONF_PATH = SSSDCONFDIR + "/sssd.conf" -DB_PATH = "dbpath" -PID_PATH = "pidpath" -PIDFILE_PATH = PID_PATH + "/sssd.pid" -LOG_PATH = "logpath" -MCACHE_PATH = "mcpath" +PREFIX = "prefix" +SYSCONFDIR = "sysconfdir" +NSS_MODULE_DIR = PREFIX + "/lib" +SSSDCONFDIR = SYSCONFDIR + "/sssd" +CONF_PATH = SSSDCONFDIR + "/sssd.conf" +DB_PATH = "dbpath" +PID_PATH = "pidpath" +PIDFILE_PATH = PID_PATH + "/sssd.pid" +LOG_PATH = "logpath" +MCACHE_PATH = "mcpath" +SESSION_RECORDING_SHELL = "session_recording_shell" diff --git a/src/tests/intg/test_enumeration.py b/src/tests/intg/test_enumeration.py index ece09f7..c648703 100644 --- a/src/tests/intg/test_enumeration.py +++ b/src/tests/intg/test_enumeration.py @@ -685,3 +685,242 @@ def test_vetoed_shells(vetoed_shells): shell="/bin/default") ) ) + + [email protected] +def session_recording_ldap(request, ldap_conn): + ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn) + ent_list.add_user("user1", 1001, 2001, loginShell="/bin/sh") + ent_list.add_user("user2", 1002, 2002, loginShell="/bin/sh") + ent_list.add_user("user3", 1003, 2003, loginShell="/bin/sh") + ent_list.add_group("group1", 2001) + ent_list.add_group("group2", 2002) + ent_list.add_group("group3", 2003) + ent_list.add_group("empty_group", 2010) + ent_list.add_group("one_user_group", 2011, ["user1"]) + ent_list.add_group("two_user_group", 2012, ["user1", "user2"]) + ent_list.add_group("three_user_group", 2013, ["user1", "user2", "user3"]) + create_ldap_fixture(request, ldap_conn, ent_list) + + [email protected] +def session_recording_none(request, ldap_conn, session_recording_ldap): + conf = \ + format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \ + unindent("""\ + [session_recording] + scope = none + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + + +def test_session_recording_none(session_recording_none): + """Test session recording "none" scope""" + ent.assert_passwd( + ent.contains_only( + dict(name="user1", uid=1001, shell="/bin/sh"), + dict(name="user2", uid=1002, shell="/bin/sh"), + dict(name="user3", uid=1003, shell="/bin/sh"), + ) + ) + + [email protected] +def session_recording_all(request, ldap_conn, session_recording_ldap): + conf = \ + format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \ + unindent("""\ + [session_recording] + scope = all + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + + +def test_session_recording_all_nam(session_recording_all): + """Test session recording "all" scope with getpwnam""" + ent.assert_each_passwd_by_name(dict( + user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL), + )) + + +def test_session_recording_all_uid(session_recording_all): + """Test session recording "all" scope with getpwuid""" + ent.assert_each_passwd_by_uid({ + 1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + 1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + 1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL), + }) + + +def test_session_recording_all_ent(session_recording_all): + """Test session recording "all" scope with getpwent""" + ent.assert_passwd_list( + ent.contains_only( + dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL), + ) + ) + + [email protected] +def session_recording_some_empty(request, ldap_conn, session_recording_ldap): + conf = \ + format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \ + unindent("""\ + [session_recording] + scope = some + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + + +def test_session_recording_some_empty(session_recording_some_empty): + """Test session recording "some" scope with no users or groups""" + ent.assert_passwd( + ent.contains_only( + dict(name="user1", uid=1001, shell="/bin/sh"), + dict(name="user2", uid=1002, shell="/bin/sh"), + dict(name="user3", uid=1003, shell="/bin/sh"), + ) + ) + + [email protected] +def session_recording_some_users(request, ldap_conn, session_recording_ldap): + conf = \ + format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \ + unindent("""\ + [session_recording] + scope = some + users = user1, user2 + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + + +def test_session_recording_some_users_nam(session_recording_some_users): + """Test session recording "some" scope with user list and getpwnam""" + ent.assert_each_passwd_by_name(dict( + user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + user3=dict(name="user3", uid=1003, shell="/bin/sh"), + )) + + +def test_session_recording_some_users_uid(session_recording_some_users): + """Test session recording "some" scope with user list and getpwuid""" + ent.assert_each_passwd_by_uid({ + 1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + 1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + 1003:dict(name="user3", uid=1003, shell="/bin/sh"), + }) + + +def test_session_recording_some_users_ent(session_recording_some_users): + """Test session recording "some" scope with user list and getpwent""" + ent.assert_passwd_list( + ent.contains_only( + dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + dict(name="user3", uid=1003, shell="/bin/sh"), + ) + ) + + [email protected] +def session_recording_some_groups(request, ldap_conn, session_recording_ldap): + conf = \ + format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \ + unindent("""\ + [session_recording] + scope = some + groups = one_user_group, two_user_group + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + + +def test_session_recording_some_groups_nam(session_recording_some_groups): + """Test session recording "some" scope with group list and getpwnam""" + ent.assert_each_passwd_by_name(dict( + user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + user3=dict(name="user3", uid=1003, shell="/bin/sh"), + )) + + +def test_session_recording_some_groups_uid(session_recording_some_groups): + """Test session recording "some" scope with group list and getpwuid""" + ent.assert_each_passwd_by_uid({ + 1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + 1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + 1003:dict(name="user3", uid=1003, shell="/bin/sh"), + }) + + +def test_session_recording_some_groups_ent(session_recording_some_groups): + """Test session recording "some" scope with group list and getpwent""" + ent.assert_passwd_list( + ent.contains_only( + dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL), + dict(name="user3", uid=1003, shell="/bin/sh"), + ) + ) + + [email protected] +def session_recording_some_users_and_groups(request, ldap_conn, session_recording_ldap): + conf = \ + format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \ + unindent("""\ + [session_recording] + scope = some + users = user3 + groups = one_user_group + """).format(**locals()) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + + +def test_session_recording_some_users_and_groups_nam( + session_recording_some_users_and_groups): + """ + Test session recording "some" scope with user and group lists and getpwnam + """ + ent.assert_each_passwd_by_name(dict( + user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + user2=dict(name="user2", uid=1002, shell="/bin/sh"), + user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL), + )) + + +def test_session_recording_some_users_and_groups_uid( + session_recording_some_users_and_groups): + """ + Test session recording "some" scope with user and group lists and getpwuid + """ + ent.assert_each_passwd_by_uid({ + 1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + 1002:dict(name="user2", uid=1002, shell="/bin/sh"), + 1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL), + }) + + +def test_session_recording_some_users_and_groups_ent( + session_recording_some_users_and_groups): + """ + Test session recording "some" scope with user and group lists and getpwent + """ + ent.assert_passwd_list( + ent.contains_only( + dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL), + dict(name="user2", uid=1002, shell="/bin/sh"), + dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL), + ) + ) -- 2.8.1
_______________________________________________ sssd-devel mailing list [email protected] https://lists.fedorahosted.org/admin/lists/[email protected]
