ARC: add optional t= tags to signing
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 3 Apr 2018 23:22:49 +0000 (00:22 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 3 Apr 2018 23:22:49 +0000 (00:22 +0100)
doc/doc-txt/experimental-spec.txt
src/src/arc.c

index 4e8e591..3c348d0 100644 (file)
@@ -798,13 +798,20 @@ Receive log lines for an ARC pass will be tagged "ARC".
 
 Signing
 --
-arc_sign = <admd-identifier> : <selector> : <privkey>
+arc_sign = <admd-identifier> : <selector> : <privkey> [ : <options> ]
 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.
index dedf64c..fdc280e 100644 (file)
@@ -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. */