*
* Copyright (C) 2009 - 2016 Tom Kistner <tom@duncanthrax.net>
* Copyright (C) 2016 - 2020 Jeremy Harris <jgh@exim.org>
+ * Copyright (c) The Exim Maintainers 2021
*
* http://duncanthrax.net/pdkim/
*
};
-static blob lineending = {.data = US"\r\n", .len = 2};
+static const blob lineending = {.data = US"\r\n", .len = 2};
/* -------------------------------------------------------------------------- */
uschar *
If we have to relax the data for this sig, return our copy of it. */
static blob *
-pdkim_update_ctx_bodyhash(pdkim_bodyhash * b, blob * orig_data, blob * relaxed_data)
+pdkim_update_ctx_bodyhash(pdkim_bodyhash * b, const blob * orig_data, blob * relaxed_data)
{
-blob * canon_data = orig_data;
+const blob * canon_data = orig_data;
+size_t left;
+
/* Defaults to simple canon (no further treatment necessary) */
if (b->canon_method == PDKIM_CANON_RELAXED)
}
/* Make sure we don't exceed the to-be-signed body length */
+left = canon_data->len;
if ( b->bodylength >= 0
- && b->signed_body_bytes + (unsigned long)canon_data->len > b->bodylength
+ && left > (unsigned long)b->bodylength - b->signed_body_bytes
)
- canon_data->len = b->bodylength - b->signed_body_bytes;
+ left = (unsigned long)b->bodylength - b->signed_body_bytes;
-if (canon_data->len > 0)
+if (left > 0)
{
- exim_sha_update(&b->body_hash_ctx, CUS canon_data->data, canon_data->len);
- b->signed_body_bytes += canon_data->len;
- DEBUG(D_acl) pdkim_quoteprint(canon_data->data, canon_data->len);
+ exim_sha_update(&b->body_hash_ctx, CUS canon_data->data, left);
+ b->signed_body_bytes += left;
+ DEBUG(D_acl) pdkim_quoteprint(canon_data->data, left);
}
return relaxed_data;
/* VERIFICATION --------------------------------------------------------- */
/* Be careful that the header sig included a bodyash */
- if ( sig->bodyhash.data
+ if (sig->bodyhash.data && sig->bodyhash.len == b->bh.len
&& memcmp(b->bh.data, sig->bodyhash.data, b->bh.len) == 0)
{
DEBUG(D_acl) debug_printf("DKIM [%s] Body hash compared OK\n", sig->domain);
last_sig->next = sig;
}
- if (--dkim_collect_input == 0)
+ if (dkim_collect_input && --dkim_collect_input == 0)
{
ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->s);
ctx->cur_header->s[ctx->cur_header->ptr = 0] = '\0';
/*
* RFC 5322 specifies that header line length SHOULD be no more than 78
- * lets make it so!
* pdkim_headcat
*
- * returns uschar * (not nul-terminated)
+ * Returns gstring (not nul-terminated) appending to one supplied
*
* col: this int holds and receives column number (octets since last '\n')
* str: partial string to append to
- * pad: padding, split line or space after before or after eg: ";"
+ * pad: padding, split line or space after before or after eg: ";".
+ * Only the initial charater is used.
* intro: - must join to payload eg "h=", usually the tag name
* payload: eg base64 data - long data can be split arbitrarily.
*
* pairs and inside long values. it also always spaces or breaks after the
* "pad"
*
- * no guarantees are made for output given out-of range input. like tag
+ * No guarantees are made for output given out-of range input. like tag
* names longer than 78, or bogus col. Input is assumed to be free of line breaks.
*/
pdkim_headcat(int * col, gstring * str,
const uschar * pad, const uschar * intro, const uschar * payload)
{
-size_t l;
-
-if (pad)
- {
- l = Ustrlen(pad);
- if (*col + l > 78)
- str = pdkim_hdr_cont(str, col);
- str = string_catn(str, pad, l);
- *col += l;
- }
-
-l = (pad?1:0) + (intro?Ustrlen(intro):0);
-
-if (*col + l > 78)
- { /*can't fit intro - start a new line to make room.*/
- str = pdkim_hdr_cont(str, col);
- l = intro?Ustrlen(intro):0;
- }
-
-l += payload ? Ustrlen(payload):0 ;
+int len, chomp, padded = 0;
-while (l>77)
- { /* this fragment will not fit on a single line */
- if (pad)
- {
- str = string_catn(str, US" ", 1);
- *col += 1;
- pad = NULL; /* only want this once */
- l--;
- }
-
- if (intro)
- {
- size_t sl = Ustrlen(intro);
-
- str = string_catn(str, intro, sl);
- *col += sl;
- l -= sl;
- intro = NULL; /* only want this once */
- }
+/* If we can fit at least the pad at the end of current line, do it now.
+Otherwise, wrap if there is a pad. */
- if (payload)
+if (pad)
+ if (*col + 1 <= 78)
{
- size_t sl = Ustrlen(payload);
- size_t chomp = *col+sl < 77 ? sl : 78-*col;
-
- str = string_catn(str, payload, chomp);
- *col += chomp;
- payload += chomp;
- l -= chomp-1;
+ str = string_catn(str, pad, 1);
+ (*col)++;
+ pad = NULL;
+ padded = 1;
}
+ else
+ str = pdkim_hdr_cont(str, col);
- /* the while precondition tells us it didn't fit. */
- str = pdkim_hdr_cont(str, col);
- }
+/* Special case: if the whole addition does not fit at the end of the current
+line, but could fit on a new line, wrap to give it its full, dedicated line. */
-if (*col + l > 78)
+len = (pad ? 2 : padded)
+ + (intro ? Ustrlen(intro) : 0)
+ + (payload ? Ustrlen(payload) : 0);
+if (len <= 77 && *col+len > 78)
{
str = pdkim_hdr_cont(str, col);
- pad = NULL;
+ padded = 0;
}
+/* Either we already dealt with the pad or we know there is room */
+
if (pad)
{
+ str = string_catn(str, pad, 1);
str = string_catn(str, US" ", 1);
- *col += 1;
- pad = NULL;
+ *col += 2;
}
-
-if (intro)
+else if (padded && *col < 78)
{
- size_t sl = Ustrlen(intro);
-
- str = string_catn(str, intro, sl);
- *col += sl;
- l -= sl;
- intro = NULL;
+ str = string_catn(str, US" ", 1);
+ (*col)++;
}
-if (payload)
- {
- size_t sl = Ustrlen(payload);
+/* Call recursively with intro as payload: it gets the same, special treatment
+(that is, not split if < 78). */
- str = string_catn(str, payload, sl);
- *col += sl;
- }
+if (intro)
+ str = pdkim_headcat(col, str, NULL, NULL, intro);
+
+if (payload)
+ for (len = Ustrlen(payload); len; len -= chomp)
+ {
+ if (*col >= 78)
+ str = pdkim_hdr_cont(str, col);
+ chomp = *col+len > 78 ? 78 - *col : len;
+ str = string_catn(str, payload, chomp);
+ *col += chomp;
+ payload += chomp;
+ }
return str;
}
if (sig->created > 0)
{
- uschar minibuf[20];
+ uschar minibuf[21];
snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->created);
hdr = pdkim_headcat(&col, hdr, US";", US"t=", minibuf);
if (sig->expires > 0)
{
- uschar minibuf[20];
+ uschar minibuf[21];
snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->expires);
hdr = pdkim_headcat(&col, hdr, US";", US"x=", minibuf);
if (sig->bodylength >= 0)
{
- uschar minibuf[20];
+ uschar minibuf[21];
snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->bodylength);
hdr = pdkim_headcat(&col, hdr, US";", US"l=", minibuf);
int excess = p->key.len - 32;
if (excess > 0)
{
- DEBUG(D_acl) debug_printf("DKIM: unexpected pubkey len %lu\n", p->key.len);
+ DEBUG(D_acl)
+ debug_printf("DKIM: unexpected pubkey len %lu\n", (unsigned long) p->key.len);
p->key.data += excess; p->key.len = 32;
}
}
/* The line-buffer is for message data, hence tainted */
ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE);
ctx->dns_txt_callback = dns_txt_callback;
+ctx->cur_header = string_get_tainted(36, TRUE);
return ctx;
}