From 499e4d63fe57875338777f0ac4455693307b4c56 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 30 Mar 2009 19:28:18 -0400 Subject: Add a more flexible way to parse and filter names. A new nss_parse_name function uses pcre to parse names, this makes it possible, in future, to make the filter user configurable. Add a new filter mechanism to filter out users that uses the negative cache by setting a permanet negative entry. Rework the entry points where the negative cache is checked for. --- server/responder/nss/nsssrv.c | 128 +++++- server/responder/nss/nsssrv.h | 8 + server/responder/nss/nsssrv_cmd.c | 923 +++++++++++++++++++++++++++----------- server/responder/nss/nsssrv_nc.c | 91 +++- server/responder/nss/nsssrv_nc.h | 15 +- 5 files changed, 885 insertions(+), 280 deletions(-) (limited to 'server/responder') diff --git a/server/responder/nss/nsssrv.c b/server/responder/nss/nsssrv.c index f5afc22f0..4f9713b17 100644 --- a/server/responder/nss/nsssrv.c +++ b/server/responder/nss/nsssrv.c @@ -118,9 +118,18 @@ static int service_reload(DBusMessage *message, struct sbus_conn_ctx *sconn) return service_pong(message, sconn); } -static int nss_get_config(struct nss_ctx *nctx, struct confdb_ctx *cdb) +static int nss_get_config(struct nss_ctx *nctx, + struct resp_ctx *rctx, + struct confdb_ctx *cdb) { - int ret; + TALLOC_CTX *tmpctx; + const char *domain, *name; + const char **domains; + char **filter_list; + int ret, num, i, j; + + tmpctx = talloc_new(nctx); + if (!tmpctx) return ENOMEM; ret = confdb_get_int(cdb, nctx, NSS_SRV_CONFIG, "EnumCacheTimeout", 120, @@ -137,10 +146,111 @@ static int nss_get_config(struct nss_ctx *nctx, struct confdb_ctx *cdb) &nctx->enum_cache_timeout); if (ret != EOK) goto done; + ret = confdb_get_param(cdb, nctx, NSS_SRV_CONFIG, + "filterUsers", &filter_list); + if (ret != EOK) goto done; + for (i = 0; filter_list[i]; i++) { + ret = nss_parse_name(tmpctx, nctx, filter_list[i], + &domain, &name); + if (ret != EOK) { + DEBUG(1, ("Invalid name in filterUsers list: [%s] (%d)\n", + filter_list[i], ret)); + continue; + } + if (domain) { + ret = nss_ncache_set_user(nctx->ncache, true, domain, name); + if (ret != EOK) { + DEBUG(1, ("Failed to store permanent user filter for [%s]" + " (%d [%s])\n", filter_list[i], + ret, strerror(ret))); + continue; + } + } else { + ret = btreemap_get_keys(tmpctx, rctx->domain_map, + (const void ***)&domains, &num); + if (ret != EOK) { + DEBUG(0, ("Unable to find domains!\n")); + return ret; + } + + for (j = 0; j < num; j++) { + ret = nss_ncache_set_user(nctx->ncache, + true, domains[j], name); + if (ret != EOK) { + DEBUG(1, ("Failed to store permanent user filter for" + " [%s:%s] (%d [%s])\n", + domains[j], filter_list[i], + ret, strerror(ret))); + continue; + } + } + } + } + talloc_free(filter_list); + + ret = confdb_get_param(cdb, nctx, NSS_SRV_CONFIG, + "filterGroups", &filter_list); + if (ret != EOK) goto done; + for (i = 0; filter_list[i]; i++) { + ret = nss_parse_name(tmpctx, nctx, filter_list[i], + &domain, &name); + if (ret != EOK) { + DEBUG(1, ("Invalid name in filterGroups list: [%s] (%d)\n", + filter_list[i], ret)); + continue; + } + if (domain) { + ret = nss_ncache_set_group(nctx->ncache, true, domain, name); + if (ret != EOK) { + DEBUG(1, ("Failed to store permanent group filter for" + " [%s] (%d [%s])\n", filter_list[i], + ret, strerror(ret))); + continue; + } + } else { + ret = btreemap_get_keys(tmpctx, rctx->domain_map, + (const void ***)&domains, &num); + if (ret != EOK) { + DEBUG(0, ("Unable to find domains!\n")); + return ret; + } + + for (j = 0; j < num; j++) { + ret = nss_ncache_set_group(nctx->ncache, + true, domains[j], name); + if (ret != EOK) { + DEBUG(1, ("Failed to store permanent group filter for" + " [%s:%s] (%d [%s])\n", + domains[j], filter_list[i], + ret, strerror(ret))); + continue; + } + } + } + } + talloc_free(filter_list); + done: + talloc_free(tmpctx); return ret; } +static void *nss_pcre_malloc(size_t size) +{ + return talloc_named_const(NULL, size, "nss_pcre_malloc"); +} + +static void nss_pcre_free(void *ctx) +{ + talloc_free(ctx); +} + +void nss_pcre_setup(void) +{ + pcre_malloc = nss_pcre_malloc; + pcre_free = nss_pcre_free; +} + int nss_process_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct confdb_ctx *cdb) @@ -150,18 +260,14 @@ int nss_process_init(TALLOC_CTX *mem_ctx, struct nss_ctx *nctx; int ret; + nss_pcre_setup(); + nctx = talloc_zero(mem_ctx, struct nss_ctx); if (!nctx) { DEBUG(0, ("fatal error initializing nss_ctx\n")); return ENOMEM; } - ret = nss_get_config(nctx, cdb); - if (ret != EOK) { - DEBUG(0, ("fatal error getting nss config\n")); - return ret; - } - ret = nss_ncache_init(nctx, &nctx->ncache); if (ret != EOK) { DEBUG(0, ("fatal error initializing negative cache\n")); @@ -183,6 +289,12 @@ int nss_process_init(TALLOC_CTX *mem_ctx, } nctx->rctx->pvt_ctx = nctx; + ret = nss_get_config(nctx, nctx->rctx, cdb); + if (ret != EOK) { + DEBUG(0, ("fatal error getting nss config\n")); + return ret; + } + DEBUG(1, ("NSS Initialization complete\n")); return EOK; diff --git a/server/responder/nss/nsssrv.h b/server/responder/nss/nsssrv.h index b2aee3826..093f795b4 100644 --- a/server/responder/nss/nsssrv.h +++ b/server/responder/nss/nsssrv.h @@ -34,6 +34,7 @@ #include "responder/common/responder_packet.h" #include "responder/common/responder_cmd.h" #include "responder/nss/nsssrv_nc.h" +#include #define NSS_SBUS_SERVICE_VERSION 0x0001 #define NSS_SBUS_SERVICE_NAME "nss" @@ -66,6 +67,8 @@ struct nss_ctx { struct getent_ctx *pctx; struct getent_ctx *gctx; + + pcre *parse_name_re; }; struct nss_packet; @@ -88,4 +91,9 @@ int nss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *memctx, struct sbus_method *get_nss_dp_methods(void); struct sss_cmd_table *get_nss_cmds(void); +int nss_parse_name(TALLOC_CTX *memctx, + struct nss_ctx *nctx, + const char *origname, + const char **domain, const char **name); + #endif /* __NSSSRV_H__ */ diff --git a/server/responder/nss/nsssrv_cmd.c b/server/responder/nss/nsssrv_cmd.c index f98de8e98..0a4703c93 100644 --- a/server/responder/nss/nsssrv_cmd.c +++ b/server/responder/nss/nsssrv_cmd.c @@ -36,7 +36,7 @@ struct nss_cmd_ctx { }; struct dom_ctx { - const char *domain; + struct sss_domain_info *domain; struct ldb_result *res; int cur; }; @@ -50,7 +50,6 @@ struct getent_ctx { struct nss_dom_ctx { struct nss_cmd_ctx *cmdctx; struct sss_domain_info *domain; - bool add_domain; bool check_provider; /* cache results */ @@ -80,53 +79,90 @@ static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err) return; \ } while(0) -static bool nss_add_domain(struct sss_domain_info *info) +int nss_parse_name(TALLOC_CTX *memctx, + struct nss_ctx *nctx, + const char *origname, + const char **domain, const char **name) { - /* FIXME: we want to actually retrieve this bool from some conf */ - return (strcasecmp(info->name, "LOCAL") != 0); -} + pcre *re = nctx->parse_name_re; + const char *errstr; + const char *pattern = "(?[^@]+)@?(?[^@]*$)"; + const char *result; + int options; + int errpos; + int errval; + int ovec[30]; + int origlen; + int ret, strnum; + + options = PCRE_DUPNAMES | PCRE_EXTENDED; + + if (!re) { + re = pcre_compile2(pattern, options, &errval, &errstr, &errpos, NULL); + if (!re) { + DEBUG(1, ("Invalid Regular Expression pattern at position %d. (%d [%s])\n", + errpos, errstr)); + return EFAULT; + } + } + nctx->parse_name_re = re; + + origlen = strlen(origname); + options = PCRE_NOTEMPTY; + + ret = pcre_exec(re, NULL, origname, origlen, 0, options, ovec, 30); + if (ret < 0) { + DEBUG(2, ("PCRE Matching error, %d\n", ret)); + return EINVAL; + } -static int nss_parse_name(struct nss_dom_ctx *dctx, const char *fullname) -{ - struct nss_cmd_ctx *cmdctx = dctx->cmdctx; - struct sss_domain_info *info; - struct btreemap *domain_map; - char *delim; - char *domain; + if (ret == 0) { + DEBUG(1, ("Too many matches, the pattern is invalid.\n")); + } + + strnum = ret; - /* TODO: add list of names to filter to configuration */ - if (strcmp(fullname, "root") == 0) return ECANCELED; + result = NULL; + ret = pcre_get_named_substring(re, origname, ovec, strnum, "name", &result); + if (ret < 0 || !result) { + DEBUG(2, ("Name not found!\n")); + return EINVAL; + } + *name = talloc_steal(memctx, result); + if (!*name) return ENOMEM; - domain_map = cmdctx->cctx->rctx->domain_map; - if ((delim = strchr(fullname, NSS_DOMAIN_DELIM)) != NULL) { - domain = delim+1; + result = NULL; + ret = pcre_get_named_substring(re, origname, ovec, strnum, "domain", &result); + if (ret < 0 || !result) { + DEBUG(4, ("Domain not provided!\n")); + *domain = NULL; } else { - domain = cmdctx->cctx->rctx->default_domain; + /* ignore "" string */ + if (*result) { + *domain = talloc_steal(memctx, result); + if (!*domain) return ENOMEM; + } else { + *domain = NULL; + } } + return EOK; +} + +static int nss_dom_ctx_init(struct nss_dom_ctx *dctx, + struct btreemap *domain_map, const char *domain) +{ + struct sss_domain_info *info; + /* Check for registered domain */ info = btreemap_get_value(domain_map, (void *)domain); if (!info) { - /* No such domain was registered. Return EINVAL. - * TODO: alternative approach? - * Alternatively, we could simply fail down to - * below, treating the entire construct as the - * full name if the domain is unspecified. - */ return EINVAL; } dctx->domain = info; - dctx->add_domain = nss_add_domain(info); - dctx->check_provider = strcasecmp(domain, "LOCAL"); - - if (delim) { - cmdctx->name = talloc_strndup(cmdctx, fullname, delim-fullname); - } else { - cmdctx->name = talloc_strdup(cmdctx, fullname); - } - if (!cmdctx->name) return ENOMEM; + dctx->check_provider = (info->provider != NULL); return EOK; } @@ -136,8 +172,9 @@ static int nss_parse_name(struct nss_dom_ctx *dctx, const char *fullname) ***************************************************************************/ static int fill_pwent(struct sss_packet *packet, - bool add_domain, - const char *domain, + struct sss_domain_info *info, + struct nss_ctx *nctx, + bool filter_users, struct ldb_message **msgs, int count) { @@ -153,6 +190,8 @@ static int fill_pwent(struct sss_packet *packet, size_t s1, s2, s3, s4; size_t dom_len = 0; int i, ret, num; + bool add_domain = info->fqnames; + const char *domain = info->name; if (add_domain) dom_len = strlen(domain) +1; @@ -165,9 +204,6 @@ static int fill_pwent(struct sss_packet *packet, msg = msgs[i]; name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); - gecos = ldb_msg_find_attr_as_string(msg, SYSDB_GECOS, NULL); - homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL); - shell = ldb_msg_find_attr_as_string(msg, SYSDB_SHELL, NULL); uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0); gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0); @@ -176,6 +212,22 @@ static int fill_pwent(struct sss_packet *packet, name?name:"", (unsigned long long int)uid)); continue; } + + if (filter_users) { + ret = nss_ncache_check_user(nctx->ncache, + nctx->neg_timeout, + domain, name); + if (ret == EEXIST) { + DEBUG(4, ("User [%s@%s] filtered out! (negative cache)\n", + name, domain)); + continue; + } + } + + gecos = ldb_msg_find_attr_as_string(msg, SYSDB_GECOS, NULL); + homedir = ldb_msg_find_attr_as_string(msg, SYSDB_HOMEDIR, NULL); + shell = ldb_msg_find_attr_as_string(msg, SYSDB_SHELL, NULL); + if (!gecos) gecos = ""; if (!homedir) homedir = "/"; if (!shell) shell = ""; @@ -244,6 +296,18 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status, nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + /* one less to go */ + cmdctx->nr--; + + /* check if another callback already replied */ + if (cmdctx->done) { + /* now check if this is the last callback */ + if (cmdctx->nr == 0) { + /* ok we are really done with this request */ + goto done; + } + } + if (status != LDB_SUCCESS) { ret = nss_cmd_send_error(cmdctx, status); if (ret != EOK) { @@ -278,29 +342,11 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status, } } - if (call_provider && res->count == 0) { - /* check negative cache before potentially expensive remote call */ - ret = nss_ncache_check_user(nctx->ncache, - nctx->neg_timeout, - dctx->domain->name, cmdctx->name); - switch (ret) { - case EEXIST: - DEBUG(2, ("Negative cache hit for getpwnam call\n")); - res->count = 0; - call_provider = false; - neghit = true; - break; - case ENOENT: - break; - default: - DEBUG(4,("Error processing ncache request: %d [%s]\n", - ret, strerror(ret))); - } - ret = EOK; - } - if (call_provider) { + /* yet one more call to go */ + cmdctx->nr++; + /* dont loop forever :-) */ dctx->check_provider = false; timeout = SSS_CLI_SOCKET_TIMEOUT/2; @@ -328,12 +374,16 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status, switch (res->count) { case 0: + if (cmdctx->nr != 0) { + /* nothing to do */ + return; + } DEBUG(2, ("No results for getpwnam call\n")); /* set negative cache only if not result of cache check */ if (!neghit) { - ret = nss_ncache_set_user(nctx->ncache, + ret = nss_ncache_set_user(nctx->ncache, false, dctx->domain->name, cmdctx->name); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); @@ -360,8 +410,8 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status, NSS_CMD_FATAL_ERROR(cctx); } ret = fill_pwent(cctx->creq->out, - dctx->add_domain, - dctx->domain->name, + dctx->domain, + nctx, false, res->msgs, res->count); sss_packet_set_error(cctx->creq->out, ret); @@ -376,6 +426,10 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status, } done: + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } sss_cmd_done(cctx, cmdctx); } @@ -427,10 +481,16 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx) { struct nss_cmd_ctx *cmdctx; struct nss_dom_ctx *dctx; + struct sss_domain_info *info; + struct nss_ctx *nctx; + const char **domains; + const char *domname; const char *rawname; uint8_t *body; size_t blen; - int ret; + int ret, num, i; + + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); if (!cmdctx) { @@ -438,38 +498,135 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx) } cmdctx->cctx = cctx; - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - dctx->cmdctx = cmdctx; - /* get user name to query */ sss_packet_get_body(cctx->creq->in, &body, &blen); /* if not terminated fail */ if (body[blen -1] != '\0') { talloc_free(cmdctx); - return EINVAL; + ret = EINVAL; + goto done; } rawname = (const char *)body; - ret = nss_parse_name(dctx, rawname); + domname = NULL; + ret = nss_parse_name(cmdctx, nctx, rawname, + &domname, &cmdctx->name); if (ret != EOK) { DEBUG(2, ("Invalid name received [%s]\n", rawname)); goto done; } + DEBUG(4, ("Requesting info for [%s] from [%s]\n", - cmdctx->name, dctx->domain->name)); + cmdctx->name, domname?domname:"")); + + if (domname) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_user(nctx->ncache, nctx->neg_timeout, + domname, cmdctx->name); + if (ret != ENOENT) { + DEBUG(3, ("User [%s] does not exist! (negative cache)\n", + rawname)); + ret = ENOENT; + goto done; + } - ret = sysdb_getpwnam(cmdctx, cctx->rctx->sysdb, - dctx->domain, cmdctx->name, - nss_cmd_getpwnam_callback, dctx); + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + dctx->cmdctx = cmdctx; - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); + ret = nss_dom_ctx_init(dctx, cctx->rctx->domain_map, domname); + if (ret != EOK) { + DEBUG(2, ("Invalid domain name received [%s]\n", domname)); + goto done; + } + + cmdctx->nr = 1; + + ret = sysdb_getpwnam(cmdctx, cctx->rctx->sysdb, + dctx->domain, cmdctx->name, + nss_cmd_getpwnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + } + + } else { + + dctx = NULL; + domains = NULL; + num = 0; + /* get domains list */ + ret = btreemap_get_keys(cmdctx, cctx->rctx->domain_map, + (const void ***)&domains, &num); + if (ret != EOK) goto done; + + cmdctx->nr = 0; + + for (i = 0; i < num; i++) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_user(nctx->ncache, nctx->neg_timeout, + domains[i], cmdctx->name); + if (ret != ENOENT) { + DEBUG(3, ("User [%s] does not exist! (neg cache)\n", + rawname)); + continue; + } + + info = btreemap_get_value(cctx->rctx->domain_map, domains[i]); + + /* skip domains that require FQnames */ + if (info->fqnames) continue; + + cmdctx->nr++; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + + dctx->cmdctx = cmdctx; + dctx->domain = info; + dctx->check_provider = (info->provider != NULL); + + DEBUG(4, ("Requesting info for [%s@%s]\n", + cmdctx->name, dctx->domain->name)); + + ret = sysdb_getpwnam(cmdctx, cctx->rctx->sysdb, + dctx->domain, cmdctx->name, + nss_cmd_getpwnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + /* shutdown ? */ + goto done; + } + } + } + + if (cmdctx->nr == 0) { + ret = ENOENT; } done: if (ret != EOK) { - ret = nss_cmd_send_error(cmdctx, ret); + if (ret == ENOENT) { + /* we do not have any entry to return */ + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret == EOK) { + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + } + } + if (ret != EOK) { + ret = nss_cmd_send_error(cmdctx, ret); + } if (ret == EOK) { sss_cmd_done(cctx, cmdctx); } @@ -545,27 +702,6 @@ static void nss_cmd_getpwuid_callback(void *ptr, int status, } } - if (call_provider && res->count == 0) { - /* check negative cache before potentially expensive remote call */ - ret = nss_ncache_check_uid(nctx->ncache, - nctx->neg_timeout, - cmdctx->id); - switch (ret) { - case EEXIST: - DEBUG(2, ("Negative cache hit for getpwuid call\n")); - res->count = 0; - call_provider = false; - neghit = true; - break; - case ENOENT: - break; - default: - DEBUG(4,("Error processing ncache request: %d [%s]\n", - ret, strerror(ret))); - } - ret = EOK; - } - if (call_provider) { /* yet one more call to go */ @@ -607,7 +743,7 @@ static void nss_cmd_getpwuid_callback(void *ptr, int status, /* set negative cache only if not result of cache check */ if (!neghit) { - ret = nss_ncache_set_uid(nctx->ncache, cmdctx->id); + ret = nss_ncache_set_uid(nctx->ncache, false, cmdctx->id); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } @@ -634,8 +770,8 @@ static void nss_cmd_getpwuid_callback(void *ptr, int status, } ret = fill_pwent(cctx->creq->out, - dctx->add_domain, - dctx->domain->name, + dctx->domain, + nctx, true, res->msgs, res->count); sss_packet_set_error(cctx->creq->out, ret); @@ -709,11 +845,14 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) struct nss_cmd_ctx *cmdctx; struct nss_dom_ctx *dctx; struct sss_domain_info *info; + struct nss_ctx *nctx; const char **domains; uint8_t *body; size_t blen; int i, num, ret; + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); if (!cmdctx) { return ENOMEM; @@ -724,7 +863,8 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) sss_packet_get_body(cctx->creq->in, &body, &blen); if (blen != sizeof(uint32_t)) { - return EINVAL; + ret = EINVAL; + goto done; } cmdctx->id = *((uint32_t *)body); @@ -736,21 +876,36 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) /* get domains list */ ret = btreemap_get_keys(cmdctx, cctx->rctx->domain_map, (const void ***)&domains, &num); - if (ret != EOK) - return ret; + if (ret != EOK) { + goto done; + } - cmdctx->nr = num; + cmdctx->nr = 0; for (i = 0; i < num; i++) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, + cmdctx->id); + if (ret != ENOENT) { + DEBUG(3, ("Uid [%lu] does not exist! (negative cache)\n", + (unsigned long)cmdctx->id)); + continue; + } + info = btreemap_get_value(cctx->rctx->domain_map, domains[i]); + cmdctx->nr++; + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; + if (!dctx) { + ret = ENOMEM; + goto done; + } dctx->cmdctx = cmdctx; dctx->domain = info; - dctx->add_domain = nss_add_domain(info); - dctx->check_provider = strcasecmp(domains[i], "LOCAL"); + dctx->check_provider = (info->provider != NULL); DEBUG(4, ("Requesting info for [%lu@%s]\n", cmdctx->id, dctx->domain->name)); @@ -761,13 +916,34 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) if (ret != EOK) { DEBUG(1, ("Failed to make request to our cache!\n")); /* shutdown ? */ + goto done; + } + } - ret = nss_cmd_send_error(cmdctx, ret); + if (cmdctx->nr == 0) { + ret = ENOENT; + } + +done: + if (ret != EOK) { + if (ret == ENOENT) { + /* we do not have any entry to return */ + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); if (ret == EOK) { - sss_cmd_done(cctx, cmdctx); + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ } - return ret; } + if (ret != EOK) { + ret = nss_cmd_send_error(cmdctx, ret); + } + if (ret == EOK) { + sss_cmd_done(cctx, cmdctx); + } + return ret; } return EOK; @@ -823,11 +999,7 @@ static void nss_cmd_setpwent_callback(void *ptr, int status, pctx->doms = talloc_realloc(pctx, pctx->doms, struct dom_ctx, pctx->num +1); if (!pctx->doms) NSS_CMD_FATAL_ERROR(cctx); - if (dctx->add_domain) { - pctx->doms[pctx->num].domain = dctx->domain->name; - } else { - pctx->doms[pctx->num].domain = NULL; - } + pctx->doms[pctx->num].domain = dctx->domain; pctx->doms[pctx->num].res = talloc_steal(pctx->doms, res); pctx->doms[pctx->num].cur = 0; @@ -954,12 +1126,11 @@ static int nss_cmd_setpwent_ext(struct cli_ctx *cctx, bool immediate) dctx->cmdctx = cmdctx; dctx->domain = info; - dctx->add_domain = nss_add_domain(info); if (cached) { dctx->check_provider = false; } else { - dctx->check_provider = strcasecmp(domains[i], "LOCAL"); + dctx->check_provider = (info->provider != NULL); } if (dctx->check_provider) { @@ -1013,8 +1184,6 @@ static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) struct getent_ctx *pctx; struct ldb_message **msgs = NULL; struct dom_ctx *pdom; - const char *dom = NULL; - bool add = false; int n = 0; nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); @@ -1038,11 +1207,8 @@ static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) msgs = &(pdom->res->msgs[pdom->cur]); pdom->cur += n; - add = (pdom->domain != NULL); - dom = pdom->domain; - done: - return fill_pwent(cctx->creq->out, add, dom, msgs, n); + return fill_pwent(cctx->creq->out, pdom->domain, nctx, true, msgs, n); } /* used only if a process calls getpwent() without first calling setpwent() @@ -1088,9 +1254,9 @@ static int nss_cmd_getpwent(struct cli_ctx *cctx) nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); /* see if we need to trigger an implicit setpwent() */ - if (nctx->gctx == NULL) { - nctx->gctx = talloc_zero(nctx, struct getent_ctx); - if (!nctx->gctx) return ENOMEM; + if (nctx->pctx == NULL) { + nctx->pctx = talloc_zero(nctx, struct getent_ctx); + if (!nctx->pctx) return ENOMEM; return nss_cmd_setpwent_ext(cctx, true); } @@ -1134,8 +1300,9 @@ done: ***************************************************************************/ static int fill_grent(struct sss_packet *packet, - bool add_domain, - const char *domain, + struct sss_domain_info *info, + struct nss_ctx *nctx, + bool filter_groups, struct ldb_message **msgs, int count) { @@ -1147,8 +1314,11 @@ static int fill_grent(struct sss_packet *packet, size_t rsize, rp, blen, mnump; int i, j, ret, num, memnum; bool get_members; + bool skip_members; size_t dom_len = 0; size_t name_len; + bool add_domain = info->fqnames; + const char *domain = info->name; if (add_domain) dom_len = strlen(domain) +1; @@ -1182,6 +1352,19 @@ static int fill_grent(struct sss_packet *packet, goto done; } + skip_members = false; + + if (filter_groups) { + ret = nss_ncache_check_group(nctx->ncache, + nctx->neg_timeout, domain, name); + if (ret == EEXIST) { + DEBUG(4, ("Group [%s@%s] filtered out! (negative cache)\n", + name, domain)); + skip_members = true; + continue; + } + } + /* fill in gid and name and set pointer for number of members */ name_len = strlen(name)+1; rsize = 2 * sizeof(uint32_t) + name_len +2; @@ -1253,6 +1436,8 @@ static int fill_grent(struct sss_packet *packet, continue; } + if (skip_members) continue; + if (!get_members) { DEBUG(1, ("Wrong object found on stack! Aborting\n")); num = 0; @@ -1270,6 +1455,14 @@ static int fill_grent(struct sss_packet *packet, goto done; } + ret = nss_ncache_check_user(nctx->ncache, + nctx->neg_timeout, domain, name); + if (ret == EEXIST) { + DEBUG(4, ("User [%s@%s] filtered out! (negative cache)\n", + name, domain)); + continue; + } + rsize = strlen(name) + 1; if (add_domain) { name_len = rsize; @@ -1334,13 +1527,24 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status, nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + /* one less to go */ + cmdctx->nr--; + + /* check if another callback already replied */ + if (cmdctx->done) { + /* now check if this is the last callback */ + if (cmdctx->nr == 0) { + /* ok we are really done with this request */ + goto done; + } + } + if (status != LDB_SUCCESS) { ret = nss_cmd_send_error(cmdctx, status); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } - sss_cmd_done(cctx, cmdctx); - return; + goto done; } if (dctx->check_provider) { @@ -1360,29 +1564,11 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status, } } - if (call_provider && res->count == 0) { - /* check negative cache before potentially expensive remote call */ - ret = nss_ncache_check_group(nctx->ncache, - nctx->neg_timeout, - dctx->domain->name, cmdctx->name); - switch (ret) { - case EEXIST: - DEBUG(2, ("Negative cache hit for getgrnam call\n")); - res->count = 0; - call_provider = false; - neghit = true; - break; - case ENOENT: - break; - default: - DEBUG(4,("Error processing ncache request: %d [%s]\n", - ret, strerror(ret))); - } - ret = EOK; - } - if (call_provider) { + /* yet one more call to go */ + cmdctx->nr++; + /* dont loop forever :-) */ dctx->check_provider = false; timeout = SSS_CLI_SOCKET_TIMEOUT/2; @@ -1411,12 +1597,16 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status, switch (res->count) { case 0: + if (cmdctx->nr != 0) { + /* nothing to do */ + return; + } DEBUG(2, ("No results for getgrnam call\n")); /* set negative cache only if not result of cache check */ if (!neghit) { - ret = nss_ncache_set_group(nctx->ncache, + ret = nss_ncache_set_group(nctx->ncache, false, dctx->domain->name, cmdctx->name); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); @@ -1447,13 +1637,17 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status, } ret = fill_grent(cctx->creq->out, - dctx->add_domain, - dctx->domain->name, + dctx->domain, + nctx, false, res->msgs, res->count); sss_packet_set_error(cctx->creq->out, ret); } done: + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } sss_cmd_done(cctx, cmdctx); } @@ -1504,10 +1698,16 @@ static int nss_cmd_getgrnam(struct cli_ctx *cctx) { struct nss_cmd_ctx *cmdctx; struct nss_dom_ctx *dctx; + struct sss_domain_info *info; + struct nss_ctx *nctx; + const char **domains; + const char *domname; const char *rawname; uint8_t *body; size_t blen; - int ret; + int ret, num, i; + + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); if (!cmdctx) { @@ -1515,37 +1715,133 @@ static int nss_cmd_getgrnam(struct cli_ctx *cctx) } cmdctx->cctx = cctx; - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - dctx->cmdctx = cmdctx; - /* get user name to query */ sss_packet_get_body(cctx->creq->in, &body, &blen); /* if not terminated fail */ if (body[blen -1] != '\0') { - talloc_free(cmdctx); - return EINVAL; + ret = EINVAL; + goto done; } rawname = (const char *)body; - ret = nss_parse_name(dctx, rawname); + domname = NULL; + ret = nss_parse_name(cmdctx, nctx, rawname, + &domname, &cmdctx->name); if (ret != EOK) { DEBUG(2, ("Invalid name received [%s]\n", rawname)); goto done; } + DEBUG(4, ("Requesting info for [%s] from [%s]\n", - cmdctx->name, dctx->domain->name)); + cmdctx->name, domname?domname:"")); + + if (domname) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_group(nctx->ncache, nctx->neg_timeout, + domname, cmdctx->name); + if (ret != ENOENT) { + DEBUG(3, ("Group [%s] does not exist! (negative cache)\n", + rawname)); + ret = ENOENT; + goto done; + } + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + dctx->cmdctx = cmdctx; - ret = sysdb_getgrnam(cmdctx, cctx->rctx->sysdb, - dctx->domain, cmdctx->name, - nss_cmd_getgrnam_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); + ret = nss_dom_ctx_init(dctx, cctx->rctx->domain_map, domname); + if (ret != EOK) { + DEBUG(2, ("Invalid domain name received [%s]\n", domname)); + goto done; + } + + cmdctx->nr = 1; + + ret = sysdb_getgrnam(cmdctx, cctx->rctx->sysdb, + dctx->domain, cmdctx->name, + nss_cmd_getgrnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + } + + } else { + + dctx = NULL; + domains = NULL; + num = 0; + /* get domains list */ + ret = btreemap_get_keys(cmdctx, cctx->rctx->domain_map, + (const void ***)&domains, &num); + if (ret != EOK) goto done; + + cmdctx->nr = 0; + + for (i = 0; i < num; i++) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_group(nctx->ncache, nctx->neg_timeout, + domains[i], cmdctx->name); + if (ret != ENOENT) { + DEBUG(3, ("Group [%s] does not exist! (negative cache)\n", + rawname)); + continue; + } + + info = btreemap_get_value(cctx->rctx->domain_map, domains[i]); + + /* skip domains that require FQnames */ + if (info->fqnames) continue; + + cmdctx->nr++; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + + dctx->cmdctx = cmdctx; + dctx->domain = info; + dctx->check_provider = (info->provider != NULL); + + DEBUG(4, ("Requesting info for [%s@%s]\n", + cmdctx->name, dctx->domain->name)); + + ret = sysdb_getgrnam(cmdctx, cctx->rctx->sysdb, + dctx->domain, cmdctx->name, + nss_cmd_getgrnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + /* shutdown ? */ + goto done; + } + } + } + + if (cmdctx->nr == 0) { + ret = ENOENT; } done: if (ret != EOK) { - ret = nss_cmd_send_error(cmdctx, ret); + if (ret == ENOENT) { + /* we do not have any entry to return */ + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret == EOK) { + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + } + } + if (ret != EOK) { + ret = nss_cmd_send_error(cmdctx, ret); + } if (ret == EOK) { sss_cmd_done(cctx, cmdctx); } @@ -1612,27 +1908,6 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status, } } - if (call_provider && res->count == 0) { - /* check negative cache before potentially expensive remote call */ - ret = nss_ncache_check_gid(nctx->ncache, - nctx->neg_timeout, - cmdctx->id); - switch (ret) { - case EEXIST: - DEBUG(2, ("Negative cache hit for getgrgid call\n")); - res->count = 0; - call_provider = false; - neghit = true; - break; - case ENOENT: - break; - default: - DEBUG(4,("Error processing ncache request: %d [%s]\n", - ret, strerror(ret))); - } - ret = EOK; - } - if (call_provider) { /* yet one more call to go */ @@ -1674,7 +1949,7 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status, /* set negative cache only if not result of cache check */ if (!neghit) { - ret = nss_ncache_set_gid(nctx->ncache, cmdctx->id); + ret = nss_ncache_set_gid(nctx->ncache, false, cmdctx->id); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } @@ -1704,8 +1979,8 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status, } ret = fill_grent(cctx->creq->out, - dctx->add_domain, - dctx->domain->name, + dctx->domain, + nctx, true, res->msgs, res->count); sss_packet_set_error(cctx->creq->out, ret); } @@ -1766,11 +2041,14 @@ static int nss_cmd_getgrgid(struct cli_ctx *cctx) struct nss_cmd_ctx *cmdctx; struct nss_dom_ctx *dctx; struct sss_domain_info *info; + struct nss_ctx *nctx; const char **domains; uint8_t *body; size_t blen; int i, num, ret; + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); if (!cmdctx) { return ENOMEM; @@ -1781,7 +2059,8 @@ static int nss_cmd_getgrgid(struct cli_ctx *cctx) sss_packet_get_body(cctx->creq->in, &body, &blen); if (blen != sizeof(uint32_t)) { - return EINVAL; + ret = EINVAL; + goto done; } cmdctx->id = *((uint32_t *)body); @@ -1794,21 +2073,35 @@ static int nss_cmd_getgrgid(struct cli_ctx *cctx) ret = btreemap_get_keys(cmdctx, cctx->rctx->domain_map, (const void ***)&domains, &num); if (ret != EOK) { - return ret; + goto done; } - cmdctx->nr = num; + cmdctx->nr = 0; for (i = 0; i < num; i++) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_gid(nctx->ncache, nctx->neg_timeout, + cmdctx->id); + if (ret != ENOENT) { + DEBUG(3, ("Gid [%lu] does not exist! (negative cache)\n", + (unsigned long)cmdctx->id)); + continue; + } + info = btreemap_get_value(cctx->rctx->domain_map, domains[i]); + cmdctx->nr++; + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; + if (!dctx) { + ret = ENOMEM; + goto done; + } dctx->cmdctx = cmdctx; dctx->domain = info; - dctx->add_domain = nss_add_domain(info); - dctx->check_provider = strcasecmp(domains[i], "LOCAL"); + dctx->check_provider = (info->provider != NULL); DEBUG(4, ("Requesting info for [%lu@%s]\n", cmdctx->id, dctx->domain->name)); @@ -1819,13 +2112,34 @@ static int nss_cmd_getgrgid(struct cli_ctx *cctx) if (ret != EOK) { DEBUG(1, ("Failed to make request to our cache!\n")); /* shutdown ? */ + goto done; + } + } - ret = nss_cmd_send_error(cmdctx, ret); + if (cmdctx->nr == 0) { + ret = ENOENT; + } + +done: + if (ret != EOK) { + if (ret == ENOENT) { + /* we do not have any entry to return */ + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); if (ret == EOK) { - sss_cmd_done(cctx, cmdctx); + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ } - return ret; } + if (ret != EOK) { + ret = nss_cmd_send_error(cmdctx, ret); + } + if (ret == EOK) { + sss_cmd_done(cctx, cmdctx); + } + return ret; } return EOK; @@ -1882,11 +2196,7 @@ static void nss_cmd_setgrent_callback(void *ptr, int status, gctx->doms = talloc_realloc(gctx, gctx->doms, struct dom_ctx, gctx->num +1); if (!gctx->doms) NSS_CMD_FATAL_ERROR(cctx); - if (dctx->add_domain) { - gctx->doms[gctx->num].domain = dctx->domain->name; - } else { - gctx->doms[gctx->num].domain = NULL; - } + gctx->doms[gctx->num].domain = dctx->domain; gctx->doms[gctx->num].res = talloc_steal(gctx->doms, res); gctx->doms[gctx->num].cur = 0; @@ -2013,12 +2323,11 @@ static int nss_cmd_setgrent_ext(struct cli_ctx *cctx, bool immediate) dctx->cmdctx = cmdctx; dctx->domain = info; - dctx->add_domain = nss_add_domain(info); if (cached) { dctx->check_provider = false; } else { - dctx->check_provider = strcasecmp(domains[i], "LOCAL"); + dctx->check_provider = (info->provider != NULL); } if (dctx->check_provider) { @@ -2070,8 +2379,6 @@ static int nss_cmd_retgrent(struct cli_ctx *cctx, int num) struct getent_ctx *gctx; struct ldb_message **msgs = NULL; struct dom_ctx *gdom; - const char *dom = NULL; - bool add = false; int n = 0; nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); @@ -2095,11 +2402,8 @@ static int nss_cmd_retgrent(struct cli_ctx *cctx, int num) msgs = &(gdom->res->msgs[gdom->cur]); gdom->cur += n; - add = (gdom->domain != NULL); - dom = gdom->domain; - done: - return fill_grent(cctx->creq->out, add, dom, msgs, n); + return fill_grent(cctx->creq->out, gdom->domain, nctx, true, msgs, n); } /* used only if a process calls getpwent() without first calling setpwent() @@ -2268,8 +2572,8 @@ static void nss_cmd_getinitgr_callback(uint16_t err_maj, uint32_t err_min, static void nss_cmd_getinit_callback(void *ptr, int status, struct ldb_result *res); -static void nss_cmd_getinitnam_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr) +static void nss_cmd_getinitnam_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr) { struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); struct nss_cmd_ctx *cmdctx = dctx->cmdctx; @@ -2328,6 +2632,18 @@ static void nss_cmd_getinit_callback(void *ptr, int status, nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); + /* one less to go */ + cmdctx->nr--; + + /* check if another callback already replied */ + if (cmdctx->done) { + /* now check if this is the last callback */ + if (cmdctx->nr == 0) { + /* ok we are really done with this request */ + goto done; + } + } + if (status != LDB_SUCCESS) { ret = nss_cmd_send_error(cmdctx, status); if (ret != EOK) { @@ -2342,7 +2658,7 @@ static void nss_cmd_getinit_callback(void *ptr, int status, call_provider = true; break; - default: + case 1: timeout = nctx->cache_timeout; lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], @@ -2350,32 +2666,23 @@ static void nss_cmd_getinit_callback(void *ptr, int status, if (lastUpdate + timeout < time(NULL)) { call_provider = true; } - } - } - - if (call_provider && res->count == 0) { - /* check negative cache before potentially expensive remote call */ - ret = nss_ncache_check_user(nctx->ncache, - nctx->neg_timeout, - dctx->domain->name, cmdctx->name); - switch (ret) { - case EEXIST: - DEBUG(2, ("Negative cache hit for initgr call\n")); - res->count = 0; - call_provider = false; - neghit = false; - break; - case ENOENT: break; + default: - DEBUG(4,("Error processing ncache request: %d [%s]\n", - ret, strerror(ret))); + DEBUG(1, ("getpwnam call returned more than one result !?!\n")); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; } - ret = EOK; } if (call_provider) { + /* yet one more call to go */ + cmdctx->nr++; + /* dont loop forever :-) */ dctx->check_provider = false; timeout = SSS_CLI_SOCKET_TIMEOUT/2; @@ -2386,7 +2693,7 @@ static void nss_cmd_getinit_callback(void *ptr, int status, } ret = nss_dp_send_acct_req(cctx->rctx, cmdctx, - nss_cmd_getinitnam_callback, dctx, + nss_cmd_getinitnam_dp_callback, dctx, timeout, dctx->domain->name, NSS_DP_USER, cmdctx->name, 0); if (ret != EOK) { @@ -2404,12 +2711,16 @@ static void nss_cmd_getinit_callback(void *ptr, int status, switch (res->count) { case 0: + if (cmdctx->nr != 0) { + /* nothing to do */ + return; + } DEBUG(2, ("No results for initgroups call\n")); /* set negative cache only if not result of cache check */ if (!neghit) { - ret = nss_ncache_set_user(nctx->ncache, + ret = nss_ncache_set_user(nctx->ncache, false, dctx->domain->name, cmdctx->name); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); @@ -2456,6 +2767,10 @@ static void nss_cmd_getinit_callback(void *ptr, int status, } done: + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } sss_cmd_done(cctx, cmdctx); } @@ -2464,10 +2779,16 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx) { struct nss_cmd_ctx *cmdctx; struct nss_dom_ctx *dctx; + struct sss_domain_info *info; + struct nss_ctx *nctx; + const char **domains; + const char *domname; const char *rawname; uint8_t *body; size_t blen; - int ret; + int ret, num, i; + + nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx); cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); if (!cmdctx) { @@ -2475,19 +2796,18 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx) } cmdctx->cctx = cctx; - dctx = talloc_zero(cmdctx, struct nss_dom_ctx); - if (!dctx) return ENOMEM; - dctx->cmdctx = cmdctx; - /* get user name to query */ sss_packet_get_body(cctx->creq->in, &body, &blen); /* if not terminated fail */ if (body[blen -1] != '\0') { - return EINVAL; + ret = EINVAL; + goto done; } rawname = (const char *)body; - ret = nss_parse_name(dctx, rawname); + domname = NULL; + ret = nss_parse_name(cmdctx, nctx, rawname, + &domname, &cmdctx->name); if (ret != EOK) { DEBUG(2, ("Invalid name received [%s]\n", rawname)); goto done; @@ -2495,16 +2815,113 @@ static int nss_cmd_initgroups(struct cli_ctx *cctx) DEBUG(4, ("Requesting info for [%s] from [%s]\n", cmdctx->name, dctx->domain->name)); - ret = sysdb_getpwnam(cmdctx, cctx->rctx->sysdb, - dctx->domain, cmdctx->name, - nss_cmd_getinit_callback, dctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); + if (domname) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_user(nctx->ncache, nctx->neg_timeout, + domname, cmdctx->name); + if (ret != ENOENT) { + DEBUG(3, ("User [%s] does not exist! (negative cache)\n", + rawname)); + ret = ENOENT; + goto done; + } + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + dctx->cmdctx = cmdctx; + + ret = nss_dom_ctx_init(dctx, cctx->rctx->domain_map, domname); + if (ret != EOK) { + DEBUG(2, ("Invalid domain name received [%s]\n", domname)); + goto done; + } + + cmdctx->nr = 1; + + ret = sysdb_getpwnam(cmdctx, cctx->rctx->sysdb, + dctx->domain, cmdctx->name, + nss_cmd_getinit_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + } + + } else { + + dctx = NULL; + domains = NULL; + num = 0; + /* get domains list */ + ret = btreemap_get_keys(cmdctx, cctx->rctx->domain_map, + (const void ***)&domains, &num); + if (ret != EOK) goto done; + + cmdctx->nr = 0; + + for (i = 0; i < num; i++) { + /* verify this user has not yet been negatively cached, + * or has been permanently filtered */ + ret = nss_ncache_check_user(nctx->ncache, nctx->neg_timeout, + domains[i], cmdctx->name); + if (ret != ENOENT) { + DEBUG(3, ("User does not exist! (neg cache)\n")); + continue; + } + + info = btreemap_get_value(cctx->rctx->domain_map, domains[i]); + + /* skip domains that require FQnames */ + if (info->fqnames) continue; + + cmdctx->nr++; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) { + ret = ENOMEM; + goto done; + } + + dctx->cmdctx = cmdctx; + dctx->domain = info; + dctx->check_provider = (info->provider != NULL); + + DEBUG(4, ("Requesting info for [%s@%s]\n", + cmdctx->name, dctx->domain->name)); + + ret = sysdb_getpwnam(cmdctx, cctx->rctx->sysdb, + dctx->domain, cmdctx->name, + nss_cmd_getinit_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + /* shutdown ? */ + goto done; + } + } + } + + if (cmdctx->nr == 0) { + ret = ENOENT; } done: if (ret != EOK) { - ret = nss_cmd_send_error(cmdctx, ret); + if (ret == ENOENT) { + /* we do not have any entry to return */ + ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t), + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret == EOK) { + sss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + } + } + if (ret != EOK) { + ret = nss_cmd_send_error(cmdctx, ret); + } if (ret == EOK) { sss_cmd_done(cctx, cmdctx); } diff --git a/server/responder/nss/nsssrv_nc.c b/server/responder/nss/nsssrv_nc.c index 74f8369b8..d6a808dc5 100644 --- a/server/responder/nss/nsssrv_nc.c +++ b/server/responder/nss/nsssrv_nc.c @@ -24,10 +24,11 @@ #include #include "tdb.h" -#define NC_USER_PREFIX "NCUSER" -#define NC_GROUP_PREFIX "NCGROUP" -#define NC_UID_PREFIX "NCUID" -#define NC_GID_PREFIX "NCGID" +#define NC_ENTRY_PREFIX "NCE/" +#define NC_USER_PREFIX NC_ENTRY_PREFIX"USER" +#define NC_GROUP_PREFIX NC_ENTRY_PREFIX"GROUP" +#define NC_UID_PREFIX NC_ENTRY_PREFIX"UID" +#define NC_GID_PREFIX NC_ENTRY_PREFIX"GID" struct nss_nc_ctx { struct tdb_context *tdb; @@ -78,6 +79,12 @@ static int nss_ncache_check_str(struct nss_nc_ctx *ctx, char *str, int ttl) goto done; } + if (ttl == -1) { + /* a negative ttl means: never expires */ + ret = EEXIST; + goto done; + } + errno = 0; timestamp = strtoull((const char *)data.dptr, &ep, 0); if (errno != 0 || *ep != '\0') { @@ -86,6 +93,12 @@ static int nss_ncache_check_str(struct nss_nc_ctx *ctx, char *str, int ttl) goto done; } + if (timestamp == 0) { + /* a 0 timestamp means this is a permanent entry */ + ret = EEXIST; + goto done; + } + if (timestamp + ttl > time(NULL)) { /* still valid */ ret = EEXIST; @@ -104,7 +117,8 @@ done: return ret; } -static int nss_ncache_set_str(struct nss_nc_ctx *ctx, char *str) +static int nss_ncache_set_str(struct nss_nc_ctx *ctx, + char *str, bool permanent) { TDB_DATA key; TDB_DATA data; @@ -114,7 +128,12 @@ static int nss_ncache_set_str(struct nss_nc_ctx *ctx, char *str) ret = string_to_tdb_data(str, &key); if (ret != EOK) return ret; - timest = talloc_asprintf(ctx, "%llu", (unsigned long long int)time(NULL)); + if (permanent) { + timest = talloc_strdup(ctx, "0"); + } else { + timest = talloc_asprintf(ctx, "%llu", + (unsigned long long int)time(NULL)); + } if (!timest) return ENOMEM; ret = string_to_tdb_data(timest, &data); @@ -194,7 +213,7 @@ int nss_ncache_check_gid(struct nss_nc_ctx *ctx, int ttl, gid_t gid) return ret; } -int nss_ncache_set_user(struct nss_nc_ctx *ctx, +int nss_ncache_set_user(struct nss_nc_ctx *ctx, bool permanent, const char *domain, const char *name) { char *str; @@ -205,13 +224,13 @@ int nss_ncache_set_user(struct nss_nc_ctx *ctx, str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name); if (!str) return ENOMEM; - ret = nss_ncache_set_str(ctx, str); + ret = nss_ncache_set_str(ctx, str, permanent); talloc_free(str); return ret; } -int nss_ncache_set_group(struct nss_nc_ctx *ctx, +int nss_ncache_set_group(struct nss_nc_ctx *ctx, bool permanent, const char *domain, const char *name) { char *str; @@ -222,13 +241,13 @@ int nss_ncache_set_group(struct nss_nc_ctx *ctx, str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name); if (!str) return ENOMEM; - ret = nss_ncache_set_str(ctx, str); + ret = nss_ncache_set_str(ctx, str, permanent); talloc_free(str); return ret; } -int nss_ncache_set_uid(struct nss_nc_ctx *ctx, uid_t uid) +int nss_ncache_set_uid(struct nss_nc_ctx *ctx, bool permanent, uid_t uid) { char *str; int ret; @@ -236,13 +255,13 @@ int nss_ncache_set_uid(struct nss_nc_ctx *ctx, uid_t uid) str = talloc_asprintf(ctx, "%s/%u", NC_UID_PREFIX, uid); if (!str) return ENOMEM; - ret = nss_ncache_set_str(ctx, str); + ret = nss_ncache_set_str(ctx, str, permanent); talloc_free(str); return ret; } -int nss_ncache_set_gid(struct nss_nc_ctx *ctx, gid_t gid) +int nss_ncache_set_gid(struct nss_nc_ctx *ctx, bool permanent, gid_t gid) { char *str; int ret; @@ -250,9 +269,53 @@ int nss_ncache_set_gid(struct nss_nc_ctx *ctx, gid_t gid) str = talloc_asprintf(ctx, "%s/%u", NC_GID_PREFIX, gid); if (!str) return ENOMEM; - ret = nss_ncache_set_str(ctx, str); + ret = nss_ncache_set_str(ctx, str, permanent); talloc_free(str); return ret; } +static int delete_permanent(struct tdb_context *tdb, + TDB_DATA key, TDB_DATA data, void *state) +{ + unsigned long long int timestamp; + bool remove = false; + char *ep; + + if (strncmp((char *)key.dptr, + NC_ENTRY_PREFIX, sizeof(NC_ENTRY_PREFIX)) != 0) { + /* not interested in this key */ + return 0; + } + + errno = 0; + timestamp = strtoull((const char *)data.dptr, &ep, 0); + if (errno != 0 || *ep != '\0') { + /* Malformed entry, remove it */ + remove = true; + goto done; + } + + if (timestamp == 0) { + /* a 0 timestamp means this is a permanent entry */ + remove = true; + } + +done: + if (remove) { + return tdb_delete(tdb, key); + } + + return 0; +} + +int nss_ncache_reset_permament(struct nss_nc_ctx *ctx) +{ + int ret; + + ret = tdb_traverse(ctx->tdb, delete_permanent, NULL); + if (ret < 0) + return EIO; + + return EOK; +} diff --git a/server/responder/nss/nsssrv_nc.h b/server/responder/nss/nsssrv_nc.h index acc9170c6..c0fa197c2 100644 --- a/server/responder/nss/nsssrv_nc.h +++ b/server/responder/nss/nsssrv_nc.h @@ -35,12 +35,17 @@ int nss_ncache_check_group(struct nss_nc_ctx *ctx, int ttl, int nss_ncache_check_uid(struct nss_nc_ctx *ctx, int ttl, uid_t uid); int nss_ncache_check_gid(struct nss_nc_ctx *ctx, int ttl, gid_t gid); -/* add a new neg-cache entry setting the timestamp to "now" */ -int nss_ncache_set_user(struct nss_nc_ctx *ctx, +/* add a new neg-cache entry setting the timestamp to "now" unless + * "permanent" is set to true, in which case the timestamps is set to 0 + * and the negative cache never expires (used to permanently filter out + * users and groups) */ +int nss_ncache_set_user(struct nss_nc_ctx *ctx, bool permanent, const char *domain, const char *name); -int nss_ncache_set_group(struct nss_nc_ctx *ctx, +int nss_ncache_set_group(struct nss_nc_ctx *ctx, bool permanent, const char *domain, const char *name); -int nss_ncache_set_uid(struct nss_nc_ctx *ctx, uid_t uid); -int nss_ncache_set_gid(struct nss_nc_ctx *ctx, gid_t gid); +int nss_ncache_set_uid(struct nss_nc_ctx *ctx, bool permanent, uid_t uid); +int nss_ncache_set_gid(struct nss_nc_ctx *ctx, bool permanent, gid_t gid); + +int nss_ncache_reset_permament(struct nss_nc_ctx *ctx); #endif /* _NSS_NEG_CACHE_H_ */ -- cgit