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