From 9aaaff891a0125dc1102668a99338530fb07abfa Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Sun, 11 Jan 2009 17:21:11 -0500 Subject: Add support for getpwuid in proxy backend --- server/nss/nsssrv_cmd.c | 91 ++++++++++++++++++++------ server/providers/dp_backend.h | 1 + server/providers/dp_backend_store.c | 123 +++++++++++++++++++++++++++++------- server/providers/proxy.c | 61 +++++++++++++++++- 4 files changed, 230 insertions(+), 46 deletions(-) (limited to 'server') diff --git a/server/nss/nsssrv_cmd.c b/server/nss/nsssrv_cmd.c index 9347c329f..4d856e065 100644 --- a/server/nss/nsssrv_cmd.c +++ b/server/nss/nsssrv_cmd.c @@ -29,7 +29,7 @@ struct nss_cmd_ctx { struct cli_ctx *cctx; const char *name; - uint64_t id; + uid_t id; bool check_expiration; }; @@ -189,12 +189,15 @@ done: 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_getpw_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; int timeout; uint64_t lastUpdate; uint8_t *body; @@ -209,16 +212,24 @@ static void nss_cmd_getpw_callback(void *ptr, int status, return; } + if (nctx->name) { + callback_fn = &nss_cmd_getpwnam_callback; + } else { + callback_fn = &nss_cmd_getpwuid_callback; + } + if (res->count == 0 && nctx->check_expiration) { /* dont loop forever :-) */ nctx->check_expiration = false; + timeout = SSS_NSS_SOCKET_TIMEOUT/2; - ret = nss_dp_send_acct_req(cctx->nctx, nctx, - nss_cmd_getpwnam_callback, nctx, - SSS_NSS_SOCKET_TIMEOUT/2, "*", - NSS_DP_USER, nctx->name, 0); + ret = nss_dp_send_acct_req(cctx->nctx, nctx, callback_fn, nctx, + timeout, "*", 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) { @@ -255,12 +266,14 @@ static void nss_cmd_getpw_callback(void *ptr, int status, /* dont loop forever :-) */ nctx->check_expiration = false; + timeout = SSS_NSS_SOCKET_TIMEOUT/2; - ret = nss_dp_send_acct_req(cctx->nctx, nctx, - nss_cmd_getpwnam_callback, nctx, - SSS_NSS_SOCKET_TIMEOUT/2, "*", - NSS_DP_USER, nctx->name, 0); + ret = nss_dp_send_acct_req(cctx->nctx, nctx, callback_fn, nctx, + timeout, "*", 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) { @@ -319,7 +332,7 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx) size_t blen; int ret; - nctx = talloc(cctx, struct nss_cmd_ctx); + nctx = talloc_zero(cctx, struct nss_cmd_ctx); if (!nctx) { return ENOMEM; } @@ -354,13 +367,45 @@ static int nss_cmd_getpwnam(struct cli_ctx *cctx) return EOK; } +static void nss_cmd_getpwuid_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; + 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 = nss_ldb_getpwuid(nctx, cctx->ev, cctx->nctx->lctx, + nctx->id, nss_cmd_getpw_callback, nctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); + + ret = nss_cmd_send_error(nctx, ret); + if (ret != EOK) { + NSS_CMD_FATAL_ERROR(cctx); + } + } +} + static int nss_cmd_getpwuid(struct cli_ctx *cctx) { struct nss_cmd_ctx *nctx; uint8_t *body; size_t blen; int ret; - uint64_t uid; + + nctx = talloc_zero(cctx, struct nss_cmd_ctx); + if (!nctx) { + return ENOMEM; + } + nctx->cctx = cctx; + nctx->check_expiration = true; /* get uid to query */ nss_packet_get_body(cctx->creq->in, &body, &blen); @@ -369,20 +414,24 @@ static int nss_cmd_getpwuid(struct cli_ctx *cctx) return EINVAL; } - uid = *((uint64_t *)body); + nctx->id = (uid_t)*((uint64_t *)body); - DEBUG(4, ("Requesting info for [%lu]\n", uid)); + DEBUG(4, ("Requesting info for [%lu]\n", nctx->id)); - nctx = talloc(cctx, struct nss_cmd_ctx); - if (!nctx) { - return ENOMEM; - } - nctx->cctx = cctx; + /* FIXME: Just ask all backends for now, until we check for ranges */ - ret = nss_ldb_getpwuid(nctx, cctx->ev, cctx->nctx->lctx, uid, - nss_cmd_getpw_callback, nctx); + ret = nss_ldb_getpwuid(nctx, cctx->ev, cctx->nctx->lctx, + nctx->id, nss_cmd_getpw_callback, nctx); + if (ret != EOK) { + DEBUG(1, ("Failed to make request to our cache!\n")); - return ret; + ret = nss_cmd_send_error(nctx, ret); + if (ret != EOK) { + return ret; + } + } + + return EOK; } /* to keep it simple at this stage we are retrieving the diff --git a/server/providers/dp_backend.h b/server/providers/dp_backend.h index c7cb61c40..1ad31d5ec 100644 --- a/server/providers/dp_backend.h +++ b/server/providers/dp_backend.h @@ -52,6 +52,7 @@ int dp_be_store_account_posix(struct be_ctx *ctx, uint64_t uid, uint64_t gid, char *gecos, char *homedir, char *shell); int dp_be_remove_account_posix(struct be_ctx *ctx, char *name); +int dp_be_remove_account_posix_by_uid(struct be_ctx *ctx, uid_t uid); int dp_be_cachedb_init(struct be_ctx *ctx); diff --git a/server/providers/dp_backend_store.c b/server/providers/dp_backend_store.c index 9bccb2184..f9c55e0be 100644 --- a/server/providers/dp_backend_store.c +++ b/server/providers/dp_backend_store.c @@ -27,6 +27,11 @@ #include "providers/dp_backend.h" #include +/* NOTE: these functions ues ldb sync calls, but the cache db is a + * local TDB, so there should never be an issue. + * In case this changes (ex. plugins that contact the network etc.. + * make sure to split functions in multiple async calls */ + int dp_be_store_account_posix(struct be_ctx *ctx, char *name, char *pwd, uint64_t uid, uint64_t gid, @@ -47,7 +52,7 @@ int dp_be_store_account_posix(struct be_ctx *ctx, } account_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb, - "uid=%s,cn=users,cn=%s,cn=remote", + "uid=%s,cn=users,cn=%s,cn=accounts", name, ctx->domain); if (!account_dn) { talloc_free(tmp_ctx); @@ -61,26 +66,8 @@ int dp_be_store_account_posix(struct be_ctx *ctx, goto done; } - res = talloc_zero(tmp_ctx, struct ldb_result); - if (!res) { - ret = ENOMEM; - goto done; - } - - lret = ldb_build_search_req(&req, ctx->ldb, tmp_ctx, - account_dn, LDB_SCOPE_BASE, - "(objectClass=User)", attrs, NULL, - res, ldb_search_default_callback, NULL); - if (lret != LDB_SUCCESS) { - DEBUG(1, ("Failed to build search request (%d) !?\n", lret)); - ret = EIO; - goto done; - } - - lret = ldb_request(ctx->ldb, req); - if (lret == LDB_SUCCESS) { - lret = ldb_wait(req->handle, LDB_WAIT_ALL); - } + lret = ldb_search(ctx->ldb, tmp_ctx, &res, account_dn, + LDB_SCOPE_BASE, attrs, "(objectClass=User)"); if (lret != LDB_SUCCESS) { DEBUG(1, ("Failed to make search request: %s(%d)[%s]\n", ldb_strerror(lret), lret, ldb_errstring(ctx->ldb))); @@ -88,7 +75,6 @@ int dp_be_store_account_posix(struct be_ctx *ctx, goto done; } - talloc_free(req); req = NULL; msg = ldb_msg_new(tmp_ctx); @@ -159,7 +145,7 @@ int dp_be_store_account_posix(struct be_ctx *ctx, lret = ldb_msg_add_empty(msg, "uidNumber", flags, NULL); if (lret == LDB_SUCCESS) { lret = ldb_msg_add_fmt(msg, "uidNumber", - "%lu", (long unsigned)uid); + "%lu", (unsigned long)uid); } if (lret != LDB_SUCCESS) { ret = errno; @@ -176,7 +162,7 @@ int dp_be_store_account_posix(struct be_ctx *ctx, lret = ldb_msg_add_empty(msg, "gidNumber", flags, NULL); if (lret == LDB_SUCCESS) { lret = ldb_msg_add_fmt(msg, "gidNumber", - "%lu", (long unsigned)gid); + "%lu", (unsigned long)gid); } if (lret != LDB_SUCCESS) { ret = errno; @@ -316,6 +302,95 @@ int dp_be_remove_account_posix(struct be_ctx *ctx, char *name) return ret; } +int dp_be_remove_account_posix_by_uid(struct be_ctx *ctx, uid_t uid) +{ + TALLOC_CTX *tmp_ctx; + const char *attrs[] = { "name", "uid", NULL }; + struct ldb_dn *base_dn; + struct ldb_dn *account_dn; + struct ldb_result *res; + int lret, ret; + + tmp_ctx = talloc_new(ctx); + if (!tmp_ctx) { + return ENOMEM; + } + + base_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb, + "cn=users,cn=%s,cn=accounts", ctx->domain); + if (!base_dn) { + talloc_free(tmp_ctx); + return ENOMEM; + } + + lret = ldb_transaction_start(ctx->ldb); + if (lret != LDB_SUCCESS) { + DEBUG(1, ("Failed ldb transaction start !? (%d)\n", lret)); + ret = EIO; + goto done; + } + + lret = ldb_search(ctx->ldb, tmp_ctx, &res, base_dn, + LDB_SCOPE_BASE, attrs, + "(&(uid=%lu)(objectClass=User))", + (unsigned long)uid); + if (lret != LDB_SUCCESS) { + DEBUG(1, ("Failed to make search request: %s(%d)[%s]\n", + ldb_strerror(lret), lret, ldb_errstring(ctx->ldb))); + ret = EIO; + goto done; + } + + if (res->count == 0) { + ret = EOK; + goto done; + } + if (res->count > 1) { + DEBUG(0, ("Cache DB corrupted, base search returned %d results\n", + res->count)); + ret = EOK; + goto done; + } + + account_dn = ldb_dn_copy(tmp_ctx, res->msgs[0]->dn); + if (!account_dn) { + ret = ENOMEM; + goto done; + } + + talloc_free(res); + res = NULL; + + ret = ldb_delete(ctx->ldb, account_dn); + + if (ret != LDB_SUCCESS) { + DEBUG(2, ("LDB Error: %s(%d)\nError Message: [%s]\n", + ldb_strerror(ret), ret, ldb_errstring(ctx->ldb))); + ret = EIO; + goto done; + } + + lret = ldb_transaction_commit(ctx->ldb); + if (lret != LDB_SUCCESS) { + DEBUG(1, ("Failed ldb transaction commit !! (%d)\n", lret)); + ret = EIO; + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + lret = ldb_transaction_cancel(ctx->ldb); + if (lret != LDB_SUCCESS) { + DEBUG(1, ("Failed to cancel ldb transaction (%d)\n", lret)); + } + } + + talloc_free(tmp_ctx); + return ret; +} + /* TODO: Unify with nss_ldb and provide a single cachedb interface */ int dp_be_cachedb_init(struct be_ctx *ctx) diff --git a/server/providers/proxy.c b/server/providers/proxy.c index b5674490e..f93a5ede1 100644 --- a/server/providers/proxy.c +++ b/server/providers/proxy.c @@ -72,7 +72,8 @@ static int get_pw_name(struct be_ctx *be_ctx, struct proxy_ctx *proxy_ctx, char ret = dp_be_remove_account_posix(be_ctx, name); break; case NSS_STATUS_SUCCESS: - ret = dp_be_store_account_posix(be_ctx, name, result.pw_passwd, + ret = dp_be_store_account_posix(be_ctx, + result.pw_name, result.pw_passwd, result.pw_uid, result.pw_gid, result.pw_gecos, result.pw_dir, result.pw_shell); @@ -93,6 +94,46 @@ static int get_pw_name(struct be_ctx *be_ctx, struct proxy_ctx *proxy_ctx, char return ret; } +static int get_pw_uid(struct be_ctx *be_ctx, struct proxy_ctx *proxy_ctx, uid_t uid) +{ + struct proxy_nss_ops *ops = &proxy_ctx->ops; + enum nss_status status; + struct passwd result; + char *buffer; + int ret; + + buffer = talloc_size(NULL, 4096); + if (!buffer) return ENOMEM; + + status = ops->getpwuid_r(uid, &result, buffer, 4096, &ret); + + switch (status) { + case NSS_STATUS_NOTFOUND: + ret = dp_be_remove_account_posix_by_uid(be_ctx, uid); + break; + case NSS_STATUS_SUCCESS: + ret = dp_be_store_account_posix(be_ctx, + result.pw_name, result.pw_passwd, + result.pw_uid, result.pw_gid, + result.pw_gecos, result.pw_dir, + result.pw_shell); + break; + default: + DEBUG(2, ("proxy -> getpwuid_r failed for '%lu' (%d)[%s]\n", + (unsigned long)uid, ret, strerror(ret))); + talloc_free(buffer); + return ret; + } + + if (ret != EOK) { + DEBUG(1, ("Failed to update LDB Cache for '%lu' (%d) !?\n", + (unsigned long)uid, ret)); + } + + talloc_free(buffer); + return ret; +} + static int proxy_check_online(struct be_ctx *be_ctx, int *reply) { *reply = MOD_ONLINE; @@ -104,6 +145,7 @@ static int proxy_get_account_info(struct be_ctx *be_ctx, int filter_type, char *filter_value) { struct proxy_ctx *ctx; + uid_t uid; ctx = talloc_get_type(be_ctx->pvt_data, struct proxy_ctx); @@ -124,6 +166,23 @@ static int proxy_get_account_info(struct be_ctx *be_ctx, } break; case BE_FILTER_IDNUM: + switch (attr_type) { + case BE_ATTR_CORE: + if (strchr(filter_value, '*')) { + return EINVAL; + } else { + char *endptr; + errno = 0; + uid = (uid_t)strtol(filter_value, &endptr, 0); + if (errno || *endptr || (filter_value == endptr)) { + return EINVAL; + } + return get_pw_uid(be_ctx, ctx, uid); + } + break; + default: + return EINVAL; + } break; default: return EINVAL; -- cgit