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