diff options
-rw-r--r-- | server/confdb/confdb.c | 15 | ||||
-rw-r--r-- | server/confdb/confdb.h | 4 | ||||
-rw-r--r-- | server/db/sysdb.c | 4 | ||||
-rw-r--r-- | server/nss/nsssrv.c | 60 | ||||
-rw-r--r-- | server/nss/nsssrv.h | 12 | ||||
-rw-r--r-- | server/nss/nsssrv_cmd.c | 1670 | ||||
-rw-r--r-- | server/nss/nsssrv_dp.c | 42 | ||||
-rw-r--r-- | server/providers/data_provider.h | 1 | ||||
-rw-r--r-- | server/providers/proxy.c | 220 |
9 files changed, 1588 insertions, 440 deletions
diff --git a/server/confdb/confdb.c b/server/confdb/confdb.c index 28f25176..95df0367 100644 --- a/server/confdb/confdb.c +++ b/server/confdb/confdb.c @@ -635,18 +635,3 @@ done: talloc_free(tmp_ctx); return ret; } - -int confdb_get_domain_basedn(struct confdb_ctx *cdb, - TALLOC_CTX *mem_ctx, - const char *domain, - char **basedn) -{ - char *section; - int ret; - - section = talloc_asprintf(mem_ctx, "config/domains/%s", domain); - ret = confdb_get_string(cdb, mem_ctx, section, "basedn", "cn=local", basedn); - - talloc_free(section); - return ret; -} diff --git a/server/confdb/confdb.h b/server/confdb/confdb.h index e6dd0d47..d117d043 100644 --- a/server/confdb/confdb.h +++ b/server/confdb/confdb.h @@ -52,7 +52,3 @@ int confdb_init(TALLOC_CTX *mem_ctx, int confdb_get_domains(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx, char ***values); -int confdb_get_domain_basedn(struct confdb_ctx *cdb, - TALLOC_CTX *mem_ctx, - const char *domain, - char **basedn); diff --git a/server/db/sysdb.c b/server/db/sysdb.c index f910110c..5a9d8f25 100644 --- a/server/db/sysdb.c +++ b/server/db/sysdb.c @@ -1247,11 +1247,11 @@ int sysdb_posix_store_group(TALLOC_CTX *memctx, switch(res->count) { case 0: flags = LDB_FLAG_MOD_ADD; - DEBUG(3, ("Adding new entry\n")); + DEBUG(7, ("Adding new entry\n")); break; case 1: flags = LDB_FLAG_MOD_REPLACE; - DEBUG(3, ("Replacing existing entry\n")); + DEBUG(7, ("Replacing existing entry\n")); break; default: DEBUG(0, ("Cache DB corrupted, base search returned %d results\n", diff --git a/server/nss/nsssrv.c b/server/nss/nsssrv.c index 5a574b41..b6191cce 100644 --- a/server/nss/nsssrv.c +++ b/server/nss/nsssrv.c @@ -388,10 +388,12 @@ static int _domain_comparator(const void *key1, const void *key2) static int nss_init_domains(struct nss_ctx *nctx) { + char *path; char **domains; - char *basedn; + char *provider; TALLOC_CTX *tmp_ctx; - int ret, i; + struct nss_domain_info *info; + int ret, i, c; int retval; tmp_ctx = talloc_new(nctx); @@ -402,15 +404,59 @@ static int nss_init_domains(struct nss_ctx *nctx) } i = 0; + c = 0; while (domains[i] != NULL) { DEBUG(3, ("Adding domain %s to the map\n", domains[i])); - /* Look up the appropriate basedn for this domain */ - ret = confdb_get_domain_basedn(nctx->cdb, tmp_ctx, domains[i], &basedn); - DEBUG(3, ("BaseDN: %s\n", basedn)); - btreemap_set_value(nctx, &nctx->domain_map, domains[i], basedn, _domain_comparator); + + path = talloc_asprintf(tmp_ctx, "config/domains/%s", domains[i]); + if (!path) { + retval = ENOMEM; + goto done; + } + + /* alloc on tmp_ctx, it will be stolen by btreemap_set_value */ + info = talloc_zero(tmp_ctx, struct nss_domain_info); + if (!info) { + retval = ENOMEM; + goto done; + } + + /* Build the basedn for this domain */ + info->basedn = talloc_asprintf(info, SYSDB_DOM_BASE, domains[i]); + DEBUG(3, ("BaseDN: %s\n", info->basedn)); + + ret = confdb_get_int(nctx->cdb, tmp_ctx, path, + "enumerate", false, &(info->enumerate)); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch enumerate for [%s]!\n", domains[i])); + } + + ret = confdb_get_bool(nctx->cdb, tmp_ctx, path, + "legacy", false, &(info->legacy)); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch legacy for [%s]!\n", domains[i])); + } + + ret = confdb_get_string(nctx->cdb, tmp_ctx, path, "provider", + NULL, &provider); + if (ret != EOK) { + DEBUG(0, ("Failed to fetch provider for [%s]!\n", domains[i])); + } + if (provider) info->has_provider = true; + + ret = btreemap_set_value(nctx, &nctx->domain_map, + domains[i], info, + _domain_comparator); + if (ret != EOK) { + DEBUG(1, ("Failed to store domain info, aborting!\n")); + retval = ret; + goto done; + } + i++; + c++; } - if (i == 0) { + if (c == 0) { /* No domains configured! * Note: this should never happen, since LOCAL should * always be configured */ diff --git a/server/nss/nsssrv.h b/server/nss/nsssrv.h index 795b8fa7..2352a505 100644 --- a/server/nss/nsssrv.h +++ b/server/nss/nsssrv.h @@ -41,6 +41,10 @@ #define NSS_DOMAIN_DELIM '@' #endif +#define NSS_ENUM_USERS 0x01 +#define NSS_ENUM_GROUPS 0x02 +#define NSS_ENUM_ALL 0x03 + struct sysdb_ctx; struct getent_ctx; @@ -69,6 +73,13 @@ struct cli_ctx { struct getent_ctx *gctx; }; +struct nss_domain_info { + char *basedn; + int enumerate; + bool has_provider; + bool legacy; +}; + struct nss_packet; struct cli_request { @@ -97,6 +108,7 @@ int nss_cmd_execute(struct cli_ctx *cctx); /* from nsssrv_dp.c */ #define NSS_DP_USER 1 #define NSS_DP_GROUP 2 +#define NSS_DP_INITGROUPS 3 typedef void (*nss_dp_callback_t)(uint16_t err_maj, uint32_t err_min, const char *err_msg, void *ptr); diff --git a/server/nss/nsssrv_cmd.c b/server/nss/nsssrv_cmd.c index dd750c28..b16d27c2 100644 --- a/server/nss/nsssrv_cmd.c +++ b/server/nss/nsssrv_cmd.c @@ -27,10 +27,12 @@ struct nss_cmd_ctx { struct cli_ctx *cctx; - const char *domain; const char *name; uid_t id; - bool check_expiration; + + bool immediate; + bool done; + int nr; }; struct getent_ctx { @@ -40,24 +42,31 @@ struct getent_ctx { int grp_cur; }; +struct nss_dom_ctx { + struct nss_cmd_ctx *cmdctx; + const char *domain; + bool check_provider; + bool legacy; +}; + struct nss_cmd_table { enum sss_nss_command cmd; int (*fn)(struct cli_ctx *cctx); }; -static void nss_cmd_done(struct nss_cmd_ctx *nctx) +static void nss_cmd_done(struct nss_cmd_ctx *cmdctx) { /* now that the packet is in place, unlock queue * making the event writable */ - EVENT_FD_WRITEABLE(nctx->cctx->cfde); + EVENT_FD_WRITEABLE(cmdctx->cctx->cfde); /* free all request related data through the talloc hierarchy */ - talloc_free(nctx); + talloc_free(cmdctx); } -static int nss_cmd_send_error(struct nss_cmd_ctx *nctx, int err) +static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err) { - struct cli_ctx *cctx = nctx->cctx; + struct cli_ctx *cctx = cmdctx->cctx; int ret; /* create response packet */ @@ -69,8 +78,6 @@ static int nss_cmd_send_error(struct nss_cmd_ctx *nctx, int err) } nss_packet_set_error(cctx->creq->out, err); - - nss_cmd_done(nctx); return EOK; } @@ -80,62 +87,63 @@ static int nss_cmd_send_error(struct nss_cmd_ctx *nctx, int err) return; \ } while(0) -static int nss_check_domain(struct ldb_dn *dn, - struct btreemap *domain_map, - const char *def_domain, const char *domain) +static int nss_parse_name(struct nss_dom_ctx *dctx, const char *fullname) { - /* FIXME: it would be better to use ldb_dn_compare() */ - const char *lineardn; - const char *basedn; - const char *key; - int blen, llen; - - lineardn = ldb_dn_get_linearized(dn); - if (!lineardn || !*lineardn) { - DEBUG(4, ("Invalid DN (empty)\n")); - return EINVAL; - } - llen = strlen(lineardn); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct nss_ctx *nctx = cmdctx->cctx->nctx; + struct nss_domain_info *info; + struct btreemap *domain_map; + char *delim; + char *domain; - /* if domain is NULL check if it is the default domain or LOCAL */ - if (domain) { - key = domain; - } else if (def_domain) { - key = def_domain; - } else { - key = "LOCAL"; - } + domain_map = nctx->domain_map; - basedn = btreemap_get_value(domain_map, key); - if (!basedn) { - DEBUG(4, ("Domain (%s) not found in map!\n", domain)); + if ((delim = strchr(fullname, NSS_DOMAIN_DELIM)) != NULL) { + domain = delim+1; + } else { + domain = nctx->default_domain; + } + + /* 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; } - blen = strlen(basedn); - if (blen < llen) { - if (strcasecmp(basedn, &lineardn[llen-blen]) == 0) - return EOK; - } + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; - DEBUG(4, ("DN %s, does not match domain %s (or %s (or LOCAL))\n", - ldb_dn_get_linearized(dn), domain, def_domain)); + dctx->domain = talloc_strdup(dctx, domain); + if (!dctx->domain) return ENOMEM; - return EINVAL; + if (delim) { + cmdctx->name = talloc_strndup(cmdctx, fullname, delim-fullname); + } else { + cmdctx->name = talloc_strdup(cmdctx, fullname); + } + if (!cmdctx->name) return ENOMEM; + + return EOK; } static int nss_cmd_get_version(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; uint8_t *body; size_t blen; int ret; - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; + cmdctx->cctx = cctx; /* create response packet */ ret = nss_packet_new(cctx->creq, sizeof(uint32_t), @@ -147,7 +155,7 @@ static int nss_cmd_get_version(struct cli_ctx *cctx) nss_packet_get_body(cctx->creq->out, &body, &blen); ((uint32_t *)body)[0] = SSS_NSS_VERSION; - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); return EOK; } @@ -230,74 +238,83 @@ done: return EOK; } -static void nss_cmd_getpwnam_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr); -static void nss_cmd_getpwuid_callback(uint16_t err_maj, uint32_t err_min, - const char *err_msg, void *ptr); +static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); -static void nss_cmd_getpw_callback(void *ptr, int status, +static void nss_cmd_getpwnam_callback(void *ptr, int status, struct ldb_result *res) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; - nss_dp_callback_t callback_fn; + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; int timeout; uint64_t lastUpdate; uint8_t *body; size_t blen; - const char *domain; + bool call_provider = false; int ret; if (status != LDB_SUCCESS) { - ret = nss_cmd_send_error(nctx, status); + ret = nss_cmd_send_error(cmdctx, status); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } - return; + goto done; } - if (nctx->name) { - callback_fn = &nss_cmd_getpwnam_callback; - } else { - callback_fn = &nss_cmd_getpwuid_callback; - } + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; - if (nctx->domain) { - domain = nctx->domain; - } else { - domain = "*"; + case 1: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + break; + + default: + 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; + } } - if (res->count == 0 && nctx->check_expiration) { + if (call_provider) { /* dont loop forever :-) */ - nctx->check_expiration = false; + dctx->check_provider = false; timeout = SSS_NSS_SOCKET_TIMEOUT/2; - ret = nss_dp_send_acct_req(cctx->nctx, nctx, callback_fn, nctx, - timeout, domain, NSS_DP_USER, - nctx->name, nctx->id); + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getpwnam_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_USER, + cmdctx->name, 0); if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)", + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", ret, strerror(ret))); - ret = nss_cmd_send_error(nctx, ret); - } - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; } - return; } - if (res->count != 1) { - if (res->count > 1) { - /* FIXME: when multiple domains are configured this is possible. - * Add logic to select which result to return */ - DEBUG(1, ("getpwnam call returned more than one result !?!\n")); - } - if (res->count == 0) { - DEBUG(2, ("No results for getpwnam call\n")); - } + switch (res->count) { + case 0: + + DEBUG(2, ("No results for getpwnam call\n")); + ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), nss_packet_get_cmd(cctx->creq->in), &cctx->creq->out); @@ -307,100 +324,40 @@ static void nss_cmd_getpw_callback(void *ptr, int status, nss_packet_get_body(cctx->creq->out, &body, &blen); ((uint32_t *)body)[0] = 0; /* 0 results */ ((uint32_t *)body)[1] = 0; /* reserved */ - goto done; - } - - if (nctx->check_expiration) { - timeout = nctx->cctx->nctx->cache_timeout; - - lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_LAST_UPDATE, 0); - if (lastUpdate + timeout < time(NULL)) { - - /* dont loop forever :-) */ - nctx->check_expiration = false; - timeout = SSS_NSS_SOCKET_TIMEOUT/2; - - ret = nss_dp_send_acct_req(cctx->nctx, nctx, callback_fn, nctx, - timeout, domain, NSS_DP_USER, - nctx->name, nctx->id); - if (ret != EOK) { - DEBUG(3, ("Failed to dispatch request: %d(%s)", - ret, strerror(ret))); - ret = nss_cmd_send_error(nctx, ret); - } - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } + break; - return; - } - } - - if (nctx->name) { - /* before returning results check if they match their domain */ - ret = nss_check_domain(res->msgs[0]->dn, cctx->nctx->domain_map, - cctx->nctx->default_domain, nctx->domain); - if (ret != EOK) { - ret = nss_cmd_send_error(nctx, ret); - } + case 1: + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } - } - - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); - } - ret = fill_pwent(cctx->creq->out, res->msgs, res->count); - nss_packet_set_error(cctx->creq->out, ret); - -done: - nss_cmd_done(nctx); -} - -static int nss_parse_name(TALLOC_CTX *memctx, - const char *fullname, - struct btreemap *domain_map, - const char **domain, const char **name) { - char *delim; - struct btreemap *node; - int ret; + ret = fill_pwent(cctx->creq->out, res->msgs, res->count); + nss_packet_set_error(cctx->creq->out, ret); - if ((delim = strchr(fullname, NSS_DOMAIN_DELIM)) != NULL) { + break; - /* Check for registered domain */ - ret = btreemap_search_key(domain_map, (void *)(delim+1), &node); - if (ret != BTREEMAP_FOUND) { - /* 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; + default: + 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); } - - *name = talloc_strndup(memctx, fullname, delim-fullname); - *domain = talloc_strdup(memctx, delim+1); - } - else { - *name = talloc_strdup(memctx, fullname); - *domain = NULL; } - return EOK; +done: + nss_cmd_done(cmdctx); } -static void nss_cmd_getpwnam_callback(uint16_t err_maj, uint32_t err_min, +static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min, const char *err_msg, void *ptr) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; int ret; if (err_maj) { @@ -410,72 +367,216 @@ static void nss_cmd_getpwnam_callback(uint16_t err_maj, uint32_t err_min, (unsigned int)err_maj, (unsigned int)err_min, err_msg)); } - ret = sysdb_getpwnam(nctx, cctx->ev, cctx->nctx->sysdb, - nctx->domain, nctx->name, - nss_cmd_getpw_callback, nctx); + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getpwnam_callback, dctx); if (ret != EOK) { DEBUG(1, ("Failed to make request to our cache!\n")); - ret = nss_cmd_send_error(nctx, ret); + ret = nss_cmd_send_error(cmdctx, ret); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } + nss_cmd_done(cmdctx); } } static int nss_cmd_getpwnam(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; uint8_t *body; size_t blen; int ret; - nctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; - nctx->check_expiration = true; + cmdctx->cctx = cctx; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + dctx->cmdctx = cmdctx; /* get user name to query */ nss_packet_get_body(cctx->creq->in, &body, &blen); /* if not terminated fail */ if (body[blen -1] != '\0') { - talloc_free(nctx); + talloc_free(cmdctx); return EINVAL; } - ret = nss_parse_name(nctx, (const char *)body, - cctx->nctx->domain_map, - &nctx->domain, &nctx->name); + ret = nss_parse_name(dctx, (const char *)body); if (ret != EOK) { DEBUG(1, ("Invalid name received\n")); - talloc_free(nctx); + talloc_free(cmdctx); return ret; } DEBUG(4, ("Requesting info for [%s] from [%s]\n", - nctx->name, nctx->domain?nctx->domain:"all domains")); + cmdctx->name, dctx->domain)); - ret = sysdb_getpwnam(nctx, cctx->ev, cctx->nctx->sysdb, - nctx->domain, nctx->name, - nss_cmd_getpw_callback, nctx); + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getpwnam_callback, dctx); if (ret != EOK) { DEBUG(1, ("Failed to make request to our cache!\n")); - ret = nss_cmd_send_error(nctx, ret); - if (ret != EOK) { - return ret; + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); } + return ret; } return EOK; } -static void nss_cmd_getpwuid_callback(uint16_t err_maj, uint32_t err_min, +static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +static void nss_cmd_getpwuid_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; + uint8_t *body; + size_t blen; + bool call_provider = false; + int ret; + + /* 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); + } + goto done; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + case 1: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + break; + + default: + DEBUG(1, ("getpwuid call returned more than one result !?!\n")); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + } + + if (call_provider) { + + /* yet one more call to go */ + cmdctx->nr++; + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_NSS_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getpwuid_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_USER, + NULL, cmdctx->id); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + return; + } + + switch (res->count) { + case 0: + if (cmdctx->nr != 0) { + /* nothing to do */ + return; + } + + DEBUG(2, ("No results for getpwuid call\n")); + + ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + case 1: + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + ret = fill_pwent(cctx->creq->out, res->msgs, res->count); + nss_packet_set_error(cctx->creq->out, ret); + + break; + + default: + 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); + } + } + +done: + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min, const char *err_msg, void *ptr) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; int ret; if (err_maj) { @@ -485,32 +586,40 @@ static void nss_cmd_getpwuid_callback(uint16_t err_maj, uint32_t err_min, (unsigned int)err_maj, (unsigned int)err_min, err_msg)); } - ret = sysdb_getpwuid(nctx, cctx->ev, cctx->nctx->sysdb, - nctx->domain, nctx->id, - nss_cmd_getpw_callback, nctx); + ret = sysdb_getpwuid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getpwuid_callback, dctx); if (ret != EOK) { DEBUG(1, ("Failed to make request to our cache!\n")); - ret = nss_cmd_send_error(nctx, ret); + ret = nss_cmd_send_error(cmdctx, ret); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } + nss_cmd_done(cmdctx); } } static int nss_cmd_getpwuid(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + struct nss_domain_info *info; + const char **domains; uint8_t *body; size_t blen; - int ret; + int i, num, ret; - nctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; - nctx->check_expiration = true; + cmdctx->cctx = cctx; /* get uid to query */ nss_packet_get_body(cctx->creq->in, &body, &blen); @@ -519,21 +628,46 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) return EINVAL; } - nctx->id = (uid_t)*((uint64_t *)body); + cmdctx->id = (uid_t)*((uint64_t *)body); /* FIXME: Just ask all backends for now, until we check for ranges */ - nctx->domain = NULL; + dctx = NULL; + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); - DEBUG(4, ("Requesting info for [%lu]@[%s]\n", nctx->id, nctx->domain)); + cmdctx->nr = num; - ret = sysdb_getpwuid(nctx, cctx->ev, cctx->nctx->sysdb, - nctx->domain, nctx->id, - nss_cmd_getpw_callback, nctx); - if (ret != EOK) { - DEBUG(1, ("Failed to make request to our cache!\n")); + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; - ret = nss_cmd_send_error(nctx, ret); + + DEBUG(4, ("Requesting info for [%lu@%s]\n", + cmdctx->id, dctx->domain)); + + ret = sysdb_getpwuid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getpwuid_callback, dctx); if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + /* shutdown ? */ + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } return ret; } } @@ -546,19 +680,71 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) * and we also block on setpwent() for the full time needed * to retrieve the data. And endpwent() frees all the data. * Next steps are: - * - use and nsssrv wide cache with data already structured + * - use an nsssrv wide cache with data already structured * so that it can be immediately returned (see nscd way) * - use mutexes so that setpwent() can return immediately * even if the data is still being fetched * - make getpwent() wait on the mutex */ +static void nss_cmd_getpwent_callback(void *ptr, int status, + struct ldb_result *res); + static void nss_cmd_setpwent_callback(void *ptr, int status, - struct ldb_result *res) + struct ldb_result *res) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; struct getent_ctx *gctx = cctx->gctx; - int ret; + struct ldb_result *store = gctx->pwds; + int i, j, c, ret; + + cmdctx->nr--; + + if (cmdctx->done) { + /* do not reply until all domain searches are done */ + if (cmdctx->nr != 0) return; + else goto done; + } + + if (status != LDB_SUCCESS) { + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_packet_set_error(cctx->creq->out, status); + cmdctx->done = true; + return; + } + + if (store) { + c = store->count + res->count; + store->msgs = talloc_realloc(store, store->msgs, + struct ldb_message *, c); + if (!store->msgs) NSS_CMD_FATAL_ERROR(cctx); + + for (i = store->count, j = 0; i < c; i++, j++) { + store->msgs[i] = talloc_steal(store->msgs, res->msgs[j]); + if (!store->msgs[i]) NSS_CMD_FATAL_ERROR(cctx); + } + store->count = c; + talloc_free(res); + } else { + gctx->pwds = talloc_steal(gctx, res); + } + + /* do not reply until all domain searches are done */ + if (cmdctx->nr) return; + + if (cmdctx->immediate) { + /* this was a getpwent call w/o setpwent, + * return immediately one result */ + nss_cmd_getpwent_callback(ptr, status, res); + + return; + } /* create response packet */ ret = nss_packet_new(cctx->creq, 0, @@ -568,35 +754,61 @@ static void nss_cmd_setpwent_callback(void *ptr, int status, NSS_CMD_FATAL_ERROR(cctx); } - if (status != LDB_SUCCESS) { - nss_packet_set_error(cctx->creq->out, status); - goto done; +done: + nss_cmd_done(cmdctx); +} + +static void nss_cmd_setpw_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; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); } - gctx->pwds = talloc_steal(gctx, res); + ret = sysdb_enumpwent(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setpwent_callback, cmdctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); -done: - nss_cmd_done(nctx); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } } -static int nss_cmd_setpwent(struct cli_ctx *cctx) +static int nss_cmd_setpwent_ext(struct cli_ctx *cctx, bool immediate) { - struct nss_cmd_ctx *nctx; + struct nss_domain_info *info; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; struct getent_ctx *gctx; - int ret; + const char **domains; + int timeout; + int i, ret, num; - DEBUG(4, ("Requesting info for all accounts\n")); + DEBUG(4, ("Requesting info for all users\n")); - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; + cmdctx->cctx = cctx; if (cctx->gctx == NULL) { gctx = talloc_zero(cctx, struct getent_ctx); if (!gctx) { - talloc_free(nctx); + talloc_free(cmdctx); return ENOMEM; } cctx->gctx = gctx; @@ -607,12 +819,79 @@ static int nss_cmd_setpwent(struct cli_ctx *cctx) cctx->gctx->pwd_cur = 0; } - ret = sysdb_enumpwent(nctx, cctx->ev, cctx->nctx->sysdb, - nss_cmd_setpwent_callback, nctx); + cmdctx->immediate = immediate; + + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); + + /* check if enumeration is enabled in any domain */ + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); + + if ((info->enumerate & NSS_ENUM_USERS) == 0) { + continue; + } + + /* TODO: enabled, check if we have a recent cached enumeration */ + + /* ok no cache, go and ask the backend to enumerate */ + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + if (dctx->check_provider) { + timeout = SSS_NSS_SOCKET_TIMEOUT/(i+2); + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_setpw_dp_callback, dctx, + timeout, domains[i], NSS_DP_USER, + NULL, 0); + } else { + ret = sysdb_enumpwent(dctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setpwent_callback, cmdctx); + } + if (ret != EOK) { + /* FIXME: shutdown ? */ + DEBUG(1, ("Failed to send enumeration request for domain [%s]!\n", + domains[i])); + continue; + } + + /* number of replies to wait for before setpwent is done */ + cmdctx->nr++; + } + + if (cmdctx->nr == 0) { + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + nss_packet_set_error(cctx->creq->out, ret); + nss_cmd_done(cmdctx); + return EOK; + } return ret; } +static int nss_cmd_setpwent(struct cli_ctx *cctx) +{ + return nss_cmd_setpwent_ext(cctx, false); +} + + static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) { struct getent_ctx *gctx = cctx->gctx; @@ -629,12 +908,12 @@ static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) } /* used only if a process calls getpwent() without first calling setpwent() - * in this case we basically trigger an implicit setpwent() */ + */ static void nss_cmd_getpwent_callback(void *ptr, int status, struct ldb_result *res) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; struct getent_ctx *gctx = cctx->gctx; uint8_t *body; size_t blen; @@ -667,12 +946,12 @@ static void nss_cmd_getpwent_callback(void *ptr, int status, nss_packet_set_error(cctx->creq->out, ret); done: - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); } static int nss_cmd_getpwent(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; struct getent_ctx *gctx; uint8_t *body; size_t blen; @@ -688,29 +967,27 @@ static int nss_cmd_getpwent(struct cli_ctx *cctx) } num = *((uint32_t *)body); - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { - return ENOMEM; - } - nctx->cctx = cctx; - /* see if we need to trigger an implicit setpwent() */ if (cctx->gctx == NULL || cctx->gctx->pwds == NULL) { if (cctx->gctx == NULL) { gctx = talloc_zero(cctx, struct getent_ctx); if (!gctx) { - talloc_free(nctx); return ENOMEM; } cctx->gctx = gctx; } if (cctx->gctx->pwds == NULL) { - ret = sysdb_enumpwent(nctx, cctx->ev, cctx->nctx->sysdb, - nss_cmd_getpwent_callback, nctx); + ret = nss_cmd_setpwent_ext(cctx, true); return ret; } } + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + /* create response packet */ ret = nss_packet_new(cctx->creq, 0, nss_packet_get_cmd(cctx->creq->in), @@ -721,22 +998,22 @@ static int nss_cmd_getpwent(struct cli_ctx *cctx) ret = nss_cmd_retpwent(cctx, num); nss_packet_set_error(cctx->creq->out, ret); - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); return EOK; } static int nss_cmd_endpwent(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; int ret; DEBUG(4, ("Terminating request info for all accounts\n")); - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; + cmdctx->cctx = cctx; /* create response packet */ ret = nss_packet_new(cctx->creq, 0, @@ -752,7 +1029,7 @@ static int nss_cmd_endpwent(struct cli_ctx *cctx) cctx->gctx->pwd_cur = 0; done: - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); return EOK; } @@ -764,13 +1041,15 @@ static int fill_grent(struct nss_packet *packet, struct ldb_message **msgs, int count) { + struct ldb_message_element *el; struct ldb_message *msg; uint8_t *body; const char *name; uint64_t gid; size_t rsize, rp, blen, mnump; - int i, ret, num, memnum; + int i, j, ret, num, memnum; bool get_group = true; + bool memnum_set = false; /* first 2 fields (len and reserved), filled up later */ ret = nss_packet_grow(packet, 2*sizeof(uint32_t)); @@ -800,15 +1079,44 @@ static int fill_grent(struct nss_packet *packet, ((uint64_t *)(&body[rp]))[0] = gid; rp += sizeof(uint64_t); ((uint32_t *)(&body[rp]))[0] = 0; /* init members num to 0 */ - mnump = rp; /* keep around pointer to set members num later */ + mnump = rp; /* keep around members num pointer to set later */ rp += sizeof(uint32_t); memcpy(&body[rp], name, strlen(name)+1); body[blen-2] = 'x'; /* group passwd field */ body[blen-1] = '\0'; - get_group = false; + memnum_set = false; memnum = 0; num++; + + /* legacy style group, members are in SYSDB_LEGACY_MEMBER */ + el = ldb_msg_find_element(msg, SYSDB_LEGACY_MEMBER); + if (el) { + /* legacy */ + memnum = el->num_values; + + for (j = 0; j < memnum; j++) { + rsize = el->values[j].length + 1; + ret = nss_packet_grow(packet, rsize); + if (ret != EOK) { + num = 0; + goto done; + } + + nss_packet_get_body(packet, &body, &blen); + rp = blen - rsize; + memcpy(&body[rp], el->values[j].data, el->values[j].length); + body[blen-1] = '\0'; + } + + nss_packet_get_body(packet, &body, &blen); + ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ + memnum_set = true; + + } else { + get_group = false; + } + continue; } @@ -822,6 +1130,7 @@ static int fill_grent(struct nss_packet *packet, i--; nss_packet_get_body(packet, &body, &blen); ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ + memnum_set = true; continue; } @@ -839,10 +1148,12 @@ static int fill_grent(struct nss_packet *packet, memnum++; } - /* fill in the last group member count */ - if (mnump != 0) { - nss_packet_get_body(packet, &body, &blen); - ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ + if (!memnum_set) { + /* fill in the last group member count */ + if (mnump != 0) { + nss_packet_get_body(packet, &body, &blen); + ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */ + } } done: @@ -853,30 +1164,75 @@ done: return EOK; } -static void nss_cmd_getgr_callback(void *ptr, int status, +static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +static void nss_cmd_getgrnam_callback(void *ptr, int status, struct ldb_result *res) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; uint8_t *body; size_t blen; + bool call_provider = false; int ret; - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_packet_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - NSS_CMD_FATAL_ERROR(cctx); + if (status != LDB_SUCCESS) { + ret = nss_cmd_send_error(cmdctx, status); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + return; } - if (status != LDB_SUCCESS) { - nss_packet_set_error(cctx->creq->out, status); - goto done; + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + default: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + } + } + + if (call_provider) { + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_NSS_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getgrnam_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_GROUP, + cmdctx->name, 0); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + return; } - if (res->count == 0) { - DEBUG(2, ("No results for getpwnam call")); + switch (res->count) { + case 0: + + DEBUG(2, ("No results for getgrnam call\n")); ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), nss_packet_get_cmd(cctx->creq->in), @@ -887,102 +1243,400 @@ static void nss_cmd_getgr_callback(void *ptr, int status, nss_packet_get_body(cctx->creq->out, &body, &blen); ((uint32_t *)body)[0] = 0; /* 0 results */ ((uint32_t *)body)[1] = 0; /* reserved */ - goto done; - } + break; - ret = fill_grent(cctx->creq->out, res->msgs, res->count); - nss_packet_set_error(cctx->creq->out, ret); + default: + + DEBUG(6, ("Returning info for group [%s]\n", cmdctx->name)); + + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + ret = fill_grent(cctx->creq->out, res->msgs, res->count); + nss_packet_set_error(cctx->creq->out, ret); + } done: - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getgrnam_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; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getgrnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getgrnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } } static int nss_cmd_getgrnam(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; uint8_t *body; size_t blen; int ret; - nctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; - nctx->check_expiration = true; + cmdctx->cctx = cctx; - /* get group name to query */ + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + dctx->cmdctx = cmdctx; + + /* get user name to query */ nss_packet_get_body(cctx->creq->in, &body, &blen); - nctx->name = (const char *)body; /* if not terminated fail */ - if (nctx->name[blen -1] != '\0') { + if (body[blen -1] != '\0') { + talloc_free(cmdctx); return EINVAL; } - /* FIXME: Just ask all backends for now, until Steve provides for name - * parsing code */ - nctx->domain = NULL; + ret = nss_parse_name(dctx, (const char *)body); + if (ret != EOK) { + DEBUG(1, ("Invalid name received\n")); + talloc_free(cmdctx); + return ret; + } + DEBUG(4, ("Requesting info for [%s] from [%s]\n", + cmdctx->name, dctx->domain)); - DEBUG(4, ("Requesting info for [%s]@[%s]\n", nctx->name, nctx->domain)); + ret = sysdb_getgrnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getgrnam_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); - ret = sysdb_getgrnam(nctx, cctx->ev, cctx->nctx->sysdb, - nctx->domain, nctx->name, - nss_cmd_getgr_callback, nctx); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } - return ret; + return EOK; } -static int nss_cmd_getgrgid(struct cli_ctx *cctx) +static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min, + const char *err_msg, void *ptr); + +static void nss_cmd_getgrgid_callback(void *ptr, int status, + struct ldb_result *res) { - struct nss_cmd_ctx *nctx; + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; uint8_t *body; size_t blen; + bool call_provider = false; + int ret; + + /* 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); + } + goto done; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + default: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + } + } + + if (call_provider) { + + /* yet one more call to go */ + cmdctx->nr++; + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_NSS_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getgrgid_dp_callback, dctx, + timeout, dctx->domain, NSS_DP_GROUP, + NULL, cmdctx->id); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + return; + } + + switch (res->count) { + case 0: + if (cmdctx->nr != 0) { + /* nothing to do */ + return; + } + + DEBUG(2, ("No results for getgrgid call\n")); + + ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + default: + + DEBUG(6, ("Returning info for group [%u]\n", (unsigned)cmdctx->id)); + + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + + ret = fill_grent(cctx->creq->out, res->msgs, res->count); + nss_packet_set_error(cctx->creq->out, ret); + } + +done: + if (cmdctx->nr != 0) { + cmdctx->done = true; /* signal that we are done */ + return; + } + nss_cmd_done(cmdctx); +} + +static void nss_cmd_getgrgid_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; + struct cli_ctx *cctx = cmdctx->cctx; int ret; - nctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!nctx) { + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getgrgid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getgrgid_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static int nss_cmd_getgrgid(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; + struct nss_domain_info *info; + const char **domains; + uint8_t *body; + size_t blen; + int i, num, ret; + + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; - nctx->check_expiration = true; + cmdctx->cctx = cctx; - /* get gid to query */ + /* get uid to query */ nss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen != sizeof(uint64_t)) { return EINVAL; } - nctx->id = (uid_t)*((uint64_t *)body); + + cmdctx->id = (gid_t)*((uint64_t *)body); /* FIXME: Just ask all backends for now, until we check for ranges */ - nctx->domain = NULL; + dctx = NULL; + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); - DEBUG(4, ("Requesting info for [%lu]@[%s]\n", nctx->id, nctx->domain)); + cmdctx->nr = num; - ret = sysdb_getgrgid(nctx, cctx->ev, cctx->nctx->sysdb, - nctx->domain, nctx->id, - nss_cmd_getgr_callback, nctx); + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); - return ret; + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + DEBUG(4, ("Requesting info for [%lu@%s]\n", + cmdctx->id, dctx->domain)); + + ret = sysdb_getgrgid(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->id, + dctx->legacy, + nss_cmd_getgrgid_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + /* shutdown ? */ + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } + } + + return EOK; } /* to keep it simple at this stage we are retrieving the * full enumeration again for each request for each process - * and we also block on setpwent() for the full time needed - * to retrieve the data. And endpwent() frees all the data. + * and we also block on setgrent() for the full time needed + * to retrieve the data. And endgrent() frees all the data. * Next steps are: * - use and nsssrv wide cache with data already structured * so that it can be immediately returned (see nscd way) - * - use mutexes so that setpwent() can return immediately + * - use mutexes so that setgrent() can return immediately * even if the data is still being fetched - * - make getpwent() wait on the mutex + * - make getgrent() wait on the mutex */ +static void nss_cmd_getgrent_callback(void *ptr, int status, + struct ldb_result *res); + static void nss_cmd_setgrent_callback(void *ptr, int status, struct ldb_result *res) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; struct getent_ctx *gctx = cctx->gctx; - int ret; + struct ldb_result *store = gctx->grps; + int i, j, c, ret; + + cmdctx->nr--; + + if (cmdctx->done) { + /* do not reply until all domain searches are done */ + if (cmdctx->nr != 0) return; + else goto done; + } + + if (status != LDB_SUCCESS) { + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_packet_set_error(cctx->creq->out, status); + cmdctx->done = true; + return; + } + + if (store) { + c = store->count + res->count; + store->msgs = talloc_realloc(store, store->msgs, + struct ldb_message *, c); + if (!store->msgs) NSS_CMD_FATAL_ERROR(cctx); + + for (i = store->count, j = 0; i < c; i++, j++) { + store->msgs[i] = talloc_steal(store->msgs, res->msgs[j]); + if (!store->msgs[i]) NSS_CMD_FATAL_ERROR(cctx); + } + store->count = c; + talloc_free(res); + } else { + gctx->grps = talloc_steal(gctx, res); + } + + /* do not reply until all domain searches are done */ + if (cmdctx->nr) return; + + if (cmdctx->immediate) { + /* this was a getgrent call w/o setgrent, + * return immediately one result */ + nss_cmd_getgrent_callback(ptr, status, res); + return; + } /* create response packet */ ret = nss_packet_new(cctx->creq, 0, @@ -992,35 +1646,61 @@ static void nss_cmd_setgrent_callback(void *ptr, int status, NSS_CMD_FATAL_ERROR(cctx); } - if (status != LDB_SUCCESS) { - nss_packet_set_error(cctx->creq->out, status); - goto done; +done: + nss_cmd_done(cmdctx); +} + +static void nss_cmd_setgr_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; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); } - gctx->grps = talloc_steal(gctx, res); + ret = sysdb_enumgrent(dctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setgrent_callback, cmdctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); -done: - nss_cmd_done(nctx); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } } -static int nss_cmd_setgrent(struct cli_ctx *cctx) +static int nss_cmd_setgrent_ext(struct cli_ctx *cctx, bool immediate) { - struct nss_cmd_ctx *nctx; + struct nss_domain_info *info; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; struct getent_ctx *gctx; - int ret; + const char **domains; + int timeout; + int i, ret, num; DEBUG(4, ("Requesting info for all groups\n")); - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; + cmdctx->cctx = cctx; if (cctx->gctx == NULL) { gctx = talloc_zero(cctx, struct getent_ctx); if (!gctx) { - talloc_free(nctx); + talloc_free(cmdctx); return ENOMEM; } cctx->gctx = gctx; @@ -1031,12 +1711,77 @@ static int nss_cmd_setgrent(struct cli_ctx *cctx) cctx->gctx->grp_cur = 0; } - ret = sysdb_enumgrent(nctx, cctx->ev, cctx->nctx->sysdb, - nss_cmd_setgrent_callback, nctx); + cmdctx->immediate = immediate; + + domains = NULL; + num = 0; + /* get domains list */ + btreemap_get_keys(cmdctx, cctx->nctx->domain_map, + (const void ***)&domains, &num); + + /* check if enumeration is enabled in any domain */ + for (i = 0; i < num; i++) { + info = btreemap_get_value(cctx->nctx->domain_map, domains[i]); + + if ((info->enumerate & NSS_ENUM_GROUPS) == 0) { + continue; + } + + /* TODO: enabled, check if we have a recent cached enumeration */ + + /* ok no cache, go and ask the backend to enumerate */ + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + + dctx->cmdctx = cmdctx; + dctx->domain = talloc_strdup(dctx, domains[i]); + if (!dctx->domain) return ENOMEM; + dctx->check_provider = info->has_provider; + dctx->legacy = info->legacy; + + if (dctx->check_provider) { + timeout = SSS_NSS_SOCKET_TIMEOUT/(i+2); + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_setgr_dp_callback, dctx, + timeout, domains[i], NSS_DP_GROUP, + NULL, 0); + } else { + ret = sysdb_enumgrent(dctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, dctx->legacy, + nss_cmd_setgrent_callback, cmdctx); + } + if (ret != EOK) { + /* FIXME: shutdown ? */ + DEBUG(1, ("Failed to send enumeration request for domain [%s]!\n", + domains[i])); + continue; + } + + cmdctx->nr++; + } + + if (cmdctx->nr == 0) { + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + nss_packet_set_error(cctx->creq->out, ret); + nss_cmd_done(cmdctx); + return EOK; + } return ret; } +static int nss_cmd_setgrent(struct cli_ctx *cctx) +{ + return nss_cmd_setgrent_ext(cctx, false); +} + static int nss_cmd_retgrent(struct cli_ctx *cctx, int num) { struct getent_ctx *gctx = cctx->gctx; @@ -1057,8 +1802,8 @@ static int nss_cmd_retgrent(struct cli_ctx *cctx, int num) static void nss_cmd_getgrent_callback(void *ptr, int status, struct ldb_result *res) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; struct getent_ctx *gctx = cctx->gctx; uint8_t *body; size_t blen; @@ -1068,10 +1813,11 @@ static void nss_cmd_getgrent_callback(void *ptr, int status, /* get max num of entries to return in one call */ nss_packet_get_body(cctx->creq->in, &body, &blen); if (blen != sizeof(uint32_t)) { - ret = nss_cmd_send_error(nctx, EIO); + ret = nss_cmd_send_error(cmdctx, EIO); if (ret != EOK) { NSS_CMD_FATAL_ERROR(cctx); } + nss_cmd_done(cmdctx); } num = *((uint32_t *)body); @@ -1094,12 +1840,12 @@ static void nss_cmd_getgrent_callback(void *ptr, int status, nss_packet_set_error(cctx->creq->out, ret); done: - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); } static int nss_cmd_getgrent(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; struct getent_ctx *gctx; uint8_t *body; size_t blen; @@ -1115,29 +1861,27 @@ static int nss_cmd_getgrent(struct cli_ctx *cctx) } num = *((uint32_t *)body); - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { - return ENOMEM; - } - nctx->cctx = cctx; - /* see if we need to trigger an implicit setpwent() */ if (cctx->gctx == NULL || cctx->gctx->grps == NULL) { if (cctx->gctx == NULL) { gctx = talloc_zero(cctx, struct getent_ctx); if (!gctx) { - talloc_free(nctx); return ENOMEM; } cctx->gctx = gctx; } if (cctx->gctx->grps == NULL) { - ret = sysdb_enumgrent(nctx, cctx->ev, cctx->nctx->sysdb, - nss_cmd_getgrent_callback, nctx); + ret = nss_cmd_setgrent_ext(cctx, true); return ret; } } + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { + return ENOMEM; + } + cmdctx->cctx = cctx; + /* create response packet */ ret = nss_packet_new(cctx->creq, 0, nss_packet_get_cmd(cctx->creq->in), @@ -1148,22 +1892,22 @@ static int nss_cmd_getgrent(struct cli_ctx *cctx) ret = nss_cmd_retgrent(cctx, num); nss_packet_set_error(cctx->creq->out, ret); - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); return EOK; } static int nss_cmd_endgrent(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; int ret; DEBUG(4, ("Terminating request info for all groups\n")); - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; + cmdctx->cctx = cctx; /* create response packet */ ret = nss_packet_new(cctx->creq, 0, @@ -1179,15 +1923,15 @@ static int nss_cmd_endgrent(struct cli_ctx *cctx) cctx->gctx->grp_cur = 0; done: - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); return EOK; } static void nss_cmd_initgr_callback(void *ptr, int status, struct ldb_result *res) { - struct nss_cmd_ctx *nctx = talloc_get_type(ptr, struct nss_cmd_ctx); - struct cli_ctx *cctx = nctx->cctx; + struct nss_cmd_ctx *cmdctx = talloc_get_type(ptr, struct nss_cmd_ctx); + struct cli_ctx *cctx = cmdctx->cctx; uint8_t *body; size_t blen; uint64_t gid; @@ -1232,43 +1976,231 @@ static void nss_cmd_initgr_callback(void *ptr, int status, ((uint32_t *)body)[1] = 0; /* reserved */ done: - nss_cmd_done(nctx); + nss_cmd_done(cmdctx); } +static void nss_cmd_getinitgr_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; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_initgroups(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_initgr_callback, cmdctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +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) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int ret; + + if (err_maj) { + DEBUG(2, ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n" + "Will try to return what we have in cache\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getinit_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_cmd_done(cmdctx); + } +} + +static void nss_cmd_getinit_callback(void *ptr, int status, + struct ldb_result *res) +{ + struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx); + struct nss_cmd_ctx *cmdctx = dctx->cmdctx; + struct cli_ctx *cctx = cmdctx->cctx; + int timeout; + uint64_t lastUpdate; + uint8_t *body; + size_t blen; + bool call_provider = false; + int ret; + + if (status != LDB_SUCCESS) { + ret = nss_cmd_send_error(cmdctx, status); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + if (dctx->check_provider) { + switch (res->count) { + case 0: + call_provider = true; + break; + + default: + timeout = cmdctx->cctx->nctx->cache_timeout; + + lastUpdate = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_LAST_UPDATE, 0); + if (lastUpdate + timeout < time(NULL)) { + call_provider = true; + } + } + } + + if (call_provider) { + + /* dont loop forever :-) */ + dctx->check_provider = false; + timeout = SSS_NSS_SOCKET_TIMEOUT/2; + + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getinitnam_callback, dctx, + timeout, dctx->domain, NSS_DP_USER, + cmdctx->name, 0); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + return; + } + + switch (res->count) { + case 0: + + DEBUG(2, ("No results for initgroups call\n")); + + ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + nss_packet_get_body(cctx->creq->out, &body, &blen); + ((uint32_t *)body)[0] = 0; /* 0 results */ + ((uint32_t *)body)[1] = 0; /* reserved */ + break; + + case 1: + + timeout = SSS_NSS_SOCKET_TIMEOUT/2; + ret = nss_dp_send_acct_req(cctx->nctx, cmdctx, + nss_cmd_getinitgr_callback, dctx, + timeout, dctx->domain, NSS_DP_INITGROUPS, + cmdctx->name, 0); + if (ret != EOK) { + DEBUG(3, ("Failed to dispatch request: %d(%s)\n", + ret, strerror(ret))); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + goto done; + } + + return; + + default: + 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); + } + } + +done: + nss_cmd_done(cmdctx); +} + +/* for now, if we are online, try to always query the backend */ static int nss_cmd_initgroups(struct cli_ctx *cctx) { - struct nss_cmd_ctx *nctx; + struct nss_cmd_ctx *cmdctx; + struct nss_dom_ctx *dctx; uint8_t *body; size_t blen; int ret; - nctx = talloc_zero(cctx, struct nss_cmd_ctx); - if (!nctx) { + cmdctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!cmdctx) { return ENOMEM; } - nctx->cctx = cctx; - nctx->check_expiration = true; + cmdctx->cctx = cctx; + + dctx = talloc_zero(cmdctx, struct nss_dom_ctx); + if (!dctx) return ENOMEM; + dctx->cmdctx = cmdctx; /* get user name to query */ nss_packet_get_body(cctx->creq->in, &body, &blen); - nctx->name = (const char *)body; + cmdctx->name = (const char *)body; /* if not terminated fail */ - if (nctx->name[blen -1] != '\0') { + if (cmdctx->name[blen -1] != '\0') { return EINVAL; } - /* FIXME: Just ask all backends for now, until Steve provides for name - * parsing code */ - nctx->domain = NULL; - - DEBUG(4, ("Requesting info for [%s]@[%s]\n", nctx->name, nctx->domain)); + ret = nss_parse_name(dctx, (const char *)body); + if (ret != EOK) { + DEBUG(1, ("Invalid name received\n")); + talloc_free(cmdctx); + return ret; + } + DEBUG(4, ("Requesting info for [%s] from [%s]\n", + cmdctx->name, dctx->domain)); + ret = sysdb_getpwnam(cmdctx, cctx->ev, cctx->nctx->sysdb, + dctx->domain, cmdctx->name, + dctx->legacy, + nss_cmd_getinit_callback, dctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); - ret = sysdb_initgroups(nctx, cctx->ev, cctx->nctx->sysdb, - nctx->domain, nctx->name, - nss_cmd_initgr_callback, nctx); + ret = nss_cmd_send_error(cmdctx, ret); + if (ret == EOK) { + nss_cmd_done(cmdctx); + } + return ret; + } - return ret; + return EOK; } struct nss_cmd_table nss_cmds[] = { diff --git a/server/nss/nsssrv_dp.c b/server/nss/nsssrv_dp.c index d6aba556..487ac285 100644 --- a/server/nss/nsssrv_dp.c +++ b/server/nss/nsssrv_dp.c @@ -30,10 +30,21 @@ struct nss_dp_req { nss_dp_callback_t callback; void *callback_ctx; - bool replied; struct timed_event *te; + DBusPendingCall *pending_reply; }; +static int nss_dp_req_destructor(void *ptr) +{ + struct nss_dp_req *req = talloc_get_type(ptr, struct nss_dp_req); + + if (req->pending_reply) { + dbus_pending_call_cancel(req->pending_reply); + } + + return 0; +} + static void nss_dp_send_acct_timeout(struct event_context *ev, struct timed_event *te, struct timeval t, void *data) @@ -44,9 +55,10 @@ static void nss_dp_send_acct_timeout(struct event_context *ev, const char *err_msg = "Request timed out"; ndp_req = talloc_get_type(data, struct nss_dp_req); - ndp_req->replied = true; ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx); + + talloc_free(ndp_req); } static int nss_dp_get_reply(DBusPendingCall *pending, @@ -63,11 +75,10 @@ static void nss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr) int ret; ndp_req = talloc_get_type(ptr, struct nss_dp_req); - if (ndp_req->replied) { - DEBUG(5, ("Callback called, but the request was already timed out!\n")); - talloc_free(ndp_req); - return; - } + + /* free timeout event and remove request destructor */ + talloc_free(ndp_req->te); + talloc_set_destructor(ndp_req, NULL); ret = nss_dp_get_reply(pending, &err_maj, &err_min, &err_msg); if (ret != EOK) { @@ -76,9 +87,9 @@ static void nss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr) err_msg = "Failed to get reply from Data Provider"; } - talloc_free(ndp_req->te); - ndp_req->callback(err_maj, err_min, err_msg, ndp_req->callback_ctx); + + talloc_free(ndp_req); } int nss_dp_send_acct_req(struct nss_ctx *nctx, TALLOC_CTX *memctx, @@ -113,6 +124,9 @@ int nss_dp_send_acct_req(struct nss_ctx *nctx, TALLOC_CTX *memctx, case NSS_DP_GROUP: be_type = BE_REQ_GROUP; break; + case NSS_DP_INITGROUPS: + be_type = BE_REQ_INITGROUPS; + break; default: return EINVAL; } @@ -169,15 +183,19 @@ int nss_dp_send_acct_req(struct nss_ctx *nctx, TALLOC_CTX *memctx, return EIO; } - /* setup the timeout handler */ - ndp_req = talloc(memctx, struct nss_dp_req); + ndp_req = talloc_zero(memctx, struct nss_dp_req); if (!ndp_req) { dbus_message_unref(msg); return ENOMEM; } ndp_req->callback = callback; ndp_req->callback_ctx = callback_ctx; - ndp_req->replied = false; + + /* set up destructor */ + ndp_req->pending_reply = pending_reply; + talloc_set_destructor((TALLOC_CTX *)ndp_req, nss_dp_req_destructor); + + /* setup the timeout handler */ gettimeofday(&tv, NULL); tv.tv_sec += timeout/1000; tv.tv_usec += (timeout%1000) * 1000; diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index 328b9ec7..5dc402ef 100644 --- a/server/providers/data_provider.h +++ b/server/providers/data_provider.h @@ -72,5 +72,6 @@ #define BE_REQ_USER 1 #define BE_REQ_GROUP 2 +#define BE_REQ_INITGROUPS 3 #endif /* __DATA_PROVIDER_ */ diff --git a/server/providers/proxy.c b/server/providers/proxy.c index 62638c1f..0ec6e530 100644 --- a/server/providers/proxy.c +++ b/server/providers/proxy.c @@ -70,7 +70,7 @@ static void get_pw_name(struct be_req *req, char *name) ctx = talloc_get_type(req->be_ctx->pvt_data, struct proxy_ctx); - buffer = talloc_size(NULL, 4096); + buffer = talloc_size(req, 4096); if (!buffer) return proxy_reply(req, ENOMEM, "Out of memory"); @@ -78,11 +78,11 @@ static void get_pw_name(struct be_req *req, char *name) switch (status) { case NSS_STATUS_NOTFOUND: - ret = sysdb_posix_remove_user(req, req->be_ctx->sysdb, - req->be_ctx->domain, name); + ret = sysdb_delete_user(req, req->be_ctx->sysdb, + req->be_ctx->domain, name); break; case NSS_STATUS_SUCCESS: - ret = sysdb_posix_store_user(req, req->be_ctx->sysdb, + ret = sysdb_legacy_store_user(req, req->be_ctx->sysdb, req->be_ctx->domain, result.pw_name, result.pw_passwd, result.pw_uid, result.pw_gid, @@ -117,7 +117,7 @@ static void get_pw_uid(struct be_req *req, uid_t uid) ctx = talloc_get_type(req->be_ctx->pvt_data, struct proxy_ctx); - buffer = talloc_size(NULL, 4096); + buffer = talloc_size(req, 4096); if (!buffer) return proxy_reply(req, ENOMEM, "Out of memory"); @@ -125,11 +125,11 @@ static void get_pw_uid(struct be_req *req, uid_t uid) switch (status) { case NSS_STATUS_NOTFOUND: - ret = sysdb_posix_remove_user_by_uid(req, req->be_ctx->sysdb, - req->be_ctx->domain,uid); + ret = sysdb_delete_user_by_uid(req, req->be_ctx->sysdb, + req->be_ctx->domain,uid); break; case NSS_STATUS_SUCCESS: - ret = sysdb_posix_store_user(req, req->be_ctx->sysdb, + ret = sysdb_legacy_store_user(req, req->be_ctx->sysdb, req->be_ctx->domain, result.pw_name, result.pw_passwd, result.pw_uid, result.pw_gid, @@ -169,7 +169,7 @@ static void enum_users(struct be_req *req) ctx = talloc_get_type(req->be_ctx->pvt_data, struct proxy_ctx); buflen = 4096; - buffer = talloc_size(NULL, buflen); + buffer = talloc_size(req, buflen); if (!buffer) return proxy_reply(req, ENOMEM, "Out of memory"); @@ -205,7 +205,7 @@ static void enum_users(struct be_req *req) break; case NSS_STATUS_SUCCESS: - ret = sysdb_posix_store_user(req, req->be_ctx->sysdb, + ret = sysdb_legacy_store_user(req, req->be_ctx->sysdb, req->be_ctx->domain, result.pw_name, result.pw_passwd, result.pw_uid, result.pw_gid, @@ -252,17 +252,16 @@ static void get_gr_name(struct be_req *req, char *name) switch (status) { case NSS_STATUS_NOTFOUND: - ret = sysdb_posix_remove_group(req, req->be_ctx->sysdb, - req->be_ctx->domain, name); + ret = sysdb_delete_group(req, req->be_ctx->sysdb, + req->be_ctx->domain, name); break; case NSS_STATUS_SUCCESS: - /* FIXME: check members are all available or fetch them */ - ret = sysdb_posix_store_group(req, req->be_ctx->sysdb, - req->be_ctx->domain, result.gr_name, - result.gr_gid, result.gr_mem); + ret = sysdb_legacy_store_group(req, req->be_ctx->sysdb, + req->be_ctx->domain, result.gr_name, + result.gr_gid, result.gr_mem); break; default: - DEBUG(2, ("proxy -> getpwnam_r failed for '%s' (%d)[%s]\n", + DEBUG(2, ("proxy -> getgrnam_r failed for '%s' (%d)[%s]\n", name, ret, strerror(ret))); talloc_free(buffer); return proxy_reply(req, ret, "Operation failed"); @@ -289,7 +288,7 @@ static void get_gr_gid(struct be_req *req, gid_t gid) ctx = talloc_get_type(req->be_ctx->pvt_data, struct proxy_ctx); - buffer = talloc_size(NULL, 4096); + buffer = talloc_size(req, 4096); if (!buffer) return proxy_reply(req, ENOMEM, "Out of memory"); @@ -297,13 +296,13 @@ static void get_gr_gid(struct be_req *req, gid_t gid) switch (status) { case NSS_STATUS_NOTFOUND: - ret = sysdb_posix_remove_group_by_gid(req, req->be_ctx->sysdb, - req->be_ctx->domain, gid); + ret = sysdb_delete_group_by_gid(req, req->be_ctx->sysdb, + req->be_ctx->domain, gid); break; case NSS_STATUS_SUCCESS: - ret = sysdb_posix_store_group(req, req->be_ctx->sysdb, - req->be_ctx->domain, result.gr_name, - result.gr_gid, result.gr_mem); + ret = sysdb_legacy_store_group(req, req->be_ctx->sysdb, + req->be_ctx->domain, result.gr_name, + result.gr_gid, result.gr_mem); break; default: DEBUG(2, ("proxy -> getgrgid_r failed for '%lu' (%d)[%s]\n", @@ -331,12 +330,12 @@ static void enum_groups(struct be_req *req) char *buffer, *newb; size_t buflen; const char * errstr; - int ret; + int ret, c; ctx = talloc_get_type(req->be_ctx->pvt_data, struct proxy_ctx); buflen = 4096; - buffer = talloc_size(NULL, buflen); + buffer = talloc_size(req, buflen); if (!buffer) return proxy_reply(req, ENOMEM, "Out of memory"); @@ -344,12 +343,21 @@ static void enum_groups(struct be_req *req) if (status != NSS_STATUS_SUCCESS) return proxy_reply(req, EIO, "Operation failed"); + c = 0; while (status == NSS_STATUS_SUCCESS) { status = ctx->ops.getgrent_r(&result, buffer, buflen, &ret); switch (status) { case NSS_STATUS_TRYAGAIN: + DEBUG(20, ("Try Again\n")); + + if (ret != ERANGE) { + DEBUG(2, ("getgrent_r failed (TRYAGAIN)(%d)[%s]\n", + ret, strerror(ret))); + errstr = "Operation failed"; + goto done; + } /* buffer too small ? */ if (buflen < MAX_BUF_SIZE) { buflen *= 2; @@ -357,8 +365,9 @@ static void enum_groups(struct be_req *req) if (buflen > MAX_BUF_SIZE) { buflen = MAX_BUF_SIZE; } - newb = talloc_realloc_size(NULL, buffer, buflen); + newb = talloc_realloc_size(req, buffer, buflen); if (!newb) { + DEBUG(4, ("Out of memory\n")); errstr = "Out of memory"; ret = ENOMEM; goto done; @@ -368,13 +377,16 @@ static void enum_groups(struct be_req *req) break; case NSS_STATUS_NOTFOUND: + DEBUG(6, ("No more entries\n")); /* we got last one */ break; case NSS_STATUS_SUCCESS: - ret = sysdb_posix_store_group(req, req->be_ctx->sysdb, - req->be_ctx->domain, result.gr_name, - result.gr_gid, result.gr_mem); + c++; + DEBUG(20, ("Storing group [%s](%d)\n", result.gr_name, c)); + ret = sysdb_legacy_store_group(req, req->be_ctx->sysdb, + req->be_ctx->domain, result.gr_name, + result.gr_gid, result.gr_mem); if (ret != EOK) { DEBUG(1, ("Failed to update LDB Cache for '%s' (%d)[%s] !?\n", (unsigned long)result.gr_name, ret, strerror(ret))); @@ -382,8 +394,7 @@ static void enum_groups(struct be_req *req) break; default: - DEBUG(2, ("proxy -> getgrent_r failed (%d)[%s]\n", - ret, strerror(ret))); + DEBUG(2, ("getgrent_r failed (%d)[%s]\n", ret, strerror(ret))); errstr = "Operation failed"; goto done; } @@ -398,6 +409,142 @@ done: return proxy_reply(req, ret, errstr); } +static int save_initgroups(struct be_req *req, gid_t *gids, long int num) +{ + struct proxy_ctx *ctx; + struct sysdb_ctx *sysdb; + enum nss_status status; + struct group result; + char *buffer; + int i, ret; + + ctx = talloc_get_type(req->be_ctx->pvt_data, struct proxy_ctx); + sysdb = req->be_ctx->sysdb; + + buffer = talloc_size(req, 4096); + if (!buffer) { + return ENOMEM; + } + + for (i = 0; i < num; i++) { + + status = ctx->ops.getgrgid_r(gids[i], &result, buffer, 4096, &ret); + + switch (status) { + case NSS_STATUS_NOTFOUND: + DEBUG(4, ("gid [%lu] not found, removing group\n")); + ret = sysdb_delete_group_by_gid(req, sysdb, + req->be_ctx->domain, + gids[i]); + break; + + case NSS_STATUS_SUCCESS: + ret = sysdb_legacy_store_group(req, sysdb, + req->be_ctx->domain, + result.gr_name, + result.gr_gid, + result.gr_mem); + break; + + default: + DEBUG(2, ("proxy -> getgrgid_r failed for '%lu' (%d)[%s]\n", + (unsigned long)(gids[i]), ret, strerror(ret))); + break; + } + } + + talloc_free(buffer); + return EOK; +} + +static void get_user_groups(struct be_req *req, char *name) +{ + struct proxy_ctx *ctx; + enum nss_status status; + struct passwd result; + char *buffer; + gid_t *groups; + long int limit; + long int start; + long int size; + long int num; + int ret; + + ctx = talloc_get_type(req->be_ctx->pvt_data, struct proxy_ctx); + + buffer = talloc_size(req, 4096); + if (!buffer) + return proxy_reply(req, ENOMEM, "Out of memory"); + + status = ctx->ops.getpwnam_r(name, &result, buffer, 4096, &ret); + + switch (status) { + case NSS_STATUS_NOTFOUND: + ret = sysdb_delete_user(req, req->be_ctx->sysdb, + req->be_ctx->domain, name); + break; + case NSS_STATUS_SUCCESS: + ret = sysdb_legacy_store_user(req, req->be_ctx->sysdb, + req->be_ctx->domain, + result.pw_name, result.pw_passwd, + result.pw_uid, result.pw_gid, + result.pw_gecos, result.pw_dir, + result.pw_shell); + if (ret != EOK) break; + + /* FIXME: let's start with 4k entries */ + start = 0; + limit = 4096; + num = 4096; + + size = num*sizeof(gid_t); + groups = talloc_size(req, size); + if (!groups) { + talloc_free(buffer); + return proxy_reply(req, ENOMEM, "Out of memory"); + } + + status = ctx->ops.initgroups_dyn(result.pw_name, result.pw_gid, + &start, &num, &groups, limit, &ret); + switch (status) { + case NSS_STATUS_SUCCESS: + + if (ret == EOK) { + DEBUG(4, ("User [%s] appears to be member of %lu groups\n", + result.pw_name, start)); + /* start is moved up by the number of groups retrieved, + * therefore represents the number of users to pass on */ + ret = save_initgroups(req, groups, start); + } + + break; + + default: + DEBUG(2, ("proxy -> initgroups_dyn failed for '%s' (%d)[%s]\n", + name, ret, strerror(ret))); + talloc_free(buffer); + return proxy_reply(req, ret, "Operation failed"); + } + break; + + default: + DEBUG(2, ("proxy -> getpwnam_r failed for '%s' (%d)[%s]\n", + name, ret, strerror(ret))); + talloc_free(buffer); + return proxy_reply(req, ret, "Operation failed"); + } + + if (ret != EOK) { + DEBUG(1, ("Failed to update LDB Cache for '%s' (%d) !?\n", + name, ret)); + talloc_free(buffer); + return proxy_reply(req, ret, "Operation failed"); + } + + talloc_free(buffer); + return proxy_reply(req, EOK, NULL); +} + /* TODO: actually do check something */ static void proxy_check_online(struct be_req *req) { @@ -498,6 +645,17 @@ static void proxy_get_account_info(struct be_req *req) } break; + case BE_REQ_INITGROUPS: /* init groups for user */ + if (ar->filter_type != BE_FILTER_NAME) { + return proxy_reply(req, EINVAL, "Invalid filter type"); + } + if (ar->attr_type != BE_ATTR_CORE) { + return proxy_reply(req, EINVAL, "Invalid attr type"); + } + if (strchr(ar->filter_value, '*')) { + return proxy_reply(req, EINVAL, "Invalid filter value"); + } + return get_user_groups(req, ar->filter_value); default: /*fail*/ return proxy_reply(req, EINVAL, "Invalid request type"); |