From be2dc085a371cd9b68770b6e3f008a952636867d Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 9 Oct 2008 12:46:38 -0400 Subject: Implement getpwent() support --- server/nss/nsssrv.h | 7 +- server/nss/nsssrv_cmd.c | 294 ++++++++++++++++++++++++++++++++++++++++----- server/nss/nsssrv_ldb.c | 97 ++++++++------- server/nss/nsssrv_ldb.h | 5 + server/nss/nsssrv_packet.c | 9 +- 5 files changed, 335 insertions(+), 77 deletions(-) (limited to 'server') diff --git a/server/nss/nsssrv.h b/server/nss/nsssrv.h index 6ed679f4f..6302c32c3 100644 --- a/server/nss/nsssrv.h +++ b/server/nss/nsssrv.h @@ -30,6 +30,7 @@ #include "../nss_client/sss_nss.h" struct nss_ldb_ctx; +struct getent_ctx; struct nss_ctx { struct task_server *task; @@ -45,6 +46,7 @@ struct cli_ctx { struct fd_event *cfde; struct sockaddr_un addr; struct cli_request *creq; + struct getent_ctx *gctx; }; struct nss_packet; @@ -65,8 +67,9 @@ int nss_packet_new(TALLOC_CTX *mem_ctx, size_t size, int nss_packet_grow(struct nss_packet *packet, size_t size); int nss_packet_recv(struct nss_packet *packet, int fd); int nss_packet_send(struct nss_packet *packet, int fd); -enum sss_nss_command nss_get_cmd(struct nss_packet *packet); -void nss_get_body(struct nss_packet *packet, uint8_t **body, size_t *blen); +enum sss_nss_command nss_packet_get_cmd(struct nss_packet *packet); +void nss_packet_get_body(struct nss_packet *packet, uint8_t **body, size_t *blen); +void nss_packet_set_error(struct nss_packet *packet, int error); /* from nsssrv_cmd.c */ int nss_cmd_execute(struct cli_ctx *cctx); diff --git a/server/nss/nsssrv_cmd.c b/server/nss/nsssrv_cmd.c index cc255e4c2..666766134 100644 --- a/server/nss/nsssrv_cmd.c +++ b/server/nss/nsssrv_cmd.c @@ -29,31 +29,52 @@ struct nss_cmd_ctx { struct cli_ctx *cctx; }; +struct getent_ctx { + struct ldb_result *pwds; + struct ldb_result *grps; + int pwd_cur; + int grp_cur; +}; + 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) +{ + /* now that the packet is in place, unlock queue + * making the event writable */ + EVENT_FD_WRITEABLE(nctx->cctx->cfde); + + /* free all request related data through the talloc hierarchy */ + talloc_free(nctx); +} + static int nss_cmd_get_version(struct cli_ctx *cctx) { + struct nss_cmd_ctx *nctx; uint8_t *body; size_t blen; int ret; + nctx = talloc(cctx, struct nss_cmd_ctx); + if (!nctx) { + return ENOMEM; + } + nctx->cctx = cctx; + /* create response packet */ ret = nss_packet_new(cctx->creq, sizeof(uint32_t), - nss_get_cmd(cctx->creq->in), + nss_packet_get_cmd(cctx->creq->in), &cctx->creq->out); if (ret != EOK) { return ret; } - nss_get_body(cctx->creq->out, &body, &blen); + nss_packet_get_body(cctx->creq->out, &body, &blen); ((uint32_t *)body)[0] = SSS_NSS_VERSION; - /* now that the packet is in place, unlock queue - * making the event writable */ - EVENT_FD_WRITEABLE(cctx->cfde); - + nss_cmd_done(nctx); return EOK; } @@ -104,7 +125,7 @@ static int fill_pwent(struct nss_packet *packet, num = 0; goto done; } - nss_get_body(packet, &body, &blen); + nss_packet_get_body(packet, &body, &blen); ((uint64_t *)(&body[rp]))[0] = uid; ((uint64_t *)(&body[rp]))[1] = gid; @@ -124,7 +145,7 @@ static int fill_pwent(struct nss_packet *packet, } done: - nss_get_body(packet, &body, &blen); + nss_packet_get_body(packet, &body, &blen); ((uint32_t *)body)[0] = num; /* num results */ ((uint32_t *)body)[1] = 0; /* reserved */ @@ -140,6 +161,19 @@ static int nss_cmd_getpw_callback(void *ptr, int status, size_t blen; 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) { + return ret; + } + + if (status != LDB_SUCCESS) { + nss_packet_set_error(cctx->creq->out, status); + goto done; + } + if (res->count != 1) { if (res->count > 1) { DEBUG(1, ("getpwnam call returned more than oine result !?!\n")); @@ -148,36 +182,23 @@ static int nss_cmd_getpw_callback(void *ptr, int status, DEBUG(2, ("No results for getpwnam call")); } ret = nss_packet_new(cctx->creq, 2*sizeof(uint32_t), - nss_get_cmd(cctx->creq->in), + nss_packet_get_cmd(cctx->creq->in), &cctx->creq->out); if (ret != EOK) { return ret; } - nss_get_body(cctx->creq->out, &body, &blen); + nss_packet_get_body(cctx->creq->out, &body, &blen); ((uint32_t *)body)[0] = 0; /* 0 results */ ((uint32_t *)body)[1] = 0; /* reserved */ goto done; } - /* create response packet */ - ret = nss_packet_new(cctx->creq, 0, - nss_get_cmd(cctx->creq->in), - &cctx->creq->out); - if (ret != EOK) { - return ret; - } - ret = fill_pwent(cctx->creq->out, res->msgs, res->count); + nss_packet_set_error(cctx->creq->out, ret); done: - /* now that the packet is in place, unlock queue - * making the event writable */ - EVENT_FD_WRITEABLE(cctx->cfde); - - /* free all request related data through the talloc hierarchy */ - talloc_free(nctx); - - return ret; + nss_cmd_done(nctx); + return EOK; } static int nss_cmd_getpwnam(struct cli_ctx *cctx) @@ -189,7 +210,7 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx) const char *name; /* get user name to query */ - nss_get_body(cctx->creq->in, &body, &blen); + nss_packet_get_body(cctx->creq->in, &body, &blen); name = (const char *)body; /* if not terminated fail */ if (name[blen -1] != '\0') { @@ -216,8 +237,8 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) int ret; uint64_t uid; - /* get user name to query */ - nss_get_body(cctx->creq->in, &body, &blen); + /* get uid to query */ + nss_packet_get_body(cctx->creq->in, &body, &blen); if (blen != sizeof(uint64_t)) { return EINVAL; @@ -237,10 +258,223 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) return ret; } +/* 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. + * 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 + * even if the data is still being fetched + * - make getpwent() wait on the mutex + */ +static int nss_cmd_setpwent_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 getent_ctx *gctx = cctx->gctx; + 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) { + return ret; + } + + if (status != LDB_SUCCESS) { + nss_packet_set_error(cctx->creq->out, status); + goto done; + } + + gctx->pwds = talloc_steal(gctx, res); + +done: + nss_cmd_done(nctx); + return EOK; +} + +static int nss_cmd_setpwent(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *nctx; + struct getent_ctx *gctx; + int ret; + + nctx = talloc(cctx, struct nss_cmd_ctx); + if (!nctx) { + return ENOMEM; + } + nctx->cctx = cctx; + + 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) { + talloc_free(cctx->gctx->pwds); + cctx->gctx->pwds = NULL; + cctx->gctx->pwd_cur = 0; + } + + ret = nss_ldb_enumpwent(nctx, cctx->ev, cctx->ldb, + nss_cmd_setpwent_callback, nctx); + + return ret; +} + +static int nss_cmd_retpwent(struct cli_ctx *cctx, int num) +{ + struct getent_ctx *gctx = cctx->gctx; + int n, ret; + + n = gctx->pwds->count - gctx->pwd_cur; + if (n > num) n = num; + + ret = fill_pwent(cctx->creq->out, &(gctx->pwds->msgs[gctx->pwd_cur]), n); + gctx->pwd_cur += n; + + return ret; +} + +/* used only if a process calls getpwent() without first calling setpwent() + * in this case we basically trigger an implicit setpwent() */ +static int 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 getent_ctx *gctx = cctx->gctx; + uint8_t *body; + size_t blen; + uint32_t num; + int ret; + + /* get max num of entries to return in one call */ + nss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen != sizeof(uint32_t)) { + return EINVAL; + } + num = *((uint32_t *)body); + + /* 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; + } + + if (status != LDB_SUCCESS) { + nss_packet_set_error(cctx->creq->out, status); + goto done; + } + + gctx->pwds = talloc_steal(gctx, res); + + ret = nss_cmd_retpwent(cctx, num); + nss_packet_set_error(cctx->creq->out, ret); + +done: + nss_cmd_done(nctx); + return EOK; +} + +static int nss_cmd_getpwent(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *nctx; + struct getent_ctx *gctx; + uint8_t *body; + size_t blen; + uint32_t num; + int ret; + + /* get max num of entries to return in one call */ + nss_packet_get_body(cctx->creq->in, &body, &blen); + if (blen != sizeof(uint32_t)) { + return EINVAL; + } + 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 = nss_ldb_enumpwent(nctx, cctx->ev, cctx->ldb, + nss_cmd_getpwent_callback, nctx); + return ret; + } + } + + /* 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; + } + + ret = nss_cmd_retpwent(cctx, num); + nss_packet_set_error(cctx->creq->out, ret); + nss_cmd_done(nctx); + return EOK; +} + +static int nss_cmd_endpwent(struct cli_ctx *cctx) +{ + struct nss_cmd_ctx *nctx; + int ret; + + nctx = talloc(cctx, struct nss_cmd_ctx); + if (!nctx) { + return ENOMEM; + } + nctx->cctx = cctx; + + /* create response packet */ + ret = nss_packet_new(cctx->creq, 0, + nss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + + if (cctx->gctx == NULL) goto done; + if (cctx->gctx->pwds == NULL) goto done; + + /* free results and reset */ + talloc_free(cctx->gctx->pwds); + cctx->gctx->pwds = NULL; + cctx->gctx->pwd_cur = 0; + +done: + nss_cmd_done(nctx); + return EOK; +} + struct nss_cmd_table nss_cmds[] = { {SSS_NSS_GET_VERSION, nss_cmd_get_version}, {SSS_NSS_GETPWNAM, nss_cmd_getpwnam}, {SSS_NSS_GETPWUID, nss_cmd_getpwuid}, + {SSS_NSS_SETPWENT, nss_cmd_setpwent}, + {SSS_NSS_GETPWENT, nss_cmd_getpwent}, + {SSS_NSS_ENDPWENT, nss_cmd_endpwent}, {SSS_NSS_NULL, NULL} }; @@ -249,7 +483,7 @@ int nss_cmd_execute(struct cli_ctx *cctx) enum sss_nss_command cmd; int i; - cmd = nss_get_cmd(cctx->creq->in); + cmd = nss_packet_get_cmd(cctx->creq->in); for (i = 0; nss_cmds[i].cmd != SSS_NSS_NULL; i++) { if (cmd == nss_cmds[i].cmd) { diff --git a/server/nss/nsssrv_ldb.c b/server/nss/nsssrv_ldb.c index 023d63bea..a54bc6a50 100644 --- a/server/nss/nsssrv_ldb.c +++ b/server/nss/nsssrv_ldb.c @@ -40,13 +40,13 @@ static int nss_ldb_error_to_errno(int lerr) static int request_error(struct nss_ldb_search_ctx *sctx, int ldb_error) { - sctx->callback(sctx->ptr, ldb_error, sctx->res); + sctx->callback(sctx->ptr, nss_ldb_error_to_errno(ldb_error), sctx->res); return ldb_error; } static int request_done(struct nss_ldb_search_ctx *sctx) { - return sctx->callback(sctx->ptr, LDB_SUCCESS, sctx->res); + return sctx->callback(sctx->ptr, EOK, sctx->res); } static int getpw_callback(struct ldb_request *req, @@ -108,35 +108,33 @@ static int getpw_callback(struct ldb_request *req, return LDB_SUCCESS; } -int nss_ldb_getpwnam(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct ldb_context *ldb, - const char *name, - nss_ldb_callback_t fn, void *ptr) +static struct nss_ldb_search_ctx *init_sctx(TALLOC_CTX *mem_ctx, + nss_ldb_callback_t fn, void *ptr) { struct nss_ldb_search_ctx *sctx; - struct ldb_request *req; - static const char *attrs[] = NSS_PW_ATTRS; - char *expression; - int ret; sctx = talloc(mem_ctx, struct nss_ldb_search_ctx); if (!sctx) { - return ENOMEM; + return NULL; } sctx->callback = fn; sctx->ptr = ptr; sctx->res = talloc_zero(sctx, struct ldb_result); if (!sctx->res) { talloc_free(sctx); - return ENOMEM; + return NULL; } - expression = talloc_asprintf(sctx, NSS_PWNAM_FILTER, name); - if (!expression) { - talloc_free(sctx); - return ENOMEM; - } + return sctx; +} + +static int do_search(struct nss_ldb_search_ctx *sctx, + struct ldb_context *ldb, + const char *expression) +{ + static const char *attrs[] = NSS_PW_ATTRS; + struct ldb_request *req; + int ret; ret = ldb_build_search_req(&req, ldb, sctx, ldb_dn_new(sctx, ldb, NSS_USER_BASE), @@ -145,7 +143,7 @@ int nss_ldb_getpwnam(TALLOC_CTX *mem_ctx, sctx, getpw_callback, NULL); if (ret != LDB_SUCCESS) { - return nss_ldb_error_to_errno(ret);; + return nss_ldb_error_to_errno(ret); } ret = ldb_request(ldb, req); @@ -156,53 +154,66 @@ int nss_ldb_getpwnam(TALLOC_CTX *mem_ctx, return EOK; } -int nss_ldb_getpwuid(TALLOC_CTX *mem_ctx, +int nss_ldb_getpwnam(TALLOC_CTX *mem_ctx, struct event_context *ev, struct ldb_context *ldb, - uint64_t uid, + const char *name, nss_ldb_callback_t fn, void *ptr) { struct nss_ldb_search_ctx *sctx; - struct ldb_request *req; - static const char *attrs[] = NSS_PW_ATTRS; - unsigned long long int filter_uid = uid; char *expression; - int ret; - sctx = talloc(mem_ctx, struct nss_ldb_search_ctx); + sctx = init_sctx(mem_ctx, fn, ptr); if (!sctx) { return ENOMEM; } - sctx->callback = fn; - sctx->ptr = ptr; - sctx->res = talloc_zero(sctx, struct ldb_result); - if (!sctx->res) { + + expression = talloc_asprintf(sctx, NSS_PWNAM_FILTER, name); + if (!expression) { talloc_free(sctx); return ENOMEM; } + return do_search(sctx, ldb, expression); +} + +int nss_ldb_getpwuid(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct ldb_context *ldb, + uint64_t uid, + nss_ldb_callback_t fn, void *ptr) +{ + struct nss_ldb_search_ctx *sctx; + unsigned long long int filter_uid = uid; + char *expression; + + sctx = init_sctx(mem_ctx, fn, ptr); + if (!sctx) { + return ENOMEM; + } + expression = talloc_asprintf(sctx, NSS_PWUID_FILTER, filter_uid); if (!expression) { talloc_free(sctx); return ENOMEM; } - ret = ldb_build_search_req(&req, ldb, sctx, - ldb_dn_new(sctx, ldb, NSS_USER_BASE), - LDB_SCOPE_SUBTREE, - expression, attrs, NULL, - sctx, getpw_callback, - NULL); - if (ret != LDB_SUCCESS) { - return nss_ldb_error_to_errno(ret); - } + return do_search(sctx, ldb, expression); +} - ret = ldb_request(ldb, req); - if (ret != LDB_SUCCESS) { - return nss_ldb_error_to_errno(ret); +int nss_ldb_enumpwent(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct ldb_context *ldb, + nss_ldb_callback_t fn, void *ptr) +{ + struct nss_ldb_search_ctx *sctx; + + sctx = init_sctx(mem_ctx, fn, ptr); + if (!sctx) { + return ENOMEM; } - return EOK; + return do_search(sctx, ldb, NSS_PWENT_FILTER); } int nss_ldb_init(TALLOC_CTX *mem_ctx, diff --git a/server/nss/nsssrv_ldb.h b/server/nss/nsssrv_ldb.h index 924c41d7a..a57a91198 100644 --- a/server/nss/nsssrv_ldb.h +++ b/server/nss/nsssrv_ldb.h @@ -23,3 +23,8 @@ int nss_ldb_getpwuid(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, uint64_t uid, nss_ldb_callback_t fn, void *ptr); + +int nss_ldb_enumpwent(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct ldb_context *ldb, + nss_ldb_callback_t fn, void *ptr); diff --git a/server/nss/nsssrv_packet.c b/server/nss/nsssrv_packet.c index c4758ba5f..c15f5c764 100644 --- a/server/nss/nsssrv_packet.c +++ b/server/nss/nsssrv_packet.c @@ -197,13 +197,18 @@ int nss_packet_send(struct nss_packet *packet, int fd) return EOK; } -enum sss_nss_command nss_get_cmd(struct nss_packet *packet) +enum sss_nss_command nss_packet_get_cmd(struct nss_packet *packet) { return (enum sss_nss_command)(*packet->cmd); } -void nss_get_body(struct nss_packet *packet, uint8_t **body, size_t *blen) +void nss_packet_get_body(struct nss_packet *packet, uint8_t **body, size_t *blen) { *body = packet->body; *blen = *packet->len - SSS_NSS_HEADER_SIZE; } + +void nss_packet_set_error(struct nss_packet *packet, int error) +{ + *(packet->status) = error; +} -- cgit