From 5fdb9d7a7bff126d6ab306313d65de27f945253d Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Wed, 18 May 2016 14:30:28 +0200 Subject: [PATCH 07/14] SYSDB: Search the timestamp caches in addition to the sysdb cache When a sysdb entry is searched, the sysdb cache is consulted first for users or groups. If an entry is found in the sysdb cache, the attributes from the timestamp cache are merged to return the full and up-to-date set of attributes. The merging is done with a single BASE search which is a direct lookup into the underlying key-value database, so it should be relatively fast. More complex merging is done only for enumeration by filter which is currently done only via the IFP back end and should be quite infrequent, so I hope we can justify a more complex merging there. --- src/db/sysdb.c | 32 +++ src/db/sysdb.h | 1 + src/db/sysdb_ops.c | 270 +++++++++++++++------ src/db/sysdb_private.h | 56 +++++ src/db/sysdb_search.c | 376 +++++++++++++++++++++++++++++ src/responder/common/responder_cache_req.c | 10 +- src/tests/cmocka/test_sysdb_sudo.c | 4 +- src/tests/common.h | 2 + src/tests/common_dom.c | 16 +- src/util/util_errors.c | 2 + src/util/util_errors.h | 2 + 11 files changed, 691 insertions(+), 80 deletions(-) diff --git a/src/db/sysdb.c b/src/db/sysdb.c index bf47a9d3942a0bc8eb3d12fb8462caa854bd22c9..261f9cc2e40d533ae9f777f993b28129b693b6bd 100644 --- a/src/db/sysdb.c +++ b/src/db/sysdb.c @@ -1661,3 +1661,35 @@ int sysdb_delete_ulong(struct ldb_message *msg, { return sysdb_ldb_msg_ulong_helper(msg, LDB_FLAG_MOD_DELETE, attr, value); } + +bool is_ts_ldb_dn(struct ldb_dn *dn) +{ + const char *sysdb_comp_name = NULL; + const struct ldb_val *sysdb_comp_val = NULL; + + if (dn == NULL) { + return false; + } + + sysdb_comp_name = ldb_dn_get_component_name(dn, 1); + if (strcasecmp("cn", sysdb_comp_name) != 0) { + /* The second component name is not "cn" */ + return false; + } + + sysdb_comp_val = ldb_dn_get_component_val(dn, 1); + if (strncasecmp("users", + (const char *) sysdb_comp_val->data, + sysdb_comp_val->length) == 0) { + return true; + } + + sysdb_comp_val = ldb_dn_get_component_val(dn, 1); + if (strncasecmp("groups", + (const char *) sysdb_comp_val->data, + sysdb_comp_val->length) == 0) { + return true; + } + + return false; +} diff --git a/src/db/sysdb.h b/src/db/sysdb.h index 2a3b00c6ccc67bc1c1b4f7ac5839f36359331d02..5ac8643c1a27a40e132493819ff18e26513a1eb1 100644 --- a/src/db/sysdb.h +++ b/src/db/sysdb.h @@ -59,6 +59,7 @@ #define SYSDB_DOMAIN_ID_RANGE_CLASS "domainIDRange" #define SYSDB_TRUSTED_AD_DOMAIN_RANGE_CLASS "TrustedADDomainRange" +#define SYSDB_DN "dn" #define SYSDB_NAME "name" #define SYSDB_NAME_ALIAS "nameAlias" #define SYSDB_OBJECTCLASS "objectClass" diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c index 79d7eef9ef9a7fb9050e053e35078ab9b5ad0d96..505af2a356cc661b82d08a758595174be4b0c02d 100644 --- a/src/db/sysdb_ops.c +++ b/src/db/sysdb_ops.c @@ -186,6 +186,46 @@ done: /* =Search-Entry========================================================== */ +static int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + struct ldb_dn *base_dn, + enum ldb_scope scope, + const char *filter, + const char **attrs, + size_t *_msgs_count, + struct ldb_message ***_msgs) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + int ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(ldb, tmp_ctx, &res, + base_dn, scope, attrs, + filter?"%s":NULL, filter); + if (ret != EOK) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + *_msgs_count = res->count; + *_msgs = talloc_steal(mem_ctx, res->msgs); + + if (res->count == 0) { + ret = ENOENT; + goto done; + } + +done: + talloc_zfree(tmp_ctx); + return ret; +} + int sysdb_search_entry(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, struct ldb_dn *base_dn, @@ -195,35 +235,33 @@ int sysdb_search_entry(TALLOC_CTX *mem_ctx, size_t *_msgs_count, struct ldb_message ***_msgs) { - TALLOC_CTX *tmp_ctx; - struct ldb_result *res; - int ret; + errno_t ret; - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - ret = ENOMEM; - goto done; - } - - ret = ldb_search(sysdb->ldb, tmp_ctx, &res, - base_dn, scope, attrs, - filter?"%s":NULL, filter); + ret = sysdb_cache_search_entry(mem_ctx, sysdb->ldb, base_dn, scope, + filter, attrs, _msgs_count, _msgs); if (ret != EOK) { - ret = sysdb_error_to_errno(ret); - goto done; + return ret; } - *_msgs_count = res->count; - *_msgs = talloc_steal(mem_ctx, res->msgs); + return sysdb_merge_msg_list_ts_attrs(sysdb, *_msgs_count, *_msgs, + attrs); +} - if (res->count == 0) { - ret = ENOENT; - goto done; +int sysdb_search_ts_entry(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct ldb_dn *base_dn, + enum ldb_scope scope, + const char *filter, + const char **attrs, + size_t *_msgs_count, + struct ldb_message ***_msgs) +{ + if (sysdb->ldb_ts == NULL) { + return EOK; } -done: - talloc_zfree(tmp_ctx); - return ret; + return sysdb_cache_search_entry(mem_ctx, sysdb->ldb_ts, base_dn, scope, + filter, attrs, _msgs_count, _msgs); } /* =Search-Entry-by-SID-string============================================ */ @@ -357,6 +395,11 @@ static int sysdb_search_by_name(TALLOC_CTX *mem_ctx, goto done; } + ret = sysdb_merge_msg_list_ts_attrs(domain->sysdb, msgs_count, msgs, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot retrieve timestamp attributes\n"); + } + *msg = talloc_steal(mem_ctx, msgs[0]); done: @@ -493,6 +536,14 @@ int sysdb_search_user_by_upn_res(TALLOC_CTX *mem_ctx, goto done; } + /* Merge in the timestamps from the fast ts db */ + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, + attrs ? attrs : def_attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + *out_res = talloc_steal(mem_ctx, res); ret = EOK; @@ -2707,6 +2758,62 @@ fail: /* =Search-Users-with-Custom-Filter====================================== */ +static int sysdb_cache_search_users(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_context *ldb, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *basedn; + char *filter; + int ret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + basedn = sysdb_user_base_dn(tmp_ctx, domain); + if (!basedn) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n"); + ret = ENOMEM; + goto fail; + } + + filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", SYSDB_UC, sub_filter); + if (!filter) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n"); + ret = ENOMEM; + goto fail; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + "Search users with filter: %s\n", filter); + + ret = sysdb_cache_search_entry(mem_ctx, ldb, basedn, + LDB_SCOPE_SUBTREE, filter, attrs, + msgs_count, msgs); + if (ret) { + goto fail; + } + + talloc_zfree(tmp_ctx); + return EOK; + +fail: + if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n"); + } + else if (ret) { + DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret)); + } + talloc_zfree(tmp_ctx); + return ret; +} + int sysdb_search_users(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, const char *sub_filter, @@ -2714,52 +2821,31 @@ int sysdb_search_users(TALLOC_CTX *mem_ctx, size_t *msgs_count, struct ldb_message ***msgs) { - TALLOC_CTX *tmp_ctx; - struct ldb_dn *basedn; - char *filter; - int ret; + errno_t ret; - tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { - return ENOMEM; + ret = sysdb_cache_search_users(mem_ctx, domain, domain->sysdb->ldb, + sub_filter, attrs, msgs_count, msgs); + if (ret != EOK) { + return ret; } - basedn = sysdb_user_base_dn(tmp_ctx, domain); - if (!basedn) { - DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n"); - ret = ENOMEM; - goto fail; - } - - filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", SYSDB_UC, sub_filter); - if (!filter) { - DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n"); - ret = ENOMEM; - goto fail; - } - - DEBUG(SSSDBG_TRACE_INTERNAL, - "Search users with filter: %s\n", filter); + return sysdb_merge_msg_list_ts_attrs(domain->sysdb, *msgs_count, *msgs, + attrs); +} - ret = sysdb_search_entry(mem_ctx, domain->sysdb, basedn, - LDB_SCOPE_SUBTREE, filter, attrs, - msgs_count, msgs); - if (ret) { - goto fail; +int sysdb_search_ts_users(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + if (domain->sysdb->ldb_ts == NULL) { + return ENOENT; } - talloc_zfree(tmp_ctx); - return EOK; - -fail: - if (ret == ENOENT) { - DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n"); - } - else if (ret) { - DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, strerror(ret)); - } - talloc_zfree(tmp_ctx); - return ret; + return sysdb_cache_search_users(mem_ctx, domain, domain->sysdb->ldb_ts, + sub_filter, attrs, msgs_count, msgs); } /* =Delete-User-by-Name-OR-uid============================================ */ @@ -2874,12 +2960,13 @@ fail: /* =Search-Groups-with-Custom-Filter===================================== */ -int sysdb_search_groups(TALLOC_CTX *mem_ctx, - struct sss_domain_info *domain, - const char *sub_filter, - const char **attrs, - size_t *msgs_count, - struct ldb_message ***msgs) +static int sysdb_cache_search_groups(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct ldb_context *ldb, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs) { TALLOC_CTX *tmp_ctx; struct ldb_dn *basedn; @@ -2908,9 +2995,9 @@ int sysdb_search_groups(TALLOC_CTX *mem_ctx, DEBUG(SSSDBG_TRACE_INTERNAL, "Search groups with filter: %s\n", filter); - ret = sysdb_search_entry(mem_ctx, domain->sysdb, basedn, - LDB_SCOPE_SUBTREE, filter, attrs, - msgs_count, msgs); + ret = sysdb_cache_search_entry(mem_ctx, ldb, basedn, + LDB_SCOPE_SUBTREE, filter, attrs, + msgs_count, msgs); if (ret) { goto fail; } @@ -2929,6 +3016,40 @@ fail: return ret; } +int sysdb_search_groups(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + errno_t ret; + + ret = sysdb_cache_search_groups(mem_ctx, domain, domain->sysdb->ldb, + sub_filter, attrs, msgs_count, msgs); + if (ret != EOK) { + return ret; + } + + return sysdb_merge_msg_list_ts_attrs(domain->sysdb, *msgs_count, *msgs, + attrs); +} + +int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + if (domain->sysdb->ldb_ts == NULL) { + return ENOENT; + } + + return sysdb_cache_search_groups(mem_ctx, domain, domain->sysdb->ldb_ts, + sub_filter, attrs, msgs_count, msgs); +} + /* =Delete-Group-by-Name-OR-gid=========================================== */ int sysdb_delete_group(struct sss_domain_info *domain, @@ -3730,6 +3851,13 @@ static errno_t sysdb_search_object_by_str_attr(TALLOC_CTX *mem_ctx, goto done; } + /* Merge in the timestamps from the fast ts db */ + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + *_res = talloc_steal(mem_ctx, res); done: diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h index 828863df7e9514a3562ec4e55cc0a433960ef116..dae348b0b34ac791e9af47506d9a807d8c7eb2a1 100644 --- a/src/db/sysdb_private.h +++ b/src/db/sysdb_private.h @@ -183,4 +183,60 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, const char *forest, uint32_t trust_direction); +/* Helper functions to deal with the timestamp cache should not be used + * outside the sysdb itself. The timestamp cache should be completely + * opaque to the sysdb consumers + */ + +/* Returns true if the 'dn' parameter is a user or a group DN, because + * at the moment, the timestamps cache only handles users and groups. + * Returns false otherwise. + */ +bool is_ts_ldb_dn(struct ldb_dn *dn); + +/* Given a ldb_result found in the timestamp cache, merge in the + * corresponding full attributes from the sysdb cache. The new + * attributes are allocated on the messages in the ldb_result. + */ +errno_t sysdb_merge_res_ts_attrs(struct sysdb_ctx *ctx, + struct ldb_result *res, + const char *attrs[]); + +/* Given an array of ldb_message structures found in the timestamp cache, + * merge in the corresponding full attributes from the sysdb cache. The + * new attributes are allocated atop the ldb messages. + */ +errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx, + size_t msgs_count, + struct ldb_message **msgs, + const char *attrs[]); + +/* Merge two sets of ldb_result structures. */ +struct ldb_result *sss_merge_ldb_results(struct ldb_result *res, + struct ldb_result *subres); + +/* Search Entry in the timestamp cache */ +int sysdb_search_ts_entry(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct ldb_dn *base_dn, + enum ldb_scope scope, + const char *filter, + const char **attrs, + size_t *_msgs_count, + struct ldb_message ***_msgs); + +int sysdb_search_ts_users(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs); + +int sysdb_search_ts_groups(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs); + #endif /* __INT_SYS_DB_H__ */ diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c index 1e403119131a7704a1f36ca2fd597d9ee64b9319..e50997906594ae5b1ad5d1cb8e93047fa4492c48 100644 --- a/src/db/sysdb_search.c +++ b/src/db/sysdb_search.c @@ -25,6 +25,155 @@ #include #include +/* helpers */ +static errno_t merge_ts_attr(struct ldb_message *ts_msg, + struct ldb_message *sysdb_msg, + const char *ts_attr, + const char *want_attrs[]) +{ + errno_t ret; + bool include = true; + struct ldb_message_element *ts_el; + struct ldb_message_element *sysdb_el; + + if (want_attrs != NULL) { + /* Otherwise merge all ts attrs */ + include = string_in_list(ts_attr, discard_const(want_attrs), true); + } + if (include == false) { + return EOK; + } + + ts_el = ldb_msg_find_element(ts_msg, ts_attr); + if (ts_el == NULL || ts_el->num_values == 0) { + return EOK; + } + + if (ts_el->num_values > 1) { + return EIO; + } + + sysdb_el = ldb_msg_find_element(sysdb_msg, ts_attr); + if (sysdb_el == NULL || sysdb_el->num_values == 0) { + ret = ldb_msg_add_steal_value(sysdb_msg, ts_attr, &ts_el->values[0]); + if (ret != EOK) { + return sysdb_error_to_errno(ret); + } + } else { + /* Assumes the timestamps cache only holds single-valued + * attributes */ + sysdb_el->values = talloc_steal(sysdb_el->values, ts_el->values); + } + + return EOK; +} + +static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb, + struct ldb_message *sysdb_msg, + const char *attrs[]) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + size_t msgs_count; + struct ldb_message **ts_msgs; + bool ts_dn; + + ts_dn = is_ts_ldb_dn(sysdb_msg->dn); + if (ts_dn == false) { + return ERR_NO_TS; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = sysdb_search_ts_entry(tmp_ctx, sysdb, sysdb_msg->dn, + LDB_SCOPE_BASE, + NULL, + sysdb_ts_cache_attrs, + &msgs_count, + &ts_msgs); + if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "No such DN in the timestamp cache: %s\n", + ldb_dn_get_linearized(sysdb_msg->dn)); + ret = ERR_TS_CACHE_MISS; + goto done; + } else if (ret != EOK) { + DEBUG(SSSDBG_TRACE_FUNC, + "Cannot find TS cache entry for [%s]: [%d]: %s\n", + ldb_dn_get_linearized(sysdb_msg->dn), + ret, sss_strerror(ret)); + goto done; + } + + if (msgs_count != 1) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Expected 1 result for base search, got %zu\n", msgs_count); + return EIO; + } + + for (size_t c = 0; sysdb_ts_cache_attrs[c]; c++) { + ret = merge_ts_attr(ts_msgs[0], sysdb_msg, + sysdb_ts_cache_attrs[c], attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Canot merge ts attr %s\n", sysdb_ts_cache_attrs[c]); + goto done; + } + } + + ret = EOK; +done: + talloc_zfree(tmp_ctx); + return ret; +} + +errno_t sysdb_merge_res_ts_attrs(struct sysdb_ctx *ctx, + struct ldb_result *res, + const char *attrs[]) +{ + errno_t ret; + + if (res == NULL || ctx->ldb_ts == NULL) { + return EOK; + } + + for (size_t c = 0; c < res->count; c++) { + ret = merge_msg_ts_attrs(ctx, res->msgs[c], attrs); + if (ret == ERR_NO_TS) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "TS cache doesn't handle this DN type, skipping\n"); + continue; + } else if (ret == ERR_TS_CACHE_MISS) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "TS cache doesn't contain this DN, skipping\n"); + continue; + } else if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot merge timestamp cache values for %s\n", + ldb_dn_get_linearized(res->msgs[c]->dn)); + /* non-fatal */ + continue; + } + } + + return EOK; +} + +errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx, + size_t msgs_count, + struct ldb_message **msgs, + const char *attrs[]) +{ + struct ldb_result res; + + res.count = msgs_count; + res.msgs = msgs; + return sysdb_merge_res_ts_attrs(ctx, &res, attrs); +} + /* users */ int sysdb_getpwnam(TALLOC_CTX *mem_ctx, @@ -75,6 +224,13 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx, goto done; } + /* Merge in the timestamps from the fast ts db */ + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + *_res = talloc_steal(mem_ctx, res); done: @@ -180,6 +336,13 @@ int sysdb_getpwuid(TALLOC_CTX *mem_ctx, goto done; } + /* Merge in the timestamps from the fast ts db */ + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + *_res = talloc_steal(mem_ctx, res); done: @@ -324,6 +487,73 @@ done: return ret; } +static errno_t search_ts_matches(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *attrs[], + struct ldb_result *ts_res, + const char *filter, + struct ldb_result **_res) +{ + char *dn_filter; + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_result *res; + errno_t ret; + + if (ts_res->count == 0) { + *_res = NULL; + ret = EOK; + goto done; + } + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (res == NULL) { + ret = ENOMEM; + goto done; + } + + dn_filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(|", SYSDB_NAME, filter); + if (dn_filter == NULL) { + ret = ENOMEM; + goto done; + } + + for (size_t i = 0; i < ts_res->count; i++) { + dn_filter = talloc_asprintf_append( + dn_filter, + "(%s=%s)", + SYSDB_DN, + ldb_dn_get_linearized(ts_res->msgs[i]->dn)); + if (dn_filter == NULL) { + ret = ENOMEM; + goto done; + } + } + + dn_filter = talloc_asprintf_append(dn_filter, "))"); + if (dn_filter == NULL) { + ret = ENOMEM; + goto done; + } + + ret = ldb_search(sysdb->ldb, tmp_ctx, &res, NULL, + LDB_SCOPE_SUBTREE, attrs, "%s", dn_filter); + if (ret) { + ret = sysdb_error_to_errno(ret); + goto done; + } + + ret = EOK; + *_res = talloc_steal(mem_ctx, res); +done: + talloc_zfree(tmp_ctx); + return ret; +} + int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, const char *name_filter, @@ -333,8 +563,11 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, TALLOC_CTX *tmp_ctx; static const char *attrs[] = SYSDB_PW_ATTRS; char *filter = NULL; + const char *ts_filter = NULL; struct ldb_dn *base_dn; struct ldb_result *res; + struct ldb_result ts_res; + struct ldb_result *ts_cache_res; int ret; tmp_ctx = talloc_new(NULL); @@ -348,6 +581,28 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, goto done; } + ts_filter = enum_filter(tmp_ctx, SYSDB_PWENT_FILTER, + NULL, addtl_filter); + if (ts_filter == NULL) { + ret = ENOMEM; + goto done; + } + DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp cache with [%s]\n", ts_filter); + + ZERO_STRUCT(ts_res); + ret = sysdb_search_ts_users(tmp_ctx, domain, ts_filter, + sysdb_ts_cache_attrs, + (size_t *) &ts_res.count, &ts_res.msgs); + if (ret != EOK && ret != ENOENT) { + goto done; + } + + ret = search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, + name_filter, &ts_cache_res); + if (ret != EOK && ret != ENOENT) { + goto done; + } + filter = enum_filter(tmp_ctx, SYSDB_PWENT_FILTER, name_filter, addtl_filter); if (filter == NULL) { @@ -363,6 +618,19 @@ int sysdb_enumpwent_filter(TALLOC_CTX *mem_ctx, goto done; } + /* Merge in the timestamps from the fast ts db */ + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + + res = sss_merge_ldb_results(res, ts_cache_res); + if (res == NULL) { + ret = ENOMEM; + goto done; + } + *_res = talloc_steal(mem_ctx, res); done: @@ -618,6 +886,12 @@ int sysdb_getgrnam(TALLOC_CTX *mem_ctx, goto done; } + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + *_res = talloc_steal(mem_ctx, res); done: @@ -755,6 +1029,12 @@ int sysdb_getgrgid(TALLOC_CTX *mem_ctx, goto done; } + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + *_res = talloc_steal(mem_ctx, res); done: @@ -771,9 +1051,12 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx, TALLOC_CTX *tmp_ctx; static const char *attrs[] = SYSDB_GRSRC_ATTRS; const char *filter = NULL; + const char *ts_filter = NULL; const char *base_filter; struct ldb_dn *base_dn; struct ldb_result *res; + struct ldb_result ts_res; + struct ldb_result *ts_cache_res; int ret; tmp_ctx = talloc_new(NULL); @@ -794,6 +1077,28 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx, goto done; } + ts_filter = enum_filter(tmp_ctx, base_filter, + NULL, addtl_filter); + if (ts_filter == NULL) { + ret = ENOMEM; + goto done; + } + DEBUG(SSSDBG_TRACE_LIBS, "Searching timestamp cache with [%s]\n", ts_filter); + + ZERO_STRUCT(ts_res); + ret = sysdb_search_ts_groups(tmp_ctx, domain, ts_filter, + sysdb_ts_cache_attrs, + (size_t *) &ts_res.count, &ts_res.msgs); + if (ret != EOK && ret != ENOENT) { + goto done; + } + + ret = search_ts_matches(tmp_ctx, domain->sysdb, attrs, &ts_res, + name_filter, &ts_cache_res); + if (ret != EOK && ret != ENOENT) { + goto done; + } + filter = enum_filter(tmp_ctx, base_filter, name_filter, addtl_filter); if (filter == NULL) { @@ -814,6 +1119,19 @@ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx, goto done; } + /* Merge in the timestamps from the fast ts db */ + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attrs); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + + res = sss_merge_ldb_results(res, ts_cache_res); + if (res == NULL) { + ret = ENOMEM; + goto done; + } + *_res = talloc_steal(mem_ctx, res); done: @@ -1212,6 +1530,13 @@ int sysdb_get_user_attr(TALLOC_CTX *mem_ctx, goto done; } + /* Merge in the timestamps from the fast ts db */ + ret = sysdb_merge_res_ts_attrs(domain->sysdb, res, attributes); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Cannot merge timestamp cache values\n"); + /* non-fatal */ + } + *_res = talloc_steal(mem_ctx, res); done: @@ -1842,3 +2167,54 @@ done: talloc_free(tmp_ctx); return ret; } + +struct ldb_result *sss_merge_ldb_results(struct ldb_result *sysdb_res, + struct ldb_result *ts_res) +{ + size_t i, ii, count, total; + int ret; + + if (ts_res == NULL || ts_res->count == 0) { + return sysdb_res; + } + + total = sysdb_res->count + ts_res->count; + sysdb_res->msgs = talloc_realloc(sysdb_res, sysdb_res->msgs, + struct ldb_message *, + total); + if (sysdb_res->msgs == NULL) { + return NULL; + } + + /* FIXME - this is O(2), so inefficient for large sets! */ + count = sysdb_res->count; + for (i = 0; i < ts_res->count; i++) { + for (ii = 0; ii < sysdb_res->count; ii++) { + ret = ldb_dn_compare(ts_res->msgs[i]->dn, sysdb_res->msgs[ii]->dn); + if (ret == 0) { + break; + } + } + + if (ii < sysdb_res->count) { + /* We already have this DN but ts_res might be more up-to-date + * wrt timestamps */ + sysdb_res->msgs[ii] = talloc_steal(sysdb_res, ts_res->msgs[i]); + continue; + } + /* new DN, merge */ + sysdb_res->msgs[count] = talloc_steal(sysdb_res, ts_res->msgs[i]); + count++; + } + + if (count < total) { + sysdb_res->msgs = talloc_realloc(sysdb_res, sysdb_res->msgs, + struct ldb_message *, + count); + if (sysdb_res->msgs == NULL) { + return NULL; + } + } + sysdb_res->count = count; + return sysdb_res; +} diff --git a/src/responder/common/responder_cache_req.c b/src/responder/common/responder_cache_req.c index a3253a5efcfea014a01dc38e42ae2a4466dff1a2..2175b91145607a33ccfac0cb707ab21d87719885 100644 --- a/src/responder/common/responder_cache_req.c +++ b/src/responder/common/responder_cache_req.c @@ -43,11 +43,14 @@ static errno_t updated_users_by_filter(TALLOC_CTX *mem_ctx, recent_filter = talloc_asprintf(mem_ctx, "(%s>=%lu)", SYSDB_LAST_UPDATE, since); + if (recent_filter == NULL) { + return ENOMEM; + } + ret = sysdb_enumpwent_filter_with_views(mem_ctx, domain, name_filter, recent_filter, _res); talloc_free(recent_filter); - return ret; } @@ -62,11 +65,14 @@ static errno_t updated_groups_by_filter(TALLOC_CTX *mem_ctx, recent_filter = talloc_asprintf(mem_ctx, "(%s>=%lu)", SYSDB_LAST_UPDATE, since); + if (recent_filter == NULL) { + return ENOMEM; + } + ret = sysdb_enumgrent_filter_with_views(mem_ctx, domain, name_filter, recent_filter, _res); talloc_free(recent_filter); - return ret; } diff --git a/src/tests/cmocka/test_sysdb_sudo.c b/src/tests/cmocka/test_sysdb_sudo.c index 7289454a4f7fb30ef1532226d3d5ea788b83f34a..97f80d7e5dfa9d921c8188dcff21ad0d2e372dc2 100644 --- a/src/tests/cmocka/test_sysdb_sudo.c +++ b/src/tests/cmocka/test_sysdb_sudo.c @@ -23,9 +23,11 @@ #include #include #include +#include #include "tests/cmocka/common_mock.h" #include "src/db/sysdb_sudo.h" +#include "src/db/sysdb_private.h" #define TESTS_PATH "tp_" BASE_FILE_STEM #define TEST_CONF_DB "test_sysdb_sudorules.ldb" @@ -138,6 +140,7 @@ static int test_sysdb_setup(void **state) create_groups(test_ctx->tctx->dom); create_users(test_ctx->tctx->dom); + reset_ldb_errstrings(test_ctx->tctx->dom); check_leaks_push(test_ctx); *state = (void *)test_ctx; @@ -710,7 +713,6 @@ int main(int argc, const char *argv[]) cmocka_unit_test_setup_teardown(test_sudo_get_filter, test_sysdb_setup, test_sysdb_teardown), - /* sysdb_get_sudo_user_info() */ cmocka_unit_test_setup_teardown(test_get_sudo_user_info, test_sysdb_setup, diff --git a/src/tests/common.h b/src/tests/common.h index c9b3815cd3e70b80d23da2d8b2363fe94cc170f9..b49cfea9b73d8b4b7b61c721912de9fd2c0ccf13 100644 --- a/src/tests/common.h +++ b/src/tests/common.h @@ -84,6 +84,8 @@ struct sss_test_conf_param { struct sss_test_ctx *create_ev_test_ctx(TALLOC_CTX *mem_ctx); +void reset_ldb_errstrings(struct sss_domain_info *dom); + struct sss_test_ctx * create_multidom_test_ctx(TALLOC_CTX *mem_ctx, const char *tests_path, diff --git a/src/tests/common_dom.c b/src/tests/common_dom.c index f1eabd412520a59ae762adaad3341ee29850e420..863d22e4786e5e0ad5436eced6546f0fa3b522f3 100644 --- a/src/tests/common_dom.c +++ b/src/tests/common_dom.c @@ -173,6 +173,14 @@ done: return ret; } +void reset_ldb_errstrings(struct sss_domain_info *dom) +{ + ldb_reset_err_string(sysdb_ctx_get_ldb(dom->sysdb)); + if (dom->sysdb->ldb_ts) { + ldb_reset_err_string(dom->sysdb->ldb_ts); + } +} + static errno_t mock_domain(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, @@ -191,12 +199,7 @@ mock_domain(TALLOC_CTX *mem_ctx, goto done; } - /* reset ldb error if any */ - ldb_reset_err_string(sysdb_ctx_get_ldb(domain->sysdb)); - - if (domain->sysdb->ldb_ts != NULL) { - ldb_reset_err_string(domain->sysdb->ldb_ts); - } + reset_ldb_errstrings(domain); /* init with an AD-style regex to be able to test flat name */ ret = sss_names_init_from_args(domain, @@ -378,6 +381,7 @@ void test_multidom_suite_cleanup(const char *tests_path, } talloc_zfree(sysdb_path); + } } diff --git a/src/util/util_errors.c b/src/util/util_errors.c index a2f0f543655ffa6277dfe7467e0db25402bafb6e..0210480c4b195e96b33e686b9fe09029c7b92a6e 100644 --- a/src/util/util_errors.c +++ b/src/util/util_errors.c @@ -87,6 +87,8 @@ struct err_string error_to_str[] = { { "SBUS request already handled" }, /* ERR_SBUS_REQUEST_HANDLED */ { "Sysdb version is too old" }, /* ERR_SYSDB_VERSION_TOO_OLD */ { "Sysdb version is too new" }, /* ERR_SYSDB_VERSION_TOO_NEW */ + { "Domain has to timestamp cache" }, /* ERR_NO_TS */ + { "No timestamp cache record" }, /* ERR_TS_CACHE_MISS */ { "ERR_LAST" } /* ERR_LAST */ }; diff --git a/src/util/util_errors.h b/src/util/util_errors.h index aa6898cd9e1f534b534f27ad8ca76115d078e723..2620e7ca86dfcccc1c2bc5aa64ac8c7947f29d19 100644 --- a/src/util/util_errors.h +++ b/src/util/util_errors.h @@ -109,6 +109,8 @@ enum sssd_errors { ERR_SBUS_REQUEST_HANDLED, ERR_SYSDB_VERSION_TOO_OLD, ERR_SYSDB_VERSION_TOO_NEW, + ERR_NO_TS, + ERR_TS_CACHE_MISS, ERR_LAST /* ALWAYS LAST */ }; -- 2.4.11