summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
authorGary Lockyer <gary@catalyst.net.nz>2018-10-18 13:54:31 +1300
committerAndrew Bartlett <abartlet@samba.org>2018-11-20 22:14:17 +0100
commit64007aa3825be8a3c5678cbc636efa8ecb09c4b1 (patch)
tree7283b590d4e91c2bc4eaf29696658fda1257502b /source4
parentfa3ea1cfc181dc9119c5fb0c6a1a12a7d31bfac2 (diff)
downloadsamba-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.c203
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.h1
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
};