Debug: fix coding for signedness
[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 "miscmods/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       DEBUG(D_acl) debug_printf("%Z\n", s);
634       exim_sha_update_string(&hhash_ctx, s);
635       r->used = TRUE;
636       break;
637       }
638
639 /* Finally add in the signature header (with the b= tag stripped); no CRLF */
640
641 s = ams->rawsig_no_b_val.data, len = ams->rawsig_no_b_val.len;
642 if (relaxed)
643   len = Ustrlen(s = pdkim_relax_header_n(s, len, FALSE));
644 DEBUG(D_acl) debug_printf("%.*Z\n", len, s);
645 exim_sha_update(&hhash_ctx, s, len);
646
647 exim_sha_finish(&hhash_ctx, hhash);
648 DEBUG(D_acl)
649   { debug_printf("ARC: header hash: %.*H\n", hhash->len, hhash->data); }
650 return;
651 }
652
653
654
655
656 static pdkim_pubkey *
657 arc_line_to_pubkey(arc_line * al)
658 {
659 uschar * dns_txt;
660 pdkim_pubkey * p;
661
662 if (!(dns_txt = dkim_exim_query_dns_txt(string_sprintf("%.*s._domainkey.%.*s",
663           (int)al->s.len, al->s.data, (int)al->d.len, al->d.data))))
664   {
665   DEBUG(D_acl) debug_printf("pubkey dns lookup fail\n");
666   return NULL;
667   }
668
669 if (  !(p = pdkim_parse_pubkey_record(dns_txt))
670    || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
671    )
672   {
673   DEBUG(D_acl) debug_printf("pubkey dns lookup format error\n");
674   return NULL;
675   }
676
677 /* If the pubkey limits use to specified hashes, reject unusable
678 signatures. XXX should we have looked for multiple dns records? */
679
680 if (p->hashes)
681   {
682   const uschar * list = p->hashes, * ele;
683   int sep = ':';
684
685   while ((ele = string_nextinlist(&list, &sep, NULL, 0)))
686     if (Ustrncmp(ele, al->a_hash.data, al->a_hash.len) == 0) break;
687   if (!ele)
688     {
689     DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%.*s\n",
690                               p->hashes, (int)al->a.len, al->a.data);
691     return NULL;
692     }
693   }
694 return p;
695 }
696
697
698
699
700 static pdkim_bodyhash *
701 arc_ams_setup_vfy_bodyhash(arc_line * ams)
702 {
703 int canon_head = -1, canon_body = -1;
704 long bodylen;
705
706 if (!ams->c.data) ams->c.data = US"simple";     /* RFC 6376 (DKIM) default */
707 pdkim_cstring_to_canons(ams->c.data, ams->c.len, &canon_head, &canon_body);
708 bodylen = ams->l.data
709         ? strtol(CS string_copyn(ams->l.data, ams->l.len), NULL, 10) : -1;
710
711 return pdkim_set_bodyhash(dkim_verify_ctx,
712         pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len),
713         canon_body,
714         bodylen);
715 }
716
717
718
719 /* Verify an AMS. This is a DKIM-sig header, but with an ARC i= tag
720 and without a DKIM v= tag.
721 */
722
723 static const uschar *
724 arc_ams_verify(arc_ctx * ctx, arc_set * as)
725 {
726 arc_line * ams = as->hdr_ams;
727 pdkim_bodyhash * b;
728 pdkim_pubkey * p;
729 blob sighash;
730 blob hhash;
731 ev_ctx vctx;
732 int hashtype;
733 const uschar * errstr;
734
735 as->ams_verify_done = US"in-progress";
736
737 /* Check the AMS has all the required tags:
738    "a="  algorithm
739    "b="  signature
740    "bh=" body hash
741    "d="  domain (for key lookup)
742    "h="  headers (included in signature)
743    "s="  key-selector (for key lookup)
744 */
745 if (  !ams->a.data || !ams->b.data || !ams->bh.data || !ams->d.data
746    || !ams->h.data || !ams->s.data)
747   {
748   as->ams_verify_done = arc_state_reason = US"required tag missing";
749   return US"fail";
750   }
751
752
753 /* The bodyhash should have been created earlier, and the dkim code should
754 have managed calculating it during message input.  Find the reference to it. */
755
756 if (!(b = arc_ams_setup_vfy_bodyhash(ams)))
757   {
758   as->ams_verify_done = arc_state_reason = US"internal hash setup error";
759   return US"fail";
760   }
761
762 DEBUG(D_acl)
763   {
764   debug_printf("ARC i=%d AMS   Body bytes hashed: %lu\n"
765                "              Body %.*s computed: ",
766                as->instance, b->signed_body_bytes,
767                (int)ams->a_hash.len, ams->a_hash.data);
768   debug_printf("%.*H\n", b->bh.len, b->bh.data);
769   }
770
771 /* We know the bh-tag blob is of a nul-term string, so safe as a string */
772
773 if (  !ams->bh.data
774    || (pdkim_decode_base64(ams->bh.data, &sighash), sighash.len != b->bh.len)
775    || memcmp(sighash.data, b->bh.data, b->bh.len) != 0
776    )
777   {
778   DEBUG(D_acl)
779     {
780     debug_printf("ARC i=%d AMS Body hash from headers: ", as->instance);
781     debug_printf("%.*H\n", sighash.len, sighash.data);
782     debug_printf("ARC i=%d AMS Body hash did NOT match\n", as->instance);
783     }
784   return as->ams_verify_done = arc_state_reason = US"AMS body hash miscompare";
785   }
786
787 DEBUG(D_acl) debug_printf("ARC i=%d AMS Body hash compared OK\n", as->instance);
788
789 /* Get the public key from DNS */
790
791 if (!(p = arc_line_to_pubkey(ams)))
792   return as->ams_verify_done = arc_state_reason = US"pubkey problem";
793
794 /* We know the b-tag blob is of a nul-term string, so safe as a string */
795 pdkim_decode_base64(ams->b.data, &sighash);
796
797 arc_get_verify_hhash(ctx, ams, &hhash);
798
799 /* Setup the interface to the signing library */
800
801 if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
802   {
803   DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
804   as->ams_verify_done = arc_state_reason = US"internal sigverify init error";
805   return US"fail";
806   }
807
808 hashtype = pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len);
809 if (hashtype == -1)
810   {
811   DEBUG(D_acl) debug_printf("ARC i=%d AMS verify bad a_hash\n", as->instance);
812   return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
813   }
814
815 if ((errstr = exim_dkim_verify(&vctx,
816           pdkim_hashes[hashtype].exim_hashmethod, &hhash, &sighash)))
817   {
818   DEBUG(D_acl) debug_printf("ARC i=%d AMS verify %s\n", as->instance, errstr);
819   return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
820   }
821
822 DEBUG(D_acl) debug_printf("ARC i=%d AMS verify pass\n", as->instance);
823 as->ams_verify_passed = TRUE;
824 return NULL;
825 }
826
827
828
829 /* Check the sets are instance-continuous and that all
830 members are present.  Check that no arc_seals are "fail".
831 Set the highest instance number global.
832 Verify the latest AMS.
833 */
834 static uschar *
835 arc_headers_check(arc_ctx * ctx)
836 {
837 arc_set * as;
838 int inst;
839 BOOL ams_fail_found = FALSE;
840
841 if (!(as = ctx->arcset_chain_last))
842   return US"none";
843
844 for(inst = as->instance; as; as = as->prev, inst--)
845   {
846   if (as->instance != inst)
847     arc_state_reason = string_sprintf("i=%d (sequence; expected %d)",
848       as->instance, inst);
849   else if (!as->hdr_aar || !as->hdr_ams || !as->hdr_as)
850     arc_state_reason = string_sprintf("i=%d (missing header)", as->instance);
851   else if (arc_cv_match(as->hdr_as, US"fail"))
852     arc_state_reason = string_sprintf("i=%d (cv)", as->instance);
853   else
854     goto good;
855
856   DEBUG(D_acl) debug_printf("ARC chain fail at %s\n", arc_state_reason);
857   return US"fail";
858
859   good:
860   /* Evaluate the oldest-pass AMS validation while we're here.
861   It does not affect the AS chain validation but is reported as
862   auxilary info. */
863
864   if (!ams_fail_found)
865     if (arc_ams_verify(ctx, as))
866       ams_fail_found = TRUE;
867     else
868       arc_oldest_pass = inst;
869   arc_state_reason = NULL;
870   }
871 if (inst != 0)
872   {
873   arc_state_reason = string_sprintf("(sequence; expected i=%d)", inst);
874   DEBUG(D_acl) debug_printf("ARC chain fail %s\n", arc_state_reason);
875   return US"fail";
876   }
877
878 arc_received = ctx->arcset_chain_last;
879 arc_received_instance = arc_received->instance;
880
881 /* We can skip the latest-AMS validation, if we already did it. */
882
883 as = ctx->arcset_chain_last;
884 if (!as->ams_verify_passed)
885   {
886   if (as->ams_verify_done)
887     {
888     arc_state_reason = as->ams_verify_done;
889     return US"fail";
890     }
891   if (!!arc_ams_verify(ctx, as))
892     return US"fail";
893   }
894 return NULL;
895 }
896
897
898 /******************************************************************************/
899 static const uschar *
900 arc_seal_verify(arc_ctx * ctx, arc_set * as)
901 {
902 arc_line * hdr_as = as->hdr_as;
903 arc_set * as2;
904 int hashtype;
905 hctx hhash_ctx;
906 blob hhash_computed;
907 blob sighash;
908 ev_ctx vctx;
909 pdkim_pubkey * p;
910 const uschar * errstr;
911
912 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d\n", as->instance);
913 /*
914        1.  If the value of the "cv" tag on that seal is "fail", the
915            chain state is "fail" and the algorithm stops here.  (This
916            step SHOULD be skipped if the earlier step (2.1) was
917            performed) [it was]
918
919        2.  In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
920            == "none" && i != 1)) then the chain state is "fail" and the
921            algorithm stops here (note that the ordering of the logic is
922            structured for short-circuit evaluation).
923 */
924
925 if (  as->instance == 1 && !arc_cv_match(hdr_as, US"none")
926    || arc_cv_match(hdr_as, US"none") && as->instance != 1
927    )
928   {
929   arc_state_reason = US"seal cv state";
930   return US"fail";
931   }
932
933 /*
934        3.  Initialize a hash function corresponding to the "a" tag of
935            the ARC-Seal.
936 */
937
938 hashtype = pdkim_hashname_to_hashtype(hdr_as->a_hash.data, hdr_as->a_hash.len);
939
940 if (  hashtype == -1
941    || !exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
942   {
943   DEBUG(D_acl)
944       debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
945   arc_state_reason = US"seal hash setup error";
946   return US"fail";
947   }
948
949 /*
950        4.  Compute the canonicalized form of the ARC header fields, in
951            the order described in Section 5.4.2, using the "relaxed"
952            header canonicalization defined in Section 3.4.2 of
953            [RFC6376].  Pass the canonicalized result to the hash
954            function.
955
956 Headers are CRLF-separated, but the last one is not crlf-terminated.
957 */
958
959 DEBUG(D_acl) debug_printf("ARC: AS header data for verification:\n");
960 for (as2 = ctx->arcset_chain;
961      as2 && as2->instance <= as->instance;
962      as2 = as2->next)
963   {
964   arc_line * al;
965   uschar * s;
966   int len;
967
968   al = as2->hdr_aar;
969   if (!(s = al->relaxed))
970     al->relaxed = s = pdkim_relax_header_n(al->complete->text,
971                                             al->complete->slen, TRUE);
972   len = Ustrlen(s);
973   DEBUG(D_acl) debug_printf("%Z\n", s);
974   exim_sha_update(&hhash_ctx, s, len);
975
976   al = as2->hdr_ams;
977   if (!(s = al->relaxed))
978     al->relaxed = s = pdkim_relax_header_n(al->complete->text,
979                                             al->complete->slen, TRUE);
980   len = Ustrlen(s);
981   DEBUG(D_acl) debug_printf("%Z\n", s);
982   exim_sha_update(&hhash_ctx, s, len);
983
984   al = as2->hdr_as;
985   if (as2->instance == as->instance)
986     s = pdkim_relax_header_n(al->rawsig_no_b_val.data,
987                                         al->rawsig_no_b_val.len, FALSE);
988   else if (!(s = al->relaxed))
989     al->relaxed = s = pdkim_relax_header_n(al->complete->text,
990                                             al->complete->slen, TRUE);
991   len = Ustrlen(s);
992   DEBUG(D_acl) debug_printf("%Z\n", s);
993   exim_sha_update(&hhash_ctx, s, len);
994   }
995
996 /*
997        5.  Retrieve the final digest from the hash function.
998 */
999
1000 exim_sha_finish(&hhash_ctx, &hhash_computed);
1001 DEBUG(D_acl)
1002   {
1003   debug_printf("ARC i=%d AS Header %.*s computed: ",
1004     as->instance, (int)hdr_as->a_hash.len, hdr_as->a_hash.data);
1005   debug_printf("%.*H\n", hhash_computed.len, hhash_computed.data);
1006   }
1007
1008
1009 /*
1010        6.  Retrieve the public key identified by the "s" and "d" tags in
1011            the ARC-Seal, as described in Section 4.1.6.
1012 */
1013
1014 if (!(p = arc_line_to_pubkey(hdr_as)))
1015   return US"pubkey problem";
1016
1017 /*
1018        7.  Determine whether the signature portion ("b" tag) of the ARC-
1019            Seal and the digest computed above are valid according to the
1020            public key.  (See also Section Section 8.4 for failure case
1021            handling)
1022
1023        8.  If the signature is not valid, the chain state is "fail" and
1024            the algorithm stops here.
1025 */
1026
1027 /* We know the b-tag blob is of a nul-term string, so safe as a string */
1028 pdkim_decode_base64(hdr_as->b.data, &sighash);
1029
1030 if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
1031   {
1032   DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
1033   return US"fail";
1034   }
1035
1036 if ((errstr = exim_dkim_verify(&vctx,
1037               pdkim_hashes[hashtype].exim_hashmethod,
1038               &hhash_computed, &sighash)))
1039   {
1040   DEBUG(D_acl)
1041     debug_printf("ARC i=%d AS headers verify: %s\n", as->instance, errstr);
1042   arc_state_reason = US"seal sigverify error";
1043   return US"fail";
1044   }
1045
1046 DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d pass\n", as->instance);
1047 return NULL;
1048 }
1049
1050
1051 static const uschar *
1052 arc_verify_seals(arc_ctx * ctx)
1053 {
1054 arc_set * as = ctx->arcset_chain_last;
1055
1056 if (!as)
1057   return US"none";
1058
1059 for ( ; as; as = as->prev) if (arc_seal_verify(ctx, as)) return US"fail";
1060
1061 DEBUG(D_acl) debug_printf("ARC: AS vfy overall pass\n");
1062 return NULL;
1063 }
1064 /******************************************************************************/
1065
1066 /* Do ARC verification.  Called from DATA ACL, on a verify = arc
1067 condition.  No arguments; we are checking globals.
1068
1069 Return:  The ARC state, or NULL on error.
1070 */
1071
1072 const uschar *
1073 acl_verify_arc(void)
1074 {
1075 const uschar * res;
1076
1077 memset(&arc_verify_ctx, 0, sizeof(arc_verify_ctx));
1078
1079 if (!dkim_verify_ctx)
1080   {
1081   DEBUG(D_acl) debug_printf("ARC: no DKIM verify context\n");
1082   return NULL;
1083   }
1084
1085 /* AS evaluation, per
1086 https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-10#section-6
1087 */
1088 /* 1.  Collect all ARC sets currently on the message.  If there were
1089        none, the ARC state is "none" and the algorithm stops here.
1090 */
1091
1092 if ((res = arc_vfy_collect_hdrs(&arc_verify_ctx)))
1093   goto out;
1094
1095 /* 2.  If the form of any ARC set is invalid (e.g., does not contain
1096        exactly one of each of the three ARC-specific header fields),
1097        then the chain state is "fail" and the algorithm stops here.
1098
1099        1.  To avoid the overhead of unnecessary computation and delay
1100            from crypto and DNS operations, the cv value for all ARC-
1101            Seal(s) MAY be checked at this point.  If any of the values
1102            are "fail", then the overall state of the chain is "fail" and
1103            the algorithm stops here.
1104
1105    3.  Conduct verification of the ARC-Message-Signature header field
1106        bearing the highest instance number.  If this verification fails,
1107        then the chain state is "fail" and the algorithm stops here.
1108 */
1109
1110 if ((res = arc_headers_check(&arc_verify_ctx)))
1111   goto out;
1112
1113 /* 4.  For each ARC-Seal from the "N"th instance to the first, apply the
1114        following logic:
1115
1116        1.  If the value of the "cv" tag on that seal is "fail", the
1117            chain state is "fail" and the algorithm stops here.  (This
1118            step SHOULD be skipped if the earlier step (2.1) was
1119            performed)
1120
1121        2.  In Boolean nomenclature: if ((i == 1 && cv != "none") or (cv
1122            == "none" && i != 1)) then the chain state is "fail" and the
1123            algorithm stops here (note that the ordering of the logic is
1124            structured for short-circuit evaluation).
1125
1126        3.  Initialize a hash function corresponding to the "a" tag of
1127            the ARC-Seal.
1128
1129        4.  Compute the canonicalized form of the ARC header fields, in
1130            the order described in Section 5.4.2, using the "relaxed"
1131            header canonicalization defined in Section 3.4.2 of
1132            [RFC6376].  Pass the canonicalized result to the hash
1133            function.
1134
1135        5.  Retrieve the final digest from the hash function.
1136
1137        6.  Retrieve the public key identified by the "s" and "d" tags in
1138            the ARC-Seal, as described in Section 4.1.6.
1139
1140        7.  Determine whether the signature portion ("b" tag) of the ARC-
1141            Seal and the digest computed above are valid according to the
1142            public key.  (See also Section Section 8.4 for failure case
1143            handling)
1144
1145        8.  If the signature is not valid, the chain state is "fail" and
1146            the algorithm stops here.
1147
1148    5.  If all seals pass validation, then the chain state is "pass", and
1149        the algorithm is complete.
1150 */
1151
1152 if ((res = arc_verify_seals(&arc_verify_ctx)))
1153   goto out;
1154
1155 res = US"pass";
1156
1157 out:
1158   return res;
1159 }
1160
1161 /******************************************************************************/
1162
1163 /* Prepend the header to the rlist */
1164
1165 static hdr_rlist *
1166 arc_rlist_entry(hdr_rlist * list, const uschar * s, int len)
1167 {
1168 hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), GET_UNTAINTED);
1169 header_line * h = r->h = (header_line *)(r+1);
1170
1171 r->prev = list;
1172 r->used = FALSE;
1173 h->next = NULL;
1174 h->type = 0;
1175 h->slen = len;
1176 h->text = US s;
1177
1178 return r;
1179 }
1180
1181
1182 /* Walk the given headers strings identifying each header, and construct
1183 a reverse-order list.
1184 */
1185
1186 static hdr_rlist *
1187 arc_sign_scan_headers(arc_ctx * ctx, gstring * sigheaders)
1188 {
1189 const uschar * s;
1190 hdr_rlist * rheaders = NULL;
1191
1192 s = sigheaders ? sigheaders->s : NULL;
1193 if (s) while (*s)
1194   {
1195   const uschar * s2 = s;
1196
1197   /* This works for either NL or CRLF lines; also nul-termination */
1198   while (*++s2)
1199     if (*s2 == '\n' && s2[1] != '\t' && s2[1] != ' ') break;
1200   s2++;         /* move past end of line */
1201
1202   rheaders = arc_rlist_entry(rheaders, s, s2 - s);
1203   s = s2;
1204   }
1205 return rheaders;
1206 }
1207
1208
1209
1210 /* Return the A-R content, without identity, with line-ending and
1211 NUL termination. */
1212
1213 static BOOL
1214 arc_sign_find_ar(header_line * headers, const uschar * identity, blob * ret)
1215 {
1216 header_line * h;
1217 int ilen = Ustrlen(identity);
1218
1219 ret->data = NULL;
1220 for(h = headers; h; h = h->next)
1221   {
1222   uschar * s = h->text, c;
1223   int len = h->slen;
1224
1225   if (Ustrncmp(s, HDR_AR, HDRLEN_AR) != 0) continue;
1226   s += HDRLEN_AR, len -= HDRLEN_AR;             /* header name */
1227   while (  len > 0
1228         && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1229     s++, len--;                                 /* FWS */
1230   if (Ustrncmp(s, identity, ilen) != 0) continue;
1231   s += ilen; len -= ilen;                       /* identity */
1232   if (len <= 0) continue;
1233   if ((c = *s) && c == ';') s++, len--;         /* identity terminator */
1234   while (  len > 0
1235         && (c = *s) && (c == ' ' || c == '\t' || c == '\r' || c == '\n'))
1236     s++, len--;                                 /* FWS */
1237   if (len <= 0) continue;
1238   ret->data = s;
1239   ret->len = len;
1240   return TRUE;
1241   }
1242 return FALSE;
1243 }
1244
1245
1246
1247 /* Append a constructed AAR including CRLF.  Add it to the arc_ctx too.  */
1248
1249 static gstring *
1250 arc_sign_append_aar(gstring * g, arc_ctx * ctx,
1251   const uschar * identity, int instance, blob * ar)
1252 {
1253 int aar_off = gstring_length(g);
1254 arc_set * as =
1255   store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), GET_UNTAINTED);
1256 arc_line * al = (arc_line *)(as+1);
1257 header_line * h = (header_line *)(al+1);
1258
1259 g = string_catn(g, ARC_HDR_AAR, ARC_HDRLEN_AAR);
1260 g = string_fmt_append(g, " i=%d; %s; smtp.remote-ip=%s;\r\n\t",
1261                          instance, identity, sender_host_address);
1262 g = string_catn(g, US ar->data, ar->len);
1263
1264 h->slen = g->ptr - aar_off;
1265 h->text = g->s + aar_off;
1266 al->complete = h;
1267 as->next = NULL;
1268 as->prev = ctx->arcset_chain_last;
1269 as->instance = instance;
1270 as->hdr_aar = al;
1271 if (instance == 1)
1272   ctx->arcset_chain = as;
1273 else
1274   ctx->arcset_chain_last->next = as;
1275 ctx->arcset_chain_last = as;
1276
1277 DEBUG(D_transport) debug_printf("ARC: AAR '%.*s'\n", h->slen - 2, h->text);
1278 return g;
1279 }
1280
1281
1282
1283 static BOOL
1284 arc_sig_from_pseudoheader(gstring * hdata, int hashtype, const uschar * privkey,
1285   blob * sig, const uschar * why)
1286 {
1287 hashmethod hm = /*sig->keytype == KEYTYPE_ED25519*/ FALSE
1288   ? HASH_SHA2_512 : pdkim_hashes[hashtype].exim_hashmethod;
1289 blob hhash;
1290 es_ctx sctx;
1291 const uschar * errstr;
1292
1293 DEBUG(D_transport)
1294   {
1295   hctx hhash_ctx;
1296   debug_printf("ARC: %s header data for signing:\n", why);
1297   debug_printf("%.*Z\n", hdata->ptr, hdata->s);
1298
1299   (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
1300   exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1301   exim_sha_finish(&hhash_ctx, &hhash);
1302   debug_printf("ARC: header hash: %.*H\n", hhash.len, hhash.data);
1303   }
1304
1305 if (FALSE /*need hash for Ed25519 or GCrypt signing*/ )
1306   {
1307   hctx hhash_ctx;
1308   (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
1309   exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
1310   exim_sha_finish(&hhash_ctx, &hhash);
1311   }
1312 else
1313   {
1314   hhash.data = hdata->s;
1315   hhash.len = hdata->ptr;
1316   }
1317
1318 if (  (errstr = exim_dkim_signing_init(privkey, &sctx))
1319    || (errstr = exim_dkim_sign(&sctx, hm, &hhash, sig)))
1320   {
1321   log_write(0, LOG_MAIN, "ARC: %s signing: %s\n", why, errstr);
1322   DEBUG(D_transport)
1323     debug_printf("private key, or private-key file content, was: '%s'\n",
1324       privkey);
1325   return FALSE;
1326   }
1327 return TRUE;
1328 }
1329
1330
1331
1332 static gstring *
1333 arc_sign_append_sig(gstring * g, blob * sig)
1334 {
1335 /*debug_printf("%s: raw sig %.*H\n", __FUNCTION__, sig->len, sig->data);*/
1336 sig->data = pdkim_encode_base64(sig);
1337 sig->len = Ustrlen(sig->data);
1338 for (;;)
1339   {
1340   int len = MIN(sig->len, 74);
1341   g = string_catn(g, sig->data, len);
1342   if ((sig->len -= len) == 0) break;
1343   sig->data += len;
1344   g = string_catn(g, US"\r\n\t  ", 5);
1345   }
1346 g = string_catn(g, US";\r\n", 3);
1347 gstring_release_unused(g);
1348 string_from_gstring(g);
1349 return g;
1350 }
1351
1352
1353 /* Append a constructed AMS including CRLF.  Add it to the arc_ctx too. */
1354
1355 static gstring *
1356 arc_sign_append_ams(gstring * g, arc_ctx * ctx, int instance,
1357   const uschar * identity, const uschar * selector, blob * bodyhash,
1358   hdr_rlist * rheaders, const uschar * privkey, unsigned options)
1359 {
1360 uschar * s;
1361 gstring * hdata = NULL;
1362 int col;
1363 int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6);       /*XXX hardwired */
1364 blob sig;
1365 int ams_off;
1366 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
1367 header_line * h = (header_line *)(al+1);
1368
1369 /* debug_printf("%s\n", __FUNCTION__); */
1370
1371 /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */
1372
1373 ams_off = gstring_length(g);
1374 g = string_fmt_append(g, "%s i=%d; a=rsa-sha256; c=relaxed; d=%s; s=%s",
1375       ARC_HDR_AMS, instance, identity, selector);       /*XXX hardwired a= */
1376 if (options & ARC_SIGN_OPT_TSTAMP)
1377   g = string_fmt_append(g, "; t=%lu", (u_long)now);
1378 if (options & ARC_SIGN_OPT_EXPIRE)
1379   g = string_fmt_append(g, "; x=%lu", (u_long)expire);
1380 g = string_fmt_append(g, ";\r\n\tbh=%s;\r\n\th=",
1381       pdkim_encode_base64(bodyhash));
1382
1383 for(col = 3; rheaders; rheaders = rheaders->prev)
1384   {
1385   const uschar * hnames = US"DKIM-Signature:" PDKIM_DEFAULT_SIGN_HEADERS;
1386   uschar * name, * htext = rheaders->h->text;
1387   int sep = ':';
1388
1389   /* Spot headers of interest */
1390
1391   while ((name = string_nextinlist(&hnames, &sep, NULL, 0)))
1392     {
1393     int len = Ustrlen(name);
1394     if (strncasecmp(CCS htext, CCS name, len) == 0)
1395       {
1396       /* If too long, fold line in h= field */
1397
1398       if (col + len > 78) g = string_catn(g, US"\r\n\t  ", 5), col = 3;
1399
1400       /* Add name to h= list */
1401
1402       g = string_catn(g, name, len);
1403       g = string_catn(g, US":", 1);
1404       col += len + 1;
1405
1406       /* Accumulate header for hashing/signing */
1407
1408       hdata = string_cat(hdata,
1409                 pdkim_relax_header_n(htext, rheaders->h->slen, TRUE));  /*XXX hardwired */
1410       break;
1411       }
1412     }
1413   }
1414
1415 /* Lose the last colon from the h= list */
1416
1417 gstring_trim_trailing(g, ':');
1418
1419 g = string_catn(g, US";\r\n\tb=;", 7);
1420
1421 /* Include the pseudo-header in the accumulation */
1422
1423 s = pdkim_relax_header_n(g->s + ams_off, g->ptr - ams_off, FALSE);
1424 hdata = string_cat(hdata, s);
1425
1426 /* Calculate the signature from the accumulation */
1427 /*XXX does that need further relaxation? there are spaces embedded in the b= strings! */
1428
1429 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AMS"))
1430   return NULL;
1431
1432 /* Lose the trailing semicolon from the psuedo-header, and append the signature
1433 (folded over lines) and termination to complete it. */
1434
1435 gstring_trim(g, 1);
1436 g = arc_sign_append_sig(g, &sig);
1437
1438 h->slen = g->ptr - ams_off;
1439 h->text = g->s + ams_off;
1440 al->complete = h;
1441 ctx->arcset_chain_last->hdr_ams = al;
1442
1443 DEBUG(D_transport) debug_printf("ARC: AMS '%.*s'\n", h->slen - 2, h->text);
1444 return g;
1445 }
1446
1447
1448
1449 /* Look for an arc= result in an A-R header blob.  We know that its data
1450 happens to be a NUL-term string. */
1451
1452 static uschar *
1453 arc_ar_cv_status(blob * ar)
1454 {
1455 const uschar * resinfo = ar->data;
1456 int sep = ';';
1457 uschar * methodspec, * s;
1458
1459 while ((methodspec = string_nextinlist(&resinfo, &sep, NULL, 0)))
1460   if (Ustrncmp(methodspec, US"arc=", 4) == 0)
1461     {
1462     uschar c;
1463     for (s = methodspec += 4;
1464          (c = *s) && c != ';' && c != ' ' && c != '\r' && c != '\n'; ) s++;
1465     return string_copyn(methodspec, s - methodspec);
1466     }
1467 return US"none";
1468 }
1469
1470
1471
1472 /* Build the AS header and prepend it */
1473
1474 static gstring *
1475 arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx,
1476   int instance, const uschar * identity, const uschar * selector, blob * ar,
1477   const uschar * privkey, unsigned options)
1478 {
1479 gstring * arcset;
1480 uschar * status = arc_ar_cv_status(ar);
1481 arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
1482 header_line * h = (header_line *)(al+1);
1483 uschar * badline_str;
1484
1485 gstring * hdata = NULL;
1486 int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6);       /*XXX hardwired */
1487 blob sig;
1488
1489 /*
1490 - Generate AS
1491   - no body coverage
1492   - no h= tag; implicit coverage
1493   - arc status from A-R
1494     - if fail:
1495       - coverage is just the new ARC set
1496         including self (but with an empty b= in self)
1497     - if non-fail:
1498       - all ARC set headers, set-number order, aar then ams then as,
1499         including self (but with an empty b= in self)
1500 */
1501 DEBUG(D_transport) debug_printf("ARC: building AS for status '%s'\n", status);
1502
1503 /* Construct the AS except for the signature */
1504
1505 arcset = string_append(NULL, 9,
1506           ARC_HDR_AS,
1507           US" i=", string_sprintf("%d", instance),
1508           US"; cv=", status,
1509           US"; a=rsa-sha256; d=", identity,                     /*XXX hardwired */
1510           US"; s=", selector);                                  /*XXX same as AMS */
1511 if (options & ARC_SIGN_OPT_TSTAMP)
1512   arcset = string_append(arcset, 2,
1513       US"; t=", string_sprintf("%lu", (u_long)now));
1514 arcset = string_cat(arcset,
1515           US";\r\n\t b=;");
1516
1517 h->slen = arcset->ptr;
1518 h->text = arcset->s;
1519 al->complete = h;
1520 ctx->arcset_chain_last->hdr_as = al;
1521
1522 /* For any but "fail" chain-verify status, walk the entire chain in order by
1523 instance.  For fail, only the new arc-set.  Accumulate the elements walked. */
1524
1525 for (arc_set * as = Ustrcmp(status, US"fail") == 0
1526         ? ctx->arcset_chain_last : ctx->arcset_chain;
1527      as; as = as->next)
1528   {
1529   arc_line * l;
1530   /* Accumulate AAR then AMS then AS.  Relaxed canonicalisation
1531   is required per standard. */
1532
1533   badline_str = US"aar";
1534   if (!(l = as->hdr_aar)) goto badline;
1535   h = l->complete;
1536   hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
1537   badline_str = US"ams";
1538   if (!(l = as->hdr_ams)) goto badline;
1539   h = l->complete;
1540   hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
1541   badline_str = US"as";
1542   if (!(l = as->hdr_as)) goto badline;
1543   h = l->complete;
1544   hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, !!as->next));
1545   }
1546
1547 /* Calculate the signature from the accumulation */
1548
1549 if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AS"))
1550   return NULL;
1551
1552 /* Lose the trailing semicolon */
1553 arcset->ptr--;
1554 arcset = arc_sign_append_sig(arcset, &sig);
1555 DEBUG(D_transport) debug_printf("ARC: AS  '%.*s'\n", arcset->ptr - 2, arcset->s);
1556
1557 /* Finally, append the AMS and AAR to the new AS */
1558
1559 return string_catn(arcset, arcset_interim->s, arcset_interim->ptr);
1560
1561 badline:
1562   DEBUG(D_transport)
1563     debug_printf("ARC: while building AS, missing %s in chain\n", badline_str);
1564   return NULL;
1565 }
1566
1567
1568 /**************************************/
1569
1570 /* Return pointer to pdkim_bodyhash for given hash method, creating new
1571 method if needed.
1572 */
1573
1574 void *
1575 arc_ams_setup_sign_bodyhash(void)
1576 {
1577 int canon_head, canon_body;
1578
1579 DEBUG(D_transport) debug_printf("ARC: requesting bodyhash\n");
1580 pdkim_cstring_to_canons(US"relaxed", 7, &canon_head, &canon_body);      /*XXX hardwired */
1581 return pdkim_set_bodyhash(&dkim_sign_ctx,
1582         pdkim_hashname_to_hashtype(US"sha256", 6),                      /*XXX hardwired */
1583         canon_body,
1584         -1);
1585 }
1586
1587
1588
1589 void
1590 arc_sign_init(void)
1591 {
1592 memset(&arc_sign_ctx, 0, sizeof(arc_sign_ctx));
1593 headers_rlist = NULL;
1594 }
1595
1596
1597
1598 /* A "normal" header line, identified by DKIM processing.  These arrive before
1599 the call to arc_sign(), which carries any newly-created DKIM headers - and
1600 those go textually before the normal ones in the message.
1601
1602 We have to take the feed from DKIM as, in the transport-filter case, the
1603 headers are not in memory at the time of the call to arc_sign().
1604
1605 Take a copy of the header and construct a reverse-order list.
1606 Also parse ARC-chain headers and build the chain struct, retaining pointers
1607 into the copies.
1608 */
1609
1610 static const uschar *
1611 arc_header_sign_feed(gstring * g)
1612 {
1613 uschar * s = string_copy_from_gstring(g);
1614 headers_rlist = arc_rlist_entry(headers_rlist, s, g->ptr);
1615 return arc_try_header(&arc_sign_ctx, headers_rlist->h, TRUE);
1616 }
1617
1618
1619
1620 /* Per RFCs 6376, 7489 the only allowed chars in either an ADMD id
1621 or a selector are ALPHA/DIGGIT/'-'/'.'
1622
1623 Check, to help catch misconfigurations such as a missing selector
1624 element in the arc_sign list.
1625 */
1626
1627 static BOOL
1628 arc_valid_id(const uschar * s)
1629 {
1630 for (uschar c; c = *s++; )
1631   if (!isalnum(c) && c != '-' && c != '.') return FALSE;
1632 return TRUE;
1633 }
1634
1635
1636
1637 /* ARC signing.  Called from the smtp transport, if the arc_sign option is set.
1638 The dkim_exim_sign() function has already been called, so will have hashed the
1639 message body for us so long as we requested a hash previously.
1640
1641 Arguments:
1642   signspec      Three-element colon-sep list: identity, selector, privkey.
1643                 Optional fourth element: comma-sep list of options.
1644                 Already expanded
1645   sigheaders    Any signature headers already generated, eg. by DKIM, or NULL
1646   errstr        Error string
1647
1648 Return value
1649   Set of headers to prepend to the message, including the supplied sigheaders
1650   but not the plainheaders.
1651 */
1652
1653 gstring *
1654 arc_sign(const uschar * signspec, gstring * sigheaders, uschar ** errstr)
1655 {
1656 const uschar * identity, * selector, * privkey, * opts, * s;
1657 unsigned options = 0;
1658 int sep = 0;
1659 header_line * headers;
1660 hdr_rlist * rheaders;
1661 blob ar;
1662 int instance;
1663 gstring * g = NULL;
1664 pdkim_bodyhash * b;
1665
1666 expire = now = 0;
1667
1668 /* Parse the signing specification */
1669
1670 if (!(identity = string_nextinlist(&signspec, &sep, NULL, 0)) || !*identity)
1671   { s = US"identity"; goto bad_arg_ret; }
1672 if (!(selector = string_nextinlist(&signspec, &sep, NULL, 0)) || !*selector)
1673   { s = US"selector"; goto bad_arg_ret; }
1674 if (!(privkey = string_nextinlist(&signspec, &sep, NULL, 0))  || !*privkey)
1675   { s = US"privkey"; goto bad_arg_ret; }
1676 if (!arc_valid_id(identity))
1677   { s = US"identity"; goto bad_arg_ret; }
1678 if (!arc_valid_id(selector))
1679   { s = US"selector"; goto bad_arg_ret; }
1680 if (*privkey == '/' && !(privkey = expand_file_big_buffer(privkey)))
1681   goto ret_sigheaders;
1682
1683 if ((opts = string_nextinlist(&signspec, &sep, NULL, 0)))
1684   {
1685   int osep = ',';
1686   while ((s = string_nextinlist(&opts, &osep, NULL, 0)))
1687     if (Ustrcmp(s, "timestamps") == 0)
1688       {
1689       options |= ARC_SIGN_OPT_TSTAMP;
1690       if (!now) now = time(NULL);
1691       }
1692     else if (Ustrncmp(s, "expire", 6) == 0)
1693       {
1694       options |= ARC_SIGN_OPT_EXPIRE;
1695       if (*(s += 6) == '=')
1696         if (*++s == '+')
1697           {
1698           if (!(expire = (time_t)atoi(CS ++s)))
1699             expire = ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1700           if (!now) now = time(NULL);
1701           expire += now;
1702           }
1703         else
1704           expire = (time_t)atol(CS s);
1705       else
1706         {
1707         if (!now) now = time(NULL);
1708         expire = now + ARC_SIGN_DEFAULT_EXPIRE_DELTA;
1709         }
1710       }
1711   }
1712
1713 DEBUG(D_transport) debug_printf("ARC: sign for %s\n", identity);
1714
1715 /* Make an rlist of any new DKIM headers, then add the "normals" rlist to it.
1716 Then scan the list for an A-R header. */
1717
1718 string_from_gstring(sigheaders);
1719 if ((rheaders = arc_sign_scan_headers(&arc_sign_ctx, sigheaders)))
1720   {
1721   hdr_rlist ** rp;
1722   for (rp = &headers_rlist; *rp; ) rp = &(*rp)->prev;
1723   *rp = rheaders;
1724   }
1725
1726 /* Finally, build a normal-order headers list */
1727 /*XXX only needed for hunt-the-AR? */
1728 /*XXX also, we really should be accepting any number of ADMD-matching ARs */
1729   {
1730   header_line * hnext = NULL;
1731   for (rheaders = headers_rlist; rheaders;
1732        hnext = rheaders->h, rheaders = rheaders->prev)
1733     rheaders->h->next = hnext;
1734   headers = hnext;
1735   }
1736
1737 if (!(arc_sign_find_ar(headers, identity, &ar)))
1738   {
1739   log_write(0, LOG_MAIN, "ARC: no Authentication-Results header for signing");
1740   goto ret_sigheaders;
1741   }
1742
1743 /* We previously built the data-struct for the existing ARC chain, if any, using a headers
1744 feed from the DKIM module.  Use that to give the instance number for the ARC set we are
1745 about to build. */
1746
1747 DEBUG(D_transport)
1748   if (arc_sign_ctx.arcset_chain_last)
1749     debug_printf("ARC: existing chain highest instance: %d\n",
1750       arc_sign_ctx.arcset_chain_last->instance);
1751   else
1752     debug_printf("ARC: no existing chain\n");
1753
1754 instance = arc_sign_ctx.arcset_chain_last ? arc_sign_ctx.arcset_chain_last->instance + 1 : 1;
1755
1756 /*
1757 - Generate AAR
1758   - copy the A-R; prepend i= & identity
1759 */
1760
1761 g = arc_sign_append_aar(g, &arc_sign_ctx, identity, instance, &ar);
1762
1763 /*
1764 - Generate AMS
1765   - Looks fairly like a DKIM sig
1766   - Cover all DKIM sig headers as well as the usuals
1767     - ? oversigning?
1768   - Covers the data
1769   - we must have requested a suitable bodyhash previously
1770 */
1771
1772 b = arc_ams_setup_sign_bodyhash();
1773 g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector,
1774       &b->bh, headers_rlist, privkey, options);
1775
1776 /*
1777 - Generate AS
1778   - no body coverage
1779   - no h= tag; implicit coverage
1780   - arc status from A-R
1781     - if fail:
1782       - coverage is just the new ARC set
1783         including self (but with an empty b= in self)
1784     - if non-fail:
1785       - all ARC set headers, set-number order, aar then ams then as,
1786         including self (but with an empty b= in self)
1787 */
1788
1789 if (g)
1790   g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar,
1791       privkey, options);
1792
1793 /* Finally, append the dkim headers and return the lot. */
1794
1795 if (sigheaders) g = string_catn(g, sigheaders->s, sigheaders->ptr);
1796
1797 out:
1798   if (!g) return string_get(1);
1799   (void) string_from_gstring(g);
1800   gstring_release_unused(g);
1801   return g;
1802
1803
1804 bad_arg_ret:
1805   log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)", s);
1806 ret_sigheaders:
1807   g = sigheaders;
1808   goto out;
1809 }
1810
1811
1812 /******************************************************************************/
1813
1814 /* Check to see if the line is an AMS and if so, set up to validate it.
1815 Called from the DKIM input processing.  This must be done now as the message
1816 body data is hashed during input.
1817
1818 We call the DKIM code to request a body-hash; it has the facility already
1819 and the hash parameters might be common with other requests.
1820 */
1821
1822 static const uschar *
1823 arc_header_vfy_feed(gstring * g)
1824 {
1825 header_line h;
1826 arc_line al;
1827 pdkim_bodyhash * b;
1828 uschar * errstr;
1829
1830 if (!dkim_verify_ctx) return US"no dkim context";
1831
1832 if (strncmpic(ARC_HDR_AMS, g->s, ARC_HDRLEN_AMS) != 0) return US"not AMS";
1833
1834 DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n");
1835 /* Parse the AMS header */
1836
1837 memset(&al, 0, sizeof(arc_line));
1838 h.next = NULL;
1839 h.slen = len_string_from_gstring(g, &h.text);
1840 if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, le_all)))
1841   {
1842   DEBUG(D_acl) if (errstr) debug_printf("ARC: %s\n", errstr);
1843   goto badline;
1844   }
1845
1846 if (!al.a_hash.data)
1847   {
1848   DEBUG(D_acl) debug_printf("ARC: no a_hash from '%.*s'\n", h.slen, h.text);
1849   goto badline;
1850   }
1851
1852 /* defaults */
1853 if (!al.c.data)
1854   {
1855   al.c_body.data = US"simple"; al.c_body.len = 6;
1856   al.c_head = al.c_body;
1857   }
1858
1859 /* Ask the dkim code to calc a bodyhash with those specs */
1860
1861 if (!(b = arc_ams_setup_vfy_bodyhash(&al)))
1862   return US"dkim hash setup fail";
1863
1864 /* Discard the reference; search again at verify time, knowing that one
1865 should have been created here. */
1866
1867 return NULL;
1868
1869 badline:
1870   return US"line parsing error";
1871 }
1872
1873
1874
1875 /* A header line has been identified by DKIM processing.
1876
1877 Arguments:
1878   g             Header line
1879   is_vfy        TRUE for verify mode or FALSE for signing mode
1880
1881 Return:
1882   NULL for success, or an error string (probably unused)
1883 */
1884
1885 const uschar *
1886 arc_header_feed(gstring * g, BOOL is_vfy)
1887 {
1888 return is_vfy ? arc_header_vfy_feed(g) : arc_header_sign_feed(g);
1889 }
1890
1891
1892
1893 /******************************************************************************/
1894
1895 /* Construct the list of domains from the ARC chain after validation */
1896
1897 uschar *
1898 fn_arc_domains(void)
1899 {
1900 arc_set * as;
1901 unsigned inst;
1902 gstring * g = NULL;
1903
1904 for (as = arc_verify_ctx.arcset_chain, inst = 1; as; as = as->next, inst++)
1905   {
1906   arc_line * hdr_as = as->hdr_as;
1907   if (hdr_as)
1908     {
1909     blob * d = &hdr_as->d;
1910
1911     for (; inst < as->instance; inst++)
1912       g = string_catn(g, US":", 1);
1913
1914     g = d->data && d->len
1915       ? string_append_listele_n(g, ':', d->data, d->len)
1916       : string_catn(g, US":", 1);
1917     }
1918   else
1919     g = string_catn(g, US":", 1);
1920   }
1921 if (!g) return US"";
1922 return string_from_gstring(g);
1923 }
1924
1925
1926 /* Construct an Authentication-Results header portion, for the ARC module */
1927
1928 gstring *
1929 authres_arc(gstring * g)
1930 {
1931 if (arc_state)
1932   {
1933   arc_line * highest_ams;
1934   int start = 0;                /* Compiler quietening */
1935   DEBUG(D_acl) start = gstring_length(g);
1936
1937   g = string_append(g, 2, US";\n\tarc=", arc_state);
1938   if (arc_received_instance > 0)
1939     {
1940     g = string_fmt_append(g, " (i=%d)", arc_received_instance);
1941     if (arc_state_reason)
1942       g = string_append(g, 3, US"(", arc_state_reason, US")");
1943     g = string_catn(g, US" header.s=", 10);
1944     highest_ams = arc_received->hdr_ams;
1945     g = string_catn(g, highest_ams->s.data, highest_ams->s.len);
1946
1947     g = string_fmt_append(g, " arc.oldest-pass=%d", arc_oldest_pass);
1948
1949     if (sender_host_address)
1950       g = string_append(g, 2, US" smtp.remote-ip=", sender_host_address);
1951     }
1952   else if (arc_state_reason)
1953     g = string_append(g, 3, US" (", arc_state_reason, US")");
1954   DEBUG(D_acl) debug_printf("ARC:\tauthres '%.*s'\n",
1955                   gstring_length(g) - start - 3, g->s + start + 3);
1956   }
1957 else
1958   DEBUG(D_acl) debug_printf("ARC:\tno authres\n");
1959 return g;
1960 }
1961
1962
1963 #  ifdef SUPPORT_DMARC
1964 /* Append a DMARC history record pair for ARC, to the given history set */
1965
1966 gstring *
1967 arc_dmarc_hist_append(gstring * g)
1968 {
1969 if (arc_state)
1970   {
1971   BOOL first = TRUE;
1972   int i = Ustrcmp(arc_state, "pass") == 0 ? ARES_RESULT_PASS
1973           : Ustrcmp(arc_state, "fail") == 0 ? ARES_RESULT_FAIL
1974           : ARES_RESULT_UNKNOWN;
1975   g = string_fmt_append(g, "arc %d\n", i);
1976   g = string_fmt_append(g, "arc_policy %d json[",
1977                           i == ARES_RESULT_PASS ? DMARC_ARC_POLICY_RESULT_PASS
1978                           : i == ARES_RESULT_FAIL ? DMARC_ARC_POLICY_RESULT_FAIL
1979                           : DMARC_ARC_POLICY_RESULT_UNUSED);
1980   /*XXX would we prefer this backwards? */
1981   for (arc_set * as = arc_verify_ctx.arcset_chain; as;
1982         as = as->next, first = FALSE)
1983     {
1984     arc_line * line = as->hdr_as;
1985     if (line)
1986       {
1987       blob * d = &line->d;
1988       blob * s = &line->s;
1989
1990       if (!first)
1991         g = string_catn(g, US",", 1);
1992
1993       g = string_fmt_append(g, " (\"i\":%u,"                    /*)*/
1994                                 " \"d\":\"%.*s\","
1995                                 " \"s\":\"%.*s\"",
1996                   as->instance,
1997                   d->data ? (int)d->len : 0, d->data && d->len ? d->data : US"",
1998                   s->data ? (int)s->len : 0, s->data && s->len ? s->data : US""
1999                            );
2000       if ((line = as->hdr_aar))
2001         {
2002         blob * ip = &line->ip;
2003         if (ip->data && ip->len)
2004           g = string_fmt_append(g, ", \"ip\":\"%.*s\"", (int)ip->len, ip->data);
2005         }
2006
2007       g = string_catn(g, US")", 1);
2008       }
2009     }
2010   g = string_catn(g, US" ]\n", 3);
2011   }
2012 else
2013   g = string_fmt_append(g, "arc %d\narc_policy %d json:[]\n",
2014                         ARES_RESULT_UNKNOWN, DMARC_ARC_POLICY_RESULT_UNUSED);
2015 return g;
2016 }
2017 #  endif
2018
2019
2020 # endif /* DISABLE_DKIM */
2021 #endif /* EXPERIMENTAL_ARC */
2022 /* vi: aw ai sw=2
2023  */