From d38ffc9c92daeb62de7d28c409bdaeff98f82775 Mon Sep 17 00:00:00 2001 From: Pavel Březina Date: Nov 14 2012 16:11:15 +0000 Subject: sudo: support users from subdomains https://fedorahosted.org/sssd/ticket/1616 --- diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c index 9a87ed7..440c8c5 100644 --- a/src/responder/sudo/sudosrv_cmd.c +++ b/src/responder/sudo/sudosrv_cmd.c @@ -156,10 +156,12 @@ errno_t sudosrv_cmd_done(struct sudo_cmd_ctx *cmd_ctx, int ret) return EOK; } +static void sudosrv_cmd_parse_query_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; - struct sudo_dom_ctx *dom_ctx = NULL; uint8_t *query_body = NULL; size_t query_len = 0; errno_t ret; @@ -199,11 +201,35 @@ static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx) goto done; } - ret = sudosrv_parse_query(cmd_ctx, cli_ctx->rctx, - query_body, query_len, - &cmd_ctx->uid, &cmd_ctx->username, &cmd_ctx->domain); + req = sudosrv_parse_query_send(cmd_ctx, cli_ctx->rctx, + query_body, query_len); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sudosrv_cmd_parse_query_done, cmd_ctx); + + ret = EAGAIN; + +done: + return sudosrv_cmd_done(cmd_ctx, ret); +} + +static void sudosrv_cmd_parse_query_done(struct tevent_req *req) +{ + struct sudo_cmd_ctx *cmd_ctx = NULL; + struct sudo_dom_ctx *dom_ctx = NULL; + errno_t ret; + + cmd_ctx = tevent_req_callback_data(req, struct sudo_cmd_ctx); + + ret = sudosrv_parse_query_recv(cmd_ctx, req, &cmd_ctx->uid, + &cmd_ctx->username, &cmd_ctx->domain); + talloc_zfree(req); if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query: %s\n", strerror(ret))); + DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid query [%d]: %s\n", + ret, strerror(ret))); goto done; } @@ -231,15 +257,14 @@ static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx) } dom_ctx->cmd_ctx = cmd_ctx; dom_ctx->domain = cmd_ctx->domain != NULL ? cmd_ctx->domain - : cli_ctx->rctx->domains; + : cmd_ctx->cli_ctx->rctx->domains; ret = sudosrv_get_sudorules(dom_ctx); done: - return sudosrv_cmd_done(cmd_ctx, ret); + sudosrv_cmd_done(cmd_ctx, ret); } - static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx) { return sudosrv_cmd(SSS_SUDO_USER, cli_ctx); diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c index 9081cbd..6d6b95d 100644 --- a/src/responder/sudo/sudosrv_get_sudorules.c +++ b/src/responder/sudo/sudosrv_get_sudorules.c @@ -29,6 +29,22 @@ #include "db/sysdb_sudo.h" #include "responder/sudo/sudosrv_private.h" +static struct sysdb_ctx* sudosrv_get_user_sysdb(struct sss_domain_info *domain) +{ + return domain->sysdb; +} + +static struct sysdb_ctx* sudosrv_get_rules_sysdb(struct sss_domain_info *domain) +{ + if (domain->parent == NULL) { + return domain->sysdb; + } else { + /* sudo rules are stored under parent domain basedn, so we will return + * parent's sysdb context */ + return domain->parent->sysdb; + } +} + static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx); errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx) @@ -112,7 +128,7 @@ static errno_t sudosrv_get_user(struct sudo_dom_ctx *dctx) DEBUG(SSSDBG_FUNC_DATA, ("Requesting info about [%s@%s]\n", name, dom->name)); - sysdb = dctx->domain->sysdb; + sysdb = sudosrv_get_user_sysdb(dctx->domain); if (sysdb == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("sysdb context not found for this domain!\n")); @@ -327,7 +343,8 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx) TALLOC_CTX *tmp_ctx = NULL; struct tevent_req *dpreq = NULL; struct dp_callback_ctx *cb_ctx = NULL; - struct sysdb_ctx *sysdb; + struct sysdb_ctx *user_sysdb = NULL; + struct sysdb_ctx *rules_sysdb = NULL; char **groupnames = NULL; size_t expired_rules_num = 0; struct sysdb_attrs **expired_rules = NULL; @@ -341,7 +358,21 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx) return EFAULT; } - sysdb = cmd_ctx->domain->sysdb; + user_sysdb = sudosrv_get_user_sysdb(cmd_ctx->domain); + if (user_sysdb == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("user sysdb context not found for this domain!\n")); + ret = EIO; + goto done; + } + + rules_sysdb = sudosrv_get_rules_sysdb(cmd_ctx->domain); + if (rules_sysdb == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("rules sysdb context not found for this domain!\n")); + ret = EIO; + goto done; + } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { @@ -367,7 +398,7 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx) * expired rules for this user and defaults at once we will save one * provider call */ - ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, sysdb, + ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, user_sysdb, NULL, &groupnames); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, @@ -379,7 +410,7 @@ errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx) | SYSDB_SUDO_FILTER_INCLUDE_DFL | SYSDB_SUDO_FILTER_ONLY_EXPIRED | SYSDB_SUDO_FILTER_USERINFO; - ret = sudosrv_get_sudorules_query_cache(tmp_ctx, sysdb, cmd_ctx->type, + ret = sudosrv_get_sudorules_query_cache(tmp_ctx, rules_sysdb, cmd_ctx->type, attrs, flags, cmd_ctx->orig_username, cmd_ctx->uid, groupnames, &expired_rules, &expired_rules_num); @@ -535,7 +566,8 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx, { TALLOC_CTX *tmp_ctx; errno_t ret; - struct sysdb_ctx *sysdb; + struct sysdb_ctx *user_sysdb = NULL; + struct sysdb_ctx *rules_sysdb = NULL; char **groupnames = NULL; const char *debug_name = NULL; unsigned int flags = SYSDB_SUDO_FILTER_NONE; @@ -565,10 +597,18 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx, return ENOMEM; } - sysdb = cmd_ctx->domain->sysdb; - if (sysdb == NULL) { + user_sysdb = sudosrv_get_user_sysdb(cmd_ctx->domain); + if (user_sysdb == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("user sysdb context not found for this domain!\n")); + ret = EIO; + goto done; + } + + rules_sysdb = sudosrv_get_rules_sysdb(cmd_ctx->domain); + if (rules_sysdb == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, - ("sysdb context not found for this domain!\n")); + ("rules sysdb context not found for this domain!\n")); ret = EIO; goto done; } @@ -576,8 +616,8 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx, switch (cmd_ctx->type) { case SSS_SUDO_USER: debug_name = cmd_ctx->cased_username; - ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, sysdb, - NULL, &groupnames); + ret = sysdb_get_sudo_user_info(tmp_ctx, cmd_ctx->orig_username, + user_sysdb, NULL, &groupnames); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to retrieve user info [%d]: %s\n", strerror(ret))); @@ -591,7 +631,7 @@ static errno_t sudosrv_get_sudorules_from_cache(TALLOC_CTX *mem_ctx, break; } - ret = sudosrv_get_sudorules_query_cache(tmp_ctx, sysdb, cmd_ctx->type, + ret = sudosrv_get_sudorules_query_cache(tmp_ctx, rules_sysdb, cmd_ctx->type, attrs, flags, cmd_ctx->orig_username, cmd_ctx->uid, groupnames, &rules, &num_rules); diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h index 55ea138..b805940 100644 --- a/src/responder/sudo/sudosrv_private.h +++ b/src/responder/sudo/sudosrv_private.h @@ -90,13 +90,16 @@ errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx); errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx); -errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx, - struct resp_ctx *rctx, - uint8_t *query_body, - size_t query_len, - uid_t *_uid, - char **_username, - struct sss_domain_info **_domain); +struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + uint8_t *query_body, + size_t query_len); + +errno_t sudosrv_parse_query_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + uid_t *_uid, + char **_username, + struct sss_domain_info **_domain); errno_t sudosrv_build_response(TALLOC_CTX *mem_ctx, uint32_t error, diff --git a/src/responder/sudo/sudosrv_query.c b/src/responder/sudo/sudosrv_query.c index 824f682..d76ecbb 100644 --- a/src/responder/sudo/sudosrv_query.c +++ b/src/responder/sudo/sudosrv_query.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "util/util.h" #include "responder/sudo/sudosrv_private.h" @@ -250,34 +251,38 @@ fail: return ret; } -/* - * Query format: - * - */ -errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx, - struct resp_ctx *rctx, - uint8_t *query_body, - size_t query_len, - uid_t *_uid, - char **_username, - struct sss_domain_info **_domain) +struct sudosrv_parse_query_state { + struct resp_ctx *rctx; + uid_t uid; + char *rawname; +}; + +static void sudosrv_parse_query_done(struct tevent_req *subreq); + +struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + uint8_t *query_body, + size_t query_len) { - TALLOC_CTX *tmp_ctx = NULL; - struct sss_domain_info *domain = NULL; + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct sudosrv_parse_query_state *state = NULL; size_t offset = 0; size_t rawname_len = 0; char *rawname = NULL; char *domainname = NULL; - char *username = NULL; - uid_t uid; errno_t ret; - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n")); - return ENOMEM; + /* create request */ + req = tevent_req_create(mem_ctx, &state, + struct sudosrv_parse_query_state); + if (req == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("tevent_req_create() failed\n")); + return NULL; } + state->rctx = rctx; + /* uid */ if (query_len < sizeof(uid_t)) { @@ -285,7 +290,7 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx, ret = EINVAL; goto done; } - safealign_memcpy(&uid, query_body, sizeof(uid_t), &offset); + safealign_memcpy(&state->uid, query_body, sizeof(uid_t), &offset); /* username[@domain] */ @@ -312,31 +317,112 @@ errno_t sudosrv_parse_query(TALLOC_CTX *mem_ctx, /* parse username */ - ret = sss_parse_name_for_domains(tmp_ctx, rctx->domains, - rctx->default_domain, rawname, - &domainname, &username); - if (ret != EOK) { + state->rawname = rawname; + ret = sss_parse_name_for_domains(state, rctx->domains, + rctx->default_domain, state->rawname, + &domainname, NULL); + if (ret == EAGAIN) { + DEBUG(SSSDBG_TRACE_FUNC, ("Domain [%s] not found, " + "sending subdomain request\n", domainname)); + + subreq = sss_dp_get_domains_send(state, rctx, true, domainname); + if (req == NULL) { + ret = ENOMEM; + } else { + tevent_req_set_callback(subreq, sudosrv_parse_query_done, req); + ret = EAGAIN; + } + goto done; + } else if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid name received [%s]\n", rawname)); goto done; } + ret = EOK; + +done: + if (ret != EAGAIN) { + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, rctx->ev); + } + + return req; +} + +static void sudosrv_parse_query_done(struct tevent_req *subreq) +{ + struct tevent_req *req = NULL; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + + ret = sss_dp_get_domains_recv(subreq); + talloc_free(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + } + + tevent_req_done(req); +} + +errno_t sudosrv_parse_query_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + uid_t *_uid, + char **_username, + struct sss_domain_info **_domain) +{ + struct sudosrv_parse_query_state *state = NULL; + struct sss_domain_info *domain = NULL; + char *username = NULL; + char *domainname = NULL; + errno_t ret; + + state = tevent_req_data(req, struct sudosrv_parse_query_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (state->rawname == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("No query specified?!\n")); + return EINVAL; + } + + /* Try to parse username@domain again because if the first call + * returned EAGAIN, then username is unset. If we get EAGAIN again, + * we will not search for it again. + */ + ret = sss_parse_name_for_domains(state, state->rctx->domains, + state->rctx->default_domain, + state->rawname, + &domainname, &username); + if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, ("Unable to parse domain [%d]: %s\n", + ret, strerror(ret))); + return ret; + } + + if (username == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("No username specified!\n")); + return EINVAL; + } + if (domainname != NULL) { /* mem_ctx because it duplicates only subdomains not domains * so I cannot easily steal it */ - domain = responder_get_domain(mem_ctx, rctx, domainname); + domain = responder_get_domain(mem_ctx, state->rctx, domainname); if (domain == NULL) { - ret = ENOENT; - goto done; + DEBUG(SSSDBG_OP_FAILURE, ("Corresponding domain [%s] has not been " + "found\n", domainname)); + return ENOENT; } } - *_uid = uid; + *_uid = state->uid; *_username = talloc_steal(mem_ctx, username); *_domain = domain; /* do not steal on mem_ctx */ - ret = EOK; - -done: - talloc_free(tmp_ctx); - return ret; + return EOK; }