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