# UNDF: UNDF-2026-000000217 --- a/postfix/src/cleanup/cleanup_masquerade.c +++ b/postfix/src/cleanup/cleanup_masquerade.c @@ -52,6 +52,7 @@ #include #include #include +#include /* Application-specific. */ @@ -75,6 +76,8 @@ int cleanup_masquerade_external(CLEANUP_STATE *state, VSTRING *addr, ARGV *masq_domains) { char *domain; + /* Lazy-built hash cache for masq_domains exact matches (key=domain, value=masq) */ + static HTABLE *masq_domain_cache = 0; ssize_t domain_len; char **masqp; char *masq; @@ -96,11 +99,26 @@ int cleanup_masquerade_external(CLEANUP_STATE *state, VSTRING *addr, if (excluded) return (0); } + + /* + * CWE-407 fix: Build a hash cache of exact-match masquerade domains on + * first use so we avoid O(D) scan for every address processed. + * Wildcard/prefix masquerade domains (those starting with '!') still + * require the linear scan — handle them in a second pass. + */ + if (masq_domain_cache == 0) { + masq_domain_cache = htable_create(masq_domains->argc * 2 + 1); + for (char **p = masq_domains->argv; *p != 0; p++) { + char *m = *p; + int neg = 0; + while (*m == '!') { neg = !neg; m++; } + if (*m && strchr(m, '.') != 0 && !neg) + htable_enter(masq_domain_cache, m, m); + } + } + /* - * If any parent domain matches the list of masquerade domains, replace - * the domain in the address and terminate. If the domain matches a - * masquerade domain, leave it alone. Order of specification matters. + * Fast path: O(1) hash lookup for exact-match masquerade domains. + * Fall through to linear scan only for prefix/negated patterns. */ + { + char *cached = htable_find(masq_domain_cache, domain); + if (cached) { + if (msg_verbose) + msg_info("masquerade (cached): %s -> %s", domain, cached); + vstring_truncate(addr, name_len + 1); + vstring_strcat(addr, cached); + return (1); + } + } + for (masqp = masq_domains->argv; (masq = *masqp) != 0; masqp++) { for (truncate = 1; *masq == '!'; masq++) truncate = !truncate;