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