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