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