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