comms/voip/smtp: 30 CWE-407 defects + 9 CLEAN; 224 sites, 101 ecosystems

This commit is contained in:
russell@unturf.com 2026-03-27 14:37:02 -04:00
parent 4d3fcc8e73
commit b3842ab6b8
86 changed files with 6516 additions and 5 deletions

View file

@ -0,0 +1,86 @@
--- a/postfix/src/util/match_list.c
+++ b/postfix/src/util/match_list.c
@@ -50,6 +50,7 @@
#include <argv.h>
#include <dict.h>
#include <match_list.h>
+#include <htable.h>
/* Application-specific */
@@ -132,6 +132,7 @@ MATCH_LIST *match_list_init(const char *pname, int flags,
list->match_count = match_count;
list->match_func =
(MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN));
list->match_args =
(const char **) mymalloc(match_count * sizeof(const char *));
va_start(ap, match_count);
for (i = 0; i < match_count; i++)
list->match_func[i] = va_arg(ap, MATCH_LIST_FN);
va_end(ap);
list->error = 0;
list->fold_buf = vstring_alloc(20);
+ list->string_cache = htable_create(8);
#define DO_MATCH 1
@@ -140,6 +141,12 @@ MATCH_LIST *match_list_init(const char *pname, int flags,
list->patterns = match_list_parse(list, argv_alloc(1), saved_patterns,
DO_MATCH);
argv_terminate(list->patterns);
+ /* Pre-index all inline string patterns (no '!' negation, no ':' dict) */
+ {
+ char **cpp;
+ for (cpp = list->patterns->argv; *cpp != 0; cpp++) {
+ char *p = *cpp;
+ int neg = 0;
+ while (*p == '!') { neg = !neg; p++; }
+ if (strchr(p, ':') == 0 && *p != '/') /* plain string pattern */
+ htable_enter(list->string_cache, p, (char *)(neg ? (void*)1 : (void*)2));
+ }
+ }
myfree(saved_patterns);
return (list);
}
@@ -160,6 +167,21 @@ int match_list_match(MATCH_LIST *list,...)
list->error = 0;
/*
+ * Fast path: O(1) hash lookup for the common case where all patterns
+ * are plain strings (no wildcards, no file includes, no type:table).
+ * Fall through to the linear scan only when non-string patterns exist.
+ */
+ if (list->match_count == 1 && htable_used(list->string_cache) > 0) {
+ casefold(list->fold_buf, list->match_args[0]);
+ HTABLE_INFO *entry = htable_find(list->string_cache, STR(list->fold_buf));
+ if (entry) {
+ /* value 2 = positive match, value 1 = negated (no match) */
+ return (entry->value == (char*)2) ? 1 : 0;
+ }
+ /* Not found in hash — could still match a non-string pattern below */
+ if (list->patterns->argc == (int)htable_used(list->string_cache))
+ return 0; /* all patterns are plain strings and none matched */
+ }
+
+ /*
* Iterate over all patterns in the list, stop at the first match.
*/
for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) {
@@ -185,5 +207,6 @@ void match_list_free(MATCH_LIST *list)
argv_free(list->patterns);
myfree((void *) list->match_func);
myfree((void *) list->match_args);
+ htable_free(list->string_cache, (void (*)(char *)) 0);
vstring_free(list->fold_buf);
myfree((void *) list);
}
--- a/postfix/src/util/match_list.h
+++ b/postfix/src/util/match_list.h
@@ -38,6 +38,7 @@ typedef int (*MATCH_LIST_FN) (struct MATCH_LIST *, const char *, const char *);
typedef struct MATCH_LIST {
const char *pname; /* parameter name for error reports */
int flags; /* MATCH_FLAG_XXX */
+ struct HTABLE *string_cache; /* O(1) index for inline string patterns */
ARGV *patterns; /* one pattern per element */
int match_count; /* number of match functions */
MATCH_LIST_FN *match_func; /* match functions */

View file

@ -0,0 +1,62 @@
--- a/postfix/src/cleanup/cleanup_masquerade.c
+++ b/postfix/src/cleanup/cleanup_masquerade.c
@@ -52,6 +52,7 @@
#include <mail_params.h>
#include <tok822.h>
#include <quote_822_local.h>
+#include <htable.h>
/* 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;