# UNDF: UNDF-2026-000001031 # UNDF: UNDF-2026-XXXXXXXXX --- a/source4/dsdb/samdb/samdb.c +++ b/source4/dsdb/samdb/samdb.c @@ -163,6 +163,35 @@ NTSTATUS samdb_set_global_schema(struct ldb_context *ldb) return NULL; } +/* + * Binary insertion into a sorted struct dom_sid array. + * + * Returns count+1 if the SID was inserted (new, unique). + * Returns count unchanged if the SID was already present (duplicate). + * + * Used by security_token_create() to replace O(N) linear dedup scan with + * O(log N) bsearch + O(N) shift. Total token-build cost drops from O(N^2) + * to O(N log N). + */ +static uint32_t sid_sorted_insert_unique(struct dom_sid *scratch, + uint32_t count, + const struct dom_sid *sid) +{ + uint32_t lo = 0, hi = count; + while (lo < hi) { + uint32_t mid = lo + (hi - lo) / 2; + int cmp = dom_sid_compare(&scratch[mid], sid); + if (cmp == 0) return count; /* duplicate */ + if (cmp < 0) lo = mid + 1; + else hi = mid; + } + /* lo is the sorted insertion point */ + memmove(&scratch[lo + 1], &scratch[lo], + (count - lo) * sizeof(struct dom_sid)); + scratch[lo] = *sid; + return count + 1; +} + /**************************************************************************** Create the SID list for this user. ****************************************************************************/ @@ -187,6 +216,16 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx, enum claims_evaluation_control evaluate_claims; bool sids_are_valid = false; bool device_sids_are_valid = false; + /* + * Sorted scratch arrays for O(N log N) dedup. + * The existing nested for-loop dedup is O(N^2): for each incoming SID + * it scans all already-accepted SIDs linearly. For a Kerberos PAC + * with ~500 group SIDs that is ~125,000 dom_sid_equal() calls at login. + * We replace it with binary-insertion-sort into a scratch array so each + * membership test costs O(log N) instead of O(N). + */ + struct dom_sid *sorted_sids = NULL; + uint32_t sorted_count = 0; + struct dom_sid *sorted_device_sids = NULL; + uint32_t sorted_device_count = 0; bool authentication_was_compounded = session_info_flags & AUTH_SESSION_INFO_FORCE_COMPOUNDED_AUTHENTICATION; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); @@ -224,31 +263,42 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx, ptoken->num_sids = 0; + sorted_sids = talloc_array(tmp_ctx, struct dom_sid, + num_sids > 0 ? num_sids : 1); + if (sorted_sids == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + for (i = 0; i < num_sids; i++) { - uint32_t check_sid_idx; - for (check_sid_idx = 0; - check_sid_idx < ptoken->num_sids; - check_sid_idx++) { - if (dom_sid_equal(&ptoken->sids[check_sid_idx], &sids[i].sid)) { - break; - } - } - - if (check_sid_idx == ptoken->num_sids) { - const struct dom_sid *sid = &sids[i].sid; - - sids_are_valid = sids_are_valid || dom_sid_equal( - sid, &global_sid_Claims_Valid); - authentication_was_compounded = authentication_was_compounded || dom_sid_equal( - sid, &global_sid_Compounded_Authentication); - - ptoken->sids = talloc_realloc(ptoken, ptoken->sids, struct dom_sid, ptoken->num_sids + 1); - if (ptoken->sids == NULL) { - talloc_free(ptoken); - return NT_STATUS_NO_MEMORY; - } - - ptoken->sids[ptoken->num_sids] = *sid; - ptoken->num_sids++; + const struct dom_sid *sid = &sids[i].sid; + uint32_t new_count; + + /* O(log sorted_count) membership test + O(sorted_count) shift */ + new_count = sid_sorted_insert_unique(sorted_sids, sorted_count, sid); + if (new_count == sorted_count) { + continue; /* duplicate */ } + sorted_count = new_count; + + sids_are_valid = sids_are_valid || dom_sid_equal( + sid, &global_sid_Claims_Valid); + authentication_was_compounded = authentication_was_compounded || dom_sid_equal( + sid, &global_sid_Compounded_Authentication); + + ptoken->sids = talloc_realloc(ptoken, ptoken->sids, + struct dom_sid, ptoken->num_sids + 1); + if (ptoken->sids == NULL) { + talloc_free(tmp_ctx); + talloc_free(ptoken); + return NT_STATUS_NO_MEMORY; + } + ptoken->sids[ptoken->num_sids] = *sid; + ptoken->num_sids++; } if (authentication_was_compounded && num_device_sids) { @@ -258,31 +308,40 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx, return NT_STATUS_NO_MEMORY; } - for (i = 0; i < num_device_sids; i++) { - uint32_t check_sid_idx; - for (check_sid_idx = 0; - check_sid_idx < ptoken->num_device_sids; - check_sid_idx++) { - if (dom_sid_equal(&ptoken->device_sids[check_sid_idx], &device_sids[i].sid)) { - break; - } - } - - if (check_sid_idx == ptoken->num_device_sids) { - const struct dom_sid *device_sid = &device_sids[i].sid; - - device_sids_are_valid = device_sids_are_valid || dom_sid_equal( - device_sid, &global_sid_Claims_Valid); - - ptoken->device_sids = talloc_realloc(ptoken, - ptoken->device_sids, - struct dom_sid, - ptoken->num_device_sids + 1); - if (ptoken->device_sids == NULL) { - talloc_free(ptoken); - return NT_STATUS_NO_MEMORY; - } - - ptoken->device_sids[ptoken->num_device_sids] = *device_sid; - ptoken->num_device_sids++; + sorted_device_sids = talloc_array(tmp_ctx, struct dom_sid, + num_device_sids > 0 ? num_device_sids : 1); + if (sorted_device_sids == NULL) { + talloc_free(tmp_ctx); + talloc_free(ptoken); + return NT_STATUS_NO_MEMORY; + } + for (i = 0; i < num_device_sids; i++) { + const struct dom_sid *device_sid = &device_sids[i].sid; + uint32_t new_dev_count; + + new_dev_count = sid_sorted_insert_unique(sorted_device_sids, + sorted_device_count, + device_sid); + if (new_dev_count == sorted_device_count) { + continue; /* duplicate */ } + sorted_device_count = new_dev_count; + + device_sids_are_valid = device_sids_are_valid || dom_sid_equal( + device_sid, &global_sid_Claims_Valid); + + ptoken->device_sids = talloc_realloc(ptoken, + ptoken->device_sids, + struct dom_sid, + ptoken->num_device_sids + 1); + if (ptoken->device_sids == NULL) { + talloc_free(tmp_ctx); + talloc_free(ptoken); + return NT_STATUS_NO_MEMORY; + } + ptoken->device_sids[ptoken->num_device_sids] = *device_sid; + ptoken->num_device_sids++; } } + /* + * Sort ptoken->sids[REMAINING_SIDS_INDEX..] so security_token_has_sid + * can use bsearch for O(log S) access checks (samba-0001 companion fix). + */ + security_token_sort_sids(ptoken); +