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