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