Fix DKIM verify when used with CHUNKING. Bug 2016
[exim.git] / src / src / pdkim / pdkim.c
... / ...
CommitLineData
1/*
2 * PDKIM - a RFC4871 (DKIM) implementation
3 *
4 * Copyright (C) 2009 - 2016 Tom Kistner <tom@duncanthrax.net>
5 * Copyright (C) 2016 Jeremy Harris <jgh@exim.org>
6 *
7 * http://duncanthrax.net/pdkim/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24#include "../exim.h"
25
26
27#ifndef DISABLE_DKIM /* entire file */
28
29#ifndef SUPPORT_TLS
30# error Need SUPPORT_TLS for DKIM
31#endif
32
33#include "crypt_ver.h"
34
35#ifdef RSA_OPENSSL
36# include <openssl/rsa.h>
37# include <openssl/ssl.h>
38# include <openssl/err.h>
39#elif defined(RSA_GNUTLS)
40# include <gnutls/gnutls.h>
41# include <gnutls/x509.h>
42#endif
43
44#include "pdkim.h"
45#include "rsa.h"
46
47#define PDKIM_SIGNATURE_VERSION "1"
48#define PDKIM_PUB_RECORD_VERSION US "DKIM1"
49
50#define PDKIM_MAX_HEADER_LEN 65536
51#define PDKIM_MAX_HEADERS 512
52#define PDKIM_MAX_BODY_LINE_LEN 16384
53#define PDKIM_DNS_TXT_MAX_NAMELEN 1024
54#define PDKIM_DEFAULT_SIGN_HEADERS "From:Sender:Reply-To:Subject:Date:"\
55 "Message-ID:To:Cc:MIME-Version:Content-Type:"\
56 "Content-Transfer-Encoding:Content-ID:"\
57 "Content-Description:Resent-Date:Resent-From:"\
58 "Resent-Sender:Resent-To:Resent-Cc:"\
59 "Resent-Message-ID:In-Reply-To:References:"\
60 "List-Id:List-Help:List-Unsubscribe:"\
61 "List-Subscribe:List-Post:List-Owner:List-Archive"
62
63/* -------------------------------------------------------------------------- */
64struct pdkim_stringlist {
65 uschar * value;
66 int tag;
67 void * next;
68};
69
70/* -------------------------------------------------------------------------- */
71/* A bunch of list constants */
72const uschar * pdkim_querymethods[] = {
73 US"dns/txt",
74 NULL
75};
76const uschar * pdkim_algos[] = {
77 US"rsa-sha256",
78 US"rsa-sha1",
79 NULL
80};
81const uschar * pdkim_canons[] = {
82 US"simple",
83 US"relaxed",
84 NULL
85};
86const uschar * pdkim_hashes[] = {
87 US"sha256",
88 US"sha1",
89 NULL
90};
91const uschar * pdkim_keytypes[] = {
92 US"rsa",
93 NULL
94};
95
96typedef struct pdkim_combined_canon_entry {
97 const uschar * str;
98 int canon_headers;
99 int canon_body;
100} pdkim_combined_canon_entry;
101
102pdkim_combined_canon_entry pdkim_combined_canons[] = {
103 { US"simple/simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
104 { US"simple/relaxed", PDKIM_CANON_SIMPLE, PDKIM_CANON_RELAXED },
105 { US"relaxed/simple", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
106 { US"relaxed/relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_RELAXED },
107 { US"simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
108 { US"relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
109 { NULL, 0, 0 }
110};
111
112
113/* -------------------------------------------------------------------------- */
114
115const char *
116pdkim_verify_status_str(int status)
117{
118switch(status)
119 {
120 case PDKIM_VERIFY_NONE: return "PDKIM_VERIFY_NONE";
121 case PDKIM_VERIFY_INVALID: return "PDKIM_VERIFY_INVALID";
122 case PDKIM_VERIFY_FAIL: return "PDKIM_VERIFY_FAIL";
123 case PDKIM_VERIFY_PASS: return "PDKIM_VERIFY_PASS";
124 default: return "PDKIM_VERIFY_UNKNOWN";
125 }
126}
127
128const char *
129pdkim_verify_ext_status_str(int ext_status)
130{
131switch(ext_status)
132 {
133 case PDKIM_VERIFY_FAIL_BODY: return "PDKIM_VERIFY_FAIL_BODY";
134 case PDKIM_VERIFY_FAIL_MESSAGE: return "PDKIM_VERIFY_FAIL_MESSAGE";
135 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: return "PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE";
136 case PDKIM_VERIFY_INVALID_BUFFER_SIZE: return "PDKIM_VERIFY_INVALID_BUFFER_SIZE";
137 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD: return "PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD";
138 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return "PDKIM_VERIFY_INVALID_PUBKEY_IMPORT";
139 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR: return "PDKIM_VERIFY_INVALID_SIGNATURE_ERROR";
140 case PDKIM_VERIFY_INVALID_DKIM_VERSION: return "PDKIM_VERIFY_INVALID_DKIM_VERSION";
141 default: return "PDKIM_VERIFY_UNKNOWN";
142 }
143}
144
145const char *
146pdkim_errstr(int status)
147{
148switch(status)
149 {
150 case PDKIM_OK: return "OK";
151 case PDKIM_FAIL: return "FAIL";
152 case PDKIM_ERR_RSA_PRIVKEY: return "RSA_PRIVKEY";
153 case PDKIM_ERR_RSA_SIGNING: return "RSA SIGNING";
154 case PDKIM_ERR_LONG_LINE: return "RSA_LONG_LINE";
155 case PDKIM_ERR_BUFFER_TOO_SMALL: return "BUFFER_TOO_SMALL";
156 case PDKIM_SIGN_PRIVKEY_WRAP: return "PRIVKEY_WRAP";
157 case PDKIM_SIGN_PRIVKEY_B64D: return "PRIVKEY_B64D";
158 default: return "(unknown)";
159 }
160}
161
162
163/* -------------------------------------------------------------------------- */
164/* Print debugging functions */
165static void
166pdkim_quoteprint(const uschar *data, int len)
167{
168int i;
169for (i = 0; i < len; i++)
170 {
171 const int c = data[i];
172 switch (c)
173 {
174 case ' ' : debug_printf("{SP}"); break;
175 case '\t': debug_printf("{TB}"); break;
176 case '\r': debug_printf("{CR}"); break;
177 case '\n': debug_printf("{LF}"); break;
178 case '{' : debug_printf("{BO}"); break;
179 case '}' : debug_printf("{BC}"); break;
180 default:
181 if ( (c < 32) || (c > 127) )
182 debug_printf("{%02x}", c);
183 else
184 debug_printf("%c", c);
185 break;
186 }
187 }
188debug_printf("\n");
189}
190
191static void
192pdkim_hexprint(const uschar *data, int len)
193{
194int i;
195for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
196debug_printf("\n");
197}
198
199
200
201static pdkim_stringlist *
202pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str)
203{
204pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist));
205
206memset(new_entry, 0, sizeof(pdkim_stringlist));
207new_entry->value = string_copy(str);
208if (base) new_entry->next = base;
209return new_entry;
210}
211
212
213
214/* Trim whitespace fore & aft */
215
216static void
217pdkim_strtrim(uschar * str)
218{
219uschar * p = str;
220uschar * q = str;
221while (*p == '\t' || *p == ' ') p++; /* skip whitespace */
222while (*p) {*q = *p; q++; p++;} /* dump the leading whitespace */
223*q = '\0';
224while (q != str && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) )
225 { /* dump trailing whitespace */
226 *q = '\0';
227 q--;
228 }
229}
230
231
232
233/* -------------------------------------------------------------------------- */
234
235DLLEXPORT void
236pdkim_free_ctx(pdkim_ctx *ctx)
237{
238}
239
240
241/* -------------------------------------------------------------------------- */
242/* Matches the name of the passed raw "header" against
243 the passed colon-separated "tick", and invalidates
244 the entry in tick. Returns OK or fail-code */
245/*XXX might be safer done using a pdkim_stringlist for "tick" */
246
247static int
248header_name_match(const uschar * header, uschar * tick)
249{
250uschar * hname;
251uschar * lcopy;
252uschar * p;
253uschar * q;
254uschar * hcolon = Ustrchr(header, ':'); /* Get header name */
255
256if (!hcolon)
257 return PDKIM_FAIL; /* This isn't a header */
258
259/* if we had strncmpic() we wouldn't need this copy */
260hname = string_copyn(header, hcolon-header);
261
262/* Copy tick-off list locally, so we can punch zeroes into it */
263p = lcopy = string_copy(tick);
264
265for (q = Ustrchr(p, ':'); q; q = Ustrchr(p, ':'))
266 {
267 *q = '\0';
268 if (strcmpic(p, hname) == 0)
269 goto found;
270
271 p = q+1;
272 }
273
274if (strcmpic(p, hname) == 0)
275 goto found;
276
277return PDKIM_FAIL;
278
279found:
280 /* Invalidate header name instance in tick-off list */
281 tick[p-lcopy] = '_';
282 return PDKIM_OK;
283}
284
285
286/* -------------------------------------------------------------------------- */
287/* Performs "relaxed" canonicalization of a header. */
288
289static uschar *
290pdkim_relax_header(const uschar * header, int crlf)
291{
292BOOL past_field_name = FALSE;
293BOOL seen_wsp = FALSE;
294const uschar * p;
295uschar * relaxed = store_get(Ustrlen(header)+3);
296uschar * q = relaxed;
297
298for (p = header; *p; p++)
299 {
300 uschar c = *p;
301 /* Ignore CR & LF */
302 if (c == '\r' || c == '\n')
303 continue;
304 if (c == '\t' || c == ' ')
305 {
306 if (seen_wsp)
307 continue;
308 c = ' '; /* Turns WSP into SP */
309 seen_wsp = TRUE;
310 }
311 else
312 if (!past_field_name && c == ':')
313 {
314 if (seen_wsp) q--; /* This removes WSP before the colon */
315 seen_wsp = TRUE; /* This removes WSP after the colon */
316 past_field_name = TRUE;
317 }
318 else
319 seen_wsp = FALSE;
320
321 /* Lowercase header name */
322 if (!past_field_name) c = tolower(c);
323 *q++ = c;
324 }
325
326if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */
327
328if (crlf) { *q++ = '\r'; *q++ = '\n'; }
329*q = '\0';
330return relaxed;
331}
332
333
334/* -------------------------------------------------------------------------- */
335#define PDKIM_QP_ERROR_DECODE -1
336
337static uschar *
338pdkim_decode_qp_char(uschar *qp_p, int *c)
339{
340uschar *initial_pos = qp_p;
341
342/* Advance one char */
343qp_p++;
344
345/* Check for two hex digits and decode them */
346if (isxdigit(*qp_p) && isxdigit(qp_p[1]))
347 {
348 /* Do hex conversion */
349 *c = (isdigit(*qp_p) ? *qp_p - '0' : toupper(*qp_p) - 'A' + 10) << 4;
350 *c |= isdigit(qp_p[1]) ? qp_p[1] - '0' : toupper(qp_p[1]) - 'A' + 10;
351 return qp_p + 2;
352 }
353
354/* Illegal char here */
355*c = PDKIM_QP_ERROR_DECODE;
356return initial_pos;
357}
358
359
360/* -------------------------------------------------------------------------- */
361
362static uschar *
363pdkim_decode_qp(uschar * str)
364{
365int nchar = 0;
366uschar * q;
367uschar * p = str;
368uschar * n = store_get(Ustrlen(str)+1);
369
370*n = '\0';
371q = n;
372while (*p)
373 {
374 if (*p == '=')
375 {
376 p = pdkim_decode_qp_char(p, &nchar);
377 if (nchar >= 0)
378 {
379 *q++ = nchar;
380 continue;
381 }
382 }
383 else
384 *q++ = *p;
385 p++;
386 }
387*q = '\0';
388return n;
389}
390
391
392/* -------------------------------------------------------------------------- */
393
394static void
395pdkim_decode_base64(uschar *str, blob * b)
396{
397int dlen;
398dlen = b64decode(str, &b->data);
399if (dlen < 0) b->data = NULL;
400b->len = dlen;
401}
402
403static uschar *
404pdkim_encode_base64(blob * b)
405{
406return b64encode(b->data, b->len);
407}
408
409
410/* -------------------------------------------------------------------------- */
411#define PDKIM_HDR_LIMBO 0
412#define PDKIM_HDR_TAG 1
413#define PDKIM_HDR_VALUE 2
414
415static pdkim_signature *
416pdkim_parse_sig_header(pdkim_ctx *ctx, uschar * raw_hdr)
417{
418pdkim_signature *sig ;
419uschar *p, *q;
420uschar * cur_tag = NULL; int ts = 0, tl = 0;
421uschar * cur_val = NULL; int vs = 0, vl = 0;
422BOOL past_hname = FALSE;
423BOOL in_b_val = FALSE;
424int where = PDKIM_HDR_LIMBO;
425int i;
426
427sig = store_get(sizeof(pdkim_signature));
428memset(sig, 0, sizeof(pdkim_signature));
429sig->bodylength = -1;
430
431/* Set so invalid/missing data error display is accurate */
432sig->algo = -1;
433sig->version = 0;
434
435q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
436
437for (p = raw_hdr; ; p++)
438 {
439 char c = *p;
440
441 /* Ignore FWS */
442 if (c == '\r' || c == '\n')
443 goto NEXT_CHAR;
444
445 /* Fast-forward through header name */
446 if (!past_hname)
447 {
448 if (c == ':') past_hname = TRUE;
449 goto NEXT_CHAR;
450 }
451
452 if (where == PDKIM_HDR_LIMBO)
453 {
454 /* In limbo, just wait for a tag-char to appear */
455 if (!(c >= 'a' && c <= 'z'))
456 goto NEXT_CHAR;
457
458 where = PDKIM_HDR_TAG;
459 }
460
461 if (where == PDKIM_HDR_TAG)
462 {
463 if (c >= 'a' && c <= 'z')
464 cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
465
466 if (c == '=')
467 {
468 cur_tag[tl] = '\0';
469 if (Ustrcmp(cur_tag, "b") == 0)
470 {
471 *q++ = '=';
472 in_b_val = TRUE;
473 }
474 where = PDKIM_HDR_VALUE;
475 goto NEXT_CHAR;
476 }
477 }
478
479 if (where == PDKIM_HDR_VALUE)
480 {
481 if (c == '\r' || c == '\n' || c == ' ' || c == '\t')
482 goto NEXT_CHAR;
483
484 if (c == ';' || c == '\0')
485 {
486 if (tl && vl)
487 {
488 cur_val[vl] = '\0';
489 pdkim_strtrim(cur_val);
490
491 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val);
492
493 switch (*cur_tag)
494 {
495 case 'b':
496 if (cur_tag[1] == 'h')
497 pdkim_decode_base64(cur_val, &sig->bodyhash);
498 else
499 pdkim_decode_base64(cur_val, &sig->sigdata);
500 break;
501 case 'v':
502 /* We only support version 1, and that is currently the
503 only version there is. */
504 sig->version =
505 Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1;
506 break;
507 case 'a':
508 for (i = 0; pdkim_algos[i]; i++)
509 if (Ustrcmp(cur_val, pdkim_algos[i]) == 0)
510 {
511 sig->algo = i;
512 break;
513 }
514 break;
515 case 'c':
516 for (i = 0; pdkim_combined_canons[i].str; i++)
517 if (Ustrcmp(cur_val, pdkim_combined_canons[i].str) == 0)
518 {
519 sig->canon_headers = pdkim_combined_canons[i].canon_headers;
520 sig->canon_body = pdkim_combined_canons[i].canon_body;
521 break;
522 }
523 break;
524 case 'q':
525 for (i = 0; pdkim_querymethods[i]; i++)
526 if (Ustrcmp(cur_val, pdkim_querymethods[i]) == 0)
527 {
528 sig->querymethod = i;
529 break;
530 }
531 break;
532 case 's':
533 sig->selector = string_copy(cur_val); break;
534 case 'd':
535 sig->domain = string_copy(cur_val); break;
536 case 'i':
537 sig->identity = pdkim_decode_qp(cur_val); break;
538 case 't':
539 sig->created = strtoul(CS cur_val, NULL, 10); break;
540 case 'x':
541 sig->expires = strtoul(CS cur_val, NULL, 10); break;
542 case 'l':
543 sig->bodylength = strtol(CS cur_val, NULL, 10); break;
544 case 'h':
545 sig->headernames = string_copy(cur_val); break;
546 case 'z':
547 sig->copiedheaders = pdkim_decode_qp(cur_val); break;
548 default:
549 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
550 break;
551 }
552 }
553 tl = 0;
554 vl = 0;
555 in_b_val = FALSE;
556 where = PDKIM_HDR_LIMBO;
557 }
558 else
559 cur_val = string_catn(cur_val, &vs, &vl, p, 1);
560 }
561
562NEXT_CHAR:
563 if (c == '\0')
564 break;
565
566 if (!in_b_val)
567 *q++ = c;
568 }
569
570*q = '\0';
571/* Chomp raw header. The final newline must not be added to the signature. */
572while (--q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n'))
573 *q = '\0';
574
575DEBUG(D_acl)
576 {
577 debug_printf(
578 "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
579 pdkim_quoteprint(US sig->rawsig_no_b_val, Ustrlen(sig->rawsig_no_b_val));
580 debug_printf(
581 "PDKIM >> Sig size: %4u bits\n", (unsigned) sig->sigdata.len*8);
582 debug_printf(
583 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
584 }
585
586exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
587return sig;
588}
589
590
591/* -------------------------------------------------------------------------- */
592
593static pdkim_pubkey *
594pdkim_parse_pubkey_record(pdkim_ctx *ctx, const uschar *raw_record)
595{
596pdkim_pubkey *pub;
597const uschar *p;
598uschar * cur_tag = NULL; int ts = 0, tl = 0;
599uschar * cur_val = NULL; int vs = 0, vl = 0;
600int where = PDKIM_HDR_LIMBO;
601
602pub = store_get(sizeof(pdkim_pubkey));
603memset(pub, 0, sizeof(pdkim_pubkey));
604
605for (p = raw_record; ; p++)
606 {
607 uschar c = *p;
608
609 /* Ignore FWS */
610 if (c != '\r' && c != '\n') switch (where)
611 {
612 case PDKIM_HDR_LIMBO: /* In limbo, just wait for a tag-char to appear */
613 if (!(c >= 'a' && c <= 'z'))
614 break;
615 where = PDKIM_HDR_TAG;
616 /*FALLTHROUGH*/
617
618 case PDKIM_HDR_TAG:
619 if (c >= 'a' && c <= 'z')
620 cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
621
622 if (c == '=')
623 {
624 cur_tag[tl] = '\0';
625 where = PDKIM_HDR_VALUE;
626 }
627 break;
628
629 case PDKIM_HDR_VALUE:
630 if (c == ';' || c == '\0')
631 {
632 if (tl && vl)
633 {
634 cur_val[vl] = '\0';
635 pdkim_strtrim(cur_val);
636 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val);
637
638 switch (cur_tag[0])
639 {
640 case 'v':
641 pub->version = string_copy(cur_val); break;
642 case 'h':
643 case 'k':
644/* This field appears to never be used. Also, unclear why
645a 'k' (key-type_ would go in this field name. There is a field
646"keytype", also never used.
647 pub->hashes = string_copy(cur_val);
648*/
649 break;
650 case 'g':
651 pub->granularity = string_copy(cur_val); break;
652 case 'n':
653 pub->notes = pdkim_decode_qp(cur_val); break;
654 case 'p':
655 pdkim_decode_base64(US cur_val, &pub->key); break;
656 case 's':
657 pub->srvtype = string_copy(cur_val); break;
658 case 't':
659 if (Ustrchr(cur_val, 'y') != NULL) pub->testing = 1;
660 if (Ustrchr(cur_val, 's') != NULL) pub->no_subdomaining = 1;
661 break;
662 default:
663 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
664 break;
665 }
666 }
667 tl = 0;
668 vl = 0;
669 where = PDKIM_HDR_LIMBO;
670 }
671 else
672 cur_val = string_catn(cur_val, &vs, &vl, p, 1);
673 break;
674 }
675
676 if (c == '\0') break;
677 }
678
679/* Set fallback defaults */
680if (!pub->version ) pub->version = string_copy(PDKIM_PUB_RECORD_VERSION);
681else if (Ustrcmp(pub->version, PDKIM_PUB_RECORD_VERSION) != 0) return NULL;
682
683if (!pub->granularity) pub->granularity = string_copy(US"*");
684/*
685if (!pub->keytype ) pub->keytype = string_copy(US"rsa");
686*/
687if (!pub->srvtype ) pub->srvtype = string_copy(US"*");
688
689/* p= is required */
690if (pub->key.data)
691 return pub;
692
693return NULL;
694}
695
696
697/* -------------------------------------------------------------------------- */
698
699static int
700pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len)
701{
702pdkim_signature *sig = ctx->sig;
703/* Cache relaxed version of data */
704uschar *relaxed_data = NULL;
705int relaxed_len = 0;
706
707/* Traverse all signatures, updating their hashes. */
708while (sig)
709 {
710 /* Defaults to simple canon (no further treatment necessary) */
711 const uschar *canon_data = CUS data;
712 int canon_len = len;
713
714 if (sig->canon_body == PDKIM_CANON_RELAXED)
715 {
716 /* Relax the line if not done already */
717 if (!relaxed_data)
718 {
719 BOOL seen_wsp = FALSE;
720 const char *p;
721 int q = 0;
722
723 /* We want to be able to free this else we allocate
724 for the entire message which could be many MB. Since
725 we don't know what allocations the SHA routines might
726 do, not safe to use store_get()/store_reset(). */
727
728 relaxed_data = store_malloc(len+1);
729
730 for (p = data; *p; p++)
731 {
732 char c = *p;
733 if (c == '\r')
734 {
735 if (q > 0 && relaxed_data[q-1] == ' ')
736 q--;
737 }
738 else if (c == '\t' || c == ' ')
739 {
740 c = ' '; /* Turns WSP into SP */
741 if (seen_wsp)
742 continue;
743 seen_wsp = TRUE;
744 }
745 else
746 seen_wsp = FALSE;
747 relaxed_data[q++] = c;
748 }
749 relaxed_data[q] = '\0';
750 relaxed_len = q;
751 }
752 canon_data = relaxed_data;
753 canon_len = relaxed_len;
754 }
755
756 /* Make sure we don't exceed the to-be-signed body length */
757 if ( sig->bodylength >= 0
758 && sig->signed_body_bytes + (unsigned long)canon_len > sig->bodylength
759 )
760 canon_len = sig->bodylength - sig->signed_body_bytes;
761
762 if (canon_len > 0)
763 {
764 exim_sha_update(&sig->body_hash, CUS canon_data, canon_len);
765 sig->signed_body_bytes += canon_len;
766 DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
767 }
768
769 sig = sig->next;
770 }
771
772if (relaxed_data) store_free(relaxed_data);
773return PDKIM_OK;
774}
775
776
777/* -------------------------------------------------------------------------- */
778
779static void
780pdkim_finish_bodyhash(pdkim_ctx *ctx)
781{
782pdkim_signature *sig;
783
784/* Traverse all signatures */
785for (sig = ctx->sig; sig; sig = sig->next)
786 { /* Finish hashes */
787 blob bh;
788
789 exim_sha_finish(&sig->body_hash, &bh);
790
791 DEBUG(D_acl)
792 {
793 debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
794 "PDKIM [%s] Body hash computed: ",
795 sig->domain, sig->signed_body_bytes, sig->domain);
796 pdkim_hexprint(CUS bh.data, bh.len);
797 }
798
799 /* SIGNING -------------------------------------------------------------- */
800 if (ctx->flags & PDKIM_MODE_SIGN)
801 {
802 sig->bodyhash = bh;
803
804 /* If bodylength limit is set, and we have received less bytes
805 than the requested amount, effectively remove the limit tag. */
806 if (sig->signed_body_bytes < sig->bodylength)
807 sig->bodylength = -1;
808 }
809
810 /* VERIFICATION --------------------------------------------------------- */
811 else
812 {
813 /* Compare bodyhash */
814 if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
815 {
816 DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
817 }
818 else
819 {
820 DEBUG(D_acl)
821 {
822 debug_printf("PDKIM [%s] Body hash signature from headers: ", sig->domain);
823 pdkim_hexprint(sig->bodyhash.data,
824 exim_sha_hashlen(&sig->body_hash));
825 debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
826 }
827 sig->verify_status = PDKIM_VERIFY_FAIL;
828 sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
829 }
830 }
831 }
832}
833
834
835
836static int
837pdkim_body_complete(pdkim_ctx * ctx)
838{
839pdkim_signature * sig = ctx->sig; /*XXX assumes only one sig */
840
841/* In simple body mode, if any empty lines were buffered,
842replace with one. rfc 4871 3.4.3 */
843/*XXX checking the signed-body-bytes is a gross hack; I think
844it indicates that all linebreaks should be buffered, including
845the one terminating a text line */
846
847if ( sig && sig->canon_body == PDKIM_CANON_SIMPLE
848 && sig->signed_body_bytes == 0
849 && ctx->num_buffered_crlf > 0
850 )
851 pdkim_update_bodyhash(ctx, "\r\n", 2);
852
853ctx->flags |= PDKIM_SEEN_EOD;
854ctx->linebuf_offset = 0;
855return PDKIM_OK;
856}
857
858
859
860/* -------------------------------------------------------------------------- */
861/* Call from pdkim_feed below for processing complete body lines */
862
863static int
864pdkim_bodyline_complete(pdkim_ctx *ctx)
865{
866char *p = ctx->linebuf;
867int n = ctx->linebuf_offset;
868pdkim_signature *sig = ctx->sig; /*XXX assumes only one sig */
869
870/* Ignore extra data if we've seen the end-of-data marker */
871if (ctx->flags & PDKIM_SEEN_EOD) goto BAIL;
872
873/* We've always got one extra byte to stuff a zero ... */
874ctx->linebuf[ctx->linebuf_offset] = '\0';
875
876/* Terminate on EOD marker */
877if (ctx->flags & PDKIM_DOT_TERM)
878 {
879 if (memcmp(p, ".\r\n", 3) == 0)
880 return pdkim_body_complete(ctx);
881
882 /* Unstuff dots */
883 if (memcmp(p, "..", 2) == 0)
884 {
885 p++;
886 n--;
887 }
888 }
889
890/* Empty lines need to be buffered until we find a non-empty line */
891if (memcmp(p, "\r\n", 2) == 0)
892 {
893 ctx->num_buffered_crlf++;
894 goto BAIL;
895 }
896
897if (sig && sig->canon_body == PDKIM_CANON_RELAXED)
898 {
899 /* Lines with just spaces need to be buffered too */
900 char *check = p;
901 while (memcmp(check, "\r\n", 2) != 0)
902 {
903 char c = *check;
904
905 if (c != '\t' && c != ' ')
906 goto PROCESS;
907 check++;
908 }
909
910 ctx->num_buffered_crlf++;
911 goto BAIL;
912}
913
914PROCESS:
915/* At this point, we have a non-empty line, so release the buffered ones. */
916while (ctx->num_buffered_crlf)
917 {
918 pdkim_update_bodyhash(ctx, "\r\n", 2);
919 ctx->num_buffered_crlf--;
920 }
921
922pdkim_update_bodyhash(ctx, p, n);
923
924BAIL:
925ctx->linebuf_offset = 0;
926return PDKIM_OK;
927}
928
929
930/* -------------------------------------------------------------------------- */
931/* Callback from pdkim_feed below for processing complete headers */
932#define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
933
934static int
935pdkim_header_complete(pdkim_ctx *ctx)
936{
937/* Special case: The last header can have an extra \r appended */
938if ( (ctx->cur_header_len > 1) &&
939 (ctx->cur_header[(ctx->cur_header_len)-1] == '\r') )
940 --ctx->cur_header_len;
941ctx->cur_header[ctx->cur_header_len] = '\0';
942
943ctx->num_headers++;
944if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
945
946/* SIGNING -------------------------------------------------------------- */
947if (ctx->flags & PDKIM_MODE_SIGN)
948 {
949 pdkim_signature *sig;
950
951 for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */
952
953 /* Add header to the signed headers list (in reverse order) */
954 sig->headers = pdkim_prepend_stringlist(sig->headers,
955 ctx->cur_header);
956 }
957
958/* VERIFICATION ----------------------------------------------------------- */
959/* DKIM-Signature: headers are added to the verification list */
960else
961 {
962 DEBUG(D_acl)
963 {
964 debug_printf("PDKIM >> raw hdr: ");
965 pdkim_quoteprint(CUS ctx->cur_header, Ustrlen(ctx->cur_header));
966 }
967 if (strncasecmp(CCS ctx->cur_header,
968 DKIM_SIGNATURE_HEADERNAME,
969 Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
970 {
971 pdkim_signature *new_sig;
972
973 /* Create and chain new signature block */
974 DEBUG(D_acl) debug_printf(
975 "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
976
977 if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header)))
978 {
979 pdkim_signature *last_sig = ctx->sig;
980 if (!last_sig)
981 ctx->sig = new_sig;
982 else
983 {
984 while (last_sig->next) last_sig = last_sig->next;
985 last_sig->next = new_sig;
986 }
987 }
988 else
989 DEBUG(D_acl) debug_printf(
990 "Error while parsing signature header\n"
991 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
992 }
993
994 /* every other header is stored for signature verification */
995 else
996 ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header);
997 }
998
999BAIL:
1000*ctx->cur_header = '\0';
1001ctx->cur_header_len = 0; /* leave buffer for reuse */
1002return PDKIM_OK;
1003}
1004
1005
1006
1007/* -------------------------------------------------------------------------- */
1008#define HEADER_BUFFER_FRAG_SIZE 256
1009
1010DLLEXPORT int
1011pdkim_feed(pdkim_ctx *ctx, char *data, int len)
1012{
1013int p;
1014
1015/* Alternate EOD signal, used in non-dotstuffing mode */
1016if (!data)
1017 pdkim_body_complete(ctx);
1018
1019else for (p = 0; p<len; p++)
1020 {
1021 uschar c = data[p];
1022
1023 if (ctx->flags & PDKIM_PAST_HDRS)
1024 {
1025 if (c == '\n' && !(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
1026 {
1027 ctx->linebuf[ctx->linebuf_offset++] = '\r';
1028 if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
1029 return PDKIM_ERR_LONG_LINE;
1030 }
1031
1032 /* Processing body byte */
1033 ctx->linebuf[ctx->linebuf_offset++] = c;
1034 if (c == '\r')
1035 ctx->flags |= PDKIM_SEEN_CR;
1036 else if (c == '\n')
1037 {
1038 int rc;
1039 ctx->flags &= ~PDKIM_SEEN_CR;
1040 if ((rc = pdkim_bodyline_complete(ctx)) != PDKIM_OK)
1041 return rc;
1042 }
1043
1044 if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
1045 return PDKIM_ERR_LONG_LINE;
1046 }
1047 else
1048 {
1049 /* Processing header byte */
1050 if (c == '\r')
1051 ctx->flags |= PDKIM_SEEN_CR;
1052 else if (c == '\n')
1053 {
1054 if (!(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
1055 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
1056 &ctx->cur_header_len, CUS "\r", 1);
1057
1058 if (ctx->flags & PDKIM_SEEN_LF)
1059 {
1060 int rc = pdkim_header_complete(ctx); /* Seen last header line */
1061 if (rc != PDKIM_OK) return rc;
1062
1063 ctx->flags = ctx->flags & ~(PDKIM_SEEN_LF|PDKIM_SEEN_CR) | PDKIM_PAST_HDRS;
1064 DEBUG(D_acl) debug_printf(
1065 "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>\n");
1066 continue;
1067 }
1068 else
1069 ctx->flags = ctx->flags & ~PDKIM_SEEN_CR | PDKIM_SEEN_LF;
1070 }
1071 else if (ctx->flags & PDKIM_SEEN_LF)
1072 {
1073 if (!(c == '\t' || c == ' '))
1074 {
1075 int rc = pdkim_header_complete(ctx); /* End of header */
1076 if (rc != PDKIM_OK) return rc;
1077 }
1078 ctx->flags &= ~PDKIM_SEEN_LF;
1079 }
1080
1081 if (ctx->cur_header_len < PDKIM_MAX_HEADER_LEN)
1082 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
1083 &ctx->cur_header_len, CUS &data[p], 1);
1084 }
1085 }
1086return PDKIM_OK;
1087}
1088
1089
1090
1091/* Extend a grwong header with a continuation-linebreak */
1092static uschar *
1093pdkim_hdr_cont(uschar * str, int * size, int * ptr, int * col)
1094{
1095*col = 1;
1096return string_catn(str, size, ptr, US"\r\n\t", 3);
1097}
1098
1099
1100
1101/*
1102 * RFC 5322 specifies that header line length SHOULD be no more than 78
1103 * lets make it so!
1104 * pdkim_headcat
1105 *
1106 * returns uschar * (not nul-terminated)
1107 *
1108 * col: this int holds and receives column number (octets since last '\n')
1109 * str: partial string to append to
1110 * size: current buffer size for str
1111 * ptr: current tail-pointer for str
1112 * pad: padding, split line or space after before or after eg: ";"
1113 * intro: - must join to payload eg "h=", usually the tag name
1114 * payload: eg base64 data - long data can be split arbitrarily.
1115 *
1116 * this code doesn't fold the header in some of the places that RFC4871
1117 * allows: As per RFC5322(2.2.3) it only folds before or after tag-value
1118 * pairs and inside long values. it also always spaces or breaks after the
1119 * "pad"
1120 *
1121 * no guarantees are made for output given out-of range input. like tag
1122 * names longer than 78, or bogus col. Input is assumed to be free of line breaks.
1123 */
1124
1125static uschar *
1126pdkim_headcat(int * col, uschar * str, int * size, int * ptr,
1127 const uschar * pad, const uschar * intro, const uschar * payload)
1128{
1129size_t l;
1130
1131if (pad)
1132 {
1133 l = Ustrlen(pad);
1134 if (*col + l > 78)
1135 str = pdkim_hdr_cont(str, size, ptr, col);
1136 str = string_catn(str, size, ptr, pad, l);
1137 *col += l;
1138 }
1139
1140l = (pad?1:0) + (intro?Ustrlen(intro):0);
1141
1142if (*col + l > 78)
1143 { /*can't fit intro - start a new line to make room.*/
1144 str = pdkim_hdr_cont(str, size, ptr, col);
1145 l = intro?Ustrlen(intro):0;
1146 }
1147
1148l += payload ? Ustrlen(payload):0 ;
1149
1150while (l>77)
1151 { /* this fragment will not fit on a single line */
1152 if (pad)
1153 {
1154 str = string_catn(str, size, ptr, US" ", 1);
1155 *col += 1;
1156 pad = NULL; /* only want this once */
1157 l--;
1158 }
1159
1160 if (intro)
1161 {
1162 size_t sl = Ustrlen(intro);
1163
1164 str = string_catn(str, size, ptr, intro, sl);
1165 *col += sl;
1166 l -= sl;
1167 intro = NULL; /* only want this once */
1168 }
1169
1170 if (payload)
1171 {
1172 size_t sl = Ustrlen(payload);
1173 size_t chomp = *col+sl < 77 ? sl : 78-*col;
1174
1175 str = string_catn(str, size, ptr, payload, chomp);
1176 *col += chomp;
1177 payload += chomp;
1178 l -= chomp-1;
1179 }
1180
1181 /* the while precondition tells us it didn't fit. */
1182 str = pdkim_hdr_cont(str, size, ptr, col);
1183 }
1184
1185if (*col + l > 78)
1186 {
1187 str = pdkim_hdr_cont(str, size, ptr, col);
1188 pad = NULL;
1189 }
1190
1191if (pad)
1192 {
1193 str = string_catn(str, size, ptr, US" ", 1);
1194 *col += 1;
1195 pad = NULL;
1196 }
1197
1198if (intro)
1199 {
1200 size_t sl = Ustrlen(intro);
1201
1202 str = string_catn(str, size, ptr, intro, sl);
1203 *col += sl;
1204 l -= sl;
1205 intro = NULL;
1206 }
1207
1208if (payload)
1209 {
1210 size_t sl = Ustrlen(payload);
1211
1212 str = string_catn(str, size, ptr, payload, sl);
1213 *col += sl;
1214 }
1215
1216return str;
1217}
1218
1219
1220/* -------------------------------------------------------------------------- */
1221
1222static uschar *
1223pdkim_create_header(pdkim_signature *sig, BOOL final)
1224{
1225uschar * base64_bh;
1226uschar * base64_b;
1227int col = 0;
1228uschar * hdr; int hdr_size = 0, hdr_len = 0;
1229uschar * canon_all; int can_size = 0, can_len = 0;
1230
1231canon_all = string_cat (NULL, &can_size, &can_len,
1232 pdkim_canons[sig->canon_headers]);
1233canon_all = string_catn(canon_all, &can_size, &can_len, US"/", 1);
1234canon_all = string_cat (canon_all, &can_size, &can_len,
1235 pdkim_canons[sig->canon_body]);
1236canon_all[can_len] = '\0';
1237
1238hdr = string_cat(NULL, &hdr_size, &hdr_len,
1239 US"DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
1240col = hdr_len;
1241
1242/* Required and static bits */
1243hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
1244 pdkim_algos[sig->algo]);
1245hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
1246 pdkim_querymethods[sig->querymethod]);
1247hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"c=",
1248 canon_all);
1249hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"d=",
1250 sig->domain);
1251hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"s=",
1252 sig->selector);
1253
1254/* list of header names can be split between items. */
1255 {
1256 uschar * n = string_copy(sig->headernames);
1257 uschar * i = US"h=";
1258 uschar * s = US";";
1259
1260 while (*n)
1261 {
1262 uschar * c = Ustrchr(n, ':');
1263
1264 if (c) *c ='\0';
1265
1266 if (!i)
1267 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, NULL, US":");
1268
1269 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, s, i, n);
1270
1271 if (!c)
1272 break;
1273
1274 n = c+1;
1275 s = NULL;
1276 i = NULL;
1277 }
1278 }
1279
1280base64_bh = pdkim_encode_base64(&sig->bodyhash);
1281hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"bh=", base64_bh);
1282
1283/* Optional bits */
1284if (sig->identity)
1285 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"i=", sig->identity);
1286
1287if (sig->created > 0)
1288 {
1289 uschar minibuf[20];
1290
1291 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->created);
1292 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"t=", minibuf);
1293}
1294
1295if (sig->expires > 0)
1296 {
1297 uschar minibuf[20];
1298
1299 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->expires);
1300 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"x=", minibuf);
1301 }
1302
1303if (sig->bodylength >= 0)
1304 {
1305 uschar minibuf[20];
1306
1307 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->bodylength);
1308 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"l=", minibuf);
1309 }
1310
1311/* Preliminary or final version? */
1312base64_b = final ? pdkim_encode_base64(&sig->sigdata) : US"";
1313hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
1314
1315/* add trailing semicolon: I'm not sure if this is actually needed */
1316hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US"");
1317
1318hdr[hdr_len] = '\0';
1319return hdr;
1320}
1321
1322
1323/* -------------------------------------------------------------------------- */
1324
1325static pdkim_pubkey *
1326pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx)
1327{
1328uschar * dns_txt_name, * dns_txt_reply;
1329pdkim_pubkey * p;
1330const uschar * errstr;
1331
1332/* Fetch public key for signing domain, from DNS */
1333
1334dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain);
1335
1336dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN);
1337memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
1338
1339if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK
1340 || dns_txt_reply[0] == '\0'
1341 )
1342 {
1343 sig->verify_status = PDKIM_VERIFY_INVALID;
1344 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
1345 return NULL;
1346 }
1347
1348DEBUG(D_acl)
1349 {
1350 debug_printf(
1351 "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
1352 " Raw record: ");
1353 pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
1354 }
1355
1356if ( !(p = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply))
1357 || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
1358 )
1359 {
1360 sig->verify_status = PDKIM_VERIFY_INVALID;
1361 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
1362
1363 DEBUG(D_acl)
1364 {
1365 if (p)
1366 debug_printf(" Invalid public key service type '%s'\n", p->srvtype);
1367 else
1368 debug_printf(" Error while parsing public key record\n");
1369 debug_printf(
1370 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1371 }
1372 return NULL;
1373 }
1374
1375DEBUG(D_acl) debug_printf(
1376 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1377
1378/* Import public key */
1379if ((errstr = exim_rsa_verify_init(&p->key, vctx)))
1380 {
1381 DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
1382 sig->verify_status = PDKIM_VERIFY_INVALID;
1383 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
1384 return NULL;
1385 }
1386
1387return p;
1388}
1389
1390
1391/* -------------------------------------------------------------------------- */
1392
1393DLLEXPORT int
1394pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures)
1395{
1396pdkim_signature *sig = ctx->sig;
1397
1398/* Check if we must still flush a (partial) header. If that is the
1399 case, the message has no body, and we must compute a body hash
1400 out of '<CR><LF>' */
1401if (ctx->cur_header && ctx->cur_header_len)
1402 {
1403 int rc = pdkim_header_complete(ctx);
1404 if (rc != PDKIM_OK) return rc;
1405 pdkim_update_bodyhash(ctx, "\r\n", 2);
1406 }
1407else
1408 DEBUG(D_acl) debug_printf(
1409 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1410
1411/* Build (and/or evaluate) body hash */
1412pdkim_finish_bodyhash(ctx);
1413
1414while (sig)
1415 {
1416 BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
1417 hctx hhash_ctx;
1418 uschar * sig_hdr = US"";
1419 blob hhash;
1420 blob hdata;
1421 int hdata_alloc = 0;
1422
1423 hdata.data = NULL;
1424 hdata.len = 0;
1425
1426 exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256);
1427
1428 DEBUG(D_acl) debug_printf(
1429 "PDKIM >> Header data for hash, canonicalized, in sequence >>>>>>>>>>>>>>\n");
1430
1431 /* SIGNING ---------------------------------------------------------------- */
1432 /* When signing, walk through our header list and add them to the hash. As we
1433 go, construct a list of the header's names to use for the h= parameter.
1434 Then append to that list any remaining header names for which there was no
1435 header to sign. */
1436
1437 if (ctx->flags & PDKIM_MODE_SIGN)
1438 {
1439 uschar * headernames = NULL; /* Collected signed header names */
1440 int hs = 0, hl = 0;
1441 pdkim_stringlist *p;
1442 const uschar * l;
1443 uschar * s;
1444 int sep = 0;
1445
1446 for (p = sig->headers; p; p = p->next)
1447 if (header_name_match(p->value, sig->sign_headers) == PDKIM_OK)
1448 {
1449 uschar * rh;
1450 /* Collect header names (Note: colon presence is guaranteed here) */
1451 uschar * q = Ustrchr(p->value, ':');
1452
1453 headernames = string_catn(headernames, &hs, &hl,
1454 p->value, (q - US p->value) + (p->next ? 1 : 0));
1455
1456 rh = sig->canon_headers == PDKIM_CANON_RELAXED
1457 ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
1458 : string_copy(CUS p->value); /* just copy it for simple canon */
1459
1460 /* Feed header to the hash algorithm */
1461 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
1462
1463 /* Remember headers block for signing (when the library cannot do incremental) */
1464 (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
1465
1466 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1467 }
1468
1469 l = sig->sign_headers;
1470 while((s = string_nextinlist(&l, &sep, NULL, 0)))
1471 if (*s != '_')
1472 { /*SSS string_append_listele() */
1473 if (hl > 0 && headernames[hl-1] != ':')
1474 headernames = string_catn(headernames, &hs, &hl, US":", 1);
1475
1476 headernames = string_cat(headernames, &hs, &hl, s);
1477 }
1478 headernames[hl] = '\0';
1479
1480 /* Copy headernames to signature struct */
1481 sig->headernames = headernames;
1482
1483 /* Create signature header with b= omitted */
1484 sig_hdr = pdkim_create_header(sig, FALSE);
1485 }
1486
1487 /* VERIFICATION ----------------------------------------------------------- */
1488 /* When verifying, walk through the header name list in the h= parameter and
1489 add the headers to the hash in that order. */
1490 else
1491 {
1492 uschar * p = sig->headernames;
1493 uschar * q;
1494 pdkim_stringlist * hdrs;
1495
1496 if (p)
1497 {
1498 /* clear tags */
1499 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1500 hdrs->tag = 0;
1501
1502 p = string_copy(p);
1503 while(1)
1504 {
1505 if ((q = Ustrchr(p, ':')))
1506 *q = '\0';
1507
1508 /*XXX walk the list of headers in same order as received. */
1509 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1510 if ( hdrs->tag == 0
1511 && strncasecmp(CCS hdrs->value, CCS p, Ustrlen(p)) == 0
1512 && (hdrs->value)[Ustrlen(p)] == ':'
1513 )
1514 {
1515 /* cook header for relaxed canon, or just copy it for simple */
1516
1517 uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
1518 ? pdkim_relax_header(hdrs->value, 1)
1519 : string_copy(CUS hdrs->value);
1520
1521 /* Feed header to the hash algorithm */
1522 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
1523
1524 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1525 hdrs->tag = 1;
1526 break;
1527 }
1528
1529 if (!q) break;
1530 p = q+1;
1531 }
1532
1533 sig_hdr = string_copy(sig->rawsig_no_b_val);
1534 }
1535 }
1536
1537 DEBUG(D_acl) debug_printf(
1538 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1539
1540 /* Relax header if necessary */
1541 if (sig->canon_headers == PDKIM_CANON_RELAXED)
1542 sig_hdr = pdkim_relax_header(sig_hdr, 0);
1543
1544 DEBUG(D_acl)
1545 {
1546 debug_printf(
1547 "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
1548 pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
1549 debug_printf(
1550 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1551 }
1552
1553 /* Finalize header hash */
1554 exim_sha_update(&hhash_ctx, CUS sig_hdr, Ustrlen(sig_hdr));
1555 exim_sha_finish(&hhash_ctx, &hhash);
1556
1557 DEBUG(D_acl)
1558 {
1559 debug_printf("PDKIM [%s] Header hash computed: ", sig->domain);
1560 pdkim_hexprint(hhash.data, hhash.len);
1561 }
1562
1563 /* Remember headers block for signing (when the library cannot do incremental) */
1564 if (ctx->flags & PDKIM_MODE_SIGN)
1565 (void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
1566
1567 /* SIGNING ---------------------------------------------------------------- */
1568 if (ctx->flags & PDKIM_MODE_SIGN)
1569 {
1570 es_ctx sctx;
1571 const uschar * errstr;
1572
1573 /* Import private key */
1574 if ((errstr = exim_rsa_signing_init(US sig->rsa_privkey, &sctx)))
1575 {
1576 DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
1577 return PDKIM_ERR_RSA_PRIVKEY;
1578 }
1579
1580 /* Do signing. With OpenSSL we are signing the hash of headers just
1581 calculated, with GnuTLS we have to sign an entire block of headers
1582 (due to available interfaces) and it recalculates the hash internally. */
1583
1584#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
1585 hdata = hhash;
1586#endif
1587
1588 if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
1589 {
1590 DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
1591 return PDKIM_ERR_RSA_SIGNING;
1592 }
1593
1594 DEBUG(D_acl)
1595 {
1596 debug_printf( "PDKIM [%s] b computed: ", sig->domain);
1597 pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
1598 }
1599
1600 sig->signature_header = pdkim_create_header(sig, TRUE);
1601 }
1602
1603 /* VERIFICATION ----------------------------------------------------------- */
1604 else
1605 {
1606 ev_ctx vctx;
1607 const uschar * errstr;
1608 pdkim_pubkey * p;
1609
1610 /* Make sure we have all required signature tags */
1611 if (!( sig->domain && *sig->domain
1612 && sig->selector && *sig->selector
1613 && sig->headernames && *sig->headernames
1614 && sig->bodyhash.data
1615 && sig->sigdata.data
1616 && sig->algo > -1
1617 && sig->version
1618 ) )
1619 {
1620 sig->verify_status = PDKIM_VERIFY_INVALID;
1621 sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
1622
1623 DEBUG(D_acl) debug_printf(
1624 " Error in DKIM-Signature header: tags missing or invalid\n"
1625 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1626 goto NEXT_VERIFY;
1627 }
1628
1629 /* Make sure sig uses supported DKIM version (only v1) */
1630 if (sig->version != 1)
1631 {
1632 sig->verify_status = PDKIM_VERIFY_INVALID;
1633 sig->verify_ext_status = PDKIM_VERIFY_INVALID_DKIM_VERSION;
1634
1635 DEBUG(D_acl) debug_printf(
1636 " Error in DKIM-Signature header: unsupported DKIM version\n"
1637 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1638 goto NEXT_VERIFY;
1639 }
1640
1641 if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx)))
1642 goto NEXT_VERIFY;
1643
1644 /* Check the signature */
1645 if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
1646 {
1647 DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
1648 sig->verify_status = PDKIM_VERIFY_FAIL;
1649 sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
1650 goto NEXT_VERIFY;
1651 }
1652
1653
1654 /* We have a winner! (if bodyhash was correct earlier) */
1655 if (sig->verify_status == PDKIM_VERIFY_NONE)
1656 sig->verify_status = PDKIM_VERIFY_PASS;
1657
1658NEXT_VERIFY:
1659
1660 DEBUG(D_acl)
1661 {
1662 debug_printf("PDKIM [%s] signature status: %s",
1663 sig->domain, pdkim_verify_status_str(sig->verify_status));
1664 if (sig->verify_ext_status > 0)
1665 debug_printf(" (%s)\n",
1666 pdkim_verify_ext_status_str(sig->verify_ext_status));
1667 else
1668 debug_printf("\n");
1669 }
1670 }
1671
1672 sig = sig->next;
1673 }
1674
1675/* If requested, set return pointer to signature(s) */
1676if (return_signatures)
1677 *return_signatures = ctx->sig;
1678
1679return PDKIM_OK;
1680}
1681
1682
1683/* -------------------------------------------------------------------------- */
1684
1685DLLEXPORT pdkim_ctx *
1686pdkim_init_verify(int(*dns_txt_callback)(char *, char *), BOOL dot_stuffing)
1687{
1688pdkim_ctx * ctx;
1689
1690ctx = store_get(sizeof(pdkim_ctx));
1691memset(ctx, 0, sizeof(pdkim_ctx));
1692
1693if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM;
1694ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
1695ctx->dns_txt_callback = dns_txt_callback;
1696
1697return ctx;
1698}
1699
1700
1701/* -------------------------------------------------------------------------- */
1702
1703DLLEXPORT pdkim_ctx *
1704pdkim_init_sign(char * domain, char * selector, char * rsa_privkey, int algo,
1705 BOOL dot_stuffed, int(*dns_txt_callback)(char *, char *))
1706{
1707pdkim_ctx * ctx;
1708pdkim_signature * sig;
1709
1710if (!domain || !selector || !rsa_privkey)
1711 return NULL;
1712
1713ctx = store_get(sizeof(pdkim_ctx) + PDKIM_MAX_BODY_LINE_LEN + sizeof(pdkim_signature));
1714memset(ctx, 0, sizeof(pdkim_ctx));
1715
1716ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;
1717ctx->linebuf = CS (ctx+1);
1718
1719DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback;
1720
1721sig = (pdkim_signature *)(ctx->linebuf + PDKIM_MAX_BODY_LINE_LEN);
1722memset(sig, 0, sizeof(pdkim_signature));
1723
1724sig->bodylength = -1;
1725ctx->sig = sig;
1726
1727sig->domain = string_copy(US domain);
1728sig->selector = string_copy(US selector);
1729sig->rsa_privkey = string_copy(US rsa_privkey);
1730sig->algo = algo;
1731
1732exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
1733
1734DEBUG(D_acl)
1735 {
1736 pdkim_signature s = *sig;
1737 ev_ctx vctx;
1738
1739 debug_printf("PDKIM (checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1740 if (!pdkim_key_from_dns(ctx, &s, &vctx))
1741 debug_printf("WARNING: bad dkim key in dns\n");
1742 debug_printf("PDKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1743 }
1744
1745return ctx;
1746}
1747
1748
1749/* -------------------------------------------------------------------------- */
1750
1751DLLEXPORT int
1752pdkim_set_optional(pdkim_ctx *ctx,
1753 char *sign_headers,
1754 char *identity,
1755 int canon_headers,
1756 int canon_body,
1757 long bodylength,
1758 unsigned long created,
1759 unsigned long expires)
1760{
1761pdkim_signature * sig = ctx->sig;
1762
1763if (identity)
1764 sig->identity = string_copy(US identity);
1765
1766sig->sign_headers = string_copy(sign_headers
1767 ? US sign_headers : US PDKIM_DEFAULT_SIGN_HEADERS);
1768
1769sig->canon_headers = canon_headers;
1770sig->canon_body = canon_body;
1771sig->bodylength = bodylength;
1772sig->created = created;
1773sig->expires = expires;
1774
1775return PDKIM_OK;
1776}
1777
1778
1779void
1780pdkim_init(void)
1781{
1782exim_rsa_init();
1783}
1784
1785
1786
1787#endif /*DISABLE_DKIM*/