From c6257286e9a31dfd42d28c99a22a69e2c4717a61 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Wed, 12 Jan 2011 16:41:26 -0500 Subject: Delete attributes that are removed from LDAP Sometimes, a value in LDAP will cease to exist (the classic example being shadowExpire). We need to make sure we purge that value from SSSD's sysdb as well. https://fedorahosted.org/sssd/ticket/750 --- src/db/sysdb.h | 7 ++ src/db/sysdb_ops.c | 118 +++++++++++++++++++++++++++++++ src/providers/ldap/ldap_common.c | 117 ++++++++++++++++++++++++++++++ src/providers/ldap/ldap_common.h | 13 ++++ src/providers/ldap/sdap_async_accounts.c | 41 ++++++++--- src/providers/proxy/proxy_id.c | 12 ++-- src/tests/sysdb-tests.c | 2 +- 7 files changed, 297 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/db/sysdb.h b/src/db/sysdb.h index 3fefdf215..ae0b33ce1 100644 --- a/src/db/sysdb.h +++ b/src/db/sysdb.h @@ -521,6 +521,7 @@ int sysdb_store_user(TALLOC_CTX *mem_ctx, const char *homedir, const char *shell, struct sysdb_attrs *attrs, + char **remove_attrs, uint64_t cache_timeout); int sysdb_store_group(TALLOC_CTX *mem_ctx, @@ -712,4 +713,10 @@ errno_t sysdb_set_enumerated(struct sysdb_ctx *ctx, struct sss_domain_info *dom, bool enumerated); +errno_t sysdb_remove_attrs(struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *name, + enum sysdb_member_type type, + char **remove_attrs); + #endif /* __SYS_DB_H__ */ diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c index e8ef9a249..c36b0ee83 100644 --- a/src/db/sysdb_ops.c +++ b/src/db/sysdb_ops.c @@ -1362,12 +1362,15 @@ int sysdb_store_user(TALLOC_CTX *mem_ctx, const char *homedir, const char *shell, struct sysdb_attrs *attrs, + char **remove_attrs, uint64_t cache_timeout) { TALLOC_CTX *tmpctx; struct ldb_message *msg; time_t now; int ret; + errno_t sret = EOK; + bool in_transaction = false; tmpctx = talloc_new(mem_ctx); if (!tmpctx) { @@ -1379,6 +1382,11 @@ int sysdb_store_user(TALLOC_CTX *mem_ctx, if (ret) goto done; } + ret = sysdb_transaction_start(ctx); + if (ret != EOK) goto done; + + in_transaction = true; + ret = sysdb_search_user_by_name(tmpctx, ctx, domain, name, NULL, &msg); if (ret && ret != ENOENT) { @@ -1443,8 +1451,33 @@ int sysdb_store_user(TALLOC_CTX *mem_ctx, ret = sysdb_set_user_attr(tmpctx, ctx, domain, name, attrs, SYSDB_MOD_REP); + if (ret != EOK) goto done; + + if (remove_attrs) { + ret = sysdb_remove_attrs(ctx, domain, name, + SYSDB_MEMBER_USER, + remove_attrs); + if (ret != EOK) { + DEBUG(4, ("Could not remove missing attributes\n")); + } + } done: + if (in_transaction) { + if (ret == EOK) { + sret = sysdb_transaction_commit(ctx); + if (sret != EOK) { + DEBUG(2, ("Could not commit transaction\n")); + } + } + + if (ret != EOK || sret != EOK){ + sret = sysdb_transaction_cancel(ctx); + if (sret != EOK) { + DEBUG(2, ("Could not cancel transaction\n")); + } + } + } if (ret) { DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); } @@ -2767,3 +2800,88 @@ done: talloc_free(msg); return ret; } + +errno_t sysdb_remove_attrs(struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *name, + enum sysdb_member_type type, + char **remove_attrs) +{ + errno_t ret; + errno_t sret = EOK; + bool in_transaction = false; + struct ldb_message *msg; + int lret; + size_t i; + + msg = ldb_msg_new(NULL); + if (!msg) return ENOMEM; + + if (type == SYSDB_MEMBER_USER) { + msg->dn = sysdb_user_dn(sysdb, msg, domain->name, name); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + } else if (type == SYSDB_MEMBER_GROUP) { + msg->dn = sysdb_group_dn(sysdb, msg, domain->name, name); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + } else { + ret = EINVAL; + goto done; + } + + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) goto done; + + in_transaction = true; + + for (i = 0; remove_attrs[i]; i++) { + DEBUG(8, ("Removing attribute [%s] from [%s]\n", + remove_attrs[i], name)); + lret = ldb_msg_add_empty(msg, remove_attrs[i], + LDB_FLAG_MOD_DELETE, NULL); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + + /* We need to do individual modifies so that we can + * skip unknown attributes. Otherwise, any nonexistent + * attribute in the sysdb will cause other removals to + * fail. + */ + lret = ldb_modify(sysdb->ldb, msg); + if (lret != LDB_SUCCESS && lret != LDB_ERR_NO_SUCH_ATTRIBUTE) { + ret = sysdb_error_to_errno(lret); + goto done; + } + + /* Remove this attribute and move on to the next one */ + ldb_msg_remove_attr(msg, remove_attrs[i]); + } + + ret = EOK; + +done: + if (in_transaction) { + if (ret == EOK) { + sret = sysdb_transaction_commit(sysdb); + if (sret != EOK) { + DEBUG(2, ("Could not commit transaction\n")); + } + } + + if (ret != EOK || sret != EOK){ + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(2, ("Could not cancel transaction\n")); + } + } + } + talloc_free(msg); + return ret; +} diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index e669ba6c7..f56d01f00 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -872,3 +872,120 @@ errno_t string_to_shadowpw_days(const char *s, long *d) return EOK; } +errno_t get_sysdb_attr_name(TALLOC_CTX *mem_ctx, + struct sdap_attr_map *map, + size_t map_size, + const char *ldap_name, + char **sysdb_name) +{ + size_t i; + + for (i = 0; i < map_size; i++) { + /* Skip map entries with no name (may depend on + * schema selected) + */ + if (!map[i].name) continue; + + /* Check if it is a mapped attribute */ + if(strcasecmp(ldap_name, map[i].name) == 0) break; + } + + if (i < map_size) { + /* We found a mapped name, return that */ + *sysdb_name = talloc_strdup(mem_ctx, map[i].sys_name); + } else { + /* Not mapped, use the same name */ + *sysdb_name = talloc_strdup(mem_ctx, ldap_name); + } + + if (!*sysdb_name) { + return ENOMEM; + } + + return EOK; +} + +errno_t list_missing_attrs(TALLOC_CTX *mem_ctx, + struct sdap_attr_map *map, + size_t map_size, + const char **expected_attrs, + struct sysdb_attrs *recvd_attrs, + char ***missing_attrs) +{ + errno_t ret; + size_t attr_count = 0; + size_t i, j, k; + char **missing = NULL; + char *sysdb_name; + TALLOC_CTX *tmp_ctx; + + if (!expected_attrs || !recvd_attrs || !missing_attrs) { + return EINVAL; + } + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + /* Count the expected attrs */ + while(expected_attrs[attr_count]) attr_count++; + + /* Allocate the maximum possible values for missing_attrs, to + * be on the safe side + */ + missing = talloc_array(tmp_ctx, char *, attr_count); + if (!missing) { + ret = ENOMEM; + goto done; + } + + k = 0; + /* Check for each expected attribute */ + for (i = 0; i < attr_count; i++) { + ret = get_sysdb_attr_name(tmp_ctx, map, map_size, + expected_attrs[i], + &sysdb_name); + if (ret != EOK) { + goto done; + } + + /* objectClass is a special-case and we need to + * check for it explicitly. + */ + if (strcasecmp(sysdb_name, "objectClass") == 0) { + talloc_free(sysdb_name); + continue; + } + + for (j = 0; j < recvd_attrs->num; j++) { + /* Check whether this expected attribute appeared in the + * received attributes and had a non-zero number of + * values. + */ + if ((strcasecmp(recvd_attrs->a[j].name, sysdb_name) == 0) && + (recvd_attrs->a[j].num_values > 0)) { + break; + } + } + + if (j < recvd_attrs->num) { + /* Attribute was found, therefore not missing */ + talloc_free(sysdb_name); + } else { + /* Attribute could not be found. Add to the missing list */ + missing[k] = talloc_steal(missing, sysdb_name); + k++; + } + } + + /* Terminate the list */ + missing[k] = NULL; + + ret = EOK; + *missing_attrs = talloc_steal(mem_ctx, missing); + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index ba8d13039..3cbf3f600 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -142,4 +142,17 @@ int setup_child(struct sdap_id_ctx *ctx); errno_t string_to_shadowpw_days(const char *s, long *d); + +errno_t get_sysdb_attr_name(TALLOC_CTX *mem_ctx, + struct sdap_attr_map *map, + size_t map_size, + const char *ldap_name, + char **sysdb_name); + +errno_t list_missing_attrs(TALLOC_CTX *mem_ctx, + struct sdap_attr_map *map, + size_t map_size, + const char **expected_attrs, + struct sysdb_attrs *recvd_attrs, + char ***missing_attrs); #endif /* _LDAP_COMMON_H_ */ diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c index 7abe3571d..648f9a734 100644 --- a/src/providers/ldap/sdap_async_accounts.c +++ b/src/providers/ldap/sdap_async_accounts.c @@ -34,6 +34,7 @@ static int sdap_save_user(TALLOC_CTX *memctx, struct sdap_options *opts, struct sss_domain_info *dom, struct sysdb_attrs *attrs, + const char **ldap_attrs, bool is_initgr, char **_usn_value) { @@ -53,6 +54,7 @@ static int sdap_save_user(TALLOC_CTX *memctx, int cache_timeout; char *usn_value = NULL; size_t c; + char **missing = NULL; DEBUG(9, ("Save user\n")); @@ -266,12 +268,28 @@ static int sdap_save_user(TALLOC_CTX *memctx, } } + /* Make sure that any attributes we requested from LDAP that we + * did not receive are also removed from the sysdb + */ + ret = list_missing_attrs(NULL, opts->user_map, SDAP_OPTS_USER, + ldap_attrs, attrs, &missing); + if (ret != EOK) { + goto fail; + } + + /* Remove missing attributes */ + if (missing && !missing[0]) { + /* Nothing to remove */ + talloc_zfree(missing); + } + DEBUG(6, ("Storing info for user %s\n", name)); ret = sysdb_store_user(memctx, ctx, dom, name, pwd, uid, gid, gecos, homedir, shell, - user_attrs, cache_timeout); + user_attrs, missing, cache_timeout); if (ret) goto fail; + talloc_zfree(missing); if (_usn_value) { *_usn_value = usn_value; @@ -281,6 +299,7 @@ static int sdap_save_user(TALLOC_CTX *memctx, fail: DEBUG(2, ("Failed to save user %s\n", name)); + talloc_free(missing); return ret; } @@ -289,6 +308,7 @@ fail: static int sdap_save_users(TALLOC_CTX *memctx, struct sysdb_ctx *sysdb, + const char **attrs, struct sss_domain_info *dom, struct sdap_options *opts, struct sysdb_attrs **users, @@ -320,7 +340,8 @@ static int sdap_save_users(TALLOC_CTX *memctx, usn_value = NULL; ret = sdap_save_user(tmpctx, sysdb, opts, dom, - users[i], false, &usn_value); + users[i], attrs, false, + &usn_value); /* Do not fail completely on errors. * Just report the failure to save and go on */ @@ -446,6 +467,7 @@ static void sdap_get_users_process(struct tevent_req *subreq) } ret = sdap_save_users(state, state->sysdb, + state->attrs, state->dom, state->opts, state->users, state->count, &state->higher_usn); @@ -1449,7 +1471,8 @@ next: } if (state->check_count == 0) { - ret = sdap_save_users(state, state->sysdb, state->dom, state->opts, + ret = sdap_save_users(state, state->sysdb, state->attrs, + state->dom, state->opts, state->new_members, state->count, NULL); if (ret) { DEBUG(2, ("Failed to store users.\n")); @@ -1770,7 +1793,8 @@ static void sdap_nested_done(struct tevent_req *subreq) /* Save all of the users first so that they are in * place for the groups to add them. */ - ret = sdap_save_users(state, state->sysdb, state->dom, state->opts, + ret = sdap_save_users(state, state->sysdb, state->attrs, + state->dom, state->opts, users, count, &state->higher_usn); if (ret != EOK) { tevent_req_error(req, ret); @@ -2320,6 +2344,7 @@ struct sdap_get_initgr_state { struct sdap_id_ctx *id_ctx; const char *name; const char **grp_attrs; + const char **ldap_attrs; struct sysdb_attrs *orig_user; }; @@ -2338,7 +2363,6 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, struct sdap_get_initgr_state *state; const char *base_dn; char *filter; - const char **attrs; int ret; DEBUG(9, ("Retrieving info for initgroups call\n")); @@ -2373,7 +2397,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, } ret = build_attrs_from_map(state, state->opts->user_map, - SDAP_OPTS_USER, &attrs); + SDAP_OPTS_USER, &state->ldap_attrs); if (ret) { talloc_zfree(req); return NULL; @@ -2382,7 +2406,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, base_dn, LDAP_SCOPE_SUBTREE, - filter, attrs, + filter, state->ldap_attrs, state->opts->user_map, SDAP_OPTS_USER, dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT)); @@ -2443,7 +2467,8 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) ret = sdap_save_user(state, state->sysdb, state->opts, state->dom, - state->orig_user, true, NULL); + state->orig_user, state->ldap_attrs, + true, NULL); if (ret) { sysdb_transaction_cancel(state->sysdb); tevent_req_error(req, ret); diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c index 4fd656fed..3df21063c 100644 --- a/src/providers/proxy/proxy_id.c +++ b/src/providers/proxy/proxy_id.c @@ -105,7 +105,8 @@ static int get_pw_name(TALLOC_CTX *mem_ctx, pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell, - NULL, ctx->entry_cache_timeout); + NULL, NULL, + ctx->entry_cache_timeout); if (ret) { goto done; } @@ -219,7 +220,8 @@ static int get_pw_uid(TALLOC_CTX *mem_ctx, pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell, - NULL, ctx->entry_cache_timeout); + NULL, NULL, + ctx->entry_cache_timeout); if (ret) { goto done; } @@ -358,7 +360,8 @@ again: pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell, - NULL, ctx->entry_cache_timeout); + NULL, NULL, + ctx->entry_cache_timeout); if (ret) { /* Do not fail completely on errors. * Just report the failure to save and go on */ @@ -933,7 +936,8 @@ static int get_initgr(TALLOC_CTX *mem_ctx, pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell, - NULL, ctx->entry_cache_timeout); + NULL, NULL, + ctx->entry_cache_timeout); if (ret) { goto done; } diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c index 2beb25884..eada9a3af 100644 --- a/src/tests/sysdb-tests.c +++ b/src/tests/sysdb-tests.c @@ -210,7 +210,7 @@ static int test_store_user(struct test_data *data) data->ctx->domain, data->username, "x", data->uid, 0, gecos, homedir, data->shell ? data->shell : "/bin/bash", - NULL, -1); + NULL, NULL, -1); return ret; } -- cgit