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