# UNDF: UNDF-2026-000001030 # UNDF: UNDF-2026-XXXXXXXXX --- a/libcli/security/security_token.c +++ b/libcli/security/security_token.c @@ -23,6 +23,7 @@ #include "replace.h" #include #include "lib/util/talloc_stack.h" +#include #include "lib/util/debug.h" #include "lib/util/fault.h" #include "libcli/security/security_token.h" @@ -138,11 +138,63 @@ bool security_token_is_anonymous(const struct security_token *token) return security_token_is_sid(token, &global_sid_Anonymous); } +/* + * Comparator for qsort/bsearch of struct dom_sid arrays. + */ +static int dom_sid_qsort_cmp(const void *a, const void *b) +{ + const struct dom_sid *sa = (const struct dom_sid *)a; + const struct dom_sid *sb = (const struct dom_sid *)b; + return dom_sid_compare(sa, sb); +} + +/** + * security_token_sort_sids - sort the extra SIDs in a token for O(log N) lookup. + * + * Indices 0 (user SID) and 1 (primary group SID) are left in place; they carry + * semantic meaning that must be preserved. All remaining SIDs + * (indices REMAINING_SIDS_INDEX..) are sorted in-place with qsort so that + * security_token_has_sid() can use bsearch instead of a linear scan. + * + * Call this once, after the token is fully assembled and before it is used + * for any access check (e.g. at the end of finalize_local_nt_token / + * security_token_create). + */ +void security_token_sort_sids(struct security_token *token) +{ + if (token == NULL || token->num_sids <= REMAINING_SIDS_INDEX) { + return; + } + qsort(&token->sids[REMAINING_SIDS_INDEX], + token->num_sids - REMAINING_SIDS_INDEX, + sizeof(struct dom_sid), + dom_sid_qsort_cmp); +} + bool security_token_has_sid(const struct security_token *token, const struct dom_sid *sid) { - uint32_t i; - for (i = 0; i < token->num_sids; i++) { + uint32_t i; + + if (token->sids == NULL) { + return false; + } + + /* + * Indices 0 (user) and 1 (primary group) are not sorted into the + * sorted tail, so check them with direct equality first. + */ + for (i = 0; i < REMAINING_SIDS_INDEX && i < token->num_sids; i++) { if (dom_sid_equal(&token->sids[i], sid)) { return true; } } - return false; + + if (token->num_sids > REMAINING_SIDS_INDEX) { + /* + * Binary search over the sorted tail + * sids[REMAINING_SIDS_INDEX .. num_sids-1]. + * Requires security_token_sort_sids() to have been called. + */ + const struct dom_sid *found = (const struct dom_sid *)bsearch( + sid, + &token->sids[REMAINING_SIDS_INDEX], + token->num_sids - REMAINING_SIDS_INDEX, + sizeof(struct dom_sid), + dom_sid_qsort_cmp); + return found != NULL; + } + + return false; } --- a/libcli/security/security_token.h +++ b/libcli/security/security_token.h @@ -55,6 +55,16 @@ bool security_token_has_sid(const struct security_token *token, const struct dom /* + * Sort token->sids[REMAINING_SIDS_INDEX..] in-place so that + * security_token_has_sid() can use binary search. Call once after + * the token is fully built (before the first access check). + * Indices 0 and 1 (user and primary group) are left in place. + */ +void security_token_sort_sids(struct security_token *token); + +/* * Return any of the domain sids found in the token matching "domain" * in _domain_sid, makes most sense if you just found one. */ --- a/source3/auth/token_util.c +++ b/source3/auth/token_util.c @@ -875,6 +875,12 @@ NTSTATUS finalize_local_nt_token(struct security_token *result, get_privileges_for_sids(&result->privilege_mask, result->sids, result->num_sids); } + + /* + * Sort sids[REMAINING_SIDS_INDEX..] so security_token_has_sid() + * can use binary search instead of O(N) linear scan. + */ + security_token_sort_sids(result); return NT_STATUS_OK; }