Merge branch 'fakereject'
[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 232
80a47a2c 233/* -------------------------------------------------------------------------- */
3045f050
JH
234
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
JH
605for (p = raw_record; ; p++)
606 {
607 char c = *p;
80a47a2c 608
3045f050
JH
609 /* Ignore FWS */
610 if (c == '\r' || c == '\n')
611 goto NEXT_CHAR;
80a47a2c 612
3045f050
JH
613 if (where == PDKIM_HDR_LIMBO)
614 {
615 /* In limbo, just wait for a tag-char to appear */
616 if (!(c >= 'a' && c <= 'z'))
617 goto NEXT_CHAR;
80a47a2c 618
3045f050 619 where = PDKIM_HDR_TAG;
80a47a2c
TK
620 }
621
3045f050
JH
622 if (where == PDKIM_HDR_TAG)
623 {
3045f050 624 if (c >= 'a' && c <= 'z')
c2f669a4 625 cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
80a47a2c 626
3045f050
JH
627 if (c == '=')
628 {
ca9cb170 629 cur_tag[tl] = '\0';
3045f050
JH
630 where = PDKIM_HDR_VALUE;
631 goto NEXT_CHAR;
80a47a2c
TK
632 }
633 }
634
3045f050
JH
635 if (where == PDKIM_HDR_VALUE)
636 {
3045f050
JH
637 if (c == ';' || c == '\0')
638 {
ca9cb170 639 if (tl && vl)
3045f050 640 {
ca9cb170 641 cur_val[vl] = '\0';
3045f050 642 pdkim_strtrim(cur_val);
ca9cb170 643 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val);
3045f050 644
ca9cb170 645 switch (cur_tag[0])
3045f050
JH
646 {
647 case 'v':
648 /* This tag isn't evaluated because:
649 - We only support version DKIM1.
650 - Which is the default for this value (set below)
651 - Other versions are currently not specified. */
652 break;
653 case 'h':
ca9cb170
JH
654 case 'k':
655 pub->hashes = string_copy(cur_val); break;
3045f050 656 case 'g':
ca9cb170 657 pub->granularity = string_copy(cur_val); break;
3045f050 658 case 'n':
ca9cb170 659 pub->notes = pdkim_decode_qp(cur_val); break;
3045f050 660 case 'p':
ca9cb170 661 pdkim_decode_base64(US cur_val, &pub->key);
2592e6c0 662 break;
3045f050 663 case 's':
ca9cb170 664 pub->srvtype = string_copy(cur_val); break;
3045f050 665 case 't':
e2e3255a
JH
666 if (Ustrchr(cur_val, 'y') != NULL) pub->testing = 1;
667 if (Ustrchr(cur_val, 's') != NULL) pub->no_subdomaining = 1;
3045f050
JH
668 break;
669 default:
0d04a285 670 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
3045f050
JH
671 break;
672 }
673 }
ca9cb170
JH
674 tl = 0;
675 vl = 0;
3045f050 676 where = PDKIM_HDR_LIMBO;
80a47a2c 677 }
3045f050 678 else
c2f669a4 679 cur_val = string_catn(cur_val, &vs, &vl, p, 1);
80a47a2c
TK
680 }
681
3045f050
JH
682NEXT_CHAR:
683 if (c == '\0') break;
80a47a2c
TK
684 }
685
3045f050 686/* Set fallback defaults */
ca9cb170
JH
687if (!pub->version ) pub->version = string_copy(PDKIM_PUB_RECORD_VERSION);
688if (!pub->granularity) pub->granularity = string_copy(US"*");
e2e3255a 689if (!pub->keytype ) pub->keytype = string_copy(US"rsa");
ca9cb170 690if (!pub->srvtype ) pub->srvtype = string_copy(US"*");
80a47a2c 691
3045f050 692/* p= is required */
2592e6c0 693if (pub->key.data)
80a47a2c 694 return pub;
3045f050 695
3045f050 696return NULL;
80a47a2c
TK
697}
698
699
700/* -------------------------------------------------------------------------- */
3045f050 701
f444c2c7 702static int
3045f050
JH
703pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len)
704{
705pdkim_signature *sig = ctx->sig;
706/* Cache relaxed version of data */
b78006ac
JH
707uschar *relaxed_data = NULL;
708int relaxed_len = 0;
3045f050
JH
709
710/* Traverse all signatures, updating their hashes. */
711while (sig)
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
ca9cb170 726 relaxed_data = store_get(len+1);
3045f050
JH
727
728 for (p = data; *p; p++)
729 {
730 char c = *p;
731 if (c == '\r')
732 {
733 if (q > 0 && relaxed_data[q-1] == ' ')
734 q--;
735 }
736 else if (c == '\t' || c == ' ')
737 {
738 c = ' '; /* Turns WSP into SP */
739 if (seen_wsp)
740 continue;
741 seen_wsp = TRUE;
742 }
743 else
744 seen_wsp = FALSE;
745 relaxed_data[q++] = c;
746 }
747 relaxed_data[q] = '\0';
748 relaxed_len = q;
80a47a2c 749 }
3045f050
JH
750 canon_data = relaxed_data;
751 canon_len = relaxed_len;
80a47a2c
TK
752 }
753
3045f050
JH
754 /* Make sure we don't exceed the to-be-signed body length */
755 if ( sig->bodylength >= 0
756 && sig->signed_body_bytes + (unsigned long)canon_len > sig->bodylength
757 )
758 canon_len = sig->bodylength - sig->signed_body_bytes;
80a47a2c 759
3045f050
JH
760 if (canon_len > 0)
761 {
e2e3255a 762 exim_sha_update(&sig->body_hash, CUS canon_data, canon_len);
3045f050 763 sig->signed_body_bytes += canon_len;
2592e6c0 764 DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
80a47a2c
TK
765 }
766
3045f050 767 sig = sig->next;
80a47a2c
TK
768 }
769
3045f050 770return PDKIM_OK;
6ab02e3f 771}
80a47a2c
TK
772
773
774/* -------------------------------------------------------------------------- */
80a47a2c 775
ca9cb170 776static void
3045f050
JH
777pdkim_finish_bodyhash(pdkim_ctx *ctx)
778{
f4d091fb 779pdkim_signature *sig;
80a47a2c 780
3045f050 781/* Traverse all signatures */
f4d091fb 782for (sig = ctx->sig; sig; sig = sig->next)
3045f050 783 { /* Finish hashes */
2592e6c0
JH
784 blob bh;
785
786 exim_sha_finish(&sig->body_hash, &bh);
3045f050 787
0d04a285 788 DEBUG(D_acl)
3045f050 789 {
0d04a285
JH
790 debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
791 "PDKIM [%s] bh computed: ",
792 sig->domain, sig->signed_body_bytes, sig->domain);
b78006ac 793 pdkim_hexprint(CUS bh.data, bh.len);
80a47a2c 794 }
3045f050
JH
795
796 /* SIGNING -------------------------------------------------------------- */
797 if (ctx->mode == PDKIM_MODE_SIGN)
798 {
2592e6c0 799 sig->bodyhash = bh;
3045f050
JH
800
801 /* If bodylength limit is set, and we have received less bytes
802 than the requested amount, effectively remove the limit tag. */
803 if (sig->signed_body_bytes < sig->bodylength)
804 sig->bodylength = -1;
80a47a2c 805 }
3045f050
JH
806
807 /* VERIFICATION --------------------------------------------------------- */
808 else
809 {
810 /* Compare bodyhash */
2592e6c0 811 if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
3045f050 812 {
0d04a285 813 DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
80a47a2c 814 }
3045f050
JH
815 else
816 {
0d04a285 817 DEBUG(D_acl)
3045f050 818 {
0d04a285 819 debug_printf("PDKIM [%s] bh signature: ", sig->domain);
2592e6c0
JH
820 pdkim_hexprint(sig->bodyhash.data,
821 exim_sha_hashlen(&sig->body_hash));
0d04a285 822 debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
3045f050 823 }
3045f050
JH
824 sig->verify_status = PDKIM_VERIFY_FAIL;
825 sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
80a47a2c
TK
826 }
827 }
80a47a2c 828 }
6ab02e3f 829}
80a47a2c
TK
830
831
832
833/* -------------------------------------------------------------------------- */
834/* Callback from pdkim_feed below for processing complete body lines */
3045f050 835
0d04a285 836static int
3045f050
JH
837pdkim_bodyline_complete(pdkim_ctx *ctx)
838{
839char *p = ctx->linebuf;
840int n = ctx->linebuf_offset;
c14470c3 841pdkim_signature *sig = ctx->sig; /*XXX assumes only one sig */
3045f050
JH
842
843/* Ignore extra data if we've seen the end-of-data marker */
844if (ctx->seen_eod) goto BAIL;
845
846/* We've always got one extra byte to stuff a zero ... */
0d04a285 847ctx->linebuf[ctx->linebuf_offset] = '\0';
3045f050 848
0d04a285
JH
849/* Terminate on EOD marker */
850if (memcmp(p, ".\r\n", 3) == 0)
3045f050 851 {
0d04a285
JH
852 /* In simple body mode, if any empty lines were buffered,
853 replace with one. rfc 4871 3.4.3 */
854 /*XXX checking the signed-body-bytes is a gross hack; I think
855 it indicates that all linebreaks should be buffered, including
856 the one terminating a text line */
857 if ( sig && sig->canon_body == PDKIM_CANON_SIMPLE
858 && sig->signed_body_bytes == 0
859 && ctx->num_buffered_crlf > 0
860 )
861 pdkim_update_bodyhash(ctx, "\r\n", 2);
862
863 ctx->seen_eod = TRUE;
864 goto BAIL;
865 }
866/* Unstuff dots */
867if (memcmp(p, "..", 2) == 0)
868 {
869 p++;
870 n--;
80a47a2c
TK
871 }
872
3045f050
JH
873/* Empty lines need to be buffered until we find a non-empty line */
874if (memcmp(p, "\r\n", 2) == 0)
875 {
876 ctx->num_buffered_crlf++;
877 goto BAIL;
80a47a2c
TK
878 }
879
c14470c3 880if (sig && sig->canon_body == PDKIM_CANON_RELAXED)
3045f050
JH
881 {
882 /* Lines with just spaces need to be buffered too */
883 char *check = p;
884 while (memcmp(check, "\r\n", 2) != 0)
885 {
886 char c = *check;
6a11a9e6 887
3045f050
JH
888 if (c != '\t' && c != ' ')
889 goto PROCESS;
890 check++;
6a11a9e6
JH
891 }
892
3045f050
JH
893 ctx->num_buffered_crlf++;
894 goto BAIL;
895}
6a11a9e6 896
3045f050
JH
897PROCESS:
898/* At this point, we have a non-empty line, so release the buffered ones. */
899while (ctx->num_buffered_crlf)
900 {
901 pdkim_update_bodyhash(ctx, "\r\n", 2);
902 ctx->num_buffered_crlf--;
80a47a2c
TK
903 }
904
3045f050 905pdkim_update_bodyhash(ctx, p, n);
80a47a2c 906
3045f050
JH
907BAIL:
908ctx->linebuf_offset = 0;
909return PDKIM_OK;
80a47a2c
TK
910}
911
912
913/* -------------------------------------------------------------------------- */
914/* Callback from pdkim_feed below for processing complete headers */
915#define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
3045f050 916
f444c2c7 917static int
3045f050
JH
918pdkim_header_complete(pdkim_ctx *ctx)
919{
3045f050 920/* Special case: The last header can have an extra \r appended */
ca9cb170
JH
921if ( (ctx->cur_header_len > 1) &&
922 (ctx->cur_header[(ctx->cur_header_len)-1] == '\r') )
923 --ctx->cur_header_len;
924ctx->cur_header[ctx->cur_header_len] = '\0';
80a47a2c 925
3045f050
JH
926ctx->num_headers++;
927if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
80a47a2c 928
3045f050
JH
929/* SIGNING -------------------------------------------------------------- */
930if (ctx->mode == PDKIM_MODE_SIGN)
0d04a285
JH
931 {
932 pdkim_signature *sig;
933
934 for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */
80a47a2c 935
ab9152ff 936 /* Add header to the signed headers list (in reverse order) */
ca9cb170
JH
937 sig->headers = pdkim_prepend_stringlist(sig->headers,
938 ctx->cur_header);
0d04a285 939 }
94431adb 940
0d04a285 941/* VERIFICATION ----------------------------------------------------------- */
3045f050
JH
942/* DKIM-Signature: headers are added to the verification list */
943if (ctx->mode == PDKIM_MODE_VERIFY)
944 {
e2e3255a 945 if (strncasecmp(CCS ctx->cur_header,
3045f050 946 DKIM_SIGNATURE_HEADERNAME,
e2e3255a 947 Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
3045f050
JH
948 {
949 pdkim_signature *new_sig;
80a47a2c 950
3045f050 951 /* Create and chain new signature block */
0d04a285 952 DEBUG(D_acl) debug_printf(
3045f050 953 "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
3045f050 954
ca9cb170 955 if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header)))
3045f050
JH
956 {
957 pdkim_signature *last_sig = ctx->sig;
958 if (!last_sig)
959 ctx->sig = new_sig;
960 else
961 {
962 while (last_sig->next) last_sig = last_sig->next;
963 last_sig->next = new_sig;
964 }
80a47a2c 965 }
abe1010c 966 else
0d04a285
JH
967 DEBUG(D_acl) debug_printf(
968 "Error while parsing signature header\n"
3045f050 969 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 970 }
37f8b554 971
3045f050
JH
972 /* every other header is stored for signature verification */
973 else
ca9cb170 974 ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header);
80a47a2c
TK
975 }
976
3045f050 977BAIL:
ca9cb170
JH
978*ctx->cur_header = '\0';
979ctx->cur_header_len = 0; /* leave buffer for reuse */
3045f050 980return PDKIM_OK;
6ab02e3f 981}
80a47a2c
TK
982
983
984
985/* -------------------------------------------------------------------------- */
986#define HEADER_BUFFER_FRAG_SIZE 256
3045f050
JH
987
988DLLEXPORT int
ca9cb170 989pdkim_feed(pdkim_ctx *ctx, char *data, int len)
3045f050
JH
990{
991int p;
992
993for (p = 0; p<len; p++)
994 {
ca9cb170 995 uschar c = data[p];
3045f050
JH
996
997 if (ctx->past_headers)
998 {
999 /* Processing body byte */
0d04a285 1000 ctx->linebuf[ctx->linebuf_offset++] = c;
3045f050
JH
1001 if (c == '\n')
1002 {
1003 int rc = pdkim_bodyline_complete(ctx); /* End of line */
1004 if (rc != PDKIM_OK) return rc;
80a47a2c 1005 }
3045f050
JH
1006 if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1))
1007 return PDKIM_ERR_LONG_LINE;
80a47a2c 1008 }
3045f050
JH
1009 else
1010 {
1011 /* Processing header byte */
1012 if (c != '\r')
1013 {
1014 if (c == '\n')
1015 {
1016 if (ctx->seen_lf)
1017 {
1018 int rc = pdkim_header_complete(ctx); /* Seen last header line */
1019 if (rc != PDKIM_OK) return rc;
1020
0d04a285 1021 ctx->past_headers = TRUE;
3045f050 1022 ctx->seen_lf = 0;
0d04a285 1023 DEBUG(D_acl) debug_printf(
8ef02a06 1024 "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>\n");
3045f050
JH
1025 continue;
1026 }
1027 else
0d04a285 1028 ctx->seen_lf = TRUE;
3045f050
JH
1029 }
1030 else if (ctx->seen_lf)
1031 {
1032 if (!(c == '\t' || c == ' '))
1033 {
1034 int rc = pdkim_header_complete(ctx); /* End of header */
1035 if (rc != PDKIM_OK) return rc;
1036 }
0d04a285 1037 ctx->seen_lf = FALSE;
3045f050 1038 }
80a47a2c 1039 }
3045f050 1040
ca9cb170 1041 if (ctx->cur_header_len < PDKIM_MAX_HEADER_LEN)
c2f669a4 1042 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
e2e3255a 1043 &ctx->cur_header_len, CUS &data[p], 1);
80a47a2c
TK
1044 }
1045 }
3045f050 1046return PDKIM_OK;
6ab02e3f 1047}
80a47a2c 1048
ca9cb170
JH
1049
1050
1051/* Extend a grwong header with a continuation-linebreak */
1052static uschar *
1053pdkim_hdr_cont(uschar * str, int * size, int * ptr, int * col)
1054{
1055*col = 1;
c2f669a4 1056return string_catn(str, size, ptr, US"\r\n\t", 3);
ca9cb170
JH
1057}
1058
1059
1060
05b7d6de
JB
1061/*
1062 * RFC 5322 specifies that header line length SHOULD be no more than 78
1063 * lets make it so!
1064 * pdkim_headcat
ca9cb170
JH
1065 *
1066 * returns uschar * (not nul-terminated)
05b7d6de
JB
1067 *
1068 * col: this int holds and receives column number (octets since last '\n')
1069 * str: partial string to append to
ca9cb170
JH
1070 * size: current buffer size for str
1071 * ptr: current tail-pointer for str
94431adb 1072 * pad: padding, split line or space after before or after eg: ";"
05b7d6de
JB
1073 * intro: - must join to payload eg "h=", usually the tag name
1074 * payload: eg base64 data - long data can be split arbitrarily.
1075 *
1076 * this code doesn't fold the header in some of the places that RFC4871
1077 * allows: As per RFC5322(2.2.3) it only folds before or after tag-value
1078 * pairs and inside long values. it also always spaces or breaks after the
94431adb 1079 * "pad"
05b7d6de
JB
1080 *
1081 * no guarantees are made for output given out-of range input. like tag
f444c2c7 1082 * names longer than 78, or bogus col. Input is assumed to be free of line breaks.
05b7d6de
JB
1083 */
1084
ca9cb170
JH
1085static uschar *
1086pdkim_headcat(int * col, uschar * str, int * size, int * ptr,
1087 const uschar * pad, const uschar * intro, const uschar * payload)
3045f050
JH
1088{
1089size_t l;
1090
1091if (pad)
05b7d6de 1092 {
ca9cb170 1093 l = Ustrlen(pad);
3045f050 1094 if (*col + l > 78)
ca9cb170 1095 str = pdkim_hdr_cont(str, size, ptr, col);
c2f669a4 1096 str = string_catn(str, size, ptr, pad, l);
3045f050 1097 *col += l;
05b7d6de
JB
1098 }
1099
ca9cb170 1100l = (pad?1:0) + (intro?Ustrlen(intro):0);
05b7d6de 1101
3045f050 1102if (*col + l > 78)
05b7d6de 1103 { /*can't fit intro - start a new line to make room.*/
ca9cb170
JH
1104 str = pdkim_hdr_cont(str, size, ptr, col);
1105 l = intro?Ustrlen(intro):0;
05b7d6de
JB
1106 }
1107
ca9cb170 1108l += payload ? Ustrlen(payload):0 ;
05b7d6de 1109
3045f050 1110while (l>77)
05b7d6de 1111 { /* this fragment will not fit on a single line */
3045f050 1112 if (pad)
05b7d6de 1113 {
c2f669a4 1114 str = string_catn(str, size, ptr, US" ", 1);
3045f050
JH
1115 *col += 1;
1116 pad = NULL; /* only want this once */
1117 l--;
05b7d6de 1118 }
3045f050
JH
1119
1120 if (intro)
05b7d6de 1121 {
ca9cb170 1122 size_t sl = Ustrlen(intro);
3045f050 1123
c2f669a4 1124 str = string_catn(str, size, ptr, intro, sl);
3045f050
JH
1125 *col += sl;
1126 l -= sl;
1127 intro = NULL; /* only want this once */
05b7d6de 1128 }
3045f050
JH
1129
1130 if (payload)
05b7d6de 1131 {
ca9cb170 1132 size_t sl = Ustrlen(payload);
3045f050
JH
1133 size_t chomp = *col+sl < 77 ? sl : 78-*col;
1134
c2f669a4 1135 str = string_catn(str, size, ptr, payload, chomp);
3045f050
JH
1136 *col += chomp;
1137 payload += chomp;
1138 l -= chomp-1;
05b7d6de 1139 }
3045f050
JH
1140
1141 /* the while precondition tells us it didn't fit. */
ca9cb170 1142 str = pdkim_hdr_cont(str, size, ptr, col);
05b7d6de 1143 }
3045f050
JH
1144
1145if (*col + l > 78)
05b7d6de 1146 {
ca9cb170 1147 str = pdkim_hdr_cont(str, size, ptr, col);
3045f050 1148 pad = NULL;
05b7d6de
JB
1149 }
1150
3045f050 1151if (pad)
05b7d6de 1152 {
c2f669a4 1153 str = string_catn(str, size, ptr, US" ", 1);
3045f050
JH
1154 *col += 1;
1155 pad = NULL;
05b7d6de
JB
1156 }
1157
3045f050 1158if (intro)
05b7d6de 1159 {
ca9cb170 1160 size_t sl = Ustrlen(intro);
3045f050 1161
c2f669a4 1162 str = string_catn(str, size, ptr, intro, sl);
3045f050
JH
1163 *col += sl;
1164 l -= sl;
1165 intro = NULL;
05b7d6de 1166 }
3045f050
JH
1167
1168if (payload)
05b7d6de 1169 {
ca9cb170 1170 size_t sl = Ustrlen(payload);
3045f050 1171
c2f669a4 1172 str = string_catn(str, size, ptr, payload, sl);
3045f050 1173 *col += sl;
05b7d6de
JB
1174 }
1175
ca9cb170 1176return str;
05b7d6de 1177}
80a47a2c 1178
3045f050 1179
80a47a2c 1180/* -------------------------------------------------------------------------- */
3045f050 1181
ca9cb170 1182static uschar *
2592e6c0 1183pdkim_create_header(pdkim_signature *sig, BOOL final)
3045f050 1184{
ca9cb170
JH
1185uschar * base64_bh;
1186uschar * base64_b;
3045f050 1187int col = 0;
ca9cb170
JH
1188uschar * hdr; int hdr_size = 0, hdr_len = 0;
1189uschar * canon_all; int can_size = 0, can_len = 0;
3045f050 1190
c2f669a4
JH
1191canon_all = string_cat (NULL, &can_size, &can_len,
1192 pdkim_canons[sig->canon_headers]);
1193canon_all = string_catn(canon_all, &can_size, &can_len, US"/", 1);
1194canon_all = string_cat (canon_all, &can_size, &can_len,
1195 pdkim_canons[sig->canon_body]);
ca9cb170 1196canon_all[can_len] = '\0';
3045f050 1197
ca9cb170 1198hdr = string_cat(NULL, &hdr_size, &hdr_len,
e2e3255a 1199 US"DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
ca9cb170 1200col = hdr_len;
3045f050
JH
1201
1202/* Required and static bits */
ca9cb170
JH
1203hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
1204 pdkim_algos[sig->algo]);
1205hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
1206 pdkim_querymethods[sig->querymethod]);
1207hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"c=",
1208 canon_all);
1209hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"d=",
1210 sig->domain);
1211hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"s=",
1212 sig->selector);
1213
1214/* list of header names can be split between items. */
3045f050 1215 {
e2e3255a 1216 uschar * n = string_copy(sig->headernames);
ca9cb170
JH
1217 uschar * i = US"h=";
1218 uschar * s = US";";
1219
1220 while (*n)
05b7d6de 1221 {
ca9cb170 1222 uschar * c = Ustrchr(n, ':');
3045f050 1223
ca9cb170 1224 if (c) *c ='\0';
05b7d6de 1225
ca9cb170
JH
1226 if (!i)
1227 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, NULL, US":");
05b7d6de 1228
ca9cb170 1229 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, s, i, n);
3045f050 1230
ca9cb170
JH
1231 if (!c)
1232 break;
3045f050 1233
ca9cb170
JH
1234 n = c+1;
1235 s = NULL;
1236 i = NULL;
80a47a2c 1237 }
ca9cb170 1238 }
05b7d6de 1239
ca9cb170
JH
1240base64_bh = pdkim_encode_base64(&sig->bodyhash);
1241hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"bh=", base64_bh);
3045f050 1242
ca9cb170
JH
1243/* Optional bits */
1244if (sig->identity)
1245 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"i=", sig->identity);
3045f050 1246
ca9cb170
JH
1247if (sig->created > 0)
1248 {
e2e3255a 1249 uschar minibuf[20];
3045f050 1250
e2e3255a 1251 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->created);
ca9cb170
JH
1252 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"t=", minibuf);
1253}
3045f050 1254
ca9cb170
JH
1255if (sig->expires > 0)
1256 {
e2e3255a 1257 uschar minibuf[20];
3045f050 1258
e2e3255a 1259 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->expires);
ca9cb170
JH
1260 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"x=", minibuf);
1261 }
3045f050 1262
ca9cb170
JH
1263if (sig->bodylength >= 0)
1264 {
e2e3255a 1265 uschar minibuf[20];
80a47a2c 1266
e2e3255a 1267 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->bodylength);
ca9cb170 1268 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"l=", minibuf);
3045f050 1269 }
05b7d6de 1270
ca9cb170
JH
1271/* Preliminary or final version? */
1272base64_b = final ? pdkim_encode_base64(&sig->sigdata) : US"";
1273hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
80a47a2c 1274
ca9cb170
JH
1275/* add trailing semicolon: I'm not sure if this is actually needed */
1276hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US"");
80a47a2c 1277
ca9cb170
JH
1278hdr[hdr_len] = '\0';
1279return hdr;
80a47a2c
TK
1280}
1281
1282
1283/* -------------------------------------------------------------------------- */
3045f050
JH
1284
1285DLLEXPORT int
1286pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures)
1287{
1288pdkim_signature *sig = ctx->sig;
ca9cb170
JH
1289uschar * headernames = NULL; /* Collected signed header names */
1290int hs = 0, hl = 0;
3045f050
JH
1291
1292/* Check if we must still flush a (partial) header. If that is the
1293 case, the message has no body, and we must compute a body hash
1294 out of '<CR><LF>' */
ca9cb170 1295if (ctx->cur_header && ctx->cur_header_len)
3045f050
JH
1296 {
1297 int rc = pdkim_header_complete(ctx);
1298 if (rc != PDKIM_OK) return rc;
1299 pdkim_update_bodyhash(ctx, "\r\n", 2);
80a47a2c 1300 }
3045f050 1301else
0d04a285 1302 DEBUG(D_acl) debug_printf(
3045f050 1303 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1304
3045f050 1305/* Build (and/or evaluate) body hash */
ca9cb170 1306pdkim_finish_bodyhash(ctx);
80a47a2c 1307
3045f050
JH
1308while (sig)
1309 {
2592e6c0
JH
1310 BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
1311 hctx hhash_ctx;
ca9cb170 1312 uschar * sig_hdr;
2592e6c0
JH
1313 blob hhash;
1314 blob hdata;
cb224393 1315 int hdata_alloc = 0;
cb224393 1316
2592e6c0
JH
1317 hdata.data = NULL;
1318 hdata.len = 0;
1319
1ed59855 1320 exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256);
80a47a2c 1321
0d04a285
JH
1322 DEBUG(D_acl) debug_printf(
1323 "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
3045f050
JH
1324
1325 /* SIGNING ---------------------------------------------------------------- */
1326 /* When signing, walk through our header list and add them to the hash. As we
8ef02a06
JH
1327 go, construct a list of the header's names to use for the h= parameter.
1328 Then append to that list any remaining header names for which there was no
1329 header to sign. */
3045f050
JH
1330
1331 if (ctx->mode == PDKIM_MODE_SIGN)
1332 {
1333 pdkim_stringlist *p;
8ef02a06
JH
1334 const uschar * l;
1335 uschar * s;
1336 int sep = 0;
3045f050
JH
1337
1338 for (p = sig->headers; p; p = p->next)
ab9152ff
JH
1339 if (header_name_match(p->value, sig->sign_headers) == PDKIM_OK)
1340 {
1341 uschar * rh;
1342 /* Collect header names (Note: colon presence is guaranteed here) */
1343 uschar * q = Ustrchr(p->value, ':');
3045f050 1344
c2f669a4 1345 headernames = string_catn(headernames, &hs, &hl,
ca9cb170 1346 p->value, (q - US p->value) + (p->next ? 1 : 0));
3045f050 1347
ab9152ff 1348 rh = sig->canon_headers == PDKIM_CANON_RELAXED
ca9cb170
JH
1349 ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
1350 : string_copy(CUS p->value); /* just copy it for simple canon */
3045f050 1351
ab9152ff 1352 /* Feed header to the hash algorithm */
e2e3255a 1353 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
f444c2c7 1354
ab9152ff
JH
1355 /* Remember headers block for signing (when the library cannot do incremental) */
1356 (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
3045f050 1357
ab9152ff
JH
1358 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1359 }
8ef02a06 1360
ca9cb170 1361 l = sig->sign_headers;
8ef02a06
JH
1362 while((s = string_nextinlist(&l, &sep, NULL, 0)))
1363 if (*s != '_')
ab9152ff 1364 { /*SSS string_append_listele() */
ca9cb170 1365 if (hl > 0 && headernames[hl-1] != ':')
c2f669a4 1366 headernames = string_catn(headernames, &hs, &hl, US":", 1);
ca9cb170 1367
c2f669a4 1368 headernames = string_cat(headernames, &hs, &hl, s);
8ef02a06 1369 }
ca9cb170
JH
1370 headernames[hl] = '\0';
1371
1372 /* Copy headernames to signature struct */
1373 sig->headernames = headernames;
1374 headernames = NULL, hs = hl = 0;
1375
1376 /* Create signature header with b= omitted */
1377 sig_hdr = pdkim_create_header(sig, FALSE);
80a47a2c 1378 }
37f8b554 1379
3045f050
JH
1380 /* VERIFICATION ----------------------------------------------------------- */
1381 /* When verifying, walk through the header name list in the h= parameter and
1382 add the headers to the hash in that order. */
1383 else
1384 {
2592e6c0
JH
1385 uschar * b = string_copy(sig->headernames);
1386 uschar * p = b;
1387 uschar * q;
1388 pdkim_stringlist * hdrs;
3045f050 1389
3045f050
JH
1390 /* clear tags */
1391 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1392 hdrs->tag = 0;
1393
1394 while(1)
1395 {
2592e6c0 1396 if ((q = Ustrchr(p, ':')))
3045f050
JH
1397 *q = '\0';
1398
ab9152ff 1399/*XXX walk the list of headers in same order as received. */
3045f050
JH
1400 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1401 if ( hdrs->tag == 0
2592e6c0
JH
1402 && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0
1403 && (hdrs->value)[Ustrlen(p)] == ':'
3045f050
JH
1404 )
1405 {
2592e6c0 1406 uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
ca9cb170
JH
1407 ? pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
1408 : string_copy(CUS hdrs->value); /* just copy it for simple canon */
3045f050
JH
1409
1410 /* Feed header to the hash algorithm */
e2e3255a 1411 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
3045f050 1412
2592e6c0 1413 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
3045f050
JH
1414 hdrs->tag = 1;
1415 break;
1416 }
1417
1418 if (!q) break;
1419 p = q+1;
80a47a2c 1420 }
ca9cb170
JH
1421
1422 sig_hdr = string_copy(sig->rawsig_no_b_val);
80a47a2c
TK
1423 }
1424
0d04a285 1425 DEBUG(D_acl) debug_printf(
3045f050 1426 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1427
3045f050
JH
1428 /* Relax header if necessary */
1429 if (sig->canon_headers == PDKIM_CANON_RELAXED)
ca9cb170 1430 sig_hdr = pdkim_relax_header(sig_hdr, 0);
80a47a2c 1431
0d04a285 1432 DEBUG(D_acl)
3045f050 1433 {
0d04a285 1434 debug_printf(
3045f050 1435 "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
e2e3255a 1436 pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
0d04a285 1437 debug_printf(
3045f050 1438 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1439 }
3045f050
JH
1440
1441 /* Finalize header hash */
e2e3255a 1442 exim_sha_update(&hhash_ctx, CUS sig_hdr, Ustrlen(sig_hdr));
2592e6c0 1443 exim_sha_finish(&hhash_ctx, &hhash);
3045f050 1444
f444c2c7
JH
1445 DEBUG(D_acl)
1446 {
1447 debug_printf("PDKIM [%s] hh computed: ", sig->domain);
2592e6c0 1448 pdkim_hexprint(hhash.data, hhash.len);
80a47a2c
TK
1449 }
1450
2592e6c0 1451 /* Remember headers block for signing (when the library cannot do incremental) */
f444c2c7 1452 if (ctx->mode == PDKIM_MODE_SIGN)
b78006ac 1453 (void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
f444c2c7 1454
3045f050
JH
1455 /* SIGNING ---------------------------------------------------------------- */
1456 if (ctx->mode == PDKIM_MODE_SIGN)
1457 {
2592e6c0
JH
1458 es_ctx sctx;
1459 const uschar * errstr;
f444c2c7
JH
1460
1461 /* Import private key */
b78006ac 1462 if ((errstr = exim_rsa_signing_init(US sig->rsa_privkey, &sctx)))
f444c2c7 1463 {
2592e6c0 1464 DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
f444c2c7
JH
1465 return PDKIM_ERR_RSA_PRIVKEY;
1466 }
80a47a2c 1467
2592e6c0
JH
1468 /* Do signing. With OpenSSL we are signing the hash of headers just
1469 calculated, with GnuTLS we have to sign an entire block of headers
1470 (due to available interfaces) and it recalculates the hash internally. */
f444c2c7 1471
2592e6c0
JH
1472#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
1473 hdata = hhash;
f444c2c7
JH
1474#endif
1475
2592e6c0 1476 if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
f444c2c7 1477 {
2592e6c0 1478 DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
f444c2c7
JH
1479 return PDKIM_ERR_RSA_SIGNING;
1480 }
80a47a2c 1481
0d04a285 1482 DEBUG(D_acl)
3045f050 1483 {
0d04a285 1484 debug_printf( "PDKIM [%s] b computed: ", sig->domain);
2592e6c0 1485 pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
80a47a2c 1486 }
80a47a2c 1487
ca9cb170 1488 sig->signature_header = pdkim_create_header(sig, TRUE);
80a47a2c 1489 }
80a47a2c 1490
3045f050
JH
1491 /* VERIFICATION ----------------------------------------------------------- */
1492 else
1493 {
2592e6c0
JH
1494 ev_ctx vctx;
1495 const uschar * errstr;
3045f050 1496
ca9cb170 1497 uschar *dns_txt_name, *dns_txt_reply;
f444c2c7 1498
07eeb4df 1499 /* Make sure we have all required signature tags */
1500 if (!( sig->domain && *sig->domain
1501 && sig->selector && *sig->selector
1502 && sig->headernames && *sig->headernames
1503 && sig->bodyhash.data
1504 && sig->sigdata.data
1505 && sig->algo > -1
1506 && sig->version
1507 ) )
1508 {
1509 sig->verify_status = PDKIM_VERIFY_INVALID;
1510 sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
1511
1512 DEBUG(D_acl) debug_printf(
1513 " Error in DKIM-Signature header: tags missing or invalid\n"
1514 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1515 goto NEXT_VERIFY;
1516 }
1517
1518 /* Make sure sig uses supported DKIM version (only v1) */
1519 if (sig->version != 1)
1520 {
1521 sig->verify_status = PDKIM_VERIFY_INVALID;
1522 sig->verify_ext_status = PDKIM_VERIFY_INVALID_DKIM_VERSION;
1523
1524 DEBUG(D_acl) debug_printf(
1525 " Error in DKIM-Signature header: unsupported DKIM version\n"
1526 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1527 goto NEXT_VERIFY;
1528 }
1529
f444c2c7 1530 /* Fetch public key for signing domain, from DNS */
3045f050 1531
ca9cb170
JH
1532 dns_txt_name = string_sprintf("%s._domainkey.%s.",
1533 sig->selector, sig->domain);
80a47a2c 1534
ca9cb170 1535 dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN);
3045f050 1536 memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
3045f050 1537
ca9cb170 1538 if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK
3045f050
JH
1539 || dns_txt_reply[0] == '\0')
1540 {
1541 sig->verify_status = PDKIM_VERIFY_INVALID;
1542 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
1543 goto NEXT_VERIFY;
80a47a2c
TK
1544 }
1545
0d04a285 1546 DEBUG(D_acl)
3045f050 1547 {
0d04a285 1548 debug_printf(
2592e6c0
JH
1549 "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
1550 " Raw record: ");
ca9cb170 1551 pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
80a47a2c 1552 }
3045f050 1553
e2e3255a 1554 if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply)))
3045f050
JH
1555 {
1556 sig->verify_status = PDKIM_VERIFY_INVALID;
df3def24 1557 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
3045f050 1558
0d04a285
JH
1559 DEBUG(D_acl) debug_printf(
1560 " Error while parsing public key record\n"
3045f050 1561 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
3045f050 1562 goto NEXT_VERIFY;
80a47a2c
TK
1563 }
1564
0d04a285 1565 DEBUG(D_acl) debug_printf(
2592e6c0 1566 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
3045f050 1567
f444c2c7 1568 /* Import public key */
2592e6c0 1569 if ((errstr = exim_rsa_verify_init(&sig->pubkey->key, &vctx)))
3045f050 1570 {
2592e6c0 1571 DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
3045f050 1572 sig->verify_status = PDKIM_VERIFY_INVALID;
df3def24 1573 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
3045f050 1574 goto NEXT_VERIFY;
80a47a2c
TK
1575 }
1576
3045f050 1577 /* Check the signature */
2592e6c0 1578 if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
3045f050 1579 {
2592e6c0 1580 DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
3045f050
JH
1581 sig->verify_status = PDKIM_VERIFY_FAIL;
1582 sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
1583 goto NEXT_VERIFY;
ff7ddfd7
TK
1584 }
1585
2592e6c0 1586
3045f050
JH
1587 /* We have a winner! (if bodydhash was correct earlier) */
1588 if (sig->verify_status == PDKIM_VERIFY_NONE)
1589 sig->verify_status = PDKIM_VERIFY_PASS;
1590
1591NEXT_VERIFY:
1592
0d04a285 1593 DEBUG(D_acl)
3045f050 1594 {
0d04a285 1595 debug_printf("PDKIM [%s] signature status: %s",
3045f050
JH
1596 sig->domain, pdkim_verify_status_str(sig->verify_status));
1597 if (sig->verify_ext_status > 0)
0d04a285 1598 debug_printf(" (%s)\n",
3045f050
JH
1599 pdkim_verify_ext_status_str(sig->verify_ext_status));
1600 else
0d04a285 1601 debug_printf("\n");
80a47a2c 1602 }
80a47a2c
TK
1603 }
1604
3045f050 1605 sig = sig->next;
80a47a2c
TK
1606 }
1607
3045f050
JH
1608/* If requested, set return pointer to signature(s) */
1609if (return_signatures)
1610 *return_signatures = ctx->sig;
80a47a2c 1611
3045f050 1612return PDKIM_OK;
80a47a2c
TK
1613}
1614
1615
1616/* -------------------------------------------------------------------------- */
3045f050
JH
1617
1618DLLEXPORT pdkim_ctx *
0d04a285 1619pdkim_init_verify(int(*dns_txt_callback)(char *, char *))
3045f050 1620{
ca9cb170 1621pdkim_ctx * ctx;
3045f050 1622
ca9cb170 1623ctx = store_get(sizeof(pdkim_ctx));
abe1010c 1624memset(ctx, 0, sizeof(pdkim_ctx));
3045f050 1625
ca9cb170 1626ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
3045f050 1627ctx->mode = PDKIM_MODE_VERIFY;
3045f050 1628ctx->dns_txt_callback = dns_txt_callback;
80a47a2c 1629
3045f050 1630return ctx;
80a47a2c
TK
1631}
1632
1633
1634/* -------------------------------------------------------------------------- */
80a47a2c 1635
3045f050 1636DLLEXPORT pdkim_ctx *
f444c2c7 1637pdkim_init_sign(char *domain, char *selector, char *rsa_privkey, int algo)
3045f050
JH
1638{
1639pdkim_ctx *ctx;
1640pdkim_signature *sig;
80a47a2c 1641
3045f050
JH
1642if (!domain || !selector || !rsa_privkey)
1643 return NULL;
80a47a2c 1644
ca9cb170 1645ctx = store_get(sizeof(pdkim_ctx));
abe1010c 1646memset(ctx, 0, sizeof(pdkim_ctx));
80a47a2c 1647
ca9cb170 1648ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
80a47a2c 1649
ca9cb170 1650sig = store_get(sizeof(pdkim_signature));
abe1010c 1651memset(sig, 0, sizeof(pdkim_signature));
80a47a2c 1652
3045f050 1653sig->bodylength = -1;
80a47a2c 1654
3045f050 1655ctx->mode = PDKIM_MODE_SIGN;
3045f050 1656ctx->sig = sig;
80a47a2c 1657
e2e3255a
JH
1658sig->domain = string_copy(US domain);
1659sig->selector = string_copy(US selector);
1660sig->rsa_privkey = string_copy(US rsa_privkey);
2592e6c0 1661sig->algo = algo;
cb224393 1662
1ed59855 1663exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
3045f050 1664return ctx;
6ab02e3f 1665}
80a47a2c 1666
f444c2c7 1667
80a47a2c 1668/* -------------------------------------------------------------------------- */
3045f050
JH
1669
1670DLLEXPORT int
1671pdkim_set_optional(pdkim_ctx *ctx,
80a47a2c
TK
1672 char *sign_headers,
1673 char *identity,
1674 int canon_headers,
1675 int canon_body,
1676 long bodylength,
80a47a2c 1677 unsigned long created,
3045f050
JH
1678 unsigned long expires)
1679{
8ef02a06
JH
1680pdkim_signature * sig = ctx->sig;
1681
3045f050 1682if (identity)
e2e3255a 1683 sig->identity = string_copy(US identity);
80a47a2c 1684
ca9cb170
JH
1685sig->sign_headers = string_copy(sign_headers
1686 ? US sign_headers : US PDKIM_DEFAULT_SIGN_HEADERS);
80a47a2c 1687
8ef02a06
JH
1688sig->canon_headers = canon_headers;
1689sig->canon_body = canon_body;
1690sig->bodylength = bodylength;
1691sig->created = created;
1692sig->expires = expires;
80a47a2c 1693
3045f050 1694return PDKIM_OK;
6ab02e3f 1695}
3045f050 1696
3045f050 1697
2592e6c0
JH
1698void
1699pdkim_init(void)
1700{
1701exim_rsa_init();
1702}
1703
1704
1705
f444c2c7 1706#endif /*DISABLE_DKIM*/