diff options
author | Simo Sorce <idra@samba.org> | 2009-02-16 20:25:16 -0500 |
---|---|---|
committer | Simo Sorce <idra@samba.org> | 2009-02-20 18:09:29 -0500 |
commit | b8f07efe5d98071777e3a2863688c8269a7912e4 (patch) | |
tree | 66693078c86c7d6ed289bce3f6aebbd9c6125ea7 | |
parent | 2d151b22408e78e4b556000125cfc5abe068c846 (diff) | |
download | sssd-b8f07efe5d98071777e3a2863688c8269a7912e4.tar.gz sssd-b8f07efe5d98071777e3a2863688c8269a7912e4.tar.bz2 sssd-b8f07efe5d98071777e3a2863688c8269a7912e4.zip |
Completely rework the nss interface to be able to use 2
types of domains: modern and legacy
modern uses member/meberof, legacy uses memberUid for group
memberships.
Rework the proxy backend to use the legacy style as that's the
format the data comes in (trying to convert would require too
many transformations and increased the number of queries).
Add support for fetching groups in nss.
Add support for enumerating users and groups (requires to enable enumeration
in config) both in nss and in the proxy provider.
Remove confdb_get_domain_basedn() and substitute with generic calls in
the nss init function.
Store a domain structure in the btree not the basedn so that we can add
enumeration flags.
Also make sure NSS understand how to make multiple calls on
enumerations, also make passing the domian parameter always
mandatory, passing in domain=* is not valid anymore.
This work fixes also a few memory, degfault, and logic bugs
found while testing all nss functions (there are still some to
fix that are less critical and much harder to find yet).
-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"); |