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