summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2017-09-10 20:23:21 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2017-09-10 20:30:02 +0100
commit99ab56234b257791ecedd149fc48dc226826d9c2 (patch)
treec09e483d3b68e0fb9d8228f0c7ac8f4e3241c67b
parent029b5c5ac2f1a272c8ece1696134e20bb452d65d (diff)
downloadexim4-99ab56234b257791ecedd149fc48dc226826d9c2.tar.gz
DKIM: fix signing bug induced by total size of parameter text
causing header-line fold between "b=" and terminating ";" of pseudo-header.
-rw-r--r--doc/doc-txt/ChangeLog5
-rw-r--r--src/src/pdkim/pdkim.c38
2 files changed, 30 insertions, 13 deletions
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 18a43d292..afd41e40a 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -38,6 +38,11 @@ JH/21 Bug 2151 (partial):
Avoid using SIZE on the MAIL for a callout verify, on any but
the main verify for receipient in uncached-mode.
+JH/26 Fix DKIM bug: when the pseudoheader generated for signing was exactly
+ the right size to place the terminating semicolon on its own folded
+ line, the header hash was calculated to an incorrect value thanks to
+ the (relaxed) space the fold became.
+
Exim version 4.89
-----------------
diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c
index 4c93de70d..7dad1b896 100644
--- a/src/src/pdkim/pdkim.c
+++ b/src/src/pdkim/pdkim.c
@@ -288,7 +288,7 @@ found:
/* Performs "relaxed" canonicalization of a header. */
static uschar *
-pdkim_relax_header(const uschar * header, int crlf)
+pdkim_relax_header(const uschar * header, BOOL append_crlf)
{
BOOL past_field_name = FALSE;
BOOL seen_wsp = FALSE;
@@ -299,8 +299,8 @@ uschar * q = relaxed;
for (p = header; *p; p++)
{
uschar c = *p;
- /* Ignore CR & LF */
- if (c == '\r' || c == '\n')
+
+ if (c == '\r' || c == '\n') /* Ignore CR & LF */
continue;
if (c == '\t' || c == ' ')
{
@@ -312,8 +312,8 @@ for (p = header; *p; p++)
else
if (!past_field_name && c == ':')
{
- if (seen_wsp) q--; /* This removes WSP before the colon */
- seen_wsp = TRUE; /* This removes WSP after the colon */
+ if (seen_wsp) q--; /* This removes WSP immediately before the colon */
+ seen_wsp = TRUE; /* This removes WSP immediately after the colon */
past_field_name = TRUE;
}
else
@@ -326,7 +326,7 @@ for (p = header; *p; p++)
if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */
-if (crlf) { *q++ = '\r'; *q++ = '\n'; }
+if (append_crlf) { *q++ = '\r'; *q++ = '\n'; }
*q = '\0';
return relaxed;
}
@@ -1303,11 +1303,23 @@ if (sig->bodylength >= 0)
}
/* Preliminary or final version? */
-base64_b = final ? pdkim_encode_base64(&sig->sighash) : US"";
-hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
+if (final)
+ {
+ base64_b = pdkim_encode_base64(&sig->sighash);
+ hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
-/* add trailing semicolon: I'm not sure if this is actually needed */
-hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US"");
+ /* add trailing semicolon: I'm not sure if this is actually needed */
+ hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US"");
+ }
+else
+ {
+ /* To satisfy the rule "all surrounding whitespace [...] deleted"
+ ( RFC 6376 section 3.7 ) we ensure there is no whitespace here. Otherwise
+ the headcat routine could insert a linebreak which the relaxer would reduce
+ to a single space preceding the terminating semicolon, resulting in an
+ incorrect header-hash. */
+ hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=;", US"");
+ }
hdr[hdr_len] = '\0';
return hdr;
@@ -1452,7 +1464,7 @@ while (sig)
p->value, (q - US p->value) + (p->next ? 1 : 0));
rh = sig->canon_headers == PDKIM_CANON_RELAXED
- ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
+ ? pdkim_relax_header(p->value, TRUE) /* cook header for relaxed canon */
: string_copy(CUS p->value); /* just copy it for simple canon */
/* Feed header to the hash algorithm */
@@ -1513,7 +1525,7 @@ while (sig)
/* cook header for relaxed canon, or just copy it for simple */
uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
- ? pdkim_relax_header(hdrs->value, 1)
+ ? pdkim_relax_header(hdrs->value, TRUE)
: string_copy(CUS hdrs->value);
/* Feed header to the hash algorithm */
@@ -1537,7 +1549,7 @@ while (sig)
/* Relax header if necessary */
if (sig->canon_headers == PDKIM_CANON_RELAXED)
- sig_hdr = pdkim_relax_header(sig_hdr, 0);
+ sig_hdr = pdkim_relax_header(sig_hdr, FALSE);
DEBUG(D_acl)
{