Revert "DKIM: reduce memory usage"
[exim.git] / src / src / pdkim / pdkim.c
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 /* -------------------------------------------------------------------------- */
64 struct pdkim_stringlist {
65 uschar * value;
66 int tag;
67 void * next;
68 };
69
70 /* -------------------------------------------------------------------------- */
71 /* A bunch of list constants */
72 const uschar * pdkim_querymethods[] = {
73 US"dns/txt",
74 NULL
75 };
76 const uschar * pdkim_algos[] = {
77 US"rsa-sha256",
78 US"rsa-sha1",
79 NULL
80 };
81 const uschar * pdkim_canons[] = {
82 US"simple",
83 US"relaxed",
84 NULL
85 };
86 const uschar * pdkim_hashes[] = {
87 US"sha256",
88 US"sha1",
89 NULL
90 };
91 const uschar * pdkim_keytypes[] = {
92 US"rsa",
93 NULL
94 };
95
96 typedef struct pdkim_combined_canon_entry {
97 const uschar * str;
98 int canon_headers;
99 int canon_body;
100 } pdkim_combined_canon_entry;
101
102 pdkim_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
115 const char *
116 pdkim_verify_status_str(int status)
117 {
118 switch(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
128 const char *
129 pdkim_verify_ext_status_str(int ext_status)
130 {
131 switch(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
145 const char *
146 pdkim_errstr(int status)
147 {
148 switch(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 */
165 static void
166 pdkim_quoteprint(const uschar *data, int len)
167 {
168 int i;
169 for (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 }
188 debug_printf("\n");
189 }
190
191 static void
192 pdkim_hexprint(const uschar *data, int len)
193 {
194 int i;
195 for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
196 debug_printf("\n");
197 }
198
199
200
201 static pdkim_stringlist *
202 pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str)
203 {
204 pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist));
205
206 memset(new_entry, 0, sizeof(pdkim_stringlist));
207 new_entry->value = string_copy(str);
208 if (base) new_entry->next = base;
209 return new_entry;
210 }
211
212
213
214 /* Trim whitespace fore & aft */
215
216 static void
217 pdkim_strtrim(uschar * str)
218 {
219 uschar * p = str;
220 uschar * q = str;
221 while (*p == '\t' || *p == ' ') p++; /* skip whitespace */
222 while (*p) {*q = *p; q++; p++;} /* dump the leading whitespace */
223 *q = '\0';
224 while (q != str && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) )
225 { /* dump trailing whitespace */
226 *q = '\0';
227 q--;
228 }
229 }
230
231
232
233 /* -------------------------------------------------------------------------- */
234
235 DLLEXPORT void
236 pdkim_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
247 static int
248 header_name_match(const uschar * header, uschar * tick)
249 {
250 uschar * hname;
251 uschar * lcopy;
252 uschar * p;
253 uschar * q;
254 uschar * hcolon = Ustrchr(header, ':'); /* Get header name */
255
256 if (!hcolon)
257 return PDKIM_FAIL; /* This isn't a header */
258
259 /* if we had strncmpic() we wouldn't need this copy */
260 hname = string_copyn(header, hcolon-header);
261
262 /* Copy tick-off list locally, so we can punch zeroes into it */
263 p = lcopy = string_copy(tick);
264
265 for (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
274 if (strcmpic(p, hname) == 0)
275 goto found;
276
277 return PDKIM_FAIL;
278
279 found:
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
289 static uschar *
290 pdkim_relax_header(const uschar * header, int crlf)
291 {
292 BOOL past_field_name = FALSE;
293 BOOL seen_wsp = FALSE;
294 const uschar * p;
295 uschar * relaxed = store_get(Ustrlen(header)+3);
296 uschar * q = relaxed;
297
298 for (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
326 if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */
327
328 if (crlf) { *q++ = '\r'; *q++ = '\n'; }
329 *q = '\0';
330 return relaxed;
331 }
332
333
334 /* -------------------------------------------------------------------------- */
335 #define PDKIM_QP_ERROR_DECODE -1
336
337 static uschar *
338 pdkim_decode_qp_char(uschar *qp_p, int *c)
339 {
340 uschar *initial_pos = qp_p;
341
342 /* Advance one char */
343 qp_p++;
344
345 /* Check for two hex digits and decode them */
346 if (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;
356 return initial_pos;
357 }
358
359
360 /* -------------------------------------------------------------------------- */
361
362 static uschar *
363 pdkim_decode_qp(uschar * str)
364 {
365 int nchar = 0;
366 uschar * q;
367 uschar * p = str;
368 uschar * n = store_get(Ustrlen(str)+1);
369
370 *n = '\0';
371 q = n;
372 while (*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';
388 return n;
389 }
390
391
392 /* -------------------------------------------------------------------------- */
393
394 static void
395 pdkim_decode_base64(uschar *str, blob * b)
396 {
397 int dlen;
398 dlen = b64decode(str, &b->data);
399 if (dlen < 0) b->data = NULL;
400 b->len = dlen;
401 }
402
403 static uschar *
404 pdkim_encode_base64(blob * b)
405 {
406 return 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
415 static pdkim_signature *
416 pdkim_parse_sig_header(pdkim_ctx *ctx, uschar * raw_hdr)
417 {
418 pdkim_signature *sig ;
419 uschar *p, *q;
420 uschar * cur_tag = NULL; int ts = 0, tl = 0;
421 uschar * cur_val = NULL; int vs = 0, vl = 0;
422 BOOL past_hname = FALSE;
423 BOOL in_b_val = FALSE;
424 int where = PDKIM_HDR_LIMBO;
425 int i;
426
427 sig = store_get(sizeof(pdkim_signature));
428 memset(sig, 0, sizeof(pdkim_signature));
429 sig->bodylength = -1;
430
431 /* Set so invalid/missing data error display is accurate */
432 sig->algo = -1;
433 sig->version = 0;
434
435 q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
436
437 for (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
562 NEXT_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. */
572 while (--q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n'))
573 *q = '\0';
574
575 DEBUG(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
586 exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
587 return sig;
588 }
589
590
591 /* -------------------------------------------------------------------------- */
592
593 static pdkim_pubkey *
594 pdkim_parse_pubkey_record(pdkim_ctx *ctx, const uschar *raw_record)
595 {
596 pdkim_pubkey *pub;
597 const uschar *p;
598 uschar * cur_tag = NULL; int ts = 0, tl = 0;
599 uschar * cur_val = NULL; int vs = 0, vl = 0;
600 int where = PDKIM_HDR_LIMBO;
601
602 pub = store_get(sizeof(pdkim_pubkey));
603 memset(pub, 0, sizeof(pdkim_pubkey));
604
605 for (p = raw_record; ; p++)
606 {
607 char c = *p;
608
609 /* Ignore FWS */
610 if (c == '\r' || c == '\n')
611 goto NEXT_CHAR;
612
613 if (where == PDKIM_HDR_LIMBO)
614 {
615 /* In limbo, just wait for a tag-char to appear */
616 if (!(c >= 'a' && c <= 'z'))
617 goto NEXT_CHAR;
618
619 where = PDKIM_HDR_TAG;
620 }
621
622 if (where == PDKIM_HDR_TAG)
623 {
624 if (c >= 'a' && c <= 'z')
625 cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
626
627 if (c == '=')
628 {
629 cur_tag[tl] = '\0';
630 where = PDKIM_HDR_VALUE;
631 goto NEXT_CHAR;
632 }
633 }
634
635 if (where == PDKIM_HDR_VALUE)
636 {
637 if (c == ';' || c == '\0')
638 {
639 if (tl && vl)
640 {
641 cur_val[vl] = '\0';
642 pdkim_strtrim(cur_val);
643 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val);
644
645 switch (cur_tag[0])
646 {
647 case 'v':
648 /* This tag isn't evaluated because:
649 - We only support version DKIM1.
650 - Which is the default for this value (set below)
651 - Other versions are currently not specified. */
652 break;
653 case 'h':
654 case 'k':
655 pub->hashes = string_copy(cur_val); break;
656 case 'g':
657 pub->granularity = string_copy(cur_val); break;
658 case 'n':
659 pub->notes = pdkim_decode_qp(cur_val); break;
660 case 'p':
661 pdkim_decode_base64(US cur_val, &pub->key);
662 break;
663 case 's':
664 pub->srvtype = string_copy(cur_val); break;
665 case 't':
666 if (Ustrchr(cur_val, 'y') != NULL) pub->testing = 1;
667 if (Ustrchr(cur_val, 's') != NULL) pub->no_subdomaining = 1;
668 break;
669 default:
670 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
671 break;
672 }
673 }
674 tl = 0;
675 vl = 0;
676 where = PDKIM_HDR_LIMBO;
677 }
678 else
679 cur_val = string_catn(cur_val, &vs, &vl, p, 1);
680 }
681
682 NEXT_CHAR:
683 if (c == '\0') break;
684 }
685
686 /* Set fallback defaults */
687 if (!pub->version ) pub->version = string_copy(PDKIM_PUB_RECORD_VERSION);
688 if (!pub->granularity) pub->granularity = string_copy(US"*");
689 if (!pub->keytype ) pub->keytype = string_copy(US"rsa");
690 if (!pub->srvtype ) pub->srvtype = string_copy(US"*");
691
692 /* p= is required */
693 if (pub->key.data)
694 return pub;
695
696 return NULL;
697 }
698
699
700 /* -------------------------------------------------------------------------- */
701
702 static int
703 pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len)
704 {
705 pdkim_signature *sig = ctx->sig;
706 /* Cache relaxed version of data */
707 uschar *relaxed_data = NULL;
708 int relaxed_len = 0;
709
710 /* Traverse all signatures, updating their hashes. */
711 while (sig)
712 {
713 /* Defaults to simple canon (no further treatment necessary) */
714 const uschar *canon_data = CUS data;
715 int canon_len = len;
716
717 if (sig->canon_body == PDKIM_CANON_RELAXED)
718 {
719 /* Relax the line if not done already */
720 if (!relaxed_data)
721 {
722 BOOL seen_wsp = FALSE;
723 const char *p;
724 int q = 0;
725
726 relaxed_data = store_get(len+1);
727
728 for (p = data; *p; p++)
729 {
730 char c = *p;
731 if (c == '\r')
732 {
733 if (q > 0 && relaxed_data[q-1] == ' ')
734 q--;
735 }
736 else if (c == '\t' || c == ' ')
737 {
738 c = ' '; /* Turns WSP into SP */
739 if (seen_wsp)
740 continue;
741 seen_wsp = TRUE;
742 }
743 else
744 seen_wsp = FALSE;
745 relaxed_data[q++] = c;
746 }
747 relaxed_data[q] = '\0';
748 relaxed_len = q;
749 }
750 canon_data = relaxed_data;
751 canon_len = relaxed_len;
752 }
753
754 /* Make sure we don't exceed the to-be-signed body length */
755 if ( sig->bodylength >= 0
756 && sig->signed_body_bytes + (unsigned long)canon_len > sig->bodylength
757 )
758 canon_len = sig->bodylength - sig->signed_body_bytes;
759
760 if (canon_len > 0)
761 {
762 exim_sha_update(&sig->body_hash, CUS canon_data, canon_len);
763 sig->signed_body_bytes += canon_len;
764 DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
765 }
766
767 sig = sig->next;
768 }
769
770 return PDKIM_OK;
771 }
772
773
774 /* -------------------------------------------------------------------------- */
775
776 static void
777 pdkim_finish_bodyhash(pdkim_ctx *ctx)
778 {
779 pdkim_signature *sig;
780
781 /* Traverse all signatures */
782 for (sig = ctx->sig; sig; sig = sig->next)
783 { /* Finish hashes */
784 blob bh;
785
786 exim_sha_finish(&sig->body_hash, &bh);
787
788 DEBUG(D_acl)
789 {
790 debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
791 "PDKIM [%s] bh computed: ",
792 sig->domain, sig->signed_body_bytes, sig->domain);
793 pdkim_hexprint(CUS bh.data, bh.len);
794 }
795
796 /* SIGNING -------------------------------------------------------------- */
797 if (ctx->mode == PDKIM_MODE_SIGN)
798 {
799 sig->bodyhash = bh;
800
801 /* If bodylength limit is set, and we have received less bytes
802 than the requested amount, effectively remove the limit tag. */
803 if (sig->signed_body_bytes < sig->bodylength)
804 sig->bodylength = -1;
805 }
806
807 /* VERIFICATION --------------------------------------------------------- */
808 else
809 {
810 /* Compare bodyhash */
811 if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
812 {
813 DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
814 }
815 else
816 {
817 DEBUG(D_acl)
818 {
819 debug_printf("PDKIM [%s] bh signature: ", sig->domain);
820 pdkim_hexprint(sig->bodyhash.data,
821 exim_sha_hashlen(&sig->body_hash));
822 debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
823 }
824 sig->verify_status = PDKIM_VERIFY_FAIL;
825 sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
826 }
827 }
828 }
829 }
830
831
832
833 /* -------------------------------------------------------------------------- */
834 /* Callback from pdkim_feed below for processing complete body lines */
835
836 static int
837 pdkim_bodyline_complete(pdkim_ctx *ctx)
838 {
839 char *p = ctx->linebuf;
840 int n = ctx->linebuf_offset;
841 pdkim_signature *sig = ctx->sig; /*XXX assumes only one sig */
842
843 /* Ignore extra data if we've seen the end-of-data marker */
844 if (ctx->seen_eod) goto BAIL;
845
846 /* We've always got one extra byte to stuff a zero ... */
847 ctx->linebuf[ctx->linebuf_offset] = '\0';
848
849 /* Terminate on EOD marker */
850 if (memcmp(p, ".\r\n", 3) == 0)
851 {
852 /* In simple body mode, if any empty lines were buffered,
853 replace with one. rfc 4871 3.4.3 */
854 /*XXX checking the signed-body-bytes is a gross hack; I think
855 it indicates that all linebreaks should be buffered, including
856 the one terminating a text line */
857 if ( sig && sig->canon_body == PDKIM_CANON_SIMPLE
858 && sig->signed_body_bytes == 0
859 && ctx->num_buffered_crlf > 0
860 )
861 pdkim_update_bodyhash(ctx, "\r\n", 2);
862
863 ctx->seen_eod = TRUE;
864 goto BAIL;
865 }
866 /* Unstuff dots */
867 if (memcmp(p, "..", 2) == 0)
868 {
869 p++;
870 n--;
871 }
872
873 /* Empty lines need to be buffered until we find a non-empty line */
874 if (memcmp(p, "\r\n", 2) == 0)
875 {
876 ctx->num_buffered_crlf++;
877 goto BAIL;
878 }
879
880 if (sig && sig->canon_body == PDKIM_CANON_RELAXED)
881 {
882 /* Lines with just spaces need to be buffered too */
883 char *check = p;
884 while (memcmp(check, "\r\n", 2) != 0)
885 {
886 char c = *check;
887
888 if (c != '\t' && c != ' ')
889 goto PROCESS;
890 check++;
891 }
892
893 ctx->num_buffered_crlf++;
894 goto BAIL;
895 }
896
897 PROCESS:
898 /* At this point, we have a non-empty line, so release the buffered ones. */
899 while (ctx->num_buffered_crlf)
900 {
901 pdkim_update_bodyhash(ctx, "\r\n", 2);
902 ctx->num_buffered_crlf--;
903 }
904
905 pdkim_update_bodyhash(ctx, p, n);
906
907 BAIL:
908 ctx->linebuf_offset = 0;
909 return PDKIM_OK;
910 }
911
912
913 /* -------------------------------------------------------------------------- */
914 /* Callback from pdkim_feed below for processing complete headers */
915 #define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
916
917 static int
918 pdkim_header_complete(pdkim_ctx *ctx)
919 {
920 /* Special case: The last header can have an extra \r appended */
921 if ( (ctx->cur_header_len > 1) &&
922 (ctx->cur_header[(ctx->cur_header_len)-1] == '\r') )
923 --ctx->cur_header_len;
924 ctx->cur_header[ctx->cur_header_len] = '\0';
925
926 ctx->num_headers++;
927 if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
928
929 /* SIGNING -------------------------------------------------------------- */
930 if (ctx->mode == PDKIM_MODE_SIGN)
931 {
932 pdkim_signature *sig;
933
934 for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */
935
936 /* Add header to the signed headers list (in reverse order) */
937 sig->headers = pdkim_prepend_stringlist(sig->headers,
938 ctx->cur_header);
939 }
940
941 /* VERIFICATION ----------------------------------------------------------- */
942 /* DKIM-Signature: headers are added to the verification list */
943 if (ctx->mode == PDKIM_MODE_VERIFY)
944 {
945 if (strncasecmp(CCS ctx->cur_header,
946 DKIM_SIGNATURE_HEADERNAME,
947 Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
948 {
949 pdkim_signature *new_sig;
950
951 /* Create and chain new signature block */
952 DEBUG(D_acl) debug_printf(
953 "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
954
955 if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header)))
956 {
957 pdkim_signature *last_sig = ctx->sig;
958 if (!last_sig)
959 ctx->sig = new_sig;
960 else
961 {
962 while (last_sig->next) last_sig = last_sig->next;
963 last_sig->next = new_sig;
964 }
965 }
966 else
967 DEBUG(D_acl) debug_printf(
968 "Error while parsing signature header\n"
969 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
970 }
971
972 /* every other header is stored for signature verification */
973 else
974 ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header);
975 }
976
977 BAIL:
978 *ctx->cur_header = '\0';
979 ctx->cur_header_len = 0; /* leave buffer for reuse */
980 return PDKIM_OK;
981 }
982
983
984
985 /* -------------------------------------------------------------------------- */
986 #define HEADER_BUFFER_FRAG_SIZE 256
987
988 DLLEXPORT int
989 pdkim_feed(pdkim_ctx *ctx, char *data, int len)
990 {
991 int p;
992
993 for (p = 0; p<len; p++)
994 {
995 uschar c = data[p];
996
997 if (ctx->past_headers)
998 {
999 /* Processing body byte */
1000 ctx->linebuf[ctx->linebuf_offset++] = c;
1001 if (c == '\n')
1002 {
1003 int rc = pdkim_bodyline_complete(ctx); /* End of line */
1004 if (rc != PDKIM_OK) return rc;
1005 }
1006 if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1))
1007 return PDKIM_ERR_LONG_LINE;
1008 }
1009 else
1010 {
1011 /* Processing header byte */
1012 if (c != '\r')
1013 {
1014 if (c == '\n')
1015 {
1016 if (ctx->seen_lf)
1017 {
1018 int rc = pdkim_header_complete(ctx); /* Seen last header line */
1019 if (rc != PDKIM_OK) return rc;
1020
1021 ctx->past_headers = TRUE;
1022 ctx->seen_lf = 0;
1023 DEBUG(D_acl) debug_printf(
1024 "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>\n");
1025 continue;
1026 }
1027 else
1028 ctx->seen_lf = TRUE;
1029 }
1030 else if (ctx->seen_lf)
1031 {
1032 if (!(c == '\t' || c == ' '))
1033 {
1034 int rc = pdkim_header_complete(ctx); /* End of header */
1035 if (rc != PDKIM_OK) return rc;
1036 }
1037 ctx->seen_lf = FALSE;
1038 }
1039 }
1040
1041 if (ctx->cur_header_len < PDKIM_MAX_HEADER_LEN)
1042 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
1043 &ctx->cur_header_len, CUS &data[p], 1);
1044 }
1045 }
1046 return PDKIM_OK;
1047 }
1048
1049
1050
1051 /* Extend a grwong header with a continuation-linebreak */
1052 static uschar *
1053 pdkim_hdr_cont(uschar * str, int * size, int * ptr, int * col)
1054 {
1055 *col = 1;
1056 return string_catn(str, size, ptr, US"\r\n\t", 3);
1057 }
1058
1059
1060
1061 /*
1062 * RFC 5322 specifies that header line length SHOULD be no more than 78
1063 * lets make it so!
1064 * pdkim_headcat
1065 *
1066 * returns uschar * (not nul-terminated)
1067 *
1068 * col: this int holds and receives column number (octets since last '\n')
1069 * str: partial string to append to
1070 * size: current buffer size for str
1071 * ptr: current tail-pointer for str
1072 * pad: padding, split line or space after before or after eg: ";"
1073 * intro: - must join to payload eg "h=", usually the tag name
1074 * payload: eg base64 data - long data can be split arbitrarily.
1075 *
1076 * this code doesn't fold the header in some of the places that RFC4871
1077 * allows: As per RFC5322(2.2.3) it only folds before or after tag-value
1078 * pairs and inside long values. it also always spaces or breaks after the
1079 * "pad"
1080 *
1081 * no guarantees are made for output given out-of range input. like tag
1082 * names longer than 78, or bogus col. Input is assumed to be free of line breaks.
1083 */
1084
1085 static uschar *
1086 pdkim_headcat(int * col, uschar * str, int * size, int * ptr,
1087 const uschar * pad, const uschar * intro, const uschar * payload)
1088 {
1089 size_t l;
1090
1091 if (pad)
1092 {
1093 l = Ustrlen(pad);
1094 if (*col + l > 78)
1095 str = pdkim_hdr_cont(str, size, ptr, col);
1096 str = string_catn(str, size, ptr, pad, l);
1097 *col += l;
1098 }
1099
1100 l = (pad?1:0) + (intro?Ustrlen(intro):0);
1101
1102 if (*col + l > 78)
1103 { /*can't fit intro - start a new line to make room.*/
1104 str = pdkim_hdr_cont(str, size, ptr, col);
1105 l = intro?Ustrlen(intro):0;
1106 }
1107
1108 l += payload ? Ustrlen(payload):0 ;
1109
1110 while (l>77)
1111 { /* this fragment will not fit on a single line */
1112 if (pad)
1113 {
1114 str = string_catn(str, size, ptr, US" ", 1);
1115 *col += 1;
1116 pad = NULL; /* only want this once */
1117 l--;
1118 }
1119
1120 if (intro)
1121 {
1122 size_t sl = Ustrlen(intro);
1123
1124 str = string_catn(str, size, ptr, intro, sl);
1125 *col += sl;
1126 l -= sl;
1127 intro = NULL; /* only want this once */
1128 }
1129
1130 if (payload)
1131 {
1132 size_t sl = Ustrlen(payload);
1133 size_t chomp = *col+sl < 77 ? sl : 78-*col;
1134
1135 str = string_catn(str, size, ptr, payload, chomp);
1136 *col += chomp;
1137 payload += chomp;
1138 l -= chomp-1;
1139 }
1140
1141 /* the while precondition tells us it didn't fit. */
1142 str = pdkim_hdr_cont(str, size, ptr, col);
1143 }
1144
1145 if (*col + l > 78)
1146 {
1147 str = pdkim_hdr_cont(str, size, ptr, col);
1148 pad = NULL;
1149 }
1150
1151 if (pad)
1152 {
1153 str = string_catn(str, size, ptr, US" ", 1);
1154 *col += 1;
1155 pad = NULL;
1156 }
1157
1158 if (intro)
1159 {
1160 size_t sl = Ustrlen(intro);
1161
1162 str = string_catn(str, size, ptr, intro, sl);
1163 *col += sl;
1164 l -= sl;
1165 intro = NULL;
1166 }
1167
1168 if (payload)
1169 {
1170 size_t sl = Ustrlen(payload);
1171
1172 str = string_catn(str, size, ptr, payload, sl);
1173 *col += sl;
1174 }
1175
1176 return str;
1177 }
1178
1179
1180 /* -------------------------------------------------------------------------- */
1181
1182 static uschar *
1183 pdkim_create_header(pdkim_signature *sig, BOOL final)
1184 {
1185 uschar * base64_bh;
1186 uschar * base64_b;
1187 int col = 0;
1188 uschar * hdr; int hdr_size = 0, hdr_len = 0;
1189 uschar * canon_all; int can_size = 0, can_len = 0;
1190
1191 canon_all = string_cat (NULL, &can_size, &can_len,
1192 pdkim_canons[sig->canon_headers]);
1193 canon_all = string_catn(canon_all, &can_size, &can_len, US"/", 1);
1194 canon_all = string_cat (canon_all, &can_size, &can_len,
1195 pdkim_canons[sig->canon_body]);
1196 canon_all[can_len] = '\0';
1197
1198 hdr = string_cat(NULL, &hdr_size, &hdr_len,
1199 US"DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
1200 col = hdr_len;
1201
1202 /* Required and static bits */
1203 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
1204 pdkim_algos[sig->algo]);
1205 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
1206 pdkim_querymethods[sig->querymethod]);
1207 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"c=",
1208 canon_all);
1209 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"d=",
1210 sig->domain);
1211 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"s=",
1212 sig->selector);
1213
1214 /* list of header names can be split between items. */
1215 {
1216 uschar * n = string_copy(sig->headernames);
1217 uschar * i = US"h=";
1218 uschar * s = US";";
1219
1220 while (*n)
1221 {
1222 uschar * c = Ustrchr(n, ':');
1223
1224 if (c) *c ='\0';
1225
1226 if (!i)
1227 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, NULL, US":");
1228
1229 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, s, i, n);
1230
1231 if (!c)
1232 break;
1233
1234 n = c+1;
1235 s = NULL;
1236 i = NULL;
1237 }
1238 }
1239
1240 base64_bh = pdkim_encode_base64(&sig->bodyhash);
1241 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"bh=", base64_bh);
1242
1243 /* Optional bits */
1244 if (sig->identity)
1245 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"i=", sig->identity);
1246
1247 if (sig->created > 0)
1248 {
1249 uschar minibuf[20];
1250
1251 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->created);
1252 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"t=", minibuf);
1253 }
1254
1255 if (sig->expires > 0)
1256 {
1257 uschar minibuf[20];
1258
1259 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->expires);
1260 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"x=", minibuf);
1261 }
1262
1263 if (sig->bodylength >= 0)
1264 {
1265 uschar minibuf[20];
1266
1267 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->bodylength);
1268 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"l=", minibuf);
1269 }
1270
1271 /* Preliminary or final version? */
1272 base64_b = final ? pdkim_encode_base64(&sig->sigdata) : US"";
1273 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
1274
1275 /* add trailing semicolon: I'm not sure if this is actually needed */
1276 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US"");
1277
1278 hdr[hdr_len] = '\0';
1279 return hdr;
1280 }
1281
1282
1283 /* -------------------------------------------------------------------------- */
1284
1285 DLLEXPORT int
1286 pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures)
1287 {
1288 pdkim_signature *sig = ctx->sig;
1289 uschar * headernames = NULL; /* Collected signed header names */
1290 int hs = 0, hl = 0;
1291
1292 /* Check if we must still flush a (partial) header. If that is the
1293 case, the message has no body, and we must compute a body hash
1294 out of '<CR><LF>' */
1295 if (ctx->cur_header && ctx->cur_header_len)
1296 {
1297 int rc = pdkim_header_complete(ctx);
1298 if (rc != PDKIM_OK) return rc;
1299 pdkim_update_bodyhash(ctx, "\r\n", 2);
1300 }
1301 else
1302 DEBUG(D_acl) debug_printf(
1303 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1304
1305 /* Build (and/or evaluate) body hash */
1306 pdkim_finish_bodyhash(ctx);
1307
1308 while (sig)
1309 {
1310 BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
1311 hctx hhash_ctx;
1312 uschar * sig_hdr;
1313 blob hhash;
1314 blob hdata;
1315 int hdata_alloc = 0;
1316
1317 hdata.data = NULL;
1318 hdata.len = 0;
1319
1320 exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256);
1321
1322 DEBUG(D_acl) debug_printf(
1323 "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
1324
1325 /* SIGNING ---------------------------------------------------------------- */
1326 /* When signing, walk through our header list and add them to the hash. As we
1327 go, construct a list of the header's names to use for the h= parameter.
1328 Then append to that list any remaining header names for which there was no
1329 header to sign. */
1330
1331 if (ctx->mode == PDKIM_MODE_SIGN)
1332 {
1333 pdkim_stringlist *p;
1334 const uschar * l;
1335 uschar * s;
1336 int sep = 0;
1337
1338 for (p = sig->headers; p; p = p->next)
1339 if (header_name_match(p->value, sig->sign_headers) == PDKIM_OK)
1340 {
1341 uschar * rh;
1342 /* Collect header names (Note: colon presence is guaranteed here) */
1343 uschar * q = Ustrchr(p->value, ':');
1344
1345 headernames = string_catn(headernames, &hs, &hl,
1346 p->value, (q - US p->value) + (p->next ? 1 : 0));
1347
1348 rh = sig->canon_headers == PDKIM_CANON_RELAXED
1349 ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
1350 : string_copy(CUS p->value); /* just copy it for simple canon */
1351
1352 /* Feed header to the hash algorithm */
1353 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
1354
1355 /* Remember headers block for signing (when the library cannot do incremental) */
1356 (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
1357
1358 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1359 }
1360
1361 l = sig->sign_headers;
1362 while((s = string_nextinlist(&l, &sep, NULL, 0)))
1363 if (*s != '_')
1364 { /*SSS string_append_listele() */
1365 if (hl > 0 && headernames[hl-1] != ':')
1366 headernames = string_catn(headernames, &hs, &hl, US":", 1);
1367
1368 headernames = string_cat(headernames, &hs, &hl, s);
1369 }
1370 headernames[hl] = '\0';
1371
1372 /* Copy headernames to signature struct */
1373 sig->headernames = headernames;
1374 headernames = NULL, hs = hl = 0;
1375
1376 /* Create signature header with b= omitted */
1377 sig_hdr = pdkim_create_header(sig, FALSE);
1378 }
1379
1380 /* VERIFICATION ----------------------------------------------------------- */
1381 /* When verifying, walk through the header name list in the h= parameter and
1382 add the headers to the hash in that order. */
1383 else
1384 {
1385 uschar * b = string_copy(sig->headernames);
1386 uschar * p = b;
1387 uschar * q;
1388 pdkim_stringlist * hdrs;
1389
1390 /* clear tags */
1391 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1392 hdrs->tag = 0;
1393
1394 while(1)
1395 {
1396 if ((q = Ustrchr(p, ':')))
1397 *q = '\0';
1398
1399 /*XXX walk the list of headers in same order as received. */
1400 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1401 if ( hdrs->tag == 0
1402 && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0
1403 && (hdrs->value)[Ustrlen(p)] == ':'
1404 )
1405 {
1406 uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
1407 ? pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
1408 : string_copy(CUS hdrs->value); /* just copy it for simple canon */
1409
1410 /* Feed header to the hash algorithm */
1411 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
1412
1413 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1414 hdrs->tag = 1;
1415 break;
1416 }
1417
1418 if (!q) break;
1419 p = q+1;
1420 }
1421
1422 sig_hdr = string_copy(sig->rawsig_no_b_val);
1423 }
1424
1425 DEBUG(D_acl) debug_printf(
1426 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1427
1428 /* Relax header if necessary */
1429 if (sig->canon_headers == PDKIM_CANON_RELAXED)
1430 sig_hdr = pdkim_relax_header(sig_hdr, 0);
1431
1432 DEBUG(D_acl)
1433 {
1434 debug_printf(
1435 "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
1436 pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
1437 debug_printf(
1438 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1439 }
1440
1441 /* Finalize header hash */
1442 exim_sha_update(&hhash_ctx, CUS sig_hdr, Ustrlen(sig_hdr));
1443 exim_sha_finish(&hhash_ctx, &hhash);
1444
1445 DEBUG(D_acl)
1446 {
1447 debug_printf("PDKIM [%s] hh computed: ", sig->domain);
1448 pdkim_hexprint(hhash.data, hhash.len);
1449 }
1450
1451 /* Remember headers block for signing (when the library cannot do incremental) */
1452 if (ctx->mode == PDKIM_MODE_SIGN)
1453 (void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
1454
1455 /* SIGNING ---------------------------------------------------------------- */
1456 if (ctx->mode == PDKIM_MODE_SIGN)
1457 {
1458 es_ctx sctx;
1459 const uschar * errstr;
1460
1461 /* Import private key */
1462 if ((errstr = exim_rsa_signing_init(US sig->rsa_privkey, &sctx)))
1463 {
1464 DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
1465 return PDKIM_ERR_RSA_PRIVKEY;
1466 }
1467
1468 /* Do signing. With OpenSSL we are signing the hash of headers just
1469 calculated, with GnuTLS we have to sign an entire block of headers
1470 (due to available interfaces) and it recalculates the hash internally. */
1471
1472 #if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
1473 hdata = hhash;
1474 #endif
1475
1476 if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
1477 {
1478 DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
1479 return PDKIM_ERR_RSA_SIGNING;
1480 }
1481
1482 DEBUG(D_acl)
1483 {
1484 debug_printf( "PDKIM [%s] b computed: ", sig->domain);
1485 pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
1486 }
1487
1488 sig->signature_header = pdkim_create_header(sig, TRUE);
1489 }
1490
1491 /* VERIFICATION ----------------------------------------------------------- */
1492 else
1493 {
1494 ev_ctx vctx;
1495 const uschar * errstr;
1496
1497 uschar *dns_txt_name, *dns_txt_reply;
1498
1499 /* Make sure we have all required signature tags */
1500 if (!( sig->domain && *sig->domain
1501 && sig->selector && *sig->selector
1502 && sig->headernames && *sig->headernames
1503 && sig->bodyhash.data
1504 && sig->sigdata.data
1505 && sig->algo > -1
1506 && sig->version
1507 ) )
1508 {
1509 sig->verify_status = PDKIM_VERIFY_INVALID;
1510 sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
1511
1512 DEBUG(D_acl) debug_printf(
1513 " Error in DKIM-Signature header: tags missing or invalid\n"
1514 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1515 goto NEXT_VERIFY;
1516 }
1517
1518 /* Make sure sig uses supported DKIM version (only v1) */
1519 if (sig->version != 1)
1520 {
1521 sig->verify_status = PDKIM_VERIFY_INVALID;
1522 sig->verify_ext_status = PDKIM_VERIFY_INVALID_DKIM_VERSION;
1523
1524 DEBUG(D_acl) debug_printf(
1525 " Error in DKIM-Signature header: unsupported DKIM version\n"
1526 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1527 goto NEXT_VERIFY;
1528 }
1529
1530 /* Fetch public key for signing domain, from DNS */
1531
1532 dns_txt_name = string_sprintf("%s._domainkey.%s.",
1533 sig->selector, sig->domain);
1534
1535 dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN);
1536 memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
1537
1538 if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK
1539 || dns_txt_reply[0] == '\0')
1540 {
1541 sig->verify_status = PDKIM_VERIFY_INVALID;
1542 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
1543 goto NEXT_VERIFY;
1544 }
1545
1546 DEBUG(D_acl)
1547 {
1548 debug_printf(
1549 "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
1550 " Raw record: ");
1551 pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
1552 }
1553
1554 if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply)))
1555 {
1556 sig->verify_status = PDKIM_VERIFY_INVALID;
1557 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
1558
1559 DEBUG(D_acl) debug_printf(
1560 " Error while parsing public key record\n"
1561 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1562 goto NEXT_VERIFY;
1563 }
1564
1565 DEBUG(D_acl) debug_printf(
1566 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1567
1568 /* Import public key */
1569 if ((errstr = exim_rsa_verify_init(&sig->pubkey->key, &vctx)))
1570 {
1571 DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
1572 sig->verify_status = PDKIM_VERIFY_INVALID;
1573 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
1574 goto NEXT_VERIFY;
1575 }
1576
1577 /* Check the signature */
1578 if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
1579 {
1580 DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
1581 sig->verify_status = PDKIM_VERIFY_FAIL;
1582 sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
1583 goto NEXT_VERIFY;
1584 }
1585
1586
1587 /* We have a winner! (if bodydhash was correct earlier) */
1588 if (sig->verify_status == PDKIM_VERIFY_NONE)
1589 sig->verify_status = PDKIM_VERIFY_PASS;
1590
1591 NEXT_VERIFY:
1592
1593 DEBUG(D_acl)
1594 {
1595 debug_printf("PDKIM [%s] signature status: %s",
1596 sig->domain, pdkim_verify_status_str(sig->verify_status));
1597 if (sig->verify_ext_status > 0)
1598 debug_printf(" (%s)\n",
1599 pdkim_verify_ext_status_str(sig->verify_ext_status));
1600 else
1601 debug_printf("\n");
1602 }
1603 }
1604
1605 sig = sig->next;
1606 }
1607
1608 /* If requested, set return pointer to signature(s) */
1609 if (return_signatures)
1610 *return_signatures = ctx->sig;
1611
1612 return PDKIM_OK;
1613 }
1614
1615
1616 /* -------------------------------------------------------------------------- */
1617
1618 DLLEXPORT pdkim_ctx *
1619 pdkim_init_verify(int(*dns_txt_callback)(char *, char *))
1620 {
1621 pdkim_ctx * ctx;
1622
1623 ctx = store_get(sizeof(pdkim_ctx));
1624 memset(ctx, 0, sizeof(pdkim_ctx));
1625
1626 ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
1627 ctx->mode = PDKIM_MODE_VERIFY;
1628 ctx->dns_txt_callback = dns_txt_callback;
1629
1630 return ctx;
1631 }
1632
1633
1634 /* -------------------------------------------------------------------------- */
1635
1636 DLLEXPORT pdkim_ctx *
1637 pdkim_init_sign(char *domain, char *selector, char *rsa_privkey, int algo)
1638 {
1639 pdkim_ctx *ctx;
1640 pdkim_signature *sig;
1641
1642 if (!domain || !selector || !rsa_privkey)
1643 return NULL;
1644
1645 ctx = store_get(sizeof(pdkim_ctx));
1646 memset(ctx, 0, sizeof(pdkim_ctx));
1647
1648 ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
1649
1650 sig = store_get(sizeof(pdkim_signature));
1651 memset(sig, 0, sizeof(pdkim_signature));
1652
1653 sig->bodylength = -1;
1654
1655 ctx->mode = PDKIM_MODE_SIGN;
1656 ctx->sig = sig;
1657
1658 sig->domain = string_copy(US domain);
1659 sig->selector = string_copy(US selector);
1660 sig->rsa_privkey = string_copy(US rsa_privkey);
1661 sig->algo = algo;
1662
1663 exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
1664 return ctx;
1665 }
1666
1667
1668 /* -------------------------------------------------------------------------- */
1669
1670 DLLEXPORT int
1671 pdkim_set_optional(pdkim_ctx *ctx,
1672 char *sign_headers,
1673 char *identity,
1674 int canon_headers,
1675 int canon_body,
1676 long bodylength,
1677 unsigned long created,
1678 unsigned long expires)
1679 {
1680 pdkim_signature * sig = ctx->sig;
1681
1682 if (identity)
1683 sig->identity = string_copy(US identity);
1684
1685 sig->sign_headers = string_copy(sign_headers
1686 ? US sign_headers : US PDKIM_DEFAULT_SIGN_HEADERS);
1687
1688 sig->canon_headers = canon_headers;
1689 sig->canon_body = canon_body;
1690 sig->bodylength = bodylength;
1691 sig->created = created;
1692 sig->expires = expires;
1693
1694 return PDKIM_OK;
1695 }
1696
1697
1698 void
1699 pdkim_init(void)
1700 {
1701 exim_rsa_init();
1702 }
1703
1704
1705
1706 #endif /*DISABLE_DKIM*/