From c6f4b8eae92b09260e24dcd643c0057e7f7a7369 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 15 Apr 2013 10:58:22 +0200 Subject: [PATCH 2/4] Add SID related calls to the NSS responder The patch adds 4 new calls to the NSS responder: - SSS_NSS_GETSIDBYNAME - SSS_NSS_GETSIDBYID - SSS_NSS_GETNAMEBYSID - SSS_NSS_GETIDBYSID to either return the SIDs of the requested object or map the SID to the name or the POSIX ID of the related object. --- Makefile.am | 1 + src/responder/nss/nsssrv_cmd.c | 950 +++++++++++++++++++++++++++++++++++- src/responder/nss/nsssrv_private.h | 1 + 3 files changed, 949 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3f6cc60..f47bf3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1231,6 +1231,7 @@ nss_srv_tests_LDFLAGS = \ -Wl,-wrap,sss_cmd_done nss_srv_tests_LDADD = \ $(CMOCKA_LIBS) \ + libsss_idmap.la \ libsss_util.la test_find_uid_DEPENDENCIES = \ diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c index 903481f..7cd852f 100644 --- a/src/responder/nss/nsssrv_cmd.c +++ b/src/responder/nss/nsssrv_cmd.c @@ -29,6 +29,7 @@ #include "responder/common/negcache.h" #include "confdb/confdb.h" #include "db/sysdb.h" +#include "sss_client/idmap/sss_nss_idmap.h" #include static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err) @@ -833,6 +834,10 @@ static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx); static int nss_cmd_initgr_send_reply(struct nss_dom_ctx *dctx); static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx); static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx); +static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx); +static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx); +//static errno_t nss_cmd_getsidbynam_search(struct nss_dom_ctx *dctx); +static errno_t nss_cmd_getsidbyid_search(struct nss_dom_ctx *dctx); static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min, const char *err_msg, void *ptr) @@ -841,6 +846,7 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min, struct nss_cmd_ctx *cmdctx = dctx->cmdctx; struct cli_ctx *cctx = cmdctx->cctx; int ret; + bool check_subdomains; if (err_maj) { DEBUG(2, ("Unable to get information from Data Provider\n" @@ -867,6 +873,12 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min, case SSS_NSS_GETGRGID: ret = nss_cmd_getgr_send_reply(dctx, true); break; + case SSS_NSS_GETNAMEBYSID: + case SSS_NSS_GETIDBYSID: + case SSS_NSS_GETSIDBYNAME: + case SSS_NSS_GETSIDBYID: + ret = nss_cmd_getbysid_send_reply(dctx); + break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n", dctx->cmdctx->cmd)); @@ -875,9 +887,24 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min, goto done; } + /* Since subdomain users and groups are fully qualified they are + * typically not subject of multi-domain searches. But since POSIX + * ID do not contain a domain name we have to decend to subdomains + * here. */ + switch (dctx->cmdctx->cmd) { + case SSS_NSS_GETPWUID: + case SSS_NSS_GETGRGID: + case SSS_NSS_GETSIDBYID: + check_subdomains = true; + break; + default: + check_subdomains = false; + } + /* no previous results, just loop to next domain if possible */ - if (cmdctx->check_next && get_next_domain(dctx->domain, false)) { - dctx->domain = get_next_domain(dctx->domain, false); + if (cmdctx->check_next && + get_next_domain(dctx->domain, check_subdomains)) { + dctx->domain = get_next_domain(dctx->domain, check_subdomains); dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider); } else { /* nothing available */ @@ -923,6 +950,25 @@ static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min, ret = nss_cmd_getgr_send_reply(dctx, true); } break; + case SSS_NSS_GETNAMEBYSID: + case SSS_NSS_GETIDBYSID: + ret = nss_cmd_getbysid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; + case SSS_NSS_GETSIDBYNAME: + ret = nss_cmd_getsidbyid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; + case SSS_NSS_GETSIDBYID: + ret = nss_cmd_getsidbyid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n", dctx->cmdctx->cmd)); @@ -959,6 +1005,7 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) case SSS_NSS_GETPWNAM: case SSS_NSS_GETGRNAM: case SSS_NSS_INITGR: + case SSS_NSS_GETSIDBYNAME: break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command type [%d].\n", cmd)); @@ -1070,6 +1117,12 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx) ret = nss_cmd_initgr_send_reply(dctx); } break; + case SSS_NSS_GETSIDBYNAME: + ret = nss_cmd_getsidbyid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n", dctx->cmdctx)); ret = EINVAL; @@ -1143,6 +1196,12 @@ static void nss_cmd_getbynam_done(struct tevent_req *req) ret = nss_cmd_initgr_send_reply(dctx); } break; + case SSS_NSS_GETSIDBYNAME: + ret = nss_cmd_getsidbyid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n", dctx->cmdctx)); ret = EINVAL; @@ -1275,7 +1334,12 @@ static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx) int ret; struct tevent_req *req; - if (cmd != SSS_NSS_GETPWUID && cmd != SSS_NSS_GETGRGID) { + switch (cmd) { + case SSS_NSS_GETPWUID: + case SSS_NSS_GETGRGID: + case SSS_NSS_GETSIDBYID: + break; + default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command type [%d].\n", cmd)); return EINVAL; } @@ -1329,6 +1393,20 @@ static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx) goto done; } break; + case SSS_NSS_GETSIDBYID: + ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, cmdctx->id); + if (ret != EEXIST) { + ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout, + cmdctx->id); + } + if (ret == EEXIST) { + DEBUG(SSSDBG_TRACE_FUNC, + ("Id [%lu] does not exist! (negative cache)\n", + (unsigned long)cmdctx->id)); + ret = ENOENT; + goto done; + } + break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n", dctx->cmdctx)); ret = EINVAL; @@ -1368,6 +1446,12 @@ static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx) ret = nss_cmd_getgr_send_reply(dctx, true); } break; + case SSS_NSS_GETSIDBYID: + ret = nss_cmd_getsidbyid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n", dctx->cmdctx)); ret = EINVAL; @@ -1405,6 +1489,30 @@ static void nss_cmd_getbyid_done(struct tevent_req *req) 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_getsidbyid_search(dctx); + if (ret == EOK) { + ret = nss_cmd_getbysid_send_reply(dctx); + } + break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command [%d].\n", dctx->cmdctx)); ret = EINVAL; @@ -3492,6 +3600,838 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx) return nss_cmd_getbynam(SSS_NSS_INITGR, cctx); } +static errno_t nss_cmd_getsidbyid_search(struct nss_dom_ctx *dctx) +{ + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct sss_domain_info *dom = dctx->domain; + struct cli_ctx *cctx = cmdctx->cctx; + struct sysdb_ctx *sysdb; + struct nss_ctx *nctx; + int ret; + const char *attrs[] = {SYSDB_NAME, SYSDB_OBJECTCLASS, SYSDB_SID_STR, NULL}; + bool user_found = false; + bool group_found = false; + struct ldb_message *msg = NULL; + char *sysdb_name = NULL; + char *name = NULL; + char *req_name; + uint32_t req_id; + int req_type; + + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + + while (dom) { + + if (cmdctx->cmd == SSS_NSS_GETSIDBYID) { + /* check that the uid is valid for this domain */ + if ((dom->id_min && (cmdctx->id < dom->id_min)) || + (dom->id_max && (cmdctx->id > dom->id_max))) { + DEBUG(SSSDBG_TRACE_FUNC, + ("Uid [%lu] does not exist in domain [%s]! " + "(id out of range)\n", + (unsigned long)cmdctx->id, dom->name)); + if (cmdctx->check_next) { + dom = get_next_domain(dom, true); + continue; + } + return ENOENT; + } + } else { + /* if it is a domainless search, skip domains that require fully + * qualified names instead */ + while (dom && cmdctx->check_next && dom->fqnames) { + dom = get_next_domain(dom, false); + } + + if (!dom) break; + } + + if (dom != dctx->domain) { + /* make sure we reset the check_provider flag when we check + * a new domain */ + dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider); + } + + /* make sure to update the dctx if we changed domain */ + dctx->domain = dom; + + if (cmdctx->cmd == SSS_NSS_GETSIDBYID) { + DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%d@%s]\n", + cmdctx->id, dom->name)); + } else { + talloc_free(name); + talloc_zfree(sysdb_name); + + name = sss_get_cased_name(cmdctx, cmdctx->name, dom->case_sensitive); + if (name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_get_cased_name failed.\n")); + return ENOMEM; + } + + /* For subdomains a fully qualified name is needed for + * sysdb_search_user_by_name and sysdb_search_group_by_name. */ + if (IS_SUBDOMAIN(dom)) { + sysdb_name = talloc_asprintf(cmdctx, dom->names->fq_fmt, + name, dom->name); + if (sysdb_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n")); + return ENOMEM; + } + } + + + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout, + dom, name); + + /* if neg cached, return we didn't find it */ + if (ret == EEXIST) { + DEBUG(SSSDBG_TRACE_FUNC, + ("User [%s] does not exist in [%s]! (negative cache)\n", + name, dom->name)); + /* if a multidomain search, try with next */ + if (cmdctx->check_next) { + dom = get_next_domain(dom, false); + continue; + } + /* There are no further domains or this was a + * fully-qualified user request. + */ + return ENOENT; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s@%s]\n", + name, dom->name)); + } + + + sysdb = dom->sysdb; + if (sysdb == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Fatal: Sysdb CTX not found for this domain!\n")); + return EIO; + } + + if (cmdctx->cmd == SSS_NSS_GETSIDBYID) { + ret = sysdb_search_user_by_uid(cmdctx, sysdb, dom, cmdctx->id, + attrs, &msg); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to make request to our cache!\n")); + return EIO; + } + + if (ret == EOK) { + user_found = true; + } else { + talloc_free(msg); + ret = sysdb_search_group_by_gid(cmdctx, sysdb, dom, cmdctx->id, + attrs, &msg); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to make request to our cache!\n")); + return EIO; + } + + if (ret == EOK) { + group_found = true; + } + } + } else { + ret = sysdb_search_user_by_name(cmdctx, sysdb, dom, + sysdb_name ? sysdb_name : name, + attrs, &msg); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to make request to our cache!\n")); + return EIO; + } + + if (ret == EOK) { + user_found = true; + } else { + talloc_free(msg); + ret = sysdb_search_group_by_name(cmdctx, sysdb, dom, + sysdb_name ? sysdb_name : name, + attrs, &msg); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to make request to our cache!\n")); + return EIO; + } + + if (ret == EOK) { + group_found = true; + } + } + } + + dctx->res = talloc_zero(cmdctx, struct ldb_result); + if (dctx->res == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_zero failed.\n")); + return ENOMEM; + } + + if (user_found || group_found) { + dctx->res->count = 1; + dctx->res->msgs = talloc_array(dctx->res, struct ldb_message *, 1); + if (dctx->res->msgs == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_array failed.\n")); + return ENOMEM; + } + dctx->res->msgs[0] = talloc_steal(dctx->res, msg); + } + + if (dctx->res->count == 0 && !dctx->check_provider) { + if (cmdctx->cmd == SSS_NSS_GETSIDBYNAME) { + ret = sss_ncache_set_user(nctx->ncache, false, dom, name); + if (ret != EOK) { + return ret; + } + + ret = sss_ncache_set_group(nctx->ncache, false, dom, name); + if (ret != EOK) { + return ret; + } + } + /* if a multidomain search, try with next */ + if (cmdctx->check_next) { + dom = get_next_domain(dom, true); + continue; + } + + DEBUG(SSSDBG_OP_FAILURE, ("No matching user or group found.\n")); + + if (cmdctx->cmd == SSS_NSS_GETSIDBYID) { + ret = sss_ncache_set_uid(nctx->ncache, false, cmdctx->id); + if (ret != EOK) { + return ret; + } + + ret = sss_ncache_set_gid(nctx->ncache, false, cmdctx->id); + if (ret != EOK) { + return ret; + } + } + + return ENOENT; + } + + /* if this is a caching provider (or if we haven't checked the cache + * yet) then verify that the cache is uptodate */ + if (dctx->check_provider) { + if (cmdctx->cmd == SSS_NSS_GETSIDBYID) { + req_name = NULL; + req_id = cmdctx->id; + } else { + req_name = name; + req_id = 0; + } + if (user_found) { + req_type = SSS_DP_USER; + } else if (group_found) { + req_type = SSS_DP_GROUP; + } else { + req_type = SSS_DP_USER_AND_GROUP; + } + + ret = check_cache(dctx, nctx, dctx->res, + req_type, req_name, req_id, + nss_cmd_getby_dp_callback, + dctx); + if (ret != EOK) { + /* Anything but EOK means we should reenter the mainloop + * because we may be refreshing the cache + */ + return ret; + } + } + + /* One result found */ + if (cmdctx->cmd == SSS_NSS_GETSIDBYID) { + DEBUG(SSSDBG_TRACE_FUNC, ("Returning info for id [%d@%s]\n", + cmdctx->id, dom->name)); + } else { + DEBUG(SSSDBG_TRACE_FUNC, ("Returning info for user/group [%s@%s]\n", + name, dom->name)); + } + + return EOK; + } + + if (cmdctx->cmd == SSS_NSS_GETSIDBYID) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("No matching domain found for [%d], fail!\n", cmdctx->id)); + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + ("No matching domain found for [%s], fail!\n", cmdctx->name)); + } + return ENOENT; +} + +#if 0 +static errno_t nss_cmd_getsidbynam_search(struct nss_dom_ctx *dctx) +{ + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct sss_domain_info *dom = dctx->domain; + struct cli_ctx *cctx = cmdctx->cctx; + char *name = NULL; + struct sysdb_ctx *sysdb; + struct nss_ctx *nctx; + int ret; + bool user_found = false; + bool group_found = false; + const char *attrs[] = {SYSDB_NAME, SYSDB_OBJECTCLASS, SYSDB_SID_STR, NULL}; + + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + + while (dom) { + /* if it is a domainless search, skip domains that require fully + * qualified names instead */ + while (dom && cmdctx->check_next && dom->fqnames) { + dom = get_next_domain(dom, false); + } + + if (!dom) break; + + if (dom != dctx->domain) { + /* make sure we reset the check_provider flag when we check + * a new domain */ + dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider); + } + + /* make sure to update the dctx if we changed domain */ + dctx->domain = dom; + + talloc_free(name); + name = sss_get_cased_name(cmdctx, cmdctx->name, dom->case_sensitive); + if (!name) return ENOMEM; + + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout, + dom, name); + + /* if neg cached, return we didn't find it */ + if (ret == EEXIST) { + DEBUG(SSSDBG_TRACE_FUNC, + ("User [%s] does not exist in [%s]! (negative cache)\n", + name, dom->name)); + /* if a multidomain search, try with next */ + if (cmdctx->check_next) { + dom = get_next_domain(dom, false); + continue; + } + /* There are no further domains or this was a + * fully-qualified user request. + */ + return ENOENT; + } + + DEBUG(4, ("Requesting info for [%s@%s]\n", name, dom->name)); + + sysdb = dom->sysdb; + if (sysdb == NULL) { + DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n")); + return EIO; + } + + ret = sysdb_get_user_attr(cmdctx, sysdb, dom, name, attrs, &dctx->res); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + return EIO; + } + + if (dctx->res->count > 1) { + DEBUG(0, ("getpwnam call returned more than one result !?!\n")); + return ENOENT; + } else if (dctx->res->count == 1) { + user_found = true; + } else { + talloc_free(dctx->res); + //ret = sysdb_get_group_attr(cmdctx, sysdb, dom, name, attrs, + // &dctx->res); + //if (ret != EOK) { + // DEBUG(1, ("Failed to make request to our cache!\n")); + // return EIO; + //} + + if (dctx->res->count > 1) { + DEBUG(0, ("getgrnam call returned more than one result !?!\n")); + return ENOENT; + } else if (dctx->res->count == 1) { + group_found = true; + } + } + + if (dctx->res->count == 0 && !dctx->check_provider) { + /* set negative cache only if not result of cache check */ + ret = sss_ncache_set_user(nctx->ncache, false, dom, name); + if (ret != EOK) { + return ret; + } + + /* if a multidomain search, try with next */ + if (cmdctx->check_next) { + dom = get_next_domain(dom, false); + if (dom) continue; + } + + DEBUG(2, ("No results for getpwnam/getgrnam call\n")); + + return ENOENT; + } + + /* if this is a caching provider (or if we haven't checked the cache + * yet) then verify that the cache is uptodate */ + if (dctx->check_provider) { + if (user_found) { + ret = check_cache(dctx, nctx, dctx->res, + SSS_DP_USER, name, 0, + nss_cmd_getby_dp_callback, + dctx); + } else if (group_found) { + ret = check_cache(dctx, nctx, dctx->res, + SSS_DP_GROUP, name, 0, + nss_cmd_getby_dp_callback, + dctx); + } else { + ret = check_cache(dctx, nctx, dctx->res, + SSS_DP_USER_AND_GROUP, name, 0, + nss_cmd_getby_dp_callback, + dctx); + } + if (ret != EOK) { + /* Anything but EOK means we should reenter the mainloop + * because we may be refreshing the cache + */ + return ret; + } + } + + /* One result found */ + DEBUG(6, ("Returning info for user [%s@%s]\n", name, dom->name)); + + return EOK; + } + + DEBUG(SSSDBG_MINOR_FAILURE, + ("No matching domain found for [%s], fail!\n", cmdctx->name)); + return ENOENT; +} +#endif + +static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx) +{ + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct sss_domain_info *dom = dctx->domain; + struct cli_ctx *cctx = cmdctx->cctx; + struct sysdb_ctx *sysdb; + struct nss_ctx *nctx; + int ret; + + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + + DEBUG(SSSDBG_TRACE_FUNC, ("Requesting info for [%s@%s]\n", cmdctx->secid, + dom->name)); + + sysdb = dom->sysdb; + if (sysdb == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Fatal: Sysdb CTX not found for this " \ + "domain!\n")); + return EIO; + } + + ret = sysdb_search_object_by_sid(cmdctx, sysdb, dom, cmdctx->secid, NULL, + &dctx->res); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to make request to our cache!\n")); + return EIO; + } + + if (dctx->res->count > 1) { + DEBUG(SSSDBG_FATAL_FAILURE, ("getbysid call returned more than one " \ + "result !?!\n")); + return ENOENT; + } + + if (dctx->res->count == 0 && !dctx->check_provider) { + DEBUG(2, ("No results for getbysid call.\n")); + + /* set negative cache only if not result of cache check */ + ret = sss_ncache_set_sid(nctx->ncache, false, cmdctx->secid); + if (ret != EOK) { + return ret; + } + + return ENOENT; + } + + /* if this is a caching provider (or if we haven't checked the cache + * yet) then verify that the cache is uptodate */ + if (dctx->check_provider) { + ret = check_cache(dctx, nctx, dctx->res, + SSS_DP_SECID, cmdctx->secid, 0, + nss_cmd_getby_dp_callback, + dctx); + if (ret != EOK) { + /* Anything but EOK means we should reenter the mainloop + * because we may be refreshing the cache + */ + return ret; + } + } + + /* One result found */ + DEBUG(SSSDBG_TRACE_FUNC, ("Returning info for sid [%s@%s]\n", cmdctx->secid, + dom->name)); + + return EOK; +} + +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_OBJECTCLASS); + if (el == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("Objectclass 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 fill_sid(struct sss_packet *packet, + enum sss_id_type id_type, + struct ldb_message *msg) +{ + int ret; + const char *sid_str; + struct sized_string sid; + uint8_t *body; + size_t blen; + + sid_str = ldb_msg_find_attr_as_string(msg, SYSDB_SID_STR, NULL); + if (sid_str == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Missing SID.\n")); + return EINVAL; + } + + to_sized_string(&sid, sid_str); + + ret = sss_packet_grow(packet, 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, &blen); + ((uint32_t *)body)[0] = 1; /* num results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + ((uint32_t *)body)[2] = id_type; + memcpy(&body[3*sizeof(uint32_t)], sid.str, sid.len); + + return EOK; +} + +static errno_t fill_name(struct sss_packet *packet, + struct sss_domain_info *dom, + enum sss_id_type id_type, + struct ldb_message *msg) +{ + int ret; + TALLOC_CTX *tmp_ctx = NULL; + const char *orig_name; + const char *cased_name; + const char *fq_name; + struct sized_string name; + bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames); + uint8_t *body; + size_t blen; + + orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + if (orig_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Missing name.\n")); + return EINVAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_new failed.\n")); + return ENOMEM; + } + + cased_name= sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive); + if (cased_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_get_cased_name failed.\n")); + ret = ENOMEM; + goto done; + } + + if (add_domain) { + fq_name = talloc_asprintf(tmp_ctx, dom->names->fq_fmt, cased_name, + dom->name); + if (fq_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_asprintf failed.\n")); + ret = ENOMEM; + goto done; + } + to_sized_string(&name, fq_name); + } else { + to_sized_string(&name, cased_name); + } + + ret = sss_packet_grow(packet, name.len + 3 * sizeof(uint32_t)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_packet_grow failed.\n")); + goto done; + } + + sss_packet_get_body(packet, &body, &blen); + ((uint32_t *)body)[0] = 1; /* num results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + ((uint32_t *)body)[2] = id_type; + memcpy(&body[3*sizeof(uint32_t)], name.str, name.len); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static errno_t fill_id(struct sss_packet *packet, + enum sss_id_type id_type, + struct ldb_message *msg) +{ + int ret; + uint8_t *body; + size_t blen; + uint64_t id; + + if (id_type == SSS_ID_TYPE_GID) { + id = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0); + } else { + id = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0); + } + + if (id == 0 || id >= UINT32_MAX) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid POSIX ID.\n")); + return EINVAL; + } + + 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, &blen); + ((uint32_t *)body)[0] = 1; /* num results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + ((uint32_t *)body)[2] = (uint32_t) id_type; + ((uint32_t *)body)[3] = (uint32_t) id; + + return EOK; +} + +static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx) +{ + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + enum sss_id_type id_type; + + if (dctx->res->count > 1) { + return EINVAL; + } else if (dctx->res->count == 0) { + return ENOENT; + } + + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return EFAULT; + } + + ret = find_sss_id_type(dctx->res->msgs[0], dctx->domain->mpg, &id_type); + if (ret != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("find_sss_id_type failed.\n")); + return ret; + } + + switch(cmdctx->cmd) { + case SSS_NSS_GETNAMEBYSID: + ret = fill_name(cctx->creq->out, + dctx->domain, + id_type, + dctx->res->msgs[0]); + break; + case SSS_NSS_GETIDBYSID: + ret = fill_id(cctx->creq->out, id_type, dctx->res->msgs[0]); + break; + case SSS_NSS_GETSIDBYNAME: + case SSS_NSS_GETSIDBYID: + ret = fill_sid(cctx->creq->out, id_type, dctx->res->msgs[0]); + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, ("Unsupported request type.\n")); + return EINVAL; + } + if (ret != EOK) { + return ret; + } + + sss_packet_set_error(cctx->creq->out, EOK); + sss_cmd_done(cctx, cmdctx); + return EOK; +} + +static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx) +{ + + struct tevent_req *req; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + const char *sid_str; + uint8_t *body; + size_t blen; + int ret; + struct nss_ctx *nctx; + enum idmap_error_code err; + uint8_t *bin_sid = NULL; + size_t bin_sid_length; + + if (cmd != SSS_NSS_GETNAMEBYSID && cmd != SSS_NSS_GETIDBYSID) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid command type [%d].\n", cmd)); + return EINVAL; + } + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + cmdctx->cmd = cmd; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + dctx->cmdctx = cmdctx; + + /* get SID to query */ + sss_packet_get_body(cctx->creq->in, &body, &blen); + + /* if not terminated fail */ + if (body[blen -1] != '\0') { + ret = EINVAL; + goto done; + } + + sid_str = (const char *) body; + + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + + /* If the body isn't a SID, fail */ + err = sss_idmap_sid_to_bin_sid(nctx->idmap_ctx, sid_str, + &bin_sid, &bin_sid_length); + talloc_free(bin_sid); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_idmap_sid_to_bin_sid failed for [%s].\n", + body)); + ret = EINVAL; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Running command [%d] with SID [%s].\n", + dctx->cmdctx->cmd, sid_str)); + + cmdctx->secid = talloc_strdup(cmdctx, sid_str); + if (cmdctx->secid == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("talloc_strdup failed.\n")); + ret = ENOMEM; + goto done; + } + + ret = responder_get_domain_by_id(cctx->rctx, cmdctx->secid, &dctx->domain); + if (ret == EAGAIN || ret == ENOENT) { + req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, NULL); + if (req == NULL) { + ret = ENOMEM; + } else { + dctx->rawname = sid_str; + tevent_req_set_callback(req, nss_cmd_getbyid_done, dctx); + ret = EAGAIN; + } + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("responder_get_domain_by_id failed.\n")); + goto done; + } + + DEBUG(4, ("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); + } + +done: + return nss_cmd_done(cmdctx, ret); +} + +static int nss_cmd_getsidbyname(struct cli_ctx *cctx) +{ + return nss_cmd_getbynam(SSS_NSS_GETSIDBYNAME, cctx); +} + +static int nss_cmd_getsidbyid(struct cli_ctx *cctx) +{ + return nss_cmd_getbyid(SSS_NSS_GETSIDBYID, cctx); +} + +static int nss_cmd_getnamebysid(struct cli_ctx *cctx) +{ + return nss_cmd_getbysid(SSS_NSS_GETNAMEBYSID, cctx); +} + +static int nss_cmd_getidbysid(struct cli_ctx *cctx) +{ + return nss_cmd_getbysid(SSS_NSS_GETIDBYSID, cctx); +} + struct cli_protocol_version *register_cli_protocol_version(void) { static struct cli_protocol_version nss_cli_protocol_version[] = { @@ -3523,6 +4463,10 @@ static struct sss_cmd_table nss_cmds[] = { {SSS_NSS_SETSERVENT, nss_cmd_setservent}, {SSS_NSS_GETSERVENT, nss_cmd_getservent}, {SSS_NSS_ENDSERVENT, nss_cmd_endservent}, + {SSS_NSS_GETSIDBYNAME, nss_cmd_getsidbyname}, + {SSS_NSS_GETSIDBYID, nss_cmd_getsidbyid}, + {SSS_NSS_GETNAMEBYSID, nss_cmd_getnamebysid}, + {SSS_NSS_GETIDBYSID, nss_cmd_getidbysid}, {SSS_CLI_NULL, NULL} }; diff --git a/src/responder/nss/nsssrv_private.h b/src/responder/nss/nsssrv_private.h index 77d8bb6..04f1330 100644 --- a/src/responder/nss/nsssrv_private.h +++ b/src/responder/nss/nsssrv_private.h @@ -32,6 +32,7 @@ struct nss_cmd_ctx { enum sss_cli_command cmd; char *name; uint32_t id; + char *secid; bool immediate; bool check_next; -- 1.7.7.6