Fix DKIM verify operation in -bh test mode. Bug 2017
[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 {
e2e3255a 962 if (strncasecmp(CCS ctx->cur_header,
3045f050 963 DKIM_SIGNATURE_HEADERNAME,
e2e3255a 964 Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
3045f050
JH
965 {
966 pdkim_signature *new_sig;
80a47a2c 967
3045f050 968 /* Create and chain new signature block */
0d04a285 969 DEBUG(D_acl) debug_printf(
3045f050 970 "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
3045f050 971
ca9cb170 972 if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header)))
3045f050
JH
973 {
974 pdkim_signature *last_sig = ctx->sig;
975 if (!last_sig)
976 ctx->sig = new_sig;
977 else
978 {
979 while (last_sig->next) last_sig = last_sig->next;
980 last_sig->next = new_sig;
981 }
80a47a2c 982 }
abe1010c 983 else
0d04a285
JH
984 DEBUG(D_acl) debug_printf(
985 "Error while parsing signature header\n"
3045f050 986 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 987 }
37f8b554 988
3045f050
JH
989 /* every other header is stored for signature verification */
990 else
ca9cb170 991 ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header);
80a47a2c
TK
992 }
993
3045f050 994BAIL:
ca9cb170
JH
995*ctx->cur_header = '\0';
996ctx->cur_header_len = 0; /* leave buffer for reuse */
3045f050 997return PDKIM_OK;
6ab02e3f 998}
80a47a2c
TK
999
1000
1001
1002/* -------------------------------------------------------------------------- */
1003#define HEADER_BUFFER_FRAG_SIZE 256
3045f050
JH
1004
1005DLLEXPORT int
ca9cb170 1006pdkim_feed(pdkim_ctx *ctx, char *data, int len)
3045f050
JH
1007{
1008int p;
1009
e983e85a
JH
1010/* Alternate EOD signal, used in non-dotstuffing mode */
1011if (!data)
1012 pdkim_body_complete(ctx);
1013
10c50704 1014else for (p = 0; p<len; p++)
3045f050 1015 {
ca9cb170 1016 uschar c = data[p];
3045f050 1017
e983e85a 1018 if (ctx->flags & PDKIM_PAST_HDRS)
3045f050 1019 {
b895f4b2
JH
1020 if (c == '\n' && !(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
1021 {
1022 ctx->linebuf[ctx->linebuf_offset++] = '\r';
1023 if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
1024 return PDKIM_ERR_LONG_LINE;
1025 }
1026
3045f050 1027 /* Processing body byte */
0d04a285 1028 ctx->linebuf[ctx->linebuf_offset++] = c;
b895f4b2
JH
1029 if (c == '\r')
1030 ctx->flags |= PDKIM_SEEN_CR;
1031 else if (c == '\n')
3045f050 1032 {
b895f4b2
JH
1033 int rc;
1034 ctx->flags &= ~PDKIM_SEEN_CR;
1035 if ((rc = pdkim_bodyline_complete(ctx)) != PDKIM_OK)
1036 return rc;
80a47a2c 1037 }
b895f4b2
JH
1038
1039 if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
3045f050 1040 return PDKIM_ERR_LONG_LINE;
80a47a2c 1041 }
3045f050
JH
1042 else
1043 {
1044 /* Processing header byte */
b895f4b2
JH
1045 if (c == '\r')
1046 ctx->flags |= PDKIM_SEEN_CR;
1047 else if (c == '\n')
3045f050 1048 {
b895f4b2
JH
1049 if (!(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
1050 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
1051 &ctx->cur_header_len, CUS "\r", 1);
1052
1053 if (ctx->flags & PDKIM_SEEN_LF)
1054 {
1055 int rc = pdkim_header_complete(ctx); /* Seen last header line */
1056 if (rc != PDKIM_OK) return rc;
1057
1058 ctx->flags = ctx->flags & ~(PDKIM_SEEN_LF|PDKIM_SEEN_CR) | PDKIM_PAST_HDRS;
1059 DEBUG(D_acl) debug_printf(
1060 "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>\n");
1061 continue;
3045f050 1062 }
b895f4b2
JH
1063 else
1064 ctx->flags = ctx->flags & ~PDKIM_SEEN_CR | PDKIM_SEEN_LF;
1065 }
1066 else if (ctx->flags & PDKIM_SEEN_LF)
1067 {
1068 if (!(c == '\t' || c == ' '))
1069 {
1070 int rc = pdkim_header_complete(ctx); /* End of header */
1071 if (rc != PDKIM_OK) return rc;
3045f050 1072 }
b895f4b2 1073 ctx->flags &= ~PDKIM_SEEN_LF;
80a47a2c 1074 }
3045f050 1075
ca9cb170 1076 if (ctx->cur_header_len < PDKIM_MAX_HEADER_LEN)
c2f669a4 1077 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
e2e3255a 1078 &ctx->cur_header_len, CUS &data[p], 1);
80a47a2c
TK
1079 }
1080 }
3045f050 1081return PDKIM_OK;
6ab02e3f 1082}
80a47a2c 1083
ca9cb170
JH
1084
1085
1086/* Extend a grwong header with a continuation-linebreak */
1087static uschar *
1088pdkim_hdr_cont(uschar * str, int * size, int * ptr, int * col)
1089{
1090*col = 1;
c2f669a4 1091return string_catn(str, size, ptr, US"\r\n\t", 3);
ca9cb170
JH
1092}
1093
1094
1095
05b7d6de
JB
1096/*
1097 * RFC 5322 specifies that header line length SHOULD be no more than 78
1098 * lets make it so!
1099 * pdkim_headcat
ca9cb170
JH
1100 *
1101 * returns uschar * (not nul-terminated)
05b7d6de
JB
1102 *
1103 * col: this int holds and receives column number (octets since last '\n')
1104 * str: partial string to append to
ca9cb170
JH
1105 * size: current buffer size for str
1106 * ptr: current tail-pointer for str
94431adb 1107 * pad: padding, split line or space after before or after eg: ";"
05b7d6de
JB
1108 * intro: - must join to payload eg "h=", usually the tag name
1109 * payload: eg base64 data - long data can be split arbitrarily.
1110 *
1111 * this code doesn't fold the header in some of the places that RFC4871
1112 * allows: As per RFC5322(2.2.3) it only folds before or after tag-value
1113 * pairs and inside long values. it also always spaces or breaks after the
94431adb 1114 * "pad"
05b7d6de
JB
1115 *
1116 * no guarantees are made for output given out-of range input. like tag
f444c2c7 1117 * names longer than 78, or bogus col. Input is assumed to be free of line breaks.
05b7d6de
JB
1118 */
1119
ca9cb170
JH
1120static uschar *
1121pdkim_headcat(int * col, uschar * str, int * size, int * ptr,
1122 const uschar * pad, const uschar * intro, const uschar * payload)
3045f050
JH
1123{
1124size_t l;
1125
1126if (pad)
05b7d6de 1127 {
ca9cb170 1128 l = Ustrlen(pad);
3045f050 1129 if (*col + l > 78)
ca9cb170 1130 str = pdkim_hdr_cont(str, size, ptr, col);
c2f669a4 1131 str = string_catn(str, size, ptr, pad, l);
3045f050 1132 *col += l;
05b7d6de
JB
1133 }
1134
ca9cb170 1135l = (pad?1:0) + (intro?Ustrlen(intro):0);
05b7d6de 1136
3045f050 1137if (*col + l > 78)
05b7d6de 1138 { /*can't fit intro - start a new line to make room.*/
ca9cb170
JH
1139 str = pdkim_hdr_cont(str, size, ptr, col);
1140 l = intro?Ustrlen(intro):0;
05b7d6de
JB
1141 }
1142
ca9cb170 1143l += payload ? Ustrlen(payload):0 ;
05b7d6de 1144
3045f050 1145while (l>77)
05b7d6de 1146 { /* this fragment will not fit on a single line */
3045f050 1147 if (pad)
05b7d6de 1148 {
c2f669a4 1149 str = string_catn(str, size, ptr, US" ", 1);
3045f050
JH
1150 *col += 1;
1151 pad = NULL; /* only want this once */
1152 l--;
05b7d6de 1153 }
3045f050
JH
1154
1155 if (intro)
05b7d6de 1156 {
ca9cb170 1157 size_t sl = Ustrlen(intro);
3045f050 1158
c2f669a4 1159 str = string_catn(str, size, ptr, intro, sl);
3045f050
JH
1160 *col += sl;
1161 l -= sl;
1162 intro = NULL; /* only want this once */
05b7d6de 1163 }
3045f050
JH
1164
1165 if (payload)
05b7d6de 1166 {
ca9cb170 1167 size_t sl = Ustrlen(payload);
3045f050
JH
1168 size_t chomp = *col+sl < 77 ? sl : 78-*col;
1169
c2f669a4 1170 str = string_catn(str, size, ptr, payload, chomp);
3045f050
JH
1171 *col += chomp;
1172 payload += chomp;
1173 l -= chomp-1;
05b7d6de 1174 }
3045f050
JH
1175
1176 /* the while precondition tells us it didn't fit. */
ca9cb170 1177 str = pdkim_hdr_cont(str, size, ptr, col);
05b7d6de 1178 }
3045f050
JH
1179
1180if (*col + l > 78)
05b7d6de 1181 {
ca9cb170 1182 str = pdkim_hdr_cont(str, size, ptr, col);
3045f050 1183 pad = NULL;
05b7d6de
JB
1184 }
1185
3045f050 1186if (pad)
05b7d6de 1187 {
c2f669a4 1188 str = string_catn(str, size, ptr, US" ", 1);
3045f050
JH
1189 *col += 1;
1190 pad = NULL;
05b7d6de
JB
1191 }
1192
3045f050 1193if (intro)
05b7d6de 1194 {
ca9cb170 1195 size_t sl = Ustrlen(intro);
3045f050 1196
c2f669a4 1197 str = string_catn(str, size, ptr, intro, sl);
3045f050
JH
1198 *col += sl;
1199 l -= sl;
1200 intro = NULL;
05b7d6de 1201 }
3045f050
JH
1202
1203if (payload)
05b7d6de 1204 {
ca9cb170 1205 size_t sl = Ustrlen(payload);
3045f050 1206
c2f669a4 1207 str = string_catn(str, size, ptr, payload, sl);
3045f050 1208 *col += sl;
05b7d6de
JB
1209 }
1210
ca9cb170 1211return str;
05b7d6de 1212}
80a47a2c 1213
3045f050 1214
80a47a2c 1215/* -------------------------------------------------------------------------- */
3045f050 1216
ca9cb170 1217static uschar *
2592e6c0 1218pdkim_create_header(pdkim_signature *sig, BOOL final)
3045f050 1219{
ca9cb170
JH
1220uschar * base64_bh;
1221uschar * base64_b;
3045f050 1222int col = 0;
ca9cb170
JH
1223uschar * hdr; int hdr_size = 0, hdr_len = 0;
1224uschar * canon_all; int can_size = 0, can_len = 0;
3045f050 1225
c2f669a4
JH
1226canon_all = string_cat (NULL, &can_size, &can_len,
1227 pdkim_canons[sig->canon_headers]);
1228canon_all = string_catn(canon_all, &can_size, &can_len, US"/", 1);
1229canon_all = string_cat (canon_all, &can_size, &can_len,
1230 pdkim_canons[sig->canon_body]);
ca9cb170 1231canon_all[can_len] = '\0';
3045f050 1232
ca9cb170 1233hdr = string_cat(NULL, &hdr_size, &hdr_len,
e2e3255a 1234 US"DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
ca9cb170 1235col = hdr_len;
3045f050
JH
1236
1237/* Required and static bits */
ca9cb170
JH
1238hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
1239 pdkim_algos[sig->algo]);
1240hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
1241 pdkim_querymethods[sig->querymethod]);
1242hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"c=",
1243 canon_all);
1244hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"d=",
1245 sig->domain);
1246hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"s=",
1247 sig->selector);
1248
1249/* list of header names can be split between items. */
3045f050 1250 {
e2e3255a 1251 uschar * n = string_copy(sig->headernames);
ca9cb170
JH
1252 uschar * i = US"h=";
1253 uschar * s = US";";
1254
1255 while (*n)
05b7d6de 1256 {
ca9cb170 1257 uschar * c = Ustrchr(n, ':');
3045f050 1258
ca9cb170 1259 if (c) *c ='\0';
05b7d6de 1260
ca9cb170
JH
1261 if (!i)
1262 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, NULL, US":");
05b7d6de 1263
ca9cb170 1264 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, s, i, n);
3045f050 1265
ca9cb170
JH
1266 if (!c)
1267 break;
3045f050 1268
ca9cb170
JH
1269 n = c+1;
1270 s = NULL;
1271 i = NULL;
80a47a2c 1272 }
ca9cb170 1273 }
05b7d6de 1274
ca9cb170
JH
1275base64_bh = pdkim_encode_base64(&sig->bodyhash);
1276hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"bh=", base64_bh);
3045f050 1277
ca9cb170
JH
1278/* Optional bits */
1279if (sig->identity)
1280 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"i=", sig->identity);
3045f050 1281
ca9cb170
JH
1282if (sig->created > 0)
1283 {
e2e3255a 1284 uschar minibuf[20];
3045f050 1285
e2e3255a 1286 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->created);
ca9cb170
JH
1287 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"t=", minibuf);
1288}
3045f050 1289
ca9cb170
JH
1290if (sig->expires > 0)
1291 {
e2e3255a 1292 uschar minibuf[20];
3045f050 1293
e2e3255a 1294 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->expires);
ca9cb170
JH
1295 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"x=", minibuf);
1296 }
3045f050 1297
ca9cb170
JH
1298if (sig->bodylength >= 0)
1299 {
e2e3255a 1300 uschar minibuf[20];
80a47a2c 1301
e2e3255a 1302 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->bodylength);
ca9cb170 1303 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"l=", minibuf);
3045f050 1304 }
05b7d6de 1305
ca9cb170
JH
1306/* Preliminary or final version? */
1307base64_b = final ? pdkim_encode_base64(&sig->sigdata) : US"";
1308hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
80a47a2c 1309
ca9cb170
JH
1310/* add trailing semicolon: I'm not sure if this is actually needed */
1311hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US"");
80a47a2c 1312
ca9cb170
JH
1313hdr[hdr_len] = '\0';
1314return hdr;
80a47a2c
TK
1315}
1316
1317
cd1a5fe0
JH
1318/* -------------------------------------------------------------------------- */
1319
1320static pdkim_pubkey *
1321pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx)
1322{
1323uschar * dns_txt_name, * dns_txt_reply;
1324pdkim_pubkey * p;
1325const uschar * errstr;
1326
1327/* Fetch public key for signing domain, from DNS */
1328
1329dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain);
1330
1331dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN);
1332memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
1333
1334if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK
1335 || dns_txt_reply[0] == '\0'
1336 )
1337 {
1338 sig->verify_status = PDKIM_VERIFY_INVALID;
1339 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
1340 return NULL;
1341 }
1342
1343DEBUG(D_acl)
1344 {
1345 debug_printf(
1346 "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
1347 " Raw record: ");
1348 pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
1349 }
1350
1351if ( !(p = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply))
1352 || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
1353 )
1354 {
1355 sig->verify_status = PDKIM_VERIFY_INVALID;
1356 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
1357
1358 DEBUG(D_acl)
1359 {
1360 if (p)
1361 debug_printf(" Invalid public key service type '%s'\n", p->srvtype);
1362 else
1363 debug_printf(" Error while parsing public key record\n");
1364 debug_printf(
1365 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1366 }
1367 return NULL;
1368 }
1369
1370DEBUG(D_acl) debug_printf(
1371 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1372
1373/* Import public key */
1374if ((errstr = exim_rsa_verify_init(&p->key, vctx)))
1375 {
1376 DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
1377 sig->verify_status = PDKIM_VERIFY_INVALID;
1378 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
1379 return NULL;
1380 }
1381
1382return p;
1383}
1384
1385
80a47a2c 1386/* -------------------------------------------------------------------------- */
3045f050
JH
1387
1388DLLEXPORT int
1389pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures)
1390{
1391pdkim_signature *sig = ctx->sig;
3045f050
JH
1392
1393/* Check if we must still flush a (partial) header. If that is the
1394 case, the message has no body, and we must compute a body hash
1395 out of '<CR><LF>' */
ca9cb170 1396if (ctx->cur_header && ctx->cur_header_len)
3045f050
JH
1397 {
1398 int rc = pdkim_header_complete(ctx);
1399 if (rc != PDKIM_OK) return rc;
1400 pdkim_update_bodyhash(ctx, "\r\n", 2);
80a47a2c 1401 }
3045f050 1402else
0d04a285 1403 DEBUG(D_acl) debug_printf(
3045f050 1404 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1405
3045f050 1406/* Build (and/or evaluate) body hash */
ca9cb170 1407pdkim_finish_bodyhash(ctx);
80a47a2c 1408
3045f050
JH
1409while (sig)
1410 {
2592e6c0
JH
1411 BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
1412 hctx hhash_ctx;
ad6f5499 1413 uschar * sig_hdr = US"";
2592e6c0
JH
1414 blob hhash;
1415 blob hdata;
cb224393 1416 int hdata_alloc = 0;
cb224393 1417
2592e6c0
JH
1418 hdata.data = NULL;
1419 hdata.len = 0;
1420
1ed59855 1421 exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256);
80a47a2c 1422
0d04a285 1423 DEBUG(D_acl) debug_printf(
e21a4d00 1424 "PDKIM >> Header data for hash, canonicalized, in sequence >>>>>>>>>>>>>>\n");
3045f050
JH
1425
1426 /* SIGNING ---------------------------------------------------------------- */
1427 /* When signing, walk through our header list and add them to the hash. As we
8ef02a06
JH
1428 go, construct a list of the header's names to use for the h= parameter.
1429 Then append to that list any remaining header names for which there was no
1430 header to sign. */
3045f050 1431
e983e85a 1432 if (ctx->flags & PDKIM_MODE_SIGN)
3045f050 1433 {
10c50704
JH
1434 uschar * headernames = NULL; /* Collected signed header names */
1435 int hs = 0, hl = 0;
3045f050 1436 pdkim_stringlist *p;
8ef02a06
JH
1437 const uschar * l;
1438 uschar * s;
1439 int sep = 0;
3045f050
JH
1440
1441 for (p = sig->headers; p; p = p->next)
ab9152ff
JH
1442 if (header_name_match(p->value, sig->sign_headers) == PDKIM_OK)
1443 {
1444 uschar * rh;
1445 /* Collect header names (Note: colon presence is guaranteed here) */
1446 uschar * q = Ustrchr(p->value, ':');
3045f050 1447
c2f669a4 1448 headernames = string_catn(headernames, &hs, &hl,
ca9cb170 1449 p->value, (q - US p->value) + (p->next ? 1 : 0));
3045f050 1450
ab9152ff 1451 rh = sig->canon_headers == PDKIM_CANON_RELAXED
ca9cb170
JH
1452 ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
1453 : string_copy(CUS p->value); /* just copy it for simple canon */
3045f050 1454
ab9152ff 1455 /* Feed header to the hash algorithm */
e2e3255a 1456 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
f444c2c7 1457
ab9152ff
JH
1458 /* Remember headers block for signing (when the library cannot do incremental) */
1459 (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
3045f050 1460
ab9152ff
JH
1461 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1462 }
8ef02a06 1463
ca9cb170 1464 l = sig->sign_headers;
8ef02a06
JH
1465 while((s = string_nextinlist(&l, &sep, NULL, 0)))
1466 if (*s != '_')
ab9152ff 1467 { /*SSS string_append_listele() */
ca9cb170 1468 if (hl > 0 && headernames[hl-1] != ':')
c2f669a4 1469 headernames = string_catn(headernames, &hs, &hl, US":", 1);
ca9cb170 1470
c2f669a4 1471 headernames = string_cat(headernames, &hs, &hl, s);
8ef02a06 1472 }
ca9cb170
JH
1473 headernames[hl] = '\0';
1474
1475 /* Copy headernames to signature struct */
1476 sig->headernames = headernames;
ca9cb170
JH
1477
1478 /* Create signature header with b= omitted */
1479 sig_hdr = pdkim_create_header(sig, FALSE);
80a47a2c 1480 }
37f8b554 1481
3045f050
JH
1482 /* VERIFICATION ----------------------------------------------------------- */
1483 /* When verifying, walk through the header name list in the h= parameter and
1484 add the headers to the hash in that order. */
1485 else
1486 {
10c50704 1487 uschar * p = sig->headernames;
2592e6c0
JH
1488 uschar * q;
1489 pdkim_stringlist * hdrs;
3045f050 1490
10c50704 1491 if (p)
3045f050 1492 {
10c50704 1493 /* clear tags */
3045f050 1494 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
10c50704 1495 hdrs->tag = 0;
3045f050 1496
10c50704
JH
1497 p = string_copy(p);
1498 while(1)
1499 {
1500 if ((q = Ustrchr(p, ':')))
1501 *q = '\0';
1502
1503 /*XXX walk the list of headers in same order as received. */
1504 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1505 if ( hdrs->tag == 0
4dc2379a 1506 && strncasecmp(CCS hdrs->value, CCS p, Ustrlen(p)) == 0
10c50704
JH
1507 && (hdrs->value)[Ustrlen(p)] == ':'
1508 )
1509 {
1510 /* cook header for relaxed canon, or just copy it for simple */
1511
1512 uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
1513 ? pdkim_relax_header(hdrs->value, 1)
1514 : string_copy(CUS hdrs->value);
1515
1516 /* Feed header to the hash algorithm */
1517 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
1518
1519 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1520 hdrs->tag = 1;
1521 break;
1522 }
3045f050 1523
10c50704
JH
1524 if (!q) break;
1525 p = q+1;
1526 }
3045f050 1527
10c50704 1528 sig_hdr = string_copy(sig->rawsig_no_b_val);
80a47a2c 1529 }
80a47a2c
TK
1530 }
1531
0d04a285 1532 DEBUG(D_acl) debug_printf(
3045f050 1533 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1534
3045f050
JH
1535 /* Relax header if necessary */
1536 if (sig->canon_headers == PDKIM_CANON_RELAXED)
ca9cb170 1537 sig_hdr = pdkim_relax_header(sig_hdr, 0);
80a47a2c 1538
0d04a285 1539 DEBUG(D_acl)
3045f050 1540 {
0d04a285 1541 debug_printf(
3045f050 1542 "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
e2e3255a 1543 pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
0d04a285 1544 debug_printf(
3045f050 1545 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1546 }
3045f050
JH
1547
1548 /* Finalize header hash */
e2e3255a 1549 exim_sha_update(&hhash_ctx, CUS sig_hdr, Ustrlen(sig_hdr));
2592e6c0 1550 exim_sha_finish(&hhash_ctx, &hhash);
3045f050 1551
f444c2c7
JH
1552 DEBUG(D_acl)
1553 {
e21a4d00 1554 debug_printf("PDKIM [%s] Header hash computed: ", sig->domain);
2592e6c0 1555 pdkim_hexprint(hhash.data, hhash.len);
80a47a2c
TK
1556 }
1557
2592e6c0 1558 /* Remember headers block for signing (when the library cannot do incremental) */
e983e85a 1559 if (ctx->flags & PDKIM_MODE_SIGN)
b78006ac 1560 (void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
f444c2c7 1561
3045f050 1562 /* SIGNING ---------------------------------------------------------------- */
e983e85a 1563 if (ctx->flags & PDKIM_MODE_SIGN)
3045f050 1564 {
2592e6c0
JH
1565 es_ctx sctx;
1566 const uschar * errstr;
f444c2c7
JH
1567
1568 /* Import private key */
b78006ac 1569 if ((errstr = exim_rsa_signing_init(US sig->rsa_privkey, &sctx)))
f444c2c7 1570 {
2592e6c0 1571 DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
f444c2c7
JH
1572 return PDKIM_ERR_RSA_PRIVKEY;
1573 }
80a47a2c 1574
2592e6c0
JH
1575 /* Do signing. With OpenSSL we are signing the hash of headers just
1576 calculated, with GnuTLS we have to sign an entire block of headers
1577 (due to available interfaces) and it recalculates the hash internally. */
f444c2c7 1578
2592e6c0
JH
1579#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
1580 hdata = hhash;
f444c2c7
JH
1581#endif
1582
2592e6c0 1583 if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
f444c2c7 1584 {
2592e6c0 1585 DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
f444c2c7
JH
1586 return PDKIM_ERR_RSA_SIGNING;
1587 }
80a47a2c 1588
0d04a285 1589 DEBUG(D_acl)
3045f050 1590 {
0d04a285 1591 debug_printf( "PDKIM [%s] b computed: ", sig->domain);
2592e6c0 1592 pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
80a47a2c 1593 }
80a47a2c 1594
ca9cb170 1595 sig->signature_header = pdkim_create_header(sig, TRUE);
80a47a2c 1596 }
80a47a2c 1597
3045f050
JH
1598 /* VERIFICATION ----------------------------------------------------------- */
1599 else
1600 {
2592e6c0
JH
1601 ev_ctx vctx;
1602 const uschar * errstr;
e21a4d00 1603 pdkim_pubkey * p;
3045f050 1604
07eeb4df 1605 /* Make sure we have all required signature tags */
1606 if (!( sig->domain && *sig->domain
1607 && sig->selector && *sig->selector
1608 && sig->headernames && *sig->headernames
1609 && sig->bodyhash.data
1610 && sig->sigdata.data
1611 && sig->algo > -1
1612 && sig->version
1613 ) )
1614 {
1615 sig->verify_status = PDKIM_VERIFY_INVALID;
1616 sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
1617
1618 DEBUG(D_acl) debug_printf(
1619 " Error in DKIM-Signature header: tags missing or invalid\n"
1620 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1621 goto NEXT_VERIFY;
1622 }
1623
1624 /* Make sure sig uses supported DKIM version (only v1) */
1625 if (sig->version != 1)
1626 {
1627 sig->verify_status = PDKIM_VERIFY_INVALID;
1628 sig->verify_ext_status = PDKIM_VERIFY_INVALID_DKIM_VERSION;
1629
1630 DEBUG(D_acl) debug_printf(
1631 " Error in DKIM-Signature header: unsupported DKIM version\n"
1632 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1633 goto NEXT_VERIFY;
1634 }
1635
cd1a5fe0 1636 if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx)))
3045f050 1637 goto NEXT_VERIFY;
80a47a2c 1638
3045f050 1639 /* Check the signature */
2592e6c0 1640 if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
3045f050 1641 {
2592e6c0 1642 DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
3045f050
JH
1643 sig->verify_status = PDKIM_VERIFY_FAIL;
1644 sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
1645 goto NEXT_VERIFY;
ff7ddfd7
TK
1646 }
1647
2592e6c0 1648
4c04137d 1649 /* We have a winner! (if bodyhash was correct earlier) */
3045f050
JH
1650 if (sig->verify_status == PDKIM_VERIFY_NONE)
1651 sig->verify_status = PDKIM_VERIFY_PASS;
1652
1653NEXT_VERIFY:
1654
0d04a285 1655 DEBUG(D_acl)
3045f050 1656 {
0d04a285 1657 debug_printf("PDKIM [%s] signature status: %s",
3045f050
JH
1658 sig->domain, pdkim_verify_status_str(sig->verify_status));
1659 if (sig->verify_ext_status > 0)
0d04a285 1660 debug_printf(" (%s)\n",
3045f050
JH
1661 pdkim_verify_ext_status_str(sig->verify_ext_status));
1662 else
0d04a285 1663 debug_printf("\n");
80a47a2c 1664 }
80a47a2c
TK
1665 }
1666
3045f050 1667 sig = sig->next;
80a47a2c
TK
1668 }
1669
3045f050
JH
1670/* If requested, set return pointer to signature(s) */
1671if (return_signatures)
1672 *return_signatures = ctx->sig;
80a47a2c 1673
3045f050 1674return PDKIM_OK;
80a47a2c
TK
1675}
1676
1677
1678/* -------------------------------------------------------------------------- */
3045f050
JH
1679
1680DLLEXPORT pdkim_ctx *
e983e85a 1681pdkim_init_verify(int(*dns_txt_callback)(char *, char *), BOOL dot_stuffing)
3045f050 1682{
ca9cb170 1683pdkim_ctx * ctx;
3045f050 1684
ca9cb170 1685ctx = store_get(sizeof(pdkim_ctx));
abe1010c 1686memset(ctx, 0, sizeof(pdkim_ctx));
3045f050 1687
e983e85a 1688if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM;
ca9cb170 1689ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
3045f050 1690ctx->dns_txt_callback = dns_txt_callback;
80a47a2c 1691
3045f050 1692return ctx;
80a47a2c
TK
1693}
1694
1695
1696/* -------------------------------------------------------------------------- */
80a47a2c 1697
3045f050 1698DLLEXPORT pdkim_ctx *
cd1a5fe0
JH
1699pdkim_init_sign(char * domain, char * selector, char * rsa_privkey, int algo,
1700 BOOL dot_stuffed, int(*dns_txt_callback)(char *, char *))
3045f050 1701{
cd1a5fe0
JH
1702pdkim_ctx * ctx;
1703pdkim_signature * sig;
80a47a2c 1704
3045f050
JH
1705if (!domain || !selector || !rsa_privkey)
1706 return NULL;
80a47a2c 1707
cd1a5fe0 1708ctx = store_get(sizeof(pdkim_ctx) + PDKIM_MAX_BODY_LINE_LEN + sizeof(pdkim_signature));
abe1010c 1709memset(ctx, 0, sizeof(pdkim_ctx));
80a47a2c 1710
e983e85a 1711ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;
cd1a5fe0 1712ctx->linebuf = CS (ctx+1);
80a47a2c 1713
cd1a5fe0
JH
1714DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback;
1715
1716sig = (pdkim_signature *)(ctx->linebuf + PDKIM_MAX_BODY_LINE_LEN);
abe1010c 1717memset(sig, 0, sizeof(pdkim_signature));
80a47a2c 1718
3045f050 1719sig->bodylength = -1;
3045f050 1720ctx->sig = sig;
80a47a2c 1721
e2e3255a
JH
1722sig->domain = string_copy(US domain);
1723sig->selector = string_copy(US selector);
1724sig->rsa_privkey = string_copy(US rsa_privkey);
2592e6c0 1725sig->algo = algo;
cb224393 1726
1ed59855 1727exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
cd1a5fe0
JH
1728
1729DEBUG(D_acl)
1730 {
1731 pdkim_signature s = *sig;
1732 ev_ctx vctx;
1733
1734 debug_printf("PDKIM (checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1735 if (!pdkim_key_from_dns(ctx, &s, &vctx))
1736 debug_printf("WARNING: bad dkim key in dns\n");
1737 debug_printf("PDKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1738 }
1739
3045f050 1740return ctx;
6ab02e3f 1741}
80a47a2c 1742
f444c2c7 1743
80a47a2c 1744/* -------------------------------------------------------------------------- */
3045f050
JH
1745
1746DLLEXPORT int
1747pdkim_set_optional(pdkim_ctx *ctx,
80a47a2c
TK
1748 char *sign_headers,
1749 char *identity,
1750 int canon_headers,
1751 int canon_body,
1752 long bodylength,
80a47a2c 1753 unsigned long created,
3045f050
JH
1754 unsigned long expires)
1755{
8ef02a06
JH
1756pdkim_signature * sig = ctx->sig;
1757
3045f050 1758if (identity)
e2e3255a 1759 sig->identity = string_copy(US identity);
80a47a2c 1760
ca9cb170
JH
1761sig->sign_headers = string_copy(sign_headers
1762 ? US sign_headers : US PDKIM_DEFAULT_SIGN_HEADERS);
80a47a2c 1763
8ef02a06
JH
1764sig->canon_headers = canon_headers;
1765sig->canon_body = canon_body;
1766sig->bodylength = bodylength;
1767sig->created = created;
1768sig->expires = expires;
80a47a2c 1769
3045f050 1770return PDKIM_OK;
6ab02e3f 1771}
3045f050 1772
3045f050 1773
2592e6c0
JH
1774void
1775pdkim_init(void)
1776{
1777exim_rsa_init();
1778}
1779
1780
1781
f444c2c7 1782#endif /*DISABLE_DKIM*/