From ac8921a1faa06f711e91bd2d3a4acd2dc8f1dae3 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Wed, 28 Mar 2012 01:04:52 +0200 Subject: [PATCH] Canonicalize user aliases in the proxy provider https://fedorahosted.org/sssd/ticket/1249 --- src/providers/proxy/proxy_id.c | 336 ++++++++++++++++++++++----------------- 1 files changed, 189 insertions(+), 147 deletions(-) diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c index bf0f50932978263cf19ba6984cac0e52a8c57242..1ddd0ebf761978847c85a71f3aebe7d7404338bd 100644 --- a/src/providers/proxy/proxy_id.c +++ b/src/providers/proxy/proxy_id.c @@ -27,11 +27,13 @@ /* =Getpwnam-wrapper======================================================*/ -static int delete_user(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, - struct sss_domain_info *domain, const char *name); - static int save_user(struct sysdb_ctx *sysdb, bool lowercase, - struct passwd *pwd, uint64_t cache_timeout); + struct passwd *pwd, const char *alias, + uint64_t cache_timeout); + +static int +handle_getpw_result(enum nss_status status, struct passwd *pwd, + struct sss_domain_info *dom, bool *del_user); static int get_pw_name(TALLOC_CTX *mem_ctx, struct proxy_id_ctx *ctx, @@ -45,10 +47,12 @@ static int get_pw_name(TALLOC_CTX *mem_ctx, char *buffer; size_t buflen; int ret; + uid_t uid; + bool del_user; - DEBUG(7, ("Searching user by name (%s)\n", name)); + DEBUG(SSSDBG_TRACE_FUNC, ("Searching user by name (%s)\n", name)); - tmpctx = talloc_new(mem_ctx); + tmpctx = talloc_new(NULL); if (!tmpctx) { return ENOMEM; } @@ -71,67 +75,120 @@ static int get_pw_name(TALLOC_CTX *mem_ctx, /* FIXME: should we move this call outside the transaction to keep the * transaction as short as possible ? */ status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret); + ret = handle_getpw_result(status, pwd, dom, &del_user); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, + ("getpwnam failed [%d]: %s\n", ret, strerror(ret))); + goto done; + } + + if (del_user) { + DEBUG(SSSDBG_TRACE_FUNC, + ("User %s does not exist (or is invalid) on remote server," + " deleting!\n", name)); + ret = sysdb_delete_user(sysdb, name, 0); + goto done; + } + + uid = pwd->pw_uid; + memset(buffer, 0, buflen); + + /* Canonicalize the username in case it was actually an alias */ + status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret); + ret = handle_getpw_result(status, pwd, dom, &del_user); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, + ("getpwuid failed [%d]: %s\n", ret, strerror(ret))); + goto done; + } + + if (del_user) { + DEBUG(SSSDBG_TRACE_FUNC, + ("User %s does not exist (or is invalid) on remote server," + " deleting!\n", name)); + ret = sysdb_delete_user(sysdb, name, uid); + goto done; + } + + /* Both lookups went fine, we can save the user now */ + ret = save_user(sysdb, !dom->case_sensitive, pwd, + name, dom->user_timeout); + +done: + talloc_zfree(tmpctx); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, + ("proxy -> getpwnam_r failed for '%s' <%d>\n", + name, status)); + } + return ret; +} + +static int +handle_getpw_result(enum nss_status status, struct passwd *pwd, + struct sss_domain_info *dom, bool *del_user) +{ + int ret = EOK; + TALLOC_CTX *tmpctx; + + if (!del_user) { + return EINVAL; + } + *del_user = false; + + tmpctx = talloc_new(NULL); + if (!tmpctx) { + return ENOMEM; + } switch (status) { case NSS_STATUS_NOTFOUND: - DEBUG(7, ("User %s not found.\n", name)); - ret = delete_user(tmpctx, sysdb, dom, name); - if (ret) { - goto done; - } + DEBUG(SSSDBG_MINOR_FAILURE, ("User not found.\n")); + *del_user = true; break; case NSS_STATUS_SUCCESS: - DEBUG(7, ("User %s found: (%s, %d, %d)\n", - name, pwd->pw_name, pwd->pw_uid, pwd->pw_gid)); + DEBUG(SSSDBG_TRACE_FUNC, ("User found: (%s, %d, %d)\n", + pwd->pw_name, pwd->pw_uid, pwd->pw_gid)); /* uid=0 or gid=0 are invalid values */ /* also check that the id is in the valid range for this domain */ if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) || OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) { - DEBUG(2, ("User [%s] filtered out! (id out of range)\n", name)); - ret = delete_user(tmpctx, sysdb, dom, name); - if (ret) { - goto done; - } + DEBUG(SSSDBG_MINOR_FAILURE, + ("User filtered out! (id out of range)\n")); + *del_user = true; break; } - - ret = save_user(sysdb, !dom->case_sensitive, pwd, dom->user_timeout); - if (ret) { - goto done; - } break; case NSS_STATUS_UNAVAIL: - /* "remote" backend unavailable. Enter offline mode */ + DEBUG(SSSDBG_MINOR_FAILURE, + ("Remote back end is not available. Entering offline mode\n")); ret = ENXIO; - goto done; + break; default: ret = EIO; - goto done; + break; } -done: talloc_zfree(tmpctx); - if (ret) { - DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n", - name, status)); - } return ret; } static int save_user(struct sysdb_ctx *sysdb, bool lowercase, - struct passwd *pwd, uint64_t cache_timeout) + struct passwd *pwd, const char *alias, + uint64_t cache_timeout) { const char *shell; char *lower; struct sysdb_attrs *attrs = NULL; errno_t ret; + const char *cased_alias; if (pwd->pw_shell && pwd->pw_shell[0] != '\0') { shell = pwd->pw_shell; @@ -139,13 +196,15 @@ static int save_user(struct sysdb_ctx *sysdb, bool lowercase, shell = NULL; } - if (lowercase) { + if (!lowercase || alias) { attrs = sysdb_new_attrs(NULL); if (!attrs) { DEBUG(SSSDBG_CRIT_FAILURE, ("Allocation error ?!\n")); return ENOMEM; } + } + if (lowercase) { lower = sss_tc_utf8_str_tolower(attrs, pwd->pw_name); if (!lower) { DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot convert name to lowercase\n")); @@ -161,6 +220,21 @@ static int save_user(struct sysdb_ctx *sysdb, bool lowercase, } } + if (alias) { + cased_alias = sss_get_cased_name(attrs, alias, !lowercase); + if (!cased_alias) { + talloc_zfree(attrs); + return ENOMEM; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, cased_alias); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not add name alias\n")); + talloc_zfree(attrs); + return ret; + } + } + ret = sysdb_store_user(sysdb, pwd->pw_name, pwd->pw_passwd, @@ -182,22 +256,6 @@ static int save_user(struct sysdb_ctx *sysdb, bool lowercase, return EOK; } -static int delete_user(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, - struct sss_domain_info *domain, const char *name) -{ - struct ldb_dn *dn; - - DEBUG(7, ("User %s does not exist (or is invalid) on remote server," - " deleting!\n", name)); - - dn = sysdb_user_dn(sysdb, mem_ctx, domain->name, name); - if (!dn) { - return ENOMEM; - } - - return sysdb_delete_entry(sysdb, dn, true); -} - /* =Getpwuid-wrapper======================================================*/ static int get_pw_uid(TALLOC_CTX *mem_ctx, @@ -214,9 +272,9 @@ static int get_pw_uid(TALLOC_CTX *mem_ctx, bool del_user = false; int ret; - DEBUG(7, ("Searching user by uid (%d)\n", uid)); + DEBUG(SSSDBG_TRACE_FUNC, ("Searching user by uid (%d)\n", uid)); - tmpctx = talloc_new(mem_ctx); + tmpctx = talloc_new(NULL); if (!tmpctx) { return ENOMEM; } @@ -224,75 +282,40 @@ static int get_pw_uid(TALLOC_CTX *mem_ctx, pwd = talloc_zero(tmpctx, struct passwd); if (!pwd) { ret = ENOMEM; - DEBUG(1, ("proxy -> getpwuid_r failed for '%d': [%d] %s\n", - uid, ret, strerror(ret))); - return ret; + goto done; } buflen = DEFAULT_BUFSIZE; buffer = talloc_size(tmpctx, buflen); if (!buffer) { ret = ENOMEM; - DEBUG(1, ("proxy -> getpwuid_r failed for '%d': [%d] %s\n", - uid, ret, strerror(ret))); - return ret; + goto done; } status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret); - - switch (status) { - case NSS_STATUS_NOTFOUND: - - DEBUG(7, ("User %d not found.\n", uid)); - del_user = true; - break; - - case NSS_STATUS_SUCCESS: - - DEBUG(7, ("User %d found (%s, %d, %d)\n", - uid, pwd->pw_name, pwd->pw_uid, pwd->pw_gid)); - - /* uid=0 or gid=0 are invalid values */ - /* also check that the id is in the valid range for this domain */ - if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) || - OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) { - - DEBUG(2, ("User [%s] filtered out! (id out of range)\n", - pwd->pw_name)); - del_user = true; - break; - } - - ret = save_user(sysdb, !dom->case_sensitive, pwd, dom->user_timeout); - if (ret) { - goto done; - } - break; - - case NSS_STATUS_UNAVAIL: - /* "remote" backend unavailable. Enter offline mode */ - ret = ENXIO; - goto done; - - default: - ret = EIO; + ret = handle_getpw_result(status, pwd, dom, &del_user); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, + ("getpwuid failed [%d]: %s\n", ret, strerror(ret))); goto done; } if (del_user) { - DEBUG(7, ("User %d does not exist (or is invalid) on remote server," - " deleting!\n", uid)); - + DEBUG(SSSDBG_TRACE_FUNC, + ("User %d does not exist (or is invalid) on remote server," + " deleting!\n", uid)); ret = sysdb_delete_user(sysdb, NULL, uid); - if (ret) { - goto done; - } + goto done; } + ret = save_user(sysdb, !dom->case_sensitive, pwd, + NULL, dom->user_timeout); + done: talloc_zfree(tmpctx); if (ret) { - DEBUG(2, ("proxy -> getpwuid_r failed for '%d' <%d>\n", uid, status)); + DEBUG(SSSDBG_CRIT_FAILURE, + ("proxy -> getpwuid_r failed for '%d' <%d>\n", uid, status)); } return ret; } @@ -394,7 +417,8 @@ again: goto again; /* skip */ } - ret = save_user(sysdb, !dom->case_sensitive, pwd, dom->user_timeout); + ret = save_user(sysdb, !dom->case_sensitive, pwd, + NULL, dom->user_timeout); if (ret) { /* Do not fail completely on errors. * Just report the failure to save and go on */ @@ -993,6 +1017,8 @@ static int get_initgr(TALLOC_CTX *mem_ctx, char *buffer; size_t buflen; int ret; + bool del_user; + uid_t uid; tmpctx = talloc_new(mem_ctx); if (!tmpctx) { @@ -1002,74 +1028,90 @@ static int get_initgr(TALLOC_CTX *mem_ctx, pwd = talloc_zero(tmpctx, struct passwd); if (!pwd) { ret = ENOMEM; - goto done; + goto fail; } buflen = DEFAULT_BUFSIZE; buffer = talloc_size(tmpctx, buflen); if (!buffer) { ret = ENOMEM; - goto done; + goto fail; } ret = sysdb_transaction_start(sysdb); if (ret) { - goto done; + goto fail; } in_transaction = true; /* FIXME: should we move this call outside the transaction to keep the * transaction as short as possible ? */ status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret); - - switch (status) { - case NSS_STATUS_NOTFOUND: - - DEBUG(7, ("User %s not found.\n", name)); - ret = delete_user(tmpctx, sysdb, dom, name); + ret = handle_getpw_result(status, pwd, dom, &del_user); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, + ("getpwnam failed [%d]: %s\n", ret, strerror(ret))); + goto fail; + } + + if (del_user) { + DEBUG(SSSDBG_TRACE_FUNC, + ("User %s does not exist (or is invalid) on remote server," + " deleting!\n", name)); + ret = sysdb_delete_user(sysdb, name, 0); if (ret) { - goto done; - } - break; - - case NSS_STATUS_SUCCESS: - - /* uid=0 or gid=0 are invalid values */ - /* also check that the id is in the valid range for this domain */ - if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) || - OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) { - - DEBUG(2, ("User [%s] filtered out! (id out of range)\n", - name)); - ret = delete_user(tmpctx, sysdb, dom, name); - break; + DEBUG(SSSDBG_OP_FAILURE, ("Could not delete user\n")); + goto fail; } - - ret = save_user(sysdb, !dom->case_sensitive, pwd, dom->user_timeout); + goto done; + } + + uid = pwd->pw_uid; + memset(buffer, 0, buflen); + + /* Canonicalize the username in case it was actually an alias */ + status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret); + ret = handle_getpw_result(status, pwd, dom, &del_user); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, + ("getpwuid failed [%d]: %s\n", ret, strerror(ret))); + goto fail; + } + + if (del_user) { + DEBUG(SSSDBG_TRACE_FUNC, + ("User %s does not exist (or is invalid) on remote server," + " deleting!\n", name)); + ret = sysdb_delete_user(sysdb, name, uid); if (ret) { - goto done; - } - - ret = get_initgr_groups_process(tmpctx, ctx, sysdb, dom, pwd); - if (ret == EOK) { - ret = sysdb_transaction_commit(sysdb); - in_transaction = true; + DEBUG(SSSDBG_OP_FAILURE, ("Could not delete user\n")); + goto fail; } - break; - - case NSS_STATUS_UNAVAIL: - /* "remote" backend unavailable. Enter offline mode */ - ret = ENXIO; - break; - - default: - DEBUG(2, ("proxy -> getpwnam_r failed for '%s' <%d>\n", - name, status)); - ret = EIO; - break; + goto done; + } + + ret = save_user(sysdb, !dom->case_sensitive, pwd, + name, dom->user_timeout); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not save user\n")); + goto fail; + } + + ret = get_initgr_groups_process(tmpctx, ctx, sysdb, dom, pwd); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not process initgroups\n")); + goto fail; } done: + ret = sysdb_transaction_commit(sysdb); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to commit transaction\n")); + goto fail; + } + in_transaction = false; + +fail: talloc_zfree(tmpctx); if (in_transaction) { sysdb_transaction_cancel(sysdb); -- 1.7.7.6