From 95f006eff4b1ee4038bf93a9aa4686f226114eea Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 4 Apr 2018 00:22:49 +0100 Subject: [PATCH] ARC: add optional t= tags to signing --- doc/doc-txt/experimental-spec.txt | 9 ++++++- src/src/arc.c | 41 +++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt index 4e8e59148..3c348d0df 100644 --- a/doc/doc-txt/experimental-spec.txt +++ b/doc/doc-txt/experimental-spec.txt @@ -798,13 +798,20 @@ Receive log lines for an ARC pass will be tagged "ARC". Signing -- -arc_sign = : : +arc_sign = : : [ : ] An option on the smtp transport, which constructs and prepends to the message an ARC set of headers. The textually-first Authentication-Results: header is used as a basis (you must have added one on entry to the ADMD). Expanded as a whole; if unset, empty or forced-failure then no signing is done. If it is set, all three elements must be non-empty. +The fourth element is optional, and if present consists of a comma-separated list +of options. The only option implemented so far is + timestamps Add a t= tag to the generated AMS and AS headers, with the + current time. + +[As of writing, gmail insist that a t= tag on the AS is mandatory] + Caveats: * There must be an Authentication-Results header, presumably added by an ACL while receiving the message, for the same ADMD, for arc_sign to succeed. diff --git a/src/src/arc.c b/src/src/arc.c index dedf64c44..fdc280eb6 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -21,6 +21,8 @@ extern pdkim_ctx * dkim_verify_ctx; extern pdkim_ctx dkim_sign_ctx; +#define ARC_SIGN_OPT_TSTAMP BIT(0) + /******************************************************************************/ typedef struct hdr_rlist { @@ -1273,7 +1275,7 @@ return g; static gstring * arc_sign_append_ams(gstring * g, arc_ctx * ctx, int instance, const uschar * identity, const uschar * selector, blob * bodyhash, - hdr_rlist * rheaders, const uschar * privkey) + hdr_rlist * rheaders, const uschar * privkey, unsigned options) { uschar * s; gstring * hdata = NULL; @@ -1289,11 +1291,15 @@ header_line * h = (header_line *)(al+1); /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */ ams_off = g->ptr; -g = string_append(g, 10, +g = string_append(g, 7, ARC_HDR_AMS, US" i=", string_sprintf("%d", instance), US"; a=rsa-sha256; c=relaxed; d=", identity, /*XXX hardwired */ - US"; s=", selector, + US"; s=", selector); +if (options & ARC_SIGN_OPT_TSTAMP) + g = string_append(g, 2, + US"; t=", string_sprintf("%lu", (u_long)time(NULL))); +g = string_append(g, 3, US";\r\n\tbh=", pdkim_encode_base64(bodyhash), US";\r\n\th="); @@ -1391,7 +1397,7 @@ return US"none"; static gstring * arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx, int instance, const uschar * identity, const uschar * selector, blob * ar, - const uschar * privkey) + const uschar * privkey, unsigned options) { gstring * arcset; arc_set * as; @@ -1418,12 +1424,16 @@ blob sig; /* Construct the AS except for the signature */ -arcset = string_append(NULL, 10, +arcset = string_append(NULL, 9, ARC_HDR_AS, US" i=", string_sprintf("%d", instance), US"; cv=", status, US"; a=rsa-sha256; d=", identity, /*XXX hardwired */ - US"; s=", selector, /*XXX same as AMS */ + US"; s=", selector); /*XXX same as AMS */ +if (options & ARC_SIGN_OPT_TSTAMP) + arcset = string_append(arcset, 2, + US"; t=", string_sprintf("%lu", (u_long)time(NULL))); +arcset = string_cat(arcset, US";\r\n\t b=;"); h->slen = arcset->ptr; @@ -1521,7 +1531,8 @@ The dkim_exim_sign() function has already been called, so will have hashed the message body for us so long as we requested a hash previously. Arguments: - signspec Three-element colon-sep list: identity, selector, privkey + signspec Three-element colon-sep list: identity, selector, privkey. + Optional fourth element: comma-sep list of options. Already expanded sigheaders Any signature headers already generated, eg. by DKIM, or NULL errstr Error string @@ -1534,7 +1545,8 @@ Return value gstring * arc_sign(const uschar * signspec, gstring * sigheaders, uschar ** errstr) { -const uschar * identity, * selector, * privkey; +const uschar * identity, * selector, * privkey, * opts, * s; +unsigned options = 0; int sep = 0; header_line * headers; hdr_rlist * rheaders; @@ -1557,6 +1569,14 @@ if ( !*identity | !*selector if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey))) return sigheaders ? sigheaders : string_get(0); +if ((opts = string_nextinlist(&signspec, &sep, NULL, 0))) + { + int osep = ','; + while ((s = string_nextinlist(&opts, &osep, NULL, 0))) + if (Ustrcmp(s, "timestamps") == 0) + options |= ARC_SIGN_OPT_TSTAMP; + } + DEBUG(D_transport) debug_printf("ARC: sign for %s\n", identity); /* Make an rlist of any new DKIM headers, then add the "normals" rlist to it. @@ -1619,7 +1639,7 @@ g = arc_sign_append_aar(g, &arc_sign_ctx, identity, instance, &ar); b = arc_ams_setup_sign_bodyhash(); g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector, - &b->bh, headers_rlist, privkey); + &b->bh, headers_rlist, privkey, options); /* - Generate AS @@ -1634,7 +1654,8 @@ g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector, including self (but with an empty b= in self) */ -g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar, privkey); +g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar, + privkey, options); /* Finally, append the dkim headers and return the lot. */ -- 2.30.2