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