Skip to content
This repository was archived by the owner on Mar 27, 2026. It is now read-only.

Security hardening: constant-time auth, MIME depth limit, BDAT size enforcement#99

Open
kodareef5 wants to merge 4 commits intoExim:masterfrom
kodareef5:fix-ntlm-timing
Open

Security hardening: constant-time auth, MIME depth limit, BDAT size enforcement#99
kodareef5 wants to merge 4 commits intoExim:masterfrom
kodareef5:fix-ntlm-timing

Conversation

@kodareef5
Copy link
Copy Markdown

@kodareef5 kodareef5 commented Mar 23, 2026

Four security fixes across Exim's authentication, MIME processing, and BDAT handling:

1. Constant-time NTLM NT Response comparison (spa.c)

The SPA/NTLM authentication handler compares the 24-byte NT Response digest using a loop that returns on the first mismatch, leaking correct byte count through timing. Replaced with volatile XOR accumulation over all 24 bytes.

2. Constant-time CRAM-MD5 digest comparison (cram_md5.c)

The CRAM-MD5 handler compares the 16-byte hex-encoded HMAC-MD5 digest with an early-return loop. Same timing side-channel as the NTLM case. Applied the same volatile XOR pattern.

3. MIME nesting depth limit (mime.c)

mime_acl_check() recursively processes nested MIME multipart structures with no depth limit. A crafted message with thousands of nested boundaries (achievable in ~1MB) causes stack exhaustion and crashes the process. Added a depth limit of 100 using the existing parent pointer chain in mime_boundary_context.

4. BDAT message size limit enforcement (receive.c)

MAX is used where MIN should be when calculating BDAT read length, allowing data to exceed message_size_limit before the late check rejects it. The excess is written to spool before rejection, enabling temporary disk exhaustion beyond the configured limit.

spa.c:263 compares the 24-byte NT Response with memcmp(), which
returns early on the first byte mismatch, leaking timing information
about how many bytes are correct. Replace with a constant-time
XOR accumulation loop that examines all 24 bytes regardless of
match position.
The CRAM-MD5 authentication handler compares the client-supplied
hex-encoded HMAC-MD5 digest against the server-computed digest using
an early-return loop that exits on the first mismatched byte. This
leaks how many leading bytes are correct through timing differences.

Apply the same constant-time comparison pattern used in the SPA/NTLM
fix (spa.c): accumulate mismatches via volatile XOR so all 16 bytes
are compared regardless of match position.
@kodareef5 kodareef5 changed the title Use constant-time comparison for NTLM NT Response verification Use constant-time comparison for authentication digest verification Mar 23, 2026
mime_acl_check() recursively processes nested MIME multipart
structures with no depth limit. A crafted message with thousands
of nested multipart boundaries causes stack exhaustion and crashes
the Exim process handling the connection. A 1MB message can embed
over 17,000 nesting levels, far exceeding the typical 8MB stack.

Add a depth check using the existing parent pointer chain in
mime_boundary_context. The limit of 100 is far beyond any
legitimate MIME nesting while preventing stack overflow.
In the BDAT/CHUNKING receive path, MAX is used where MIN should be
when calculating how many bytes to read. This reads the GREATER of
the remaining chunk data or the remaining size limit allowance,
allowing data to exceed the configured message_size_limit before
the late check at line 1122 catches it and rejects the message.

The excess data is written to the spool filesystem before rejection,
enabling temporary disk space exhaustion beyond the admin's intended
limit.
@kodareef5 kodareef5 changed the title Use constant-time comparison for authentication digest verification Security hardening: constant-time auth, MIME depth limit, BDAT size enforcement Mar 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant