diff options
author | Gary Lockyer <gary@catalyst.net.nz> | 2018-10-18 13:54:31 +1300 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2018-11-20 22:14:17 +0100 |
commit | 64007aa3825be8a3c5678cbc636efa8ecb09c4b1 (patch) | |
tree | 7283b590d4e91c2bc4eaf29696658fda1257502b /source4 | |
parent | fa3ea1cfc181dc9119c5fb0c6a1a12a7d31bfac2 (diff) | |
download | samba-64007aa3825be8a3c5678cbc636efa8ecb09c4b1.tar.gz |
source4 samr: cache samr_EnumDomainUsers results
Add a cache of GUID's that matched the last samr_EnunDomainUsers made on a
domain handle. The cache is cleared if resume_handle is zero, and when the
final results are returned to the caller.
The existing code repeated the database query for each chunk requested.
Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'source4')
-rw-r--r-- | source4/rpc_server/samr/dcesrv_samr.c | 203 | ||||
-rw-r--r-- | source4/rpc_server/samr/dcesrv_samr.h | 1 |
2 files changed, 156 insertions, 48 deletions
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c index b485b5a2b62..3a5b0146ba7 100644 --- a/source4/rpc_server/samr/dcesrv_samr.c +++ b/source4/rpc_server/samr/dcesrv_samr.c @@ -1516,12 +1516,18 @@ static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, struct dcesrv_handle *h; struct samr_domain_state *d_state; struct ldb_message **res; - int i, ldb_cnt; - uint32_t first, count; + uint32_t i; + uint32_t count; + uint32_t results; + uint32_t max_entries; + uint32_t remaining_entries; + uint32_t resume_handle; struct samr_SamEntry *entries; const char * const attrs[] = { "objectSid", "sAMAccountName", "userAccountControl", NULL }; + const char *const cache_attrs[] = {"objectSid", "objectGUID", NULL}; struct samr_SamArray *sam; + struct samr_guid_cache *cache = NULL; *r->out.resume_handle = 0; *r->out.sam = NULL; @@ -1530,73 +1536,174 @@ static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN); d_state = h->data; + cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_USERS_CACHE]; - /* search for all domain users in this domain. This could possibly be - cached and resumed on resume_key */ - ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, - d_state->domain_dn, - &res, attrs, - d_state->domain_sid, - "(objectClass=user)"); - if (ldb_cnt < 0) { - return NT_STATUS_INTERNAL_DB_CORRUPTION; + /* + * If the resume_handle is zero, query the database and cache the + * matching GUID's + */ + if (*r->in.resume_handle == 0) { + NTSTATUS status; + int ldb_cnt; + clear_guid_cache(cache); + /* + * search for all domain users in this domain. + */ + ldb_cnt = samdb_search_domain(d_state->sam_ctx, + mem_ctx, + d_state->domain_dn, + &res, + cache_attrs, + d_state->domain_sid, + "(objectClass=user)"); + if (ldb_cnt < 0) { + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + /* + * Sort the results into RID order, while the spec states there + * is no order, Windows appears to sort the results by RID and + * so it is possible that there are clients that depend on + * this ordering + */ + TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid); + + /* + * cache the sorted GUID's + */ + status = load_guid_cache(cache, d_state, ldb_cnt, res); + TALLOC_FREE(res); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + cache->handle = 0; } - /* convert to SamEntry format */ - entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt); - if (!entries) { - return NT_STATUS_NO_MEMORY; + /* + * If the resume handle is out of range we return an empty response + * and invalidate the cache. + * + * From the specification: + * Servers SHOULD validate that EnumerationContext is an expected + * value for the server's implementation. Windows does NOT validate + * the input, though the result of malformed information merely results + * in inconsistent output to the client. + */ + if (*r->in.resume_handle >= cache->size) { + clear_guid_cache(cache); + sam = talloc(mem_ctx, struct samr_SamArray); + if (!sam) { + return NT_STATUS_NO_MEMORY; + } + sam->entries = NULL; + sam->count = 0; + + *r->out.sam = sam; + *r->out.resume_handle = 0; + return NT_STATUS_OK; } + /* + * Calculate the number of entries to return limit by max_size. + * Note that we use the w2k3 element size value of 54 + */ + max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER); + remaining_entries = cache->size - *r->in.resume_handle; + results = MIN(remaining_entries, max_entries); + + /* + * Process the list of result GUID's. + * Read the details of each object and populate the Entries + * for the current level. + */ count = 0; + resume_handle = *r->in.resume_handle; + entries = talloc_array(mem_ctx, struct samr_SamEntry, results); + if (entries == NULL) { + clear_guid_cache(cache); + return NT_STATUS_NO_MEMORY; + } + for (i = 0; i < results; i++) { + struct dom_sid *sid; + struct ldb_result *rec; + const uint32_t idx = *r->in.resume_handle + i; + int ret; + const char *name = NULL; - for (i=0;i<ldb_cnt;i++) { - /* Check if a mask has been requested */ - if (r->in.acct_flags - && ((samdb_result_acct_flags(res[i], NULL) & r->in.acct_flags) == 0)) { + resume_handle++; + /* + * Read an object from disk using the GUID as the key + * + * If the object can not be read, or it does not have a SID + * it is ignored. + * + * As a consequence of this, if all the remaining GUID's + * have been deleted an empty result will be returned. + * i.e. even if the previous call returned a non zero + * resume_handle it is possible for no results to be returned. + * + */ + ret = dsdb_search_by_dn_guid(d_state->sam_ctx, + mem_ctx, + &rec, + &cache->entries[idx], + attrs, + 0); + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + char *guid_str = + GUID_string(mem_ctx, &cache->entries[idx]); + DBG_WARNING("GUID [%s] not found\n", guid_str); continue; + } else if (ret != LDB_SUCCESS) { + clear_guid_cache(cache); + return NT_STATUS_INTERNAL_DB_CORRUPTION; } - entries[count].idx = samdb_result_rid_from_sid(mem_ctx, res[i], - "objectSid", 0); - entries[count].name.string = ldb_msg_find_attr_as_string(res[i], - "sAMAccountName", ""); - count += 1; + sid = samdb_result_dom_sid(mem_ctx, rec->msgs[0], "objectSID"); + if (sid == NULL) { + char *guid_str = + GUID_string(mem_ctx, &cache->entries[idx]); + DBG_WARNING("objectSID for GUID [%s] not found\n", + guid_str); + continue; + } + if (r->in.acct_flags && + ((samdb_result_acct_flags(rec->msgs[0], NULL) & + r->in.acct_flags) == 0)) { + continue; + } + entries[count].idx = samdb_result_rid_from_sid( + mem_ctx, rec->msgs[0], "objectSid", 0); + name = ldb_msg_find_attr_as_string( + rec->msgs[0], "sAMAccountName", ""); + entries[count].name.string = talloc_strdup(entries, name); + count++; } - /* sort the results by rid */ - TYPESAFE_QSORT(entries, count, compare_SamEntry); - - /* find the first entry to return */ - for (first=0; - first<count && entries[first].idx <= *r->in.resume_handle; - first++) ; - - /* return the rest, limit by max_size. Note that we - use the w2k3 element size value of 54 */ - *r->out.num_entries = count - first; - *r->out.num_entries = MIN(*r->out.num_entries, - 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER)); - sam = talloc(mem_ctx, struct samr_SamArray); if (!sam) { + clear_guid_cache(cache); return NT_STATUS_NO_MEMORY; } - sam->entries = entries+first; - sam->count = *r->out.num_entries; + sam->entries = entries; + sam->count = count; *r->out.sam = sam; + *r->out.resume_handle = resume_handle; + *r->out.num_entries = count; - if (first == count) { + /* + * Signal no more results by returning zero resume handle, + * the cache is also cleared at this point + */ + if (*r->out.resume_handle >= cache->size) { + *r->out.resume_handle = 0; + clear_guid_cache(cache); return NT_STATUS_OK; } - - if (*r->out.num_entries < count - first) { - *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx; - return STATUS_MORE_ENTRIES; - } - - return NT_STATUS_OK; + /* + * There are more results to be returned. + */ + return STATUS_MORE_ENTRIES; } diff --git a/source4/rpc_server/samr/dcesrv_samr.h b/source4/rpc_server/samr/dcesrv_samr.h index 04866aca757..d530fc67bd9 100644 --- a/source4/rpc_server/samr/dcesrv_samr.h +++ b/source4/rpc_server/samr/dcesrv_samr.h @@ -54,6 +54,7 @@ struct samr_guid_cache { enum samr_guid_cache_id { SAMR_QUERY_DISPLAY_INFO_CACHE, SAMR_ENUM_DOMAIN_GROUPS_CACHE, + SAMR_ENUM_DOMAIN_USERS_CACHE, SAMR_LAST_CACHE }; |