68 lines
2.6 KiB
Markdown
68 lines
2.6 KiB
Markdown
# postfix-0002 — Quadratic address masquerade exception check
|
||
|
||
**Target:** Postfix (vdukhovni/postfix mirror of postfix.org)
|
||
**Severity:** MEDIUM
|
||
**CWE:** CWE-407 (Algorithmic Complexity — Quadratic)
|
||
**Status:** PATCHED (patch/postfix-0002.patch)
|
||
|
||
## Summary
|
||
|
||
`cleanup_masquerade_external()` in `cleanup/cleanup_masquerade.c` calls
|
||
`string_list_match(cleanup_masq_exceptions, name)` for every address in a message's
|
||
envelope and headers. `string_list_match` is O(E) for E inline exception patterns.
|
||
The function is also called for each BCC auto-expansion address. With E exceptions
|
||
and N total addresses per message the cost is O(N × E).
|
||
|
||
Additionally, the masquerade-domain loop inside the same function
|
||
(`for (masqp = masq_domains->argv; ...; masqp++)`) is O(D) per address,
|
||
giving O(N × D) for D masquerade domains.
|
||
|
||
A message with 500 To/Cc recipients and a `masquerade_exceptions` list of 200
|
||
user names produces 100 000 string comparisons in the cleanup daemon per message.
|
||
|
||
## Location
|
||
|
||
```
|
||
postfix/src/cleanup/cleanup_masquerade.c
|
||
line 108 string_list_match(cleanup_masq_exceptions, name) — O(E) per address
|
||
line 125 for (masqp = masq_domains->argv; ...) — O(D) per address
|
||
|
||
postfix/src/cleanup/cleanup_addr.c
|
||
line 150 cleanup_masquerade_internal(state, clean_addr, cleanup_masq_domains)
|
||
line 218 cleanup_masquerade_internal(state, clean_addr, cleanup_masq_domains)
|
||
line 277 cleanup_masquerade_internal(state, clean_addr, cleanup_masq_domains)
|
||
|
||
postfix/src/cleanup/cleanup_message.c
|
||
line 187 cleanup_masquerade_tree(...)
|
||
line 244 cleanup_masquerade_tree(...)
|
||
```
|
||
|
||
## Root Cause
|
||
|
||
`string_list_match(cleanup_masq_exceptions, name)` performs a linear ARGV scan for
|
||
each address. `cleanup_masq_exceptions` is initialized once from `var_masq_exceptions`
|
||
at startup but never converted to a hash structure. The masquerade domains array is
|
||
also a raw `ARGV *` with no hash index, scanned linearly for every address processed.
|
||
|
||
## Fix
|
||
|
||
1. Convert `cleanup_masq_exceptions` from `STRING_LIST` (linear `ARGV`) to
|
||
`HTABLE *` (Postfix hash table) at initialization time: one O(E) pass at startup,
|
||
then O(1) per lookup.
|
||
|
||
2. Sort `masq_domains->argv` at initialization and binary-search on match;
|
||
or build a parallel `HTABLE *` for exact-match domains.
|
||
|
||
## Complexity
|
||
|
||
- Slow: O(N × (E + D)) — N addresses × E exceptions × D masq domains
|
||
- Fast: O(N) — O(1) hash lookup per address for both exceptions and domains
|
||
- Speedup at N=500, E=200, D=50: ~250×
|
||
|
||
## Patch
|
||
|
||
See `defects/postfix/patch/postfix-0002.patch`
|
||
|
||
## Unit Test
|
||
|
||
See `defects/postfix/unit/PostfixTest.java` (combined with postfix-0001)
|