From f5d4b05027acce06e3509ecb68869d1c7ef37180 Mon Sep 17 00:00:00 2001 From: Pavel Březina Date: Tue, 7 Feb 2012 14:08:55 +0100 Subject: Redesign purging of the sudo cache https://fedorahosted.org/sssd/ticket/1173 --- src/db/sysdb_sudo.c | 372 +++++++++++++++++++++++++++++++++-------- src/db/sysdb_sudo.h | 18 +- src/providers/ldap/sdap_sudo.c | 74 +++++--- 3 files changed, 370 insertions(+), 94 deletions(-) (limited to 'src') diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c index 84245d53a..f579ba8b8 100644 --- a/src/db/sysdb_sudo.c +++ b/src/db/sysdb_sudo.c @@ -339,33 +339,6 @@ done: return EOK; } -static errno_t -sysdb_sudo_purge_subdir(struct sysdb_ctx *sysdb, - struct sss_domain_info *domain, - const char *subdir) -{ - struct ldb_dn *base_dn = NULL; - TALLOC_CTX *tmp_ctx = NULL; - errno_t ret; - - tmp_ctx = talloc_new(NULL); - NULL_CHECK(tmp_ctx, ret, done); - - base_dn = sysdb_custom_subtree_dn(sysdb, tmp_ctx, domain->name, subdir); - NULL_CHECK(base_dn, ret, done); - - ret = sysdb_delete_recursive(sysdb, base_dn, true); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("sysdb_delete_recursive failed.\n")); - goto done; - } - - ret = EOK; -done: - talloc_free(tmp_ctx); - return EOK; -} - errno_t sysdb_save_sudorule(struct sysdb_ctx *sysdb_ctx, const char *rule_name, @@ -400,10 +373,150 @@ sysdb_save_sudorule(struct sysdb_ctx *sysdb_ctx, return EOK; } -errno_t -sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, - struct sss_domain_info *domain, - const char *filter) +errno_t sysdb_sudo_set_refreshed(struct sysdb_ctx *sysdb, + bool refreshed) +{ + errno_t ret; + struct ldb_dn *dn; + TALLOC_CTX *tmp_ctx; + + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + ret = ENOMEM; + goto done; + } + + dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE, + SUDORULE_SUBDIR, sysdb->domain->name); + if (!dn) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_set_bool(sysdb, dn, SUDORULE_SUBDIR, + SYSDB_SUDO_AT_REFRESHED, refreshed); + +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t sysdb_sudo_get_refreshed(struct sysdb_ctx *sysdb, + bool *refreshed) +{ + errno_t ret; + struct ldb_dn *dn; + TALLOC_CTX *tmp_ctx; + + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + ret = ENOMEM; + goto done; + } + + dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE, + SUDORULE_SUBDIR, sysdb->domain->name); + if (!dn) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_get_bool(sysdb, dn, SYSDB_SUDO_AT_REFRESHED, refreshed); + +done: + talloc_free(tmp_ctx); + return ret; +} + +char **sysdb_sudo_build_sudouser(TALLOC_CTX *mem_ctx, const char *username, + uid_t uid, char **groupnames, bool include_all) +{ + char **sudouser = NULL; + int count = 0; + errno_t ret; + int i; + + if (username == NULL || uid == 0) { + return NULL; + } + + count = include_all ? 3 : 2; + sudouser = talloc_array(NULL, char*, count + 1); + NULL_CHECK(sudouser, ret, done); + + sudouser[0] = talloc_strdup(sudouser, username); + NULL_CHECK(sudouser[0], ret, done); + + sudouser[1] = talloc_asprintf(sudouser, "#%llu", (unsigned long long)uid); + NULL_CHECK(sudouser[1], ret, done); + + if (include_all) { + sudouser[2] = talloc_strdup(sudouser, "ALL"); + NULL_CHECK(sudouser[2], ret, done); + } + + if (groupnames != NULL) { + for (i = 0; groupnames[i] != NULL; i++) { + count++; + sudouser = talloc_realloc(NULL, sudouser, char*, count + 1); + NULL_CHECK(sudouser, ret, done); + + sudouser[count - 1] = talloc_asprintf(sudouser, "%s", groupnames[i]); + NULL_CHECK(sudouser[count - 1], ret, done); + } + } + + sudouser[count] = NULL; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(sudouser); + return NULL; + } + + return talloc_steal(mem_ctx, sudouser); +} + +/* ==================== Purge functions ==================== */ + +errno_t sysdb_sudo_purge_all(struct sysdb_ctx *sysdb) +{ + struct ldb_dn *base_dn = NULL; + TALLOC_CTX *tmp_ctx = NULL; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + NULL_CHECK(tmp_ctx, ret, done); + + base_dn = sysdb_custom_subtree_dn(sysdb, tmp_ctx, sysdb->domain->name, + SUDORULE_SUBDIR); + NULL_CHECK(base_dn, ret, done); + + ret = sysdb_delete_recursive(sysdb, base_dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("sysdb_delete_recursive failed.\n")); + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return EOK; +} + +errno_t sysdb_sudo_purge_byname(struct sysdb_ctx *sysdb, + const char *name) +{ + DEBUG(SSSDBG_TRACE_INTERNAL, ("Deleting sudo rule %s\n", name)); + return sysdb_delete_custom(sysdb, name, SUDORULE_SUBDIR); +} + +errno_t sysdb_sudo_purge_byfilter(struct sysdb_ctx *sysdb, + const char *filter) { TALLOC_CTX *tmp_ctx; size_t count; @@ -411,6 +524,8 @@ sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, const char *name; int i; errno_t ret; + errno_t sret; + bool in_transaction = false; const char *attrs[] = { SYSDB_OBJECTCLASS, SYSDB_NAME, SYSDB_SUDO_CACHE_AT_OC, @@ -419,7 +534,7 @@ sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, /* just purge all if there's no filter */ if (!filter) { - return sysdb_sudo_purge_subdir(sysdb, domain, SUDORULE_SUBDIR); + return sysdb_sudo_purge_all(sysdb); } tmp_ctx = talloc_new(NULL); @@ -429,15 +544,21 @@ sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, ret = sysdb_search_custom(tmp_ctx, sysdb, filter, SUDORULE_SUBDIR, attrs, &count, &msgs); - if (ret != EOK && ret != ENOENT) { - DEBUG(SSSDBG_CRIT_FAILURE, ("Error looking up SUDO rules")); - goto done; - } if (ret == ENOENT) { + if (ret == ENOENT) { DEBUG(SSSDBG_TRACE_FUNC, ("No rules matched\n")); ret = EOK; goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error looking up SUDO rules")); + goto done; } + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) { + goto done; + } + in_transaction = true; + for (i = 0; i < count; i++) { name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL); if (name == NULL) { @@ -446,72 +567,181 @@ sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, continue; } - ret = sysdb_delete_custom(sysdb, name, SUDORULE_SUBDIR); + ret = sysdb_sudo_purge_byname(sysdb, name); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("Could not delete rule %s\n", name)); goto done; } } - ret = EOK; + ret = sysdb_transaction_commit(sysdb); + if (ret == EOK) { + in_transaction = false; + } + done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not cancel transaction\n")); + } + } + talloc_free(tmp_ctx); return ret; } -errno_t sysdb_sudo_set_refreshed(struct sysdb_ctx *sysdb, - bool refreshed) +errno_t sysdb_sudo_purge_bysudouser(struct sysdb_ctx *sysdb, + char **sudouser) { + TALLOC_CTX *tmp_ctx = NULL; + char *filter = NULL; + char *value = NULL; + const char *rule_name = NULL; + struct ldb_message_element *attr = NULL; + struct ldb_message *msg = NULL; + struct ldb_message **rules = NULL; + size_t num_rules; errno_t ret; - struct ldb_dn *dn; - TALLOC_CTX *tmp_ctx; + errno_t sret; + int lret; + int i, j, k; + bool in_transaction = false; + const char *attrs[] = { SYSDB_OBJECTCLASS, + SYSDB_NAME, + SYSDB_SUDO_CACHE_AT_USER, + NULL }; + if (sudouser == NULL || sudouser[0] == NULL) { + return EOK; + } tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { - ret = ENOMEM; + NULL_CHECK(tmp_ctx, ret, done); + + /* create search filter */ + filter = talloc_strdup(tmp_ctx, "(|"); + NULL_CHECK(filter, ret, done); + for (i = 0; sudouser[i] != NULL; i++) { + filter = talloc_asprintf_append(filter, "(%s=%s)", + SYSDB_SUDO_CACHE_AT_USER, sudouser[i]); + NULL_CHECK(filter, ret, done); + } + filter = talloc_strdup_append(filter, ")"); + NULL_CHECK(filter, ret, done); + + /* search the rules */ + ret = sysdb_search_custom(tmp_ctx, sysdb, filter, SUDORULE_SUBDIR, attrs, + &num_rules, &rules); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error looking up SUDO rules")); + goto done; + } if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_FUNC, ("No rules matched\n")); + ret = EOK; goto done; } - dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE, - SUDORULE_SUBDIR, sysdb->domain->name); - if (!dn) { - ret = ENOMEM; + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) { goto done; } + in_transaction = true; - ret = sysdb_set_bool(sysdb, dn, SUDORULE_SUBDIR, - SYSDB_SUDO_AT_REFRESHED, refreshed); + /* + * remove values from sudoUser and delete the rule + * if the attribute is empty afterwards + */ -done: - talloc_free(tmp_ctx); - return ret; -} + for (i = 0; i < num_rules; i++) { + /* find name */ + rule_name = ldb_msg_find_attr_as_string(rules[i], SYSDB_NAME, NULL); + if (rule_name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("A rule without a name?\n")); + /* skip this one but still delete other entries */ + continue; + } -errno_t sysdb_sudo_get_refreshed(struct sysdb_ctx *sysdb, - bool *refreshed) -{ - errno_t ret; - struct ldb_dn *dn; - TALLOC_CTX *tmp_ctx; + /* find sudoUser */ + attr = ldb_msg_find_element(rules[i], SYSDB_SUDO_CACHE_AT_USER); + if (attr == NULL) { + /* this should never happen because we search by this attribute */ + DEBUG(SSSDBG_CRIT_FAILURE, ("BUG: sudoUser attribute is missing\n")); + continue; + } + /* create message */ + msg = ldb_msg_new(tmp_ctx); + NULL_CHECK(msg, ret, done); - tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { - ret = ENOMEM; - goto done; - } + msg->dn = ldb_dn_new_fmt(msg, sysdb->ldb, SYSDB_TMPL_CUSTOM, rule_name, + SUDORULE_SUBDIR, sysdb->domain->name); + NULL_CHECK(msg->dn, ret, done); - dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE, - SUDORULE_SUBDIR, sysdb->domain->name); - if (!dn) { - ret = ENOMEM; - goto done; + /* create empty sudoUser */ + lret = ldb_msg_add_empty(msg, SYSDB_SUDO_CACHE_AT_USER, + LDB_FLAG_MOD_DELETE, NULL); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + + /* filter values */ + for (j = 0; j < attr->num_values; j++) { + value = (char*)(attr->values[j].data); + for (k = 0; sudouser[k] != NULL; k++) { + if (strcmp(value, sudouser[k]) == 0) { + /* delete value from cache */ + lret = ldb_msg_add_string(msg, SYSDB_SUDO_CACHE_AT_USER, + sudouser[k]); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + break; + } + } + } + + /* update the cache */ + if (msg->elements[0].num_values == attr->num_values) { + /* sudoUser would remain empty, delete the rule */ + ret = sysdb_sudo_purge_byname(sysdb, rule_name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not delete rule %s\n", + rule_name)); + goto done; + } + } else { + /* sudoUser will not be empty, modify the rule */ + DEBUG(SSSDBG_TRACE_INTERNAL, ("Modifying sudoUser of rule %s\n", + rule_name)); + lret = ldb_modify(sysdb->ldb, msg); + if (lret != LDB_SUCCESS) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not modify rule %s\n", + rule_name)); + ret = sysdb_error_to_errno(lret); + goto done; + } + } + + talloc_free(msg); } - ret = sysdb_get_bool(sysdb, dn, SYSDB_SUDO_AT_REFRESHED, refreshed); + ret = sysdb_transaction_commit(sysdb); + if (ret == EOK) { + in_transaction = false; + } done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not cancel transaction\n")); + } + } + talloc_free(tmp_ctx); return ret; } + diff --git a/src/db/sysdb_sudo.h b/src/db/sysdb_sudo.h index 87a3b9472..a8e2f8910 100644 --- a/src/db/sysdb_sudo.h +++ b/src/db/sysdb_sudo.h @@ -81,14 +81,24 @@ sysdb_save_sudorule(struct sysdb_ctx *sysdb_ctx, const char *rule_name, struct sysdb_attrs *attrs); -errno_t sysdb_purge_sudorule_subtree(struct sysdb_ctx *sysdb, - struct sss_domain_info *domain, - const char *filter); - errno_t sysdb_sudo_set_refreshed(struct sysdb_ctx *sysdb, bool refreshed); errno_t sysdb_sudo_get_refreshed(struct sysdb_ctx *sysdb, bool *refreshed); +char **sysdb_sudo_build_sudouser(TALLOC_CTX *mem_ctx, const char *username, + uid_t uid, char **groupnames, bool include_all); + +errno_t sysdb_sudo_purge_all(struct sysdb_ctx *sysdb); + +errno_t sysdb_sudo_purge_byname(struct sysdb_ctx *sysdb, + const char *name); + +errno_t sysdb_sudo_purge_byfilter(struct sysdb_ctx *sysdb, + const char *filter); + +errno_t sysdb_sudo_purge_bysudouser(struct sysdb_ctx *sysdb, + char **sudoUser); + #endif /* _SYSDB_SUDO_H_ */ diff --git a/src/providers/ldap/sdap_sudo.c b/src/providers/ldap/sdap_sudo.c index 5c7448f96..4aaf04ced 100644 --- a/src/providers/ldap/sdap_sudo.c +++ b/src/providers/ldap/sdap_sudo.c @@ -653,7 +653,10 @@ int sdap_sudo_purge_sudoers(struct sysdb_ctx *sysdb_ctx, { TALLOC_CTX *tmp_ctx; char *filter = NULL; + char **sudouser = NULL; int ret = EOK; + errno_t sret; + bool in_transaction = false; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { @@ -661,20 +664,52 @@ int sdap_sudo_purge_sudoers(struct sysdb_ctx *sysdb_ctx, return ENOMEM; } + ret = sysdb_transaction_start(sysdb_ctx); + if (ret != EOK) { + goto done; + } + in_transaction = true; + switch (sudo_req->type) { case BE_REQ_SUDO_ALL: - filter = NULL; + DEBUG(SSSDBG_TRACE_FUNC, ("Purging SUDOers cache of all rules\n")); + ret = sysdb_sudo_purge_all(sysdb_ctx); break; case BE_REQ_SUDO_DEFAULTS: - ret = sysdb_get_sudo_filter(tmp_ctx, NULL, 0, NULL, - SYSDB_SUDO_FILTER_INCLUDE_DFL, &filter); + DEBUG(SSSDBG_TRACE_FUNC, ("Purging SUDOers cache of default options\n")); + ret = sysdb_sudo_purge_byname(sysdb_ctx, SDAP_SUDO_DEFAULTS); break; case BE_REQ_SUDO_USER: - ret = sysdb_get_sudo_filter(tmp_ctx, sudo_req->username, sudo_req->uid, - sudo_req->groups, - SYSDB_SUDO_FILTER_USERINFO - | SYSDB_SUDO_FILTER_INCLUDE_ALL - | SYSDB_SUDO_FILTER_INCLUDE_DFL, &filter); + DEBUG(SSSDBG_TRACE_FUNC, ("Purging SUDOers cache of user's [%s] rules\n", + sudo_req->username)); + + /* netgroups */ + ret = sysdb_get_sudo_filter(tmp_ctx, NULL, 0, NULL, + SYSDB_SUDO_FILTER_NGRS, &filter); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create filter to purge " + "SUDOers cache [%d]: %s\n", ret, strerror(ret))); + goto done; + } + + ret = sysdb_sudo_purge_byfilter(sysdb_ctx, filter); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to purge SUDOers cache " + "(netgroups) [%d]: %s\n", ret, strerror(ret))); + goto done; + } + + /* user, uid, groups */ + sudouser = sysdb_sudo_build_sudouser(tmp_ctx, sudo_req->username, + sudo_req->uid, sudo_req->groups, + true); + if (sudouser == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create sudoUser to purge " + "SUDOers cache [%d]: %s\n", ret, strerror(ret))); + goto done; + } + + ret = sysdb_sudo_purge_bysudouser(sysdb_ctx, sudouser); break; default: DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid request type %d\n", sudo_req->type)); @@ -682,23 +717,24 @@ int sdap_sudo_purge_sudoers(struct sysdb_ctx *sysdb_ctx, } if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create filter to purge " - "sudoers cache [%d]: %s\n", ret, strerror(ret))); - goto done; - } - - /* Purge rules */ - DEBUG(SSSDBG_TRACE_FUNC, ("Purging sudo cache with filter [%s]\n", filter)); - ret = sysdb_purge_sudorule_subtree(sysdb_ctx, domain, filter); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to purge sudoers cache [%d]: %s\n", + DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to purge SUDOers cache [%d]: %s\n", ret, strerror(ret))); goto done; } - ret = EOK; + ret = sysdb_transaction_commit(sysdb_ctx); + if (ret == EOK) { + in_transaction = false; + } done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb_ctx); + if (sret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not cancel transaction\n")); + } + } + talloc_free(tmp_ctx); return ret; } -- cgit