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>
Sun, 15 Apr 2018 16:25:47 +0000 (17:25 +0100)
doc/doc-txt/experimental-spec.txt
src/src/arc.c

index 0eeb2275897d9783a03d1f2f8c459af84d48c2ce..d51169073cc73b1746917ba7a3909b356567414e 100644 (file)
@@ -800,13 +800,20 @@ Receive log lines for an ARC pass will be tagged "ARC".
 
 Signing
 --
 
 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.
 
 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.
 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 6f567dc5faf92bad38cd56d2ba48aabfc498fa75..f3c567a8c86ff86965ee604189114b3ec9c6da93 100644 (file)
@@ -21,6 +21,8 @@
 extern pdkim_ctx * dkim_verify_ctx;
 extern pdkim_ctx dkim_sign_ctx;
 
 extern pdkim_ctx * dkim_verify_ctx;
 extern pdkim_ctx dkim_sign_ctx;
 
+#define ARC_SIGN_OPT_TSTAMP    BIT(0)
+
 /******************************************************************************/
 
 typedef struct hdr_rlist {
 /******************************************************************************/
 
 typedef struct hdr_rlist {
@@ -1272,7 +1274,7 @@ return g;
 static gstring *
 arc_sign_append_ams(gstring * g, arc_ctx * ctx, int instance,
   const uschar * identity, const uschar * selector, blob * bodyhash,
 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;
 {
 uschar * s;
 gstring * hdata = NULL;
@@ -1288,11 +1290,15 @@ header_line * h = (header_line *)(al+1);
 /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */
 
 ams_off = g->ptr;
 /* 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 */
       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=");
 
       US";\r\n\tbh=", pdkim_encode_base64(bodyhash),
       US";\r\n\th=");
 
@@ -1390,7 +1396,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,
 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;
 {
 gstring * arcset;
 arc_set * as;
@@ -1417,12 +1423,16 @@ blob sig;
 
 /* Construct the AS except for the signature */
 
 
 /* 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 */
          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;
          US";\r\n\t b=;");
 
 h->slen = arcset->ptr;
@@ -1520,7 +1530,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:
 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
                Already expanded
   sigheaders   Any signature headers already generated, eg. by DKIM, or NULL
   errstr       Error string
@@ -1533,7 +1544,8 @@ Return value
 gstring *
 arc_sign(const uschar * signspec, gstring * sigheaders, uschar ** errstr)
 {
 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;
 int sep = 0;
 header_line * headers;
 hdr_rlist * rheaders;
@@ -1556,6 +1568,14 @@ if (  !*identity | !*selector
 if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey)))
   return sigheaders ? sigheaders : string_get(0);
 
 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.
 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.
@@ -1617,7 +1637,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 = 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
 
 /*
 - Generate AS
@@ -1632,7 +1652,8 @@ g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector,
         including self (but with an empty b= in self)
 */
 
         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. */
 
 
 /* Finally, append the dkim headers and return the lot. */