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