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