ARC: limit verify chain to 50-deep
[exim.git] / src / src / arc.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4 /* Experimental ARC support for Exim
5    Copyright (c) Jeremy Harris 2018
6    License: GPL
7 */
8
9 #include "exim.h"
10 #ifdef EXPERIMENTAL_ARC
11 # if !defined SUPPORT_SPF
12 #  error SPF must also be enabled for ARC
13 # elif defined DISABLE_DKIM
14 #  error DKIM must also be enabled for ARC
15 # else
16
17 #  include "functions.h"
18 #  include "pdkim/pdkim.h"
19 #  include "pdkim/signing.h"
20
21 extern pdkim_ctx * dkim_verify_ctx;
22 extern pdkim_ctx dkim_sign_ctx;
23
24 #define ARC_SIGN_OPT_TSTAMP     BIT(0)
25 #define ARC_SIGN_OPT_EXPIRE     BIT(1)
26
27 #define ARC_SIGN_DEFAULT_EXPIRE_DELTA (60 * 60 * 24 * 30)       /* one month */
28
29 /******************************************************************************/
30
31 typedef struct hdr_rlist {
32   struct hdr_rlist *    prev;
33   BOOL                  used;
34   header_line *         h;
35 } hdr_rlist;
36
37 typedef struct arc_line {
38   header_line * complete;       /* including the header name; nul-term */
39   uschar *      relaxed;
40
41   /* identified tag contents */
42   /*XXX t= for AS? */
43   blob          i;
44   blob          cv;
45   blob          a;
46   blob          b;
47   blob          bh;
48   blob          d;
49   blob          h;
50   blob          s;
51   blob          c;
52   blob          l;
53
54   /* tag content sub-portions */
55   blob          a_algo;
56   blob          a_hash;
57
58   blob          c_head;
59   blob          c_body;
60
61   /* modified copy of b= field in line */
62   blob          rawsig_no_b_val;
63 } arc_line;
64
65 typedef struct arc_set {
66   struct arc_set *      next;
67   struct arc_set *      prev;
68
69   unsigned              instance;
70   arc_line *            hdr_aar;
71   arc_line *            hdr_ams;
72   arc_line *            hdr_as;
73
74   const uschar *        ams_verify_done;
75   BOOL                  ams_verify_passed;
76 } arc_set;
77
78 typedef struct arc_ctx {
79   arc_set *     arcset_chain;
80   arc_set *     arcset_chain_last;
81 } arc_ctx;
82
83 #define ARC_HDR_AAR     US"ARC-Authentication-Results:"
84 #define ARC_HDRLEN_AAR  27
85 #define ARC_HDR_AMS     US"ARC-Message-Signature:"
86 #define ARC_HDRLEN_AMS  22
87 #define ARC_HDR_AS      US"ARC-Seal:"
88 #define ARC_HDRLEN_AS   9
89 #define HDR_AR          US"Authentication-Results:"
90 #define HDRLEN_AR       23
91
92 static time_t now;
93 static time_t expire;
94 static hdr_rlist * headers_rlist;
95 static arc_ctx arc_sign_ctx = { NULL };
96
97
98 /******************************************************************************/
99
100
101 /* Get the instance number from the header.
102 Return 0 on error */
103 static unsigned
104 arc_instance_from_hdr(const arc_line * al)
105 {
106 const uschar * s = al->i.data;
107 if (!s || !al->i.len) return 0;
108 return (unsigned) atoi(CCS s);
109 }
110
111
112 static uschar *
113 skip_fws(uschar * s)
114 {
115 uschar c = *s;
116 while (c && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) c = *++s;
117 return s;
118 }
119
120
121 /* Locate instance struct on chain, inserting a new one if
122 needed.  The chain is in increasing-instance-number order
123 by the "next" link, and we have a "prev" link also.
124 */
125
126 static arc_set *
127 arc_find_set(arc_ctx * ctx, unsigned i)
128 {
129 arc_set ** pas, * as, * next, * prev;
130
131 for (pas = &ctx->arcset_chain, prev = NULL, next = ctx->arcset_chain;
132      as = *pas; pas = &as->next)
133   {
134   if (as->instance > i) break;
135   if (as->instance == i)
136     {
137     DEBUG(D_acl) debug_printf("ARC: existing instance %u\n", i);
138     return as;
139     }
140   next = as->next;
141   prev = as;
142   }
143
144 DEBUG(D_acl) debug_printf("ARC: new instance %u\n", i);
145 *pas = as = store_get(sizeof(arc_set));
146 memset(as, 0, sizeof(arc_set));
147 as->next = next;
148 as->prev = prev;
149 as->instance = i;
150 if (next)
151   next->prev = as;
152 else
153   ctx->arcset_chain_last = as;
154 return as;
155 }
156
157
158
159 /* Insert a tag content into the line structure.
160 Note this is a reference to existing data, not a copy.
161 Check for already-seen tag.
162 The string-pointer is on the '=' for entry.  Update it past the
163 content (to the ;) on return;
164 */
165
166 static uschar *
167 arc_insert_tagvalue(arc_line * al, unsigned loff, uschar ** ss)
168 {
169 uschar * s = *ss;
170 uschar c = *++s;
171 blob * b = (blob *)(US al + loff);
172 size_t len = 0;
173
174 /* [FWS] tag-value [FWS] */
175
176 if (b->data) return US"fail";
177 s = skip_fws(s);                                                /* FWS */
178
179 b->data = s;
180 while ((c = *s) && c != ';') { len++; s++; }
181 *ss = s;
182 while (len && ((c = s[-1]) == ' ' || c == '\t' || c == '\n' || c == '\r'))
183   { s--; len--; }                                               /* FWS */
184 b->len = len;
185 return NULL;
186 }
187
188
189 /* Inspect a header line, noting known tag fields.
190 Check for duplicates. */
191
192 static uschar *
193 arc_parse_line(arc_line * al, header_line * h, unsigned off, BOOL instance_only)
194 {
195 uschar * s = h->text + off;
196 uschar * r = NULL;      /* compiler-quietening */
197 uschar c;
198
199 al->complete = h;
200
201 if (!instance_only)
202   {
203   al->rawsig_no_b_val.data = store_get(h->slen + 1);
204   memcpy(al->rawsig_no_b_val.data, h->text, off);       /* copy the header name blind */
205   r = al->rawsig_no_b_val.data + off;
206   al->rawsig_no_b_val.len = off;
207   }
208
209 /* tag-list  =  tag-spec *( ";" tag-spec ) [ ";" ] */
210
211 while ((c = *s))
212   {
213   char tagchar;
214   uschar * t;
215   unsigned i = 0;
216   uschar * fieldstart = s;
217   uschar * bstart = NULL, * bend;
218
219   /* tag-spec  =  [FWS] tag-name [FWS] "=" [FWS] tag-value [FWS] */
220
221   s = skip_fws(s);                                              /* FWS */
222   if (!*s) break;
223 /* debug_printf("%s: consider '%s'\n", __FUNCTION__, s); */
224   tagchar = *s++;
225   s = skip_fws(s);                                              /* FWS */
226   if (!*s) break;
227
228   if (!instance_only || tagchar == 'i') switch (tagchar)
229     {
230     case 'a':                           /* a= AMS algorithm */
231       {
232       if (*s != '=') return US"no 'a' value";
233       if (arc_insert_tagvalue(al, offsetof(arc_line, a), &s)) return US"a tag dup";
234
235       /* substructure: algo-hash   (eg. rsa-sha256) */
236
237       t = al->a_algo.data = al->a.data;
238       while (*t != '-')
239         if (!*t++ || ++i > al->a.len) return US"no '-' in 'a' value";
240       al->a_algo.len = i;
241       if (*t++ != '-') return US"no '-' in 'a' value";
242       al->a_hash.data = t;
243       al->a_hash.len = al->a.len - i - 1;
244       }
245       break;
246     case 'b':
247       {
248       gstring * g = NULL;
249
250       switch (*s)
251         {
252         case '=':                       /* b= AMS signature */
253           if (al->b.data) return US"already b data";
254           bstart = s+1;
255
256           /* The signature can have FWS inserted in the content;
257           make a stripped copy */
258
259           while ((c = *++s) && c != ';')
260             if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
261               g = string_catn(g, s, 1);
262           al->b.data = string_from_gstring(g);
263           al->b.len = g->ptr;
264           gstring_reset_unused(g);
265           bend = s;
266           break;
267         case 'h':                       /* bh= AMS body hash */
268           s = skip_fws(++s);                                    /* FWS */
269           if (*s != '=') return US"no bh value";
270           if (al->bh.data) return US"already bh data";
271
272           /* The bodyhash can have FWS inserted in the content;
273           make a stripped copy */
274
275           while ((c = *++s) && c != ';')
276             if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
277               g = string_catn(g, s, 1);
278           al->bh.data = string_from_gstring(g);
279           al->bh.len = g->ptr;
280           gstring_reset_unused(g);
281           break;
282         default:
283           return US"b? tag";
284         }
285       }
286       break;
287     case 'c':
288       switch (*s)
289         {
290         case '=':                       /* c= AMS canonicalisation */
291           if (arc_insert_tagvalue(al, offsetof(arc_line, c), &s)) return US"c tag dup";
292
293           /* substructure: head/body   (eg. relaxed/simple)) */
294
295           t = al->c_head.data = al->c.data;
296           while (isalpha(*t))
297             if (!*t++ || ++i > al->a.len) break;
298           al->c_head.len = i;
299           if (*t++ == '/')              /* /body is optional */
300             {
301             al->c_body.data = t;
302             al->c_body.len = al->c.len - i - 1;
303             }
304           else
305             {
306             al->c_body.data = US"simple";
307             al->c_body.len = 6;
308             }
309           break;
310         case 'v':                       /* cv= AS validity */
311           if (*++s != '=') return US"cv tag val";
312           if (arc_insert_tagvalue(al, offsetof(arc_line, cv), &s)) return US"cv tag dup";
313           break;
314         default:
315           return US"c? tag";
316         }
317       break;
318     case 'd':                           /* d= AMS domain */
319       if (*s != '=') return US"d tag val";
320       if (arc_insert_tagvalue(al, offsetof(arc_line, d), &s)) return US"d tag dup";
321       break;
322     case 'h':                           /* h= AMS headers */
323       if (*s != '=') return US"h tag val";
324       if (arc_insert_tagvalue(al, offsetof(arc_line, h), &s)) return US"h tag dup";
325       break;
326     case 'i':                           /* i= ARC set instance */
327       if (*s != '=') return US"i tag val";
328       if (arc_insert_tagvalue(al, offsetof(arc_line, i), &s)) return US"i tag dup";
329       if (instance_only) goto done;
330       break;
331     case 'l':                           /* l= bodylength */
332       if (*s != '=') return US"l tag val";
333       if (arc_insert_tagvalue(al, offsetof(arc_line, l), &s)) return US"l tag dup";
334       break;
335     case 's':                           /* s= AMS selector */
336       if (*s != '=') return US"s tag val";
337       if (arc_insert_tagvalue(al, offsetof(arc_line, s), &s)) return US"s tag dup";
338       break;
339     }
340
341   while ((c = *s) && c != ';') s++;
342   if (c) s++;                           /* ; after tag-spec */
343
344   /* for all but the b= tag, copy the field including FWS.  For the b=,
345   drop the tag content. */
346
347   if (!instance_only)
348     if (bstart)
349       {
350       size_t n = bstart - fieldstart;
351       memcpy(r, fieldstart, n);         /* FWS "b=" */
352       r += n;
353       al->rawsig_no_b_val.len += n;
354       n = s - bend;
355       memcpy(r, bend, n);               /* FWS ";" */
356       r += n;
357       al->rawsig_no_b_val.len += n;
358       }
359     else
360       {
361       size_t n = s - fieldstart;
362       memcpy(r, fieldstart, n);
363       r += n;
364       al->rawsig_no_b_val.len += n;
365       }
366   }
367
368 if (!instance_only)
369   *r = '\0';
370
371 done:
372 /* debug_printf("%s: finshed\n", __FUNCTION__); */
373 return NULL;
374 }
375
376
377 /* Insert one header line in the correct set of the chain,
378 adding instances as needed and checking for duplicate lines.
379 */
380
381 static uschar *
382 arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff,
383   BOOL instance_only)
384 {
385 unsigned i;
386 arc_set * as;
387 arc_line * al = store_get(sizeof(arc_line)), ** alp;
388 uschar * e;
389
390 memset(al, 0, sizeof(arc_line));
391
392 if ((e = arc_parse_line(al, h, off, instance_only)))
393   {
394   DEBUG(D_acl) if (e) debug_printf("ARC: %s\n", e);
395   return US"line parse";
396   }
397 if (!(i = arc_instance_from_hdr(al)))   return US"instance find";
398 if (i > 50)                             return US"overlarge instance number";
399 if (!(as = arc_find_set(ctx, i)))       return US"set find";
400 if (*(alp = (arc_line **)(US as + hoff))) return US"dup hdr";
401
402 *alp = al;
403 return NULL;
404 }
405
406
407
408
409 static const uschar *
410 arc_try_header(arc_ctx * ctx, header_line * h, BOOL instance_only)
411 {
412 const uschar * e;
413
414 /*debug_printf("consider hdr '%s'\n", h->text);*/
415 if (strncmpic(ARC_HDR_AAR, h->text, ARC_HDRLEN_AAR) == 0)
416   {
417   DEBUG(D_acl)
418     {
419     int len = h->slen;
420     uschar * s;
421     for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
422       s--, len--;
423     debug_printf("ARC: found AAR: %.*s\n", len, h->text);
424     }
425   if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AAR, offsetof(arc_set, hdr_aar),
426                           TRUE)))
427     {
428     DEBUG(D_acl) debug_printf("inserting AAR: %s\n", e);
429     return US"inserting AAR";
430     }
431   }
432 else if (strncmpic(ARC_HDR_AMS, h->text, ARC_HDRLEN_AMS) == 0)
433   {
434   arc_line * ams;
435
436   DEBUG(D_acl)
437     {
438     int len = h->slen;
439     uschar * s;
440     for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
441       s--, len--;
442     debug_printf("ARC: found AMS: %.*s\n", len, h->text);
443     }
444   if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AMS, offsetof(arc_set, hdr_ams),
445                           instance_only)))
446     {
447     DEBUG(D_acl) debug_printf("inserting AMS: %s\n", e);
448     return US"inserting AMS";
449     }
450
451   /* defaults */
452   /*XXX dubious selection of ams here */
453   ams = ctx->arcset_chain->hdr_ams;
454   if (!ams->c.data)
455     {
456     ams->c_head.data = US"simple"; ams->c_head.len = 6;
457     ams->c_body = ams->c_head;
458     }
459   }
460 else if (strncmpic(ARC_HDR_AS, h->text, ARC_HDRLEN_AS) == 0)
461   {
462   DEBUG(D_acl)
463     {
464     int len = h->slen;
465     uschar * s;
466     for (s = h->text + h->slen; s[-1] == '\r' || s[-1] == '\n'; )
467       s--, len--;
468     debug_printf("ARC: found AS: %.*s\n", len, h->text);
469     }
470   if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AS, offsetof(arc_set, hdr_as),
471                           instance_only)))
472     {
473     DEBUG(D_acl) debug_printf("inserting AS: %s\n", e);
474     return US"inserting AS";
475     }
476   }
477 return NULL;
478 }
479
480
481
482 /* Gather the chain of arc sets from the headers.
483 Check for duplicates while that is done.  Also build the
484 reverse-order headers list;
485
486 Return: ARC state if determined, eg. by lack of any ARC chain.
487 */
488
489 static const uschar *
490 arc_vfy_collect_hdrs(arc_ctx * ctx)
491 {
492 header_line * h;
493 hdr_rlist * r = NULL, * rprev = NULL;
494 const uschar * e;
495
496 DEBUG(D_acl) debug_printf("ARC: collecting arc sets\n");
497 for (h = header_list; h; h = h->next)
498   {
499   r = store_get(sizeof(hdr_rlist));
500   r->prev = rprev;
501   r->used = FALSE;
502   r->h = h;
503   rprev = r;
504
505   if ((e = arc_try_header(ctx, h, FALSE)))
506     {
507     arc_state_reason = string_sprintf("collecting headers: %s", e);
508     return US"fail";
509     }
510   }
511 headers_rlist = r;
512
513 if (!ctx->arcset_chain) return US"none";
514 return NULL;
515 }
516
517
518 static BOOL
519 arc_cv_match(arc_line * al, const uschar * s)
520 {
521 return Ustrncmp(s, al->cv.data, al->cv.len) == 0;
522 }
523
524 /******************************************************************************/
525
526 /* Return the hash of headers from the message that the AMS claims it
527 signed.
528 */
529
530 static void
531 arc_get_verify_hhash(arc_ctx * ctx, arc_line * ams, blob * hhash)
532 {
533 const uschar * headernames = string_copyn(ams->h.data, ams->h.len);
534 const uschar * hn;
535 int sep = ':';
536 hdr_rlist * r;
537 BOOL relaxed = Ustrncmp(US"relaxed", ams->c_head.data, ams->c_head.len) == 0;
538 int hashtype = pdkim_hashname_to_hashtype(
539                     ams->a_hash.data, ams->a_hash.len);
540 hctx hhash_ctx;
541 const uschar * s;
542 int len;
543
544 if (!exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
545   {
546   DEBUG(D_acl)
547       debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
548   return;
549   }
550
551 /* For each headername in the list from the AMS (walking in order)
552 walk the message headers in reverse order, adding to the hash any
553 found for the first time. For that last point, maintain used-marks
554 on the list of message headers. */
555
556 DEBUG(D_acl) debug_printf("ARC: AMS header data for verification:\n");
557
558 for (r = headers_rlist; r; r = r->prev)
559   r->used = FALSE;
560 while ((hn = string_nextinlist(&headernames, &sep, NULL, 0)))
561   for (r = headers_rlist; r; r = r->prev)
562     if (  !r->used
563        && strncasecmp(CCS (s = r->h->text), CCS hn, Ustrlen(hn)) == 0
564        )
565       {
566       if (relaxed) s = pdkim_relax_header_n(s, r->h->slen, TRUE);
567
568       len = Ustrlen(s);
569       DEBUG(D_acl) pdkim_quoteprint(s, len);
570       exim_sha_update(&hhash_ctx, s, Ustrlen(s));
571       r->used = TRUE;
572       break;
573       }
574
575 /* Finally add in the signature header (with the b= tag stripped); no CRLF */
576
577 s = ams->rawsig_no_b_val.data, len = ams->rawsig_no_b_val.len;
578 if (relaxed)
579   len = Ustrlen(s = pdkim_relax_header_n(s, len, FALSE));
580 DEBUG(D_acl) pdkim_quoteprint(s, len);
581 exim_sha_update(&hhash_ctx, s, len);
582
583 exim_sha_finish(&hhash_ctx, hhash);
584 DEBUG(D_acl)
585   { debug_printf("ARC: header hash: "); pdkim_hexprint(hhash->data, hhash->len); }
586 return;
587 }
588
589
590
591
592 static pdkim_pubkey *
593 arc_line_to_pubkey(arc_line * al)
594 {
595 uschar * dns_txt;
596 pdkim_pubkey * p;
597
598 if (!(dns_txt = dkim_exim_query_dns_txt(string_sprintf("%.*s._domainkey.%.*s",
599           al->s.len, al->s.data, al->d.len, al->d.data))))
600   {
601   DEBUG(D_acl) debug_printf("pubkey dns lookup fail\n");
602   return NULL;
603   }
604
605 if (  !(p = pdkim_parse_pubkey_record(dns_txt))
606    || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
607    )
608   {
609   DEBUG(D_acl) debug_printf("pubkey dns lookup format error\n");
610   return NULL;
611   }
612
613 /* If the pubkey limits use to specified hashes, reject unusable
614 signatures. XXX should we have looked for multiple dns records? */
615
616 if (p->hashes)
617   {
618   const uschar * list = p->hashes, * ele;
619   int sep = ':';
620
621   while ((ele = string_nextinlist(&list, &sep, NULL, 0)))
622     if (Ustrncmp(ele, al->a_hash.data, al->a_hash.len) == 0) break;
623   if (!ele)
624     {
625     DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%.*s\n",
626                               p->hashes, (int)al->a.len, al->a.data);
627     return NULL;
628     }
629   }
630 return p;
631 }
632
633
634
635
636 static pdkim_bodyhash *
637 arc_ams_setup_vfy_bodyhash(arc_line * ams)
638 {
639 int canon_head, canon_body;
640 long bodylen;
641
642 if (!ams->c.data) ams->c.data = US"simple";     /* RFC 6376 (DKIM) default */
643 pdkim_cstring_to_canons(ams->c.data, ams->c.len, &canon_head, &canon_body);
644 bodylen = ams->l.data
645         ? strtol(CS string_copyn(ams->l.data, ams->l.len), NULL, 10) : -1;
646
647 return pdkim_set_bodyhash(dkim_verify_ctx,
648         pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len),
649         canon_body,
650         bodylen);
651 }
652
653
654
655 /* Verify an AMS. This is a DKIM-sig header, but with an ARC i= tag
656 and without a DKIM v= tag.
657 */
658
659 static const uschar *
660 arc_ams_verify(arc_ctx * ctx, arc_set * as)
661 {
662 arc_line * ams = as->hdr_ams;
663 pdkim_bodyhash * b;
664 pdkim_pubkey * p;
665 blob sighash;
666 blob hhash;
667 ev_ctx vctx;
668 int hashtype;
669 const uschar * errstr;
670
671 as->ams_verify_done = US"in-progress";
672
673 /* Check the AMS has all the required tags:
674    "a="  algorithm
675    "b="  signature
676    "bh=" body hash
677    "d="  domain (for key lookup)
678    "h="  headers (included in signature)
679    "s="  key-selector (for key lookup)
680 */
681 if (  !ams->a.data || !ams->b.data || !ams->bh.data || !ams->d.data
682    || !ams->h.data || !ams->s.data)
683   {
684   as->ams_verify_done = arc_state_reason = US"required tag missing";
685   return US"fail";
686   }
687
688
689 /* The bodyhash should have been created earlier, and the dkim code should
690 have managed calculating it during message input.  Find the reference to it. */
691
692 if (!(b = arc_ams_setup_vfy_bodyhash(ams)))
693   {
694   as->ams_verify_done = arc_state_reason = US"internal hash setup error";
695   return US"fail";
696   }
697
698 DEBUG(D_acl)
699   {
700   debug_printf("ARC i=%d AMS   Body bytes hashed: %lu\n"
701                "              Body %.*s computed: ",
702                as->instance, b->signed_body_bytes,
703                (int)ams->a_hash.len, ams->a_hash.data);
704   pdkim_hexprint(CUS b->bh.data, b->bh.len);
705   }
706
707 /* We know the bh-tag blob is of a nul-term string, so safe as a string */
708
709 if (  !ams->bh.data
710    || (pdkim_decode_base64(ams->bh.data, &sighash), sighash.len != b->bh.len)
711    || memcmp(sighash.data, b->bh.data, b->bh.len) != 0
712    )
713   {
714   DEBUG(D_acl)
715     {
716     debug_printf("ARC i=%d AMS Body hash from headers: ", as->instance);
717     pdkim_hexprint(sighash.data, sighash.len);
718     debug_printf("ARC i=%d AMS Body hash did NOT match\n", as->instance);
719     }
720   return as->ams_verify_done = arc_state_reason = US"AMS body hash miscompare";
721   }
722
723 DEBUG(D_acl) debug_printf("ARC i=%d AMS Body hash compared OK\n", as->instance);
724
725 /* Get the public key from DNS */
726
727 if (!(p = arc_line_to_pubkey(ams)))
728   return as->ams_verify_done = arc_state_reason = US"pubkey problem";
729
730 /* We know the b-tag blob is of a nul-term string, so safe as a string */
731 pdkim_decode_base64(ams->b.data, &sighash);
732
733 arc_get_verify_hhash(ctx, ams, &hhash);
734
735 /* Setup the interface to the signing library */
736
737 if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx)))
738   {
739   DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
740   as->ams_verify_done = arc_state_reason = US"internal sigverify init error";
741   return US"fail";
742   }
743
744 hashtype = pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len);
745
746 if ((errstr = exim_dkim_verify(&vctx,
747           pdkim_hashes[hashtype].exim_hashmethod, &hhash, &sighash)))
748   {
749   DEBUG(D_acl) debug_printf("ARC i=%d AMS verify %s\n", as->instance, errstr);
750   return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
751   }
752
753 DEBUG(D_acl) debug_printf("ARC i=%d AMS verify pass\n", as->instance);
754 as->ams_verify_passed = TRUE;
755 return NULL;
756 }
757
758
759
760 /* Check the sets are instance-continuous and that all
761 members are present.  Check that no arc_seals are "fail".
762 Set the highest instance number global.
763 Verify the latest AMS.
764 */
765 static uschar *
766 arc_headers_check(arc_ctx * ctx)
767 {
768 arc_set * as;
769 int inst;
770 BOOL ams_fail_found = FALSE;
771
772 if (!(as = ctx->arcset_chain))
773   return US"none";
774
775 for(inst = 0; as; as = as->next)
776   {
777   if (  as->instance != ++inst
778      || !as->hdr_aar || !as->hdr_ams || !as->hdr_as
779      || arc_cv_match(as->hdr_as, US"fail")
780      )
781     {
782     arc_state_reason = string_sprintf("i=%d"
783       " (cv, sequence or missing header)", as->instance);
784     DEBUG(D_acl) debug_printf("ARC chain fail at %s\n", arc_state_reason);
785     return US"fail";
786     }
787
788   /* Evaluate the oldest-pass AMS validation while we're here.
789   It does not affect the AS chain validation but is reported as
790   auxilary info. */
791
792   if (!ams_fail_found)
793     if (arc_ams_verify(ctx, as))
794       ams_fail_found = TRUE;
795     else
796       arc_oldest_pass = inst;
797   arc_state_reason = NULL;
798   }
799
800 arc_received = ctx->arcset_chain_last;
801 arc_received_instance = inst;
802
803 /* We can skip the latest-AMS validation, if we already did it. */
804
805 as = ctx->arcset_chain_last;
806 if (!as->ams_verify_passed)
807   {
808   if (as->ams_verify_done)
809     {
810     arc_state_reason = as->ams_verify_done;
811     return US"fail";
812     }
813   if (!!arc_ams_verify(ctx, as))
814     return US"fail";
815   }
816 return NULL;
817 }
818
819
820 /******************************************************************************/
821 static const uschar *
822 arc_seal_verify(arc_ctx * ctx, arc_set * as)
823 {
824 arc_line * hdr_as = as->hdr_as;
825 arc_set * as2;
826 int hashtype;
827 hctx hhash_ctx;
828 blob hhash_computed;
829 blob sighash;
830 ev_ctx vctx;
831 pdkim_pubkey * p;
832 const uschar * errstr;
833
834 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d\n", as->instance);
835 /*
836        1.  If the value of the "cv" tag on that seal is "fail", the
837            chain state is "fail" and the algorithm stops here.  (This
838            step SHOULD be skipped if the earlier step (2.1) was
839            performed) [it was]
840
841        2.  In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
842            == "none" && i != 1)) then the chain state is "fail" and the
843            algorithm stops here (note that the ordering of the logic is
844            structured for short-circuit evaluation).
845 */
846
847 if (  as->instance == 1 && !arc_cv_match(hdr_as, US"none")
848    || arc_cv_match(hdr_as, US"none") && as->instance != 1
849    )
850   {
851   arc_state_reason = US"seal cv state";
852   return US"fail";
853   }
854
855 /*
856        3.  Initialize a hash function corresponding to the "a" tag of
857            the ARC-Seal.
858 */
859
860 hashtype = pdkim_hashname_to_hashtype(hdr_as->a_hash.data, hdr_as->a_hash.len);
861
862 if (!exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
863   {
864   DEBUG(D_acl)
865       debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
866   arc_state_reason = US"seal hash setup error";
867   return US"fail";
868   }
869
870 /*
871        4.  Compute the canonicalized form of the ARC header fields, in
872            the order described in Section 5.4.2, using the "relaxed"
873            header canonicalization defined in Section 3.4.2 of
874            [RFC6376].  Pass the canonicalized result to the hash
875            function.
876
877 Headers are CRLF-separated, but the last one is not crlf-terminated.
878 */
879
880 DEBUG(D_acl) debug_printf("ARC: AS header data for verification:\n");
881 for (as2 = ctx->arcset_chain;
882      as2 && as2->instance <= as->instance;
883      as2 = as2->next)
884   {
885   arc_line * al;
886   uschar * s;
887   int len;
888
889   al = as2->hdr_aar;
890   if (!(s = al->relaxed))
891     al->relaxed = s = pdkim_relax_header_n(al->complete->text,
892                                             al->complete->slen, TRUE);
893   len = Ustrlen(s);
894   DEBUG(D_acl) pdkim_quoteprint(s, len);
895   exim_sha_update(&hhash_ctx, s, len);
896
897   al = as2->hdr_ams;
898   if (!(s = al->relaxed))
899     al->relaxed = s = pdkim_relax_header_n(al->complete->text,
900                                             al->complete->slen, TRUE);
901   len = Ustrlen(s);
902   DEBUG(D_acl) pdkim_quoteprint(s, len);
903   exim_sha_update(&hhash_ctx, s, len);
904
905   al = as2->hdr_as;
906   if (as2->instance == as->instance)
907     s = pdkim_relax_header_n(al->rawsig_no_b_val.data,
908                                         al->rawsig_no_b_val.len, FALSE);
909   else if (!(s = al->relaxed))
910     al->relaxed = s = pdkim_relax_header_n(al->complete->text,
911                                             al->complete->slen, TRUE);
912   len = Ustrlen(s);
913   DEBUG(D_acl) pdkim_quoteprint(s, len);
914   exim_sha_update(&hhash_ctx, s, len);
915   }
916
917 /*
918        5.  Retrieve the final digest from the hash function.
919 */
920
921 exim_sha_finish(&hhash_ctx, &hhash_computed);
922 DEBUG(D_acl)
923   {
924   debug_printf("ARC i=%d AS Header %.*s computed: ",
925     as->instance, (int)hdr_as->a_hash.len, hdr_as->a_hash.data);
926   pdkim_hexprint(hhash_computed.data, hhash_computed.len);
927   }
928
929
930 /*
931        6.  Retrieve the public key identified by the "s" and "d" tags in
932            the ARC-Seal, as described in Section 4.1.6.
933 */
934
935 if (!(p = arc_line_to_pubkey(hdr_as)))
936   return US"pubkey problem";
937
938 /*
939        7.  Determine whether the signature portion ("b" tag) of the ARC-
940            Seal and the digest computed above are valid according to the
941            public key.  (See also Section Section 8.4 for failure case
942            handling)
943
944        8.  If the signature is not valid, the chain state is "fail" and
945            the algorithm stops here.
946 */
947
948 /* We know the b-tag blob is of a nul-term string, so safe as a string */
949 pdkim_decode_base64(hdr_as->b.data, &sighash);
950
951 if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx)))
952   {
953   DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
954   return US"fail";
955   }
956
957 hashtype = pdkim_hashname_to_hashtype(hdr_as->a_hash.data, hdr_as->a_hash.len);
958
959 if ((errstr = exim_dkim_verify(&vctx,
960               pdkim_hashes[hashtype].exim_hashmethod,
961               &hhash_computed, &sighash)))
962   {
963   DEBUG(D_acl)
964     debug_printf("ARC i=%d AS headers verify: %s\n", as->instance, errstr);
965   arc_state_reason = US"seal sigverify error";
966   return US"fail";
967   }
968
969 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d pass\n", as->instance);
970 return NULL;
971 }
972
973
974 static const uschar *
975 arc_verify_seals(arc_ctx * ctx)
976 {
977 arc_set * as = ctx->arcset_chain;
978
979 if (!as)
980   return US"none";
981
982 while (as)
983   {
984   if (arc_seal_verify(ctx, as)) return US"fail";
985   as = as->next;
986   }
987 DEBUG(D_acl) debug_printf("ARC: AS vfy overall pass\n");
988 return NULL;
989 }
990 /******************************************************************************/
991
992 /* Do ARC verification.  Called from DATA ACL, on a verify = arc
993 condition.  No arguments; we are checking globals.
994
995 Return:  The ARC state, or NULL on error.
996 */
997
998 const uschar *
999 acl_verify_arc(void)
1000 {
1001 arc_ctx ctx = { NULL };
1002 const uschar * res;
1003
1004 if (!dkim_verify_ctx)
1005   {
1006   DEBUG(D_acl) debug_printf("ARC: no DKIM verify context\n");
1007   return NULL;
1008   }
1009
1010 /* AS evaluation, per
1011 https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-10#section-6
1012 */
1013 /* 1.  Collect all ARC sets currently on the message.  If there were
1014        none, the ARC state is "none" and the algorithm stops here.
1015 */
1016
1017 if ((res = arc_vfy_collect_hdrs(&ctx)))
1018   goto out;
1019
1020 /* 2.  If the form of any ARC set is invalid (e.g., does not contain
1021        exactly one of each of the three ARC-specific header fields),
1022        then the chain state is "fail" and the algorithm stops here.
1023
1024        1.  To avoid the overhead of unnecessary computation and delay
1025            from crypto and DNS operations, the cv value for all ARC-
1026            Seal(s) MAY be checked at this point.  If any of the values
1027            are "fail", then the overall state of the chain is "fail" and
1028            the algorithm stops here.
1029
1030    3.  Conduct verification of the ARC-Message-Signature header field
1031        bearing the highest instance number.  If this verification fails,
1032        then the chain state is "fail" and the algorithm stops here.
1033 */
1034
1035 if ((res = arc_headers_check(&ctx)))
1036   goto out;
1037
1038 /* 4.  For each ARC-Seal from the "N"th instance to the first, apply the
1039        following logic:
1040
1041        1.  If the value of the "cv" tag on that seal is "fail", the
1042            chain state is "fail" and the algorithm stops here.  (This
1043            step SHOULD be skipped if the earlier step (2.1) was
1044            performed)
1045
1046        2.  In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
1047            == "none" && i != 1)) then the chain state is "fail" and the
1048            algorithm stops here (note that the ordering of the logic is
1049            structured for short-circuit evaluation).
1050
1051        3.  Initialize a hash function corresponding to the "a" tag of
1052            the ARC-Seal.
1053
1054        4.  Compute the canonicalized form of the ARC header fields, in
1055            the order described in Section 5.4.2, using the "relaxed"
1056            header canonicalization defined in Section 3.4.2 of
1057            [RFC6376].  Pass the canonicalized result to the hash
1058            function.
1059
1060        5.  Retrieve the final digest from the hash function.
1061
1062        6.  Retrieve the public key identified by the "s" and "d" tags in
1063            the ARC-Seal, as described in Section 4.1.6.
1064
1065        7.  Determine whether the signature portion ("b" tag) of the ARC-
1066            Seal and the digest computed above are valid according to the
1067            public key.  (See also Section Section 8.4 for failure case
1068            handling)
1069
1070        8.  If the signature is not valid, the chain state is "fail" and
1071            the algorithm stops here.
1072
1073    5.  If all seals pass validation, then the chain state is "pass", and
1074        the algorithm is complete.
1075 */
1076
1077 if ((res = arc_verify_seals(&ctx)))
1078   goto out;
1079
1080 res = US"pass";
1081
1082 out:
1083   return res;
1084 }
1085
1086 /******************************************************************************/
1087
1088 /* Prepend the header to the rlist */
1089
1090 static hdr_rlist *
1091 arc_rlist_entry(hdr_rlist * list, const uschar * s, int len)
1092 {
1093 hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line));
1094 header_line * h = r->h = (header_line *)(r+1);
1095
1096 r->prev = list;
1097 r->used = FALSE;
1098 h->next = NULL;
1099 h->type = 0;
1100 h->slen = len;
1101 h->text = US s;
1102
1103 /* This works for either NL or CRLF lines; also nul-termination */
1104 while (*++s)
1105   if (*s == '\n' && s[1] != '\t' && s[1] != ' ') break;
1106 s++;            /* move past end of line */
1107
1108 return r;
1109 }
1110
1111
1112 /* Walk the given headers strings identifying each header, and construct
1113 a reverse-order list.
1114 */
1115
1116 static hdr_rlist *
1117 arc_sign_scan_headers(arc_ctx * ctx, gstring * sigheaders)
1118 {
1119 const uschar * s;
1120 hdr_rlist * rheaders = NULL;
1121
1122 s = sigheaders ? sigheaders->s : NULL;
1123 if (s) while (*s)
1124   {
1125   const uschar * s2 = s;
1126
1127   /* This works for either NL or CRLF lines; also nul-termination */
1128   while (*++s2)
1129     if (*s2 == '\n' && s2[1] != '\t' && s2[1] != ' ') break;
1130   s2++;         /* move past end of line */
1131
1132   rheaders = arc_rlist_entry(rheaders, s, s2 - s);
1133   s = s2;
1134   }
1135 return rheaders;
1136 }
1137
1138
1139
1140 /* Return the A-R content, without identity, with line-ending and
1141 NUL termination. */
1142
1143 static BOOL
1144 arc_sign_find_ar(header_line * headers, const uschar * identity, blob * ret)
1145 {
1146 header_line * h;
1147 int ilen = Ustrlen(identity);
1148
1149 ret->data = NULL;
1150 for(h = headers; h; h = h->next)
1151   {
1152   uschar * s = h->text, c;
1153   int len = h->slen;
1154
1155   if (Ustrncmp(s, HDR_AR, HDRLEN_AR) != 0) continue;
1156   s += HDRLEN_AR, len -= HDRLEN_AR;             /* header name */
1157   while (  len > 0
1158         && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1159     s++, len--;                                 /* FWS */
1160   if (Ustrncmp(s, identity, ilen) != 0) continue;
1161   s += ilen; len -= ilen;                       /* identity */
1162   if (len <= 0) continue;
1163   if ((c = *s) && c == ';') s++, len--;         /* identity terminator */
1164   while (  len > 0
1165         && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1166     s++, len--;                                 /* FWS */
1167   if (len <= 0) continue;
1168   ret->data = s;
1169   ret->len = len;
1170   return TRUE;
1171   }
1172 return FALSE;
1173 }
1174
1175
1176
1177 /* Append a constructed AAR including CRLF.  Add it to the arc_ctx too.  */
1178
1179 static gstring *
1180 arc_sign_append_aar(gstring * g, arc_ctx * ctx,
1181   const uschar * identity, int instance, blob * ar)
1182 {
1183 int aar_off = g ? g->ptr : 0;
1184 arc_set * as = store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line));
1185 arc_line * al = (arc_line *)(as+1);
1186 header_line * h = (header_line *)(al+1);
1187
1188 g = string_catn(g, ARC_HDR_AAR, ARC_HDRLEN_AAR);
1189 g = string_cat(g, string_sprintf(" i=%d; %s;\r\n\t", instance, identity));
1190 g = string_catn(g, US ar->data, ar->len);
1191
1192 h->slen = g->ptr - aar_off;
1193 h->text = g->s + aar_off;
1194 al->complete = h;
1195 as->next = NULL;
1196 as->prev = ctx->arcset_chain_last;
1197 as->instance = instance;
1198 as->hdr_aar = al;
1199 if (instance == 1)
1200   ctx->arcset_chain = as;
1201 else
1202   ctx->arcset_chain_last->next = as;
1203 ctx->arcset_chain_last = as;
1204
1205 DEBUG(D_transport) debug_printf("ARC: AAR '%.*s'\n", h->slen - 2, h->text);
1206 return g;
1207 }
1208
1209
1210
1211 static BOOL
1212 arc_sig_from_pseudoheader(gstring * hdata, int hashtype, const uschar * privkey,
1213   blob * sig, const uschar * why)
1214 {
1215 hashmethod hm = /*sig->keytype == KEYTYPE_ED25519*/ FALSE
1216   ? HASH_SHA2_512 : pdkim_hashes[hashtype].exim_hashmethod;
1217 blob hhash;
1218 es_ctx sctx;
1219 const uschar * errstr;
1220
1221 DEBUG(D_transport)
1222   {
1223   hctx hhash_ctx;
1224   debug_printf("ARC: %s header data for signing:\n", why);
1225   pdkim_quoteprint(hdata->s, hdata->ptr);
1226
1227   (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
1228   exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1229   exim_sha_finish(&hhash_ctx, &hhash);
1230   debug_printf("ARC: header hash: "); pdkim_hexprint(hhash.data, hhash.len);
1231   }
1232
1233 if (FALSE /*need hash for Ed25519 or GCrypt signing*/ )
1234   {
1235   hctx hhash_ctx;
1236   (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
1237   exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1238   exim_sha_finish(&hhash_ctx, &hhash);
1239   }
1240 else
1241   {
1242   hhash.data = hdata->s;
1243   hhash.len = hdata->ptr;
1244   }
1245
1246 if (  (errstr = exim_dkim_signing_init(privkey, &sctx))
1247    || (errstr = exim_dkim_sign(&sctx, hm, &hhash, sig)))
1248   {
1249   log_write(0, LOG_MAIN, "ARC: %s signing: %s\n", why, errstr);
1250   return FALSE;
1251   }
1252 return TRUE;
1253 }
1254
1255
1256
1257 static gstring *
1258 arc_sign_append_sig(gstring * g, blob * sig)
1259 {
1260 /*debug_printf("%s: raw sig ", __FUNCTION__); pdkim_hexprint(sig->data, sig->len);*/
1261 sig->data = pdkim_encode_base64(sig);
1262 sig->len = Ustrlen(sig->data);
1263 for (;;)
1264   {
1265   int len = MIN(sig->len, 74);
1266   g = string_catn(g, sig->data, len);
1267   if ((sig->len -= len) == 0) break;
1268   sig->data += len;
1269   g = string_catn(g, US"\r\n\t  ", 5);
1270   }
1271 g = string_catn(g, US";\r\n", 3);
1272 gstring_reset_unused(g);
1273 string_from_gstring(g);
1274 return g;
1275 }
1276
1277
1278 /* Append a constructed AMS including CRLF.  Add it to the arc_ctx too. */
1279
1280 static gstring *
1281 arc_sign_append_ams(gstring * g, arc_ctx * ctx, int instance,
1282   const uschar * identity, const uschar * selector, blob * bodyhash,
1283   hdr_rlist * rheaders, const uschar * privkey, unsigned options)
1284 {
1285 uschar * s;
1286 gstring * hdata = NULL;
1287 int col;
1288 int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6);       /*XXX hardwired */
1289 blob sig;
1290 int ams_off;
1291 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line));
1292 header_line * h = (header_line *)(al+1);
1293
1294 /* debug_printf("%s\n", __FUNCTION__); */
1295
1296 /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */
1297
1298 ams_off = g->ptr;
1299 g = string_append(g, 7,
1300       ARC_HDR_AMS,
1301       US" i=", string_sprintf("%d", instance),
1302       US"; a=rsa-sha256; c=relaxed; d=", identity,              /*XXX hardwired */
1303       US"; s=", selector);
1304 if (options & ARC_SIGN_OPT_TSTAMP)
1305   g = string_append(g, 2,
1306       US"; t=", string_sprintf("%lu", (u_long)now));
1307 if (options & ARC_SIGN_OPT_EXPIRE)
1308   g = string_append(g, 2,
1309       US"; x=", string_sprintf("%lu", (u_long)expire));
1310 g = string_append(g, 3,
1311       US";\r\n\tbh=", pdkim_encode_base64(bodyhash),
1312       US";\r\n\th=");
1313
1314 for(col = 3; rheaders; rheaders = rheaders->prev)
1315   {
1316   const uschar * hnames = US"DKIM-Signature:" PDKIM_DEFAULT_SIGN_HEADERS;
1317   uschar * name, * htext = rheaders->h->text;
1318   int sep = ':';
1319
1320   /* Spot headers of interest */
1321
1322   while ((name = string_nextinlist(&hnames, &sep, NULL, 0)))
1323     {
1324     int len = Ustrlen(name);
1325     if (strncasecmp(CCS htext, CCS name, len) == 0)
1326       {
1327       /* If too long, fold line in h= field */
1328
1329       if (col + len > 78) g = string_catn(g, US"\r\n\t  ", 5), col = 3;
1330
1331       /* Add name to h= list */
1332
1333       g = string_catn(g, name, len);
1334       g = string_catn(g, US":", 1);
1335       col += len + 1;
1336
1337       /* Accumulate header for hashing/signing */
1338
1339       hdata = string_cat(hdata,
1340                 pdkim_relax_header_n(htext, rheaders->h->slen, TRUE));  /*XXX hardwired */
1341       break;
1342       }
1343     }
1344   }
1345
1346 /* Lose the last colon from the h= list */
1347
1348 if (g->s[g->ptr - 1] == ':') g->ptr--;
1349
1350 g = string_catn(g, US";\r\n\tb=;", 7);
1351
1352 /* Include the pseudo-header in the accumulation */
1353
1354 s = pdkim_relax_header_n(g->s + ams_off, g->ptr - ams_off, FALSE);
1355 hdata = string_cat(hdata, s);
1356
1357 /* Calculate the signature from the accumulation */
1358 /*XXX does that need further relaxation? there are spaces embedded in the b= strings! */
1359
1360 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AMS"))
1361   return NULL;
1362
1363 /* Lose the trailing semicolon from the psuedo-header, and append the signature
1364 (folded over lines) and termination to complete it. */
1365
1366 g->ptr--;
1367 g = arc_sign_append_sig(g, &sig);
1368
1369 h->slen = g->ptr - ams_off;
1370 h->text = g->s + ams_off;
1371 al->complete = h;
1372 ctx->arcset_chain_last->hdr_ams = al;
1373
1374 DEBUG(D_transport) debug_printf("ARC: AMS '%.*s'\n", h->slen - 2, h->text);
1375 return g;
1376 }
1377
1378
1379
1380 /* Look for an arc= result in an A-R header blob.  We know that its data
1381 happens to be a NUL-term string. */
1382
1383 static uschar *
1384 arc_ar_cv_status(blob * ar)
1385 {
1386 const uschar * resinfo = ar->data;
1387 int sep = ';';
1388 uschar * methodspec, * s;
1389
1390 while ((methodspec = string_nextinlist(&resinfo, &sep, NULL, 0)))
1391   if (Ustrncmp(methodspec, US"arc=", 4) == 0)
1392     {
1393     uschar c;
1394     for (s = methodspec += 4;
1395          (c = *s) && c != ';' && c != ' ' && c != '\r' && c != '\n'; ) s++;
1396     return string_copyn(methodspec, s - methodspec);
1397     }
1398 return US"none";
1399 }
1400
1401
1402
1403 /* Build the AS header and prepend it */
1404
1405 static gstring *
1406 arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx,
1407   int instance, const uschar * identity, const uschar * selector, blob * ar,
1408   const uschar * privkey, unsigned options)
1409 {
1410 gstring * arcset;
1411 arc_set * as;
1412 uschar * status = arc_ar_cv_status(ar);
1413 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line));
1414 header_line * h = (header_line *)(al+1);
1415
1416 gstring * hdata = NULL;
1417 int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6);       /*XXX hardwired */
1418 blob sig;
1419
1420 /*
1421 - Generate AS
1422   - no body coverage
1423   - no h= tag; implicit coverage
1424   - arc status from A-R
1425     - if fail:
1426       - coverage is just the new ARC set
1427         including self (but with an empty b= in self)
1428     - if non-fail:
1429       - all ARC set headers, set-number order, aar then ams then as,
1430         including self (but with an empty b= in self)
1431 */
1432
1433 /* Construct the AS except for the signature */
1434
1435 arcset = string_append(NULL, 9,
1436           ARC_HDR_AS,
1437           US" i=", string_sprintf("%d", instance),
1438           US"; cv=", status,
1439           US"; a=rsa-sha256; d=", identity,                     /*XXX hardwired */
1440           US"; s=", selector);                                  /*XXX same as AMS */
1441 if (options & ARC_SIGN_OPT_TSTAMP)
1442   arcset = string_append(arcset, 2,
1443       US"; t=", string_sprintf("%lu", (u_long)now));
1444 arcset = string_cat(arcset,
1445           US";\r\n\t b=;");
1446
1447 h->slen = arcset->ptr;
1448 h->text = arcset->s;
1449 al->complete = h;
1450 ctx->arcset_chain_last->hdr_as = al;
1451
1452 /* For any but "fail" chain-verify status, walk the entire chain in order by
1453 instance.  For fail, only the new arc-set.  Accumulate the elements walked. */
1454
1455 for (as = Ustrcmp(status, US"fail") == 0
1456         ? ctx->arcset_chain_last : ctx->arcset_chain;
1457      as; as = as->next)
1458   {
1459   /* Accumulate AAR then AMS then AS.  Relaxed canonicalisation
1460   is required per standard. */
1461
1462   h = as->hdr_aar->complete;
1463   hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
1464   h = as->hdr_ams->complete;
1465   hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
1466   h = as->hdr_as->complete;
1467   hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, !!as->next));
1468   }
1469
1470 /* Calculate the signature from the accumulation */
1471
1472 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AS"))
1473   return NULL;
1474
1475 /* Lose the trailing semicolon */
1476 arcset->ptr--;
1477 arcset = arc_sign_append_sig(arcset, &sig);
1478 DEBUG(D_transport) debug_printf("ARC: AS  '%.*s'\n", arcset->ptr - 2, arcset->s);
1479
1480 /* Finally, append the AMS and AAR to the new AS */
1481
1482 return string_catn(arcset, arcset_interim->s, arcset_interim->ptr);
1483 }
1484
1485
1486 /**************************************/
1487
1488 /* Return pointer to pdkim_bodyhash for given hash method, creating new
1489 method if needed.
1490 */
1491
1492 void *
1493 arc_ams_setup_sign_bodyhash(void)
1494 {
1495 int canon_head, canon_body;
1496
1497 DEBUG(D_transport) debug_printf("ARC: requesting bodyhash\n");
1498 pdkim_cstring_to_canons(US"relaxed", 7, &canon_head, &canon_body);      /*XXX hardwired */
1499 return pdkim_set_bodyhash(&dkim_sign_ctx,
1500         pdkim_hashname_to_hashtype(US"sha256", 6),                      /*XXX hardwired */
1501         canon_body,
1502         -1);
1503 }
1504
1505
1506
1507 void
1508 arc_sign_init(void)
1509 {
1510 memset(&arc_sign_ctx, 0, sizeof(arc_sign_ctx));
1511 }
1512
1513
1514
1515 /* A "normal" header line, identified by DKIM processing.  These arrive before
1516 the call to arc_sign(), which carries any newly-created DKIM headers - and
1517 those go textually before the normal ones in the message.
1518
1519 We have to take the feed from DKIM as, in the transport-filter case, the
1520 headers are not in memory at the time of the call to arc_sign().
1521
1522 Take a copy of the header and construct a reverse-order list.
1523 Also parse ARC-chain headers and build the chain struct, retaining pointers
1524 into the copies.
1525 */
1526
1527 static const uschar *
1528 arc_header_sign_feed(gstring * g)
1529 {
1530 uschar * s = string_copyn(g->s, g->ptr);
1531 headers_rlist = arc_rlist_entry(headers_rlist, s, g->ptr);
1532 return arc_try_header(&arc_sign_ctx, headers_rlist->h, TRUE);
1533 }
1534
1535
1536
1537 /* ARC signing.  Called from the smtp transport, if the arc_sign option is set.
1538 The dkim_exim_sign() function has already been called, so will have hashed the
1539 message body for us so long as we requested a hash previously.
1540
1541 Arguments:
1542   signspec      Three-element colon-sep list: identity, selector, privkey.
1543                 Optional fourth element: comma-sep list of options.
1544                 Already expanded
1545   sigheaders    Any signature headers already generated, eg. by DKIM, or NULL
1546   errstr        Error string
1547
1548 Return value
1549   Set of headers to prepend to the message, including the supplied sigheaders
1550   but not the plainheaders.
1551 */
1552
1553 gstring *
1554 arc_sign(const uschar * signspec, gstring * sigheaders, uschar ** errstr)
1555 {
1556 const uschar * identity, * selector, * privkey, * opts, * s;
1557 unsigned options = 0;
1558 int sep = 0;
1559 header_line * headers;
1560 hdr_rlist * rheaders;
1561 blob ar;
1562 int instance;
1563 gstring * g = NULL;
1564 pdkim_bodyhash * b;
1565
1566 expire = now = 0;
1567
1568 /* Parse the signing specification */
1569
1570 identity = string_nextinlist(&signspec, &sep, NULL, 0);
1571 selector = string_nextinlist(&signspec, &sep, NULL, 0);
1572 if (  !*identity || !*selector
1573    || !(privkey = string_nextinlist(&signspec, &sep, NULL, 0)) || !*privkey)
1574   {
1575   log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)",
1576     !*identity ? "identity" : !*selector ? "selector" : "private-key");
1577   return sigheaders ? sigheaders : string_get(0);
1578   }
1579 if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey)))
1580   return sigheaders ? sigheaders : string_get(0);
1581
1582 if ((opts = string_nextinlist(&signspec, &sep, NULL, 0)))
1583   {
1584   int osep = ',';
1585   while ((s = string_nextinlist(&opts, &osep, NULL, 0)))
1586     if (Ustrcmp(s, "timestamps") == 0)
1587       {
1588       options |= ARC_SIGN_OPT_TSTAMP;
1589       if (!now) now = time(NULL);
1590       }
1591     else if (Ustrncmp(s, "expire", 6) == 0)
1592       {
1593       options |= ARC_SIGN_OPT_EXPIRE;
1594       if (*(s += 6) == '=')
1595         if (*++s == '+')
1596           {
1597           if (!(expire = (time_t)atoi(++s)))
1598             expire = ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1599           if (!now) now = time(NULL);
1600           expire += now;
1601           }
1602         else
1603           expire = (time_t)atol(s);
1604       else
1605         {
1606         if (!now) now = time(NULL);
1607         expire = now + ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1608         }
1609       }
1610   }
1611
1612 DEBUG(D_transport) debug_printf("ARC: sign for %s\n", identity);
1613
1614 /* Make an rlist of any new DKIM headers, then add the "normals" rlist to it.
1615 Then scan the list for an A-R header. */
1616
1617 string_from_gstring(sigheaders);
1618 if ((rheaders = arc_sign_scan_headers(&arc_sign_ctx, sigheaders)))
1619   {
1620   hdr_rlist ** rp;
1621   for (rp = &headers_rlist; *rp; ) rp = &(*rp)->prev;
1622   *rp = rheaders;
1623   }
1624
1625 /* Finally, build a normal-order headers list */
1626 /*XXX only needed for hunt-the-AR? */
1627 /*XXX also, we really should be accepting any number of ADMD-matching ARs */
1628   {
1629   header_line * hnext = NULL;
1630   for (rheaders = headers_rlist; rheaders;
1631        hnext = rheaders->h, rheaders = rheaders->prev)
1632     rheaders->h->next = hnext;
1633   headers = hnext;
1634   }
1635
1636 if (!(arc_sign_find_ar(headers, identity, &ar)))
1637   {
1638   log_write(0, LOG_MAIN, "ARC: no Authentication-Results header for signing");
1639   return sigheaders ? sigheaders : string_get(0);
1640   }
1641
1642 /* We previously built the data-struct for the existing ARC chain, if any, using a headers
1643 feed from the DKIM module.  Use that to give the instance number for the ARC set we are
1644 about to build. */
1645
1646 DEBUG(D_transport)
1647   if (arc_sign_ctx.arcset_chain_last)
1648     debug_printf("ARC: existing chain highest instance: %d\n",
1649       arc_sign_ctx.arcset_chain_last->instance);
1650   else
1651     debug_printf("ARC: no existing chain\n");
1652
1653 instance = arc_sign_ctx.arcset_chain_last ? arc_sign_ctx.arcset_chain_last->instance + 1 : 1;
1654
1655 /*
1656 - Generate AAR
1657   - copy the A-R; prepend i= & identity
1658 */
1659
1660 g = arc_sign_append_aar(g, &arc_sign_ctx, identity, instance, &ar);
1661
1662 /*
1663 - Generate AMS
1664   - Looks fairly like a DKIM sig
1665   - Cover all DKIM sig headers as well as the usuals
1666     - ? oversigning?
1667   - Covers the data
1668   - we must have requested a suitable bodyhash previously
1669 */
1670
1671 b = arc_ams_setup_sign_bodyhash();
1672 g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector,
1673       &b->bh, headers_rlist, privkey, options);
1674
1675 /*
1676 - Generate AS
1677   - no body coverage
1678   - no h= tag; implicit coverage
1679   - arc status from A-R
1680     - if fail:
1681       - coverage is just the new ARC set
1682         including self (but with an empty b= in self)
1683     - if non-fail:
1684       - all ARC set headers, set-number order, aar then ams then as,
1685         including self (but with an empty b= in self)
1686 */
1687
1688 g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar,
1689       privkey, options);
1690
1691 /* Finally, append the dkim headers and return the lot. */
1692
1693 g = string_catn(g, sigheaders->s, sigheaders->ptr);
1694 (void) string_from_gstring(g);
1695 gstring_reset_unused(g);
1696 return g;
1697 }
1698
1699
1700 /******************************************************************************/
1701
1702 /* Check to see if the line is an AMS and if so, set up to validate it.
1703 Called from the DKIM input processing.  This must be done now as the message
1704 body data is hashed during input.
1705
1706 We call the DKIM code to request a body-hash; it has the facility already
1707 and the hash parameters might be common with other requests.
1708 */
1709
1710 static const uschar *
1711 arc_header_vfy_feed(gstring * g)
1712 {
1713 header_line h;
1714 arc_line al;
1715 pdkim_bodyhash * b;
1716 uschar * errstr;
1717
1718 if (!dkim_verify_ctx) return US"no dkim context";
1719
1720 if (strncmpic(ARC_HDR_AMS, g->s, ARC_HDRLEN_AMS) != 0) return US"not AMS";
1721
1722 DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n");
1723 /* Parse the AMS header */
1724
1725 h.next = NULL;
1726 h.slen = g->size;
1727 h.text = g->s;
1728 memset(&al, 0, sizeof(arc_line));
1729 if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, FALSE)))
1730   {
1731   DEBUG(D_acl) if (errstr) debug_printf("ARC: %s\n", errstr);
1732   return US"line parsing error";
1733   }
1734
1735 /* defaults */
1736 if (!al.c.data)
1737   {
1738   al.c_body.data = US"simple"; al.c_body.len = 6;
1739   al.c_head = al.c_body;
1740   }
1741
1742 /* Ask the dkim code to calc a bodyhash with those specs */
1743
1744 if (!(b = arc_ams_setup_vfy_bodyhash(&al)))
1745   return US"dkim hash setup fail";
1746
1747 /* Discard the reference; search again at verify time, knowing that one
1748 should have been created here. */
1749
1750 return NULL;
1751 }
1752
1753
1754
1755 /* A header line has been identified by DKIM processing.
1756
1757 Arguments:
1758   g             Header line
1759   is_vfy        TRUE for verify mode or FALSE for signing mode
1760
1761 Return:
1762   NULL for success, or an error string (probably unused)
1763 */
1764
1765 const uschar *
1766 arc_header_feed(gstring * g, BOOL is_vfy)
1767 {
1768 return is_vfy ? arc_header_vfy_feed(g) : arc_header_sign_feed(g);
1769 }
1770
1771
1772
1773 /******************************************************************************/
1774
1775 /* Construct an Authenticate-Results header portion, for the ARC module */
1776
1777 gstring *
1778 authres_arc(gstring * g)
1779 {
1780 if (arc_state)
1781   {
1782   arc_line * highest_ams;
1783   int start = 0;                /* Compiler quietening */
1784   DEBUG(D_acl) start = g->ptr;
1785
1786   g = string_append(g, 2, US";\n\tarc=", arc_state);
1787   if (arc_received_instance > 0)
1788     {
1789     g = string_append(g, 3, US" (i=",
1790       string_sprintf("%d", arc_received_instance), US")");
1791     if (arc_state_reason)
1792       g = string_append(g, 3, US"(", arc_state_reason, US")");
1793     g = string_catn(g, US" header.s=", 10);
1794     highest_ams = arc_received->hdr_ams;
1795     g = string_catn(g, highest_ams->s.data, highest_ams->s.len);
1796
1797     g = string_append(g, 2,
1798       US" arc.oldest-pass=", string_sprintf("%d", arc_oldest_pass));
1799
1800     if (sender_host_address)
1801       g = string_append(g, 2, US" smtp.client-ip=", sender_host_address);
1802     }
1803   else if (arc_state_reason)
1804     g = string_append(g, 3, US" (", arc_state_reason, US")");
1805   DEBUG(D_acl) debug_printf("ARC:  authres '%.*s'\n",
1806                   g->ptr - start - 3, g->s + start + 3);
1807   }
1808 else
1809   DEBUG(D_acl) debug_printf("ARC:  no authres\n");
1810 return g;
1811 }
1812
1813
1814 # endif /* SUPPORT_SPF */
1815 #endif /* EXPERIMENTAL_ARC */
1816 /* vi: aw ai sw=2
1817  */