DKIM: support oversigning. Bugs 1309, 1310
[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"
48#define PDKIM_PUB_RECORD_VERSION "DKIM1"
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 {
65 char *value;
37f8b554 66 int tag;
80a47a2c
TK
67 void *next;
68};
69
70#define PDKIM_STR_ALLOC_FRAG 256
71struct pdkim_str {
72 char *str;
73 unsigned int len;
74 unsigned int allocated;
75};
76
77/* -------------------------------------------------------------------------- */
78/* A bunch of list constants */
1ba28e2b 79const char *pdkim_querymethods[] = {
80a47a2c
TK
80 "dns/txt",
81 NULL
82};
1ba28e2b 83const char *pdkim_algos[] = {
80a47a2c
TK
84 "rsa-sha256",
85 "rsa-sha1",
86 NULL
87};
1ba28e2b 88const char *pdkim_canons[] = {
80a47a2c
TK
89 "simple",
90 "relaxed",
91 NULL
92};
1ba28e2b 93const char *pdkim_hashes[] = {
80a47a2c
TK
94 "sha256",
95 "sha1",
96 NULL
97};
1ba28e2b 98const char *pdkim_keytypes[] = {
80a47a2c
TK
99 "rsa",
100 NULL
101};
102
103typedef struct pdkim_combined_canon_entry {
1ba28e2b 104 const char *str;
80a47a2c
TK
105 int canon_headers;
106 int canon_body;
107} pdkim_combined_canon_entry;
f444c2c7 108
80a47a2c
TK
109pdkim_combined_canon_entry pdkim_combined_canons[] = {
110 { "simple/simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
111 { "simple/relaxed", PDKIM_CANON_SIMPLE, PDKIM_CANON_RELAXED },
112 { "relaxed/simple", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
113 { "relaxed/relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_RELAXED },
114 { "simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
115 { "relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
116 { NULL, 0, 0 }
117};
118
119
f444c2c7
JH
120/* -------------------------------------------------------------------------- */
121
122const char *
123pdkim_verify_status_str(int status)
124{
ff7ddfd7
TK
125 switch(status) {
126 case PDKIM_VERIFY_NONE: return "PDKIM_VERIFY_NONE";
127 case PDKIM_VERIFY_INVALID: return "PDKIM_VERIFY_INVALID";
128 case PDKIM_VERIFY_FAIL: return "PDKIM_VERIFY_FAIL";
129 case PDKIM_VERIFY_PASS: return "PDKIM_VERIFY_PASS";
130 default: return "PDKIM_VERIFY_UNKNOWN";
131 }
132}
f444c2c7
JH
133
134const char *
135pdkim_verify_ext_status_str(int ext_status)
136{
ff7ddfd7
TK
137 switch(ext_status) {
138 case PDKIM_VERIFY_FAIL_BODY: return "PDKIM_VERIFY_FAIL_BODY";
139 case PDKIM_VERIFY_FAIL_MESSAGE: return "PDKIM_VERIFY_FAIL_MESSAGE";
140 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: return "PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE";
141 case PDKIM_VERIFY_INVALID_BUFFER_SIZE: return "PDKIM_VERIFY_INVALID_BUFFER_SIZE";
df3def24
JH
142 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD: return "PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD";
143 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return "PDKIM_VERIFY_INVALID_PUBKEY_IMPORT";
ff7ddfd7
TK
144 default: return "PDKIM_VERIFY_UNKNOWN";
145 }
146}
147
148
80a47a2c
TK
149/* -------------------------------------------------------------------------- */
150/* Print debugging functions */
2592e6c0
JH
151static void
152pdkim_quoteprint(const char *data, int len)
3045f050
JH
153{
154int i;
155const unsigned char *p = (const unsigned char *)data;
156
0d04a285 157for (i = 0; i < len; i++)
3045f050
JH
158 {
159 const int c = p[i];
160 switch (c)
161 {
0d04a285
JH
162 case ' ' : debug_printf("{SP}"); break;
163 case '\t': debug_printf("{TB}"); break;
164 case '\r': debug_printf("{CR}"); break;
165 case '\n': debug_printf("{LF}"); break;
166 case '{' : debug_printf("{BO}"); break;
167 case '}' : debug_printf("{BC}"); break;
3045f050
JH
168 default:
169 if ( (c < 32) || (c > 127) )
0d04a285 170 debug_printf("{%02x}", c);
3045f050 171 else
0d04a285 172 debug_printf("%c", c);
80a47a2c
TK
173 break;
174 }
175 }
2592e6c0 176debug_printf("\n");
80a47a2c 177}
80a47a2c 178
2592e6c0
JH
179static void
180pdkim_hexprint(const char *data, int len)
3045f050
JH
181{
182int i;
183const unsigned char *p = (const unsigned char *)data;
184
0d04a285
JH
185for (i = 0 ; i < len; i++)
186 debug_printf("%02x", p[i]);
2592e6c0 187debug_printf("\n");
80a47a2c 188}
80a47a2c
TK
189
190
f444c2c7 191
8ef02a06 192/* SSS probably want to keep the "stringlist" notion */
f444c2c7
JH
193
194static pdkim_stringlist *
195pdkim_prepend_stringlist(pdkim_stringlist *base, char *str)
3045f050
JH
196{
197pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist));
198
199if (!new_entry) return NULL;
abe1010c 200memset(new_entry, 0, sizeof(pdkim_stringlist));
3045f050
JH
201if (!(new_entry->value = strdup(str))) return NULL;
202if (base)
203 {
204 pdkim_stringlist *last = base;
205 while (last->next != NULL) { last = last->next; }
206 last->next = new_entry;
207 return base;
97713345 208 }
3045f050 209else
97713345 210 return new_entry;
6ab02e3f 211}
80a47a2c
TK
212
213
214/* -------------------------------------------------------------------------- */
215/* A small "growing string" implementation to escape malloc/realloc hell */
8ef02a06
JH
216/* String package: should be replaced by Exim standard ones */
217/* SSS Ustrcpy */
3045f050 218
f444c2c7 219static pdkim_str *
3045f050
JH
220pdkim_strnew (const char *cstr)
221{
222unsigned int len = cstr ? strlen(cstr) : 0;
223pdkim_str *p = malloc(sizeof(pdkim_str));
224
225if (!p) return NULL;
abe1010c 226memset(p, 0, sizeof(pdkim_str));
3045f050
JH
227if (!(p->str = malloc(len+1)))
228 {
229 free(p);
230 return NULL;
80a47a2c 231 }
3045f050
JH
232p->allocated = len+1;
233p->len = len;
234if (cstr)
abe1010c 235 strcpy(p->str, cstr);
3045f050
JH
236else
237 p->str[p->len] = '\0';
238return p;
6ab02e3f 239}
3045f050 240
8ef02a06
JH
241
242/*SSS Ustrncat */
243
f444c2c7 244static char *
3045f050
JH
245pdkim_strncat(pdkim_str *str, const char *data, int len)
246{
247if ((str->allocated - str->len) < (len+1))
248 {
249 /* Extend the buffer */
250 int num_frags = ((len+1)/PDKIM_STR_ALLOC_FRAG)+1;
251 char *n = realloc(str->str,
252 (str->allocated+(num_frags*PDKIM_STR_ALLOC_FRAG)));
253 if (n == NULL) return NULL;
254 str->str = n;
255 str->allocated += (num_frags*PDKIM_STR_ALLOC_FRAG);
80a47a2c 256 }
3045f050
JH
257strncpy(&(str->str[str->len]), data, len);
258str->len += len;
259str->str[str->len] = '\0';
260return str->str;
6ab02e3f 261}
3045f050 262
8ef02a06
JH
263
264/* SSS Ustrcat */
265
f444c2c7 266static char *
3045f050
JH
267pdkim_strcat(pdkim_str *str, const char *cstr)
268{
269return pdkim_strncat(str, cstr, strlen(cstr));
6ab02e3f 270}
05b7d6de 271
8ef02a06
JH
272
273
274/* Trim whitespace fore & aft */
275
f444c2c7 276static char *
3045f050
JH
277pdkim_strtrim(pdkim_str *str)
278{
279char *p = str->str;
280char *q = str->str;
8ef02a06
JH
281while (*p == '\t' || *p == ' ') p++; /* skip whitespace */
282while (*p) {*q = *p; q++; p++;} /* dump the leading whitespace */
3045f050 283*q = '\0';
8ef02a06
JH
284while (q != str->str && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) )
285 { /* dump trailing whitespace */
80a47a2c 286 *q = '\0';
3045f050 287 q--;
80a47a2c 288 }
3045f050
JH
289str->len = strlen(str->str);
290return str->str;
6ab02e3f 291}
3045f050 292
8ef02a06
JH
293
294
f444c2c7 295static char *
3045f050
JH
296pdkim_strclear(pdkim_str *str)
297{
298str->str[0] = '\0';
299str->len = 0;
300return str->str;
6ab02e3f 301}
3045f050 302
8ef02a06
JH
303
304
f444c2c7 305static void
3045f050
JH
306pdkim_strfree(pdkim_str *str)
307{
308if (!str) return;
309if (str->str) free(str->str);
310free(str);
6ab02e3f 311}
80a47a2c
TK
312
313
8ef02a06
JH
314static void
315pdkim_stringlist_free(pdkim_stringlist * e)
316{
317while(e)
318 {
319 pdkim_stringlist * c = e;
320 if (e->value) free(e->value);
321 e = e->next;
322 free(c);
323 }
324}
325
326
80a47a2c
TK
327
328/* -------------------------------------------------------------------------- */
3045f050 329
f444c2c7 330static void
3045f050
JH
331pdkim_free_pubkey(pdkim_pubkey *pub)
332{
333if (pub)
334 {
335 if (pub->version ) free(pub->version);
336 if (pub->granularity) free(pub->granularity);
337 if (pub->hashes ) free(pub->hashes);
338 if (pub->keytype ) free(pub->keytype);
339 if (pub->srvtype ) free(pub->srvtype);
340 if (pub->notes ) free(pub->notes);
3045f050 341 free(pub);
80a47a2c
TK
342 }
343}
344
345
346/* -------------------------------------------------------------------------- */
3045f050 347
f444c2c7 348static void
3045f050
JH
349pdkim_free_sig(pdkim_signature *sig)
350{
351if (sig)
352 {
353 pdkim_signature *next = (pdkim_signature *)sig->next;
354
8ef02a06 355 pdkim_stringlist_free(sig->headers);
3045f050
JH
356 if (sig->selector ) free(sig->selector);
357 if (sig->domain ) free(sig->domain);
358 if (sig->identity ) free(sig->identity);
3045f050
JH
359 if (sig->copiedheaders ) free(sig->copiedheaders);
360 if (sig->rsa_privkey ) free(sig->rsa_privkey);
361 if (sig->sign_headers ) free(sig->sign_headers);
362 if (sig->signature_header) free(sig->signature_header);
3045f050
JH
363
364 if (sig->pubkey) pdkim_free_pubkey(sig->pubkey);
365
366 free(sig);
367 if (next) pdkim_free_sig(next);
80a47a2c 368 }
6ab02e3f 369}
80a47a2c
TK
370
371
372/* -------------------------------------------------------------------------- */
3045f050
JH
373
374DLLEXPORT void
375pdkim_free_ctx(pdkim_ctx *ctx)
376{
377if (ctx)
378 {
8ef02a06 379 pdkim_stringlist_free(ctx->headers);
3045f050
JH
380 pdkim_free_sig(ctx->sig);
381 pdkim_strfree(ctx->cur_header);
382 free(ctx);
80a47a2c 383 }
6ab02e3f 384}
80a47a2c
TK
385
386
387/* -------------------------------------------------------------------------- */
388/* Matches the name of the passed raw "header" against
8ef02a06
JH
389 the passed colon-separated "tick", and invalidates
390 the entry in tick. Returns OK or fail-code */
391/*XXX might be safer done using a pdkim_stringlist for "tick" */
3045f050 392
f444c2c7 393static int
8ef02a06 394header_name_match(const char * header, char * tick)
3045f050
JH
395{
396char *hname;
397char *lcopy;
398char *p;
399char *q;
400int rc = PDKIM_FAIL;
401
402/* Get header name */
abe1010c 403char *hcolon = strchr(header, ':');
3045f050
JH
404
405if (!hcolon) return rc; /* This isn't a header */
406
407if (!(hname = malloc((hcolon-header)+1)))
408 return PDKIM_ERR_OOM;
abe1010c
JH
409memset(hname, 0, (hcolon-header)+1);
410strncpy(hname, header, (hcolon-header));
3045f050
JH
411
412/* Copy tick-off list locally, so we can punch zeroes into it */
413if (!(lcopy = strdup(tick)))
414 {
415 free(hname);
416 return PDKIM_ERR_OOM;
80a47a2c 417 }
3045f050 418p = lcopy;
abe1010c 419q = strchr(p, ':');
3045f050
JH
420while (q)
421 {
422 *q = '\0';
80a47a2c 423
abe1010c 424 if (strcasecmp(p, hname) == 0)
3045f050
JH
425 {
426 rc = PDKIM_OK;
427 /* Invalidate header name instance in tick-off list */
8ef02a06 428 tick[p-lcopy] = '_';
3045f050 429 goto BAIL;
80a47a2c
TK
430 }
431
3045f050 432 p = q+1;
abe1010c 433 q = strchr(p, ':');
80a47a2c
TK
434 }
435
abe1010c 436if (strcasecmp(p, hname) == 0)
3045f050
JH
437 {
438 rc = PDKIM_OK;
439 /* Invalidate header name instance in tick-off list */
8ef02a06 440 tick[p-lcopy] = '_';
80a47a2c
TK
441 }
442
3045f050
JH
443BAIL:
444free(hname);
445free(lcopy);
446return rc;
80a47a2c
TK
447}
448
449
450/* -------------------------------------------------------------------------- */
451/* Performs "relaxed" canonicalization of a header. The returned pointer needs
452 to be free()d. */
3045f050 453
f444c2c7 454static char *
3045f050
JH
455pdkim_relax_header (char *header, int crlf)
456{
457BOOL past_field_name = FALSE;
458BOOL seen_wsp = FALSE;
459char *p;
460char *q;
461char *relaxed = malloc(strlen(header)+3);
462
463if (!relaxed) return NULL;
464
465q = relaxed;
466for (p = header; *p != '\0'; p++)
467 {
468 int c = *p;
469 /* Ignore CR & LF */
470 if (c == '\r' || c == '\n')
471 continue;
472 if (c == '\t' || c == ' ')
473 {
474 if (seen_wsp)
80a47a2c 475 continue;
3045f050
JH
476 c = ' '; /* Turns WSP into SP */
477 seen_wsp = TRUE;
80a47a2c 478 }
3045f050
JH
479 else
480 if (!past_field_name && c == ':')
481 {
482 if (seen_wsp) q--; /* This removes WSP before the colon */
483 seen_wsp = TRUE; /* This removes WSP after the colon */
484 past_field_name = TRUE;
80a47a2c 485 }
3045f050
JH
486 else
487 seen_wsp = FALSE;
488
489 /* Lowercase header name */
490 if (!past_field_name) c = tolower(c);
491 *q++ = c;
80a47a2c 492 }
3045f050
JH
493
494if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */
495*q = '\0';
496
abe1010c 497if (crlf) strcat(relaxed, "\r\n");
3045f050 498return relaxed;
6ab02e3f 499}
80a47a2c
TK
500
501
502/* -------------------------------------------------------------------------- */
503#define PDKIM_QP_ERROR_DECODE -1
3045f050 504
a5840e10 505static char *
3045f050
JH
506pdkim_decode_qp_char(char *qp_p, int *c)
507{
508char *initial_pos = qp_p;
509
510/* Advance one char */
511qp_p++;
512
513/* Check for two hex digits and decode them */
514if (isxdigit(*qp_p) && isxdigit(qp_p[1]))
515 {
516 /* Do hex conversion */
517 *c = (isdigit(*qp_p) ? *qp_p - '0' : toupper(*qp_p) - 'A' + 10) << 4;
a5840e10 518 *c |= isdigit(qp_p[1]) ? qp_p[1] - '0' : toupper(qp_p[1]) - 'A' + 10;
3045f050 519 return qp_p + 2;
6ab02e3f 520 }
80a47a2c 521
3045f050
JH
522/* Illegal char here */
523*c = PDKIM_QP_ERROR_DECODE;
524return initial_pos;
80a47a2c
TK
525}
526
527
528/* -------------------------------------------------------------------------- */
3045f050 529
f444c2c7 530static char *
3045f050
JH
531pdkim_decode_qp(char *str)
532{
533int nchar = 0;
534char *q;
535char *p = str;
536char *n = malloc(strlen(p)+1);
537
538if (!n) return NULL;
539
540*n = '\0';
541q = n;
542while (*p != '\0')
543 {
544 if (*p == '=')
545 {
546 p = pdkim_decode_qp_char(p, &nchar);
547 if (nchar >= 0)
548 {
549 *q++ = nchar;
550 continue;
80a47a2c
TK
551 }
552 }
3045f050
JH
553 else
554 *q++ = *p;
555 p++;
80a47a2c 556 }
3045f050
JH
557*q = '\0';
558return n;
80a47a2c
TK
559}
560
561
562/* -------------------------------------------------------------------------- */
3045f050 563
2592e6c0
JH
564static void
565pdkim_decode_base64(uschar *str, blob * b)
3045f050 566{
2592e6c0 567int dlen;
3045f050 568char *res;
2592e6c0
JH
569dlen = b64decode(str, &b->data);
570if (dlen < 0) b->data = NULL;
571b->len = dlen;
80a47a2c
TK
572}
573
574/* -------------------------------------------------------------------------- */
3045f050 575
f444c2c7 576static char *
2592e6c0 577pdkim_encode_base64(blob * b)
3045f050 578{
f4d091fb
JH
579char * ret;
580int old_pool = store_pool;
3045f050 581
f4d091fb 582store_pool = POOL_PERM;
2592e6c0 583ret = CS b64encode(b->data, b->len);
f4d091fb
JH
584store_pool = old_pool;
585return ret;
80a47a2c
TK
586}
587
588
589/* -------------------------------------------------------------------------- */
590#define PDKIM_HDR_LIMBO 0
591#define PDKIM_HDR_TAG 1
592#define PDKIM_HDR_VALUE 2
3045f050 593
f444c2c7 594static pdkim_signature *
3045f050
JH
595pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr)
596{
597pdkim_signature *sig ;
abe1010c 598char *p, *q;
3045f050
JH
599pdkim_str *cur_tag = NULL;
600pdkim_str *cur_val = NULL;
601BOOL past_hname = FALSE;
602BOOL in_b_val = FALSE;
603int where = PDKIM_HDR_LIMBO;
604int i;
2592e6c0
JH
605int old_pool = store_pool;
606
607/* There is a store-reset between header & body reception
608so cannot use the main pool. Any allocs done by Exim
609memory-handling must use the perm pool. */
610
611store_pool = POOL_PERM;
3045f050
JH
612
613if (!(sig = malloc(sizeof(pdkim_signature)))) return NULL;
abe1010c 614memset(sig, 0, sizeof(pdkim_signature));
3045f050
JH
615sig->bodylength = -1;
616
617if (!(sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1)))
618 {
619 free(sig);
620 return NULL;
80a47a2c
TK
621 }
622
3045f050 623q = sig->rawsig_no_b_val;
80a47a2c 624
3045f050
JH
625for (p = raw_hdr; ; p++)
626 {
627 char c = *p;
80a47a2c 628
3045f050
JH
629 /* Ignore FWS */
630 if (c == '\r' || c == '\n')
631 goto NEXT_CHAR;
80a47a2c 632
3045f050
JH
633 /* Fast-forward through header name */
634 if (!past_hname)
635 {
636 if (c == ':') past_hname = TRUE;
637 goto NEXT_CHAR;
80a47a2c
TK
638 }
639
3045f050
JH
640 if (where == PDKIM_HDR_LIMBO)
641 {
642 /* In limbo, just wait for a tag-char to appear */
643 if (!(c >= 'a' && c <= 'z'))
644 goto NEXT_CHAR;
80a47a2c 645
3045f050 646 where = PDKIM_HDR_TAG;
80a47a2c
TK
647 }
648
3045f050
JH
649 if (where == PDKIM_HDR_TAG)
650 {
651 if (!cur_tag)
652 cur_tag = pdkim_strnew(NULL);
80a47a2c 653
3045f050
JH
654 if (c >= 'a' && c <= 'z')
655 pdkim_strncat(cur_tag, p, 1);
80a47a2c 656
3045f050
JH
657 if (c == '=')
658 {
659 if (strcmp(cur_tag->str, "b") == 0)
660 {
661 *q = '='; q++;
662 in_b_val = TRUE;
663 }
664 where = PDKIM_HDR_VALUE;
665 goto NEXT_CHAR;
80a47a2c
TK
666 }
667 }
668
3045f050
JH
669 if (where == PDKIM_HDR_VALUE)
670 {
671 if (!cur_val)
672 cur_val = pdkim_strnew(NULL);
673
674 if (c == '\r' || c == '\n' || c == ' ' || c == '\t')
675 goto NEXT_CHAR;
676
677 if (c == ';' || c == '\0')
678 {
679 if (cur_tag->len > 0)
680 {
681 pdkim_strtrim(cur_val);
682
0d04a285 683 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str);
3045f050
JH
684
685 switch (cur_tag->str[0])
686 {
687 case 'b':
688 if (cur_tag->str[1] == 'h')
2592e6c0 689 pdkim_decode_base64(US cur_val->str, &sig->bodyhash);
3045f050 690 else
2592e6c0 691 pdkim_decode_base64(US cur_val->str, &sig->sigdata);
3045f050
JH
692 break;
693 case 'v':
694 /* We only support version 1, and that is currently the
695 only version there is. */
696 if (strcmp(cur_val->str, PDKIM_SIGNATURE_VERSION) == 0)
697 sig->version = 1;
698 break;
699 case 'a':
700 for (i = 0; pdkim_algos[i]; i++)
701 if (strcmp(cur_val->str, pdkim_algos[i]) == 0)
702 {
703 sig->algo = i;
704 break;
705 }
706 break;
707 case 'c':
708 for (i = 0; pdkim_combined_canons[i].str; i++)
709 if (strcmp(cur_val->str, pdkim_combined_canons[i].str) == 0)
710 {
711 sig->canon_headers = pdkim_combined_canons[i].canon_headers;
712 sig->canon_body = pdkim_combined_canons[i].canon_body;
713 break;
714 }
715 break;
716 case 'q':
717 for (i = 0; pdkim_querymethods[i]; i++)
718 if (strcmp(cur_val->str, pdkim_querymethods[i]) == 0)
719 {
720 sig->querymethod = i;
721 break;
722 }
723 break;
724 case 's':
725 sig->selector = strdup(cur_val->str); break;
726 case 'd':
727 sig->domain = strdup(cur_val->str); break;
728 case 'i':
729 sig->identity = pdkim_decode_qp(cur_val->str); break;
730 case 't':
731 sig->created = strtoul(cur_val->str, NULL, 10); break;
732 case 'x':
733 sig->expires = strtoul(cur_val->str, NULL, 10); break;
734 case 'l':
735 sig->bodylength = strtol(cur_val->str, NULL, 10); break;
736 case 'h':
2592e6c0 737 sig->headernames = string_copy(cur_val->str); break;
3045f050
JH
738 case 'z':
739 sig->copiedheaders = pdkim_decode_qp(cur_val->str); break;
740 default:
0d04a285 741 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
3045f050
JH
742 break;
743 }
744 }
745 pdkim_strclear(cur_tag);
746 pdkim_strclear(cur_val);
747 in_b_val = FALSE;
748 where = PDKIM_HDR_LIMBO;
80a47a2c 749 }
3045f050
JH
750 else
751 pdkim_strncat(cur_val, p, 1);
80a47a2c
TK
752 }
753
3045f050
JH
754NEXT_CHAR:
755 if (c == '\0')
756 break;
80a47a2c 757
3045f050
JH
758 if (!in_b_val)
759 *q++ = c;
80a47a2c
TK
760 }
761
2592e6c0
JH
762store_pool = old_pool;
763
3045f050
JH
764/* Make sure the most important bits are there. */
765if (!(sig->domain && (*(sig->domain) != '\0') &&
766 sig->selector && (*(sig->selector) != '\0') &&
767 sig->headernames && (*(sig->headernames) != '\0') &&
3045f050
JH
768 sig->version))
769 {
770 pdkim_free_sig(sig);
771 return NULL;
80a47a2c
TK
772 }
773
3045f050
JH
774*q = '\0';
775/* Chomp raw header. The final newline must not be added to the signature. */
776q--;
777while (q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n'))
778 *q = '\0'; q--; /*XXX questionable code layout; possible bug */
80a47a2c 779
0d04a285 780DEBUG(D_acl)
3045f050 781 {
0d04a285 782 debug_printf(
3045f050 783 "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
2592e6c0 784 pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val));
0d04a285 785 debug_printf(
2592e6c0 786 "PDKIM >> Sig size: %4d bits\n", sig->sigdata.len*8);
0d04a285 787 debug_printf(
3045f050 788 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 789 }
80a47a2c 790
2592e6c0 791exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1);
3045f050 792return sig;
80a47a2c
TK
793}
794
795
796/* -------------------------------------------------------------------------- */
80a47a2c 797
f444c2c7 798static pdkim_pubkey *
3045f050
JH
799pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record)
800{
801pdkim_pubkey *pub;
802char *p;
803pdkim_str *cur_tag = NULL;
804pdkim_str *cur_val = NULL;
805int where = PDKIM_HDR_LIMBO;
80a47a2c 806
3045f050 807if (!(pub = malloc(sizeof(pdkim_pubkey)))) return NULL;
abe1010c 808memset(pub, 0, sizeof(pdkim_pubkey));
80a47a2c 809
3045f050
JH
810for (p = raw_record; ; p++)
811 {
812 char c = *p;
80a47a2c 813
3045f050
JH
814 /* Ignore FWS */
815 if (c == '\r' || c == '\n')
816 goto NEXT_CHAR;
80a47a2c 817
3045f050
JH
818 if (where == PDKIM_HDR_LIMBO)
819 {
820 /* In limbo, just wait for a tag-char to appear */
821 if (!(c >= 'a' && c <= 'z'))
822 goto NEXT_CHAR;
80a47a2c 823
3045f050 824 where = PDKIM_HDR_TAG;
80a47a2c
TK
825 }
826
3045f050
JH
827 if (where == PDKIM_HDR_TAG)
828 {
829 if (!cur_tag)
830 cur_tag = pdkim_strnew(NULL);
80a47a2c 831
3045f050
JH
832 if (c >= 'a' && c <= 'z')
833 pdkim_strncat(cur_tag, p, 1);
80a47a2c 834
3045f050
JH
835 if (c == '=')
836 {
837 where = PDKIM_HDR_VALUE;
838 goto NEXT_CHAR;
80a47a2c
TK
839 }
840 }
841
3045f050
JH
842 if (where == PDKIM_HDR_VALUE)
843 {
844 if (!cur_val)
845 cur_val = pdkim_strnew(NULL);
846
847 if (c == '\r' || c == '\n')
848 goto NEXT_CHAR;
849
850 if (c == ';' || c == '\0')
851 {
852 if (cur_tag->len > 0)
853 {
854 pdkim_strtrim(cur_val);
0d04a285 855 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str);
3045f050
JH
856
857 switch (cur_tag->str[0])
858 {
859 case 'v':
860 /* This tag isn't evaluated because:
861 - We only support version DKIM1.
862 - Which is the default for this value (set below)
863 - Other versions are currently not specified. */
864 break;
865 case 'h':
866 pub->hashes = strdup(cur_val->str); break;
867 case 'g':
868 pub->granularity = strdup(cur_val->str); break;
869 case 'n':
870 pub->notes = pdkim_decode_qp(cur_val->str); break;
871 case 'p':
2592e6c0
JH
872 pdkim_decode_base64(US cur_val->str, &pub->key);
873 break;
3045f050
JH
874 case 'k':
875 pub->hashes = strdup(cur_val->str); break;
876 case 's':
877 pub->srvtype = strdup(cur_val->str); break;
878 case 't':
abe1010c
JH
879 if (strchr(cur_val->str, 'y') != NULL) pub->testing = 1;
880 if (strchr(cur_val->str, 's') != NULL) pub->no_subdomaining = 1;
3045f050
JH
881 break;
882 default:
0d04a285 883 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
3045f050
JH
884 break;
885 }
886 }
887 pdkim_strclear(cur_tag);
888 pdkim_strclear(cur_val);
889 where = PDKIM_HDR_LIMBO;
80a47a2c 890 }
3045f050
JH
891 else
892 pdkim_strncat(cur_val, p, 1);
80a47a2c
TK
893 }
894
3045f050
JH
895NEXT_CHAR:
896 if (c == '\0') break;
80a47a2c
TK
897 }
898
3045f050
JH
899/* Set fallback defaults */
900if (!pub->version ) pub->version = strdup(PDKIM_PUB_RECORD_VERSION);
901if (!pub->granularity) pub->granularity = strdup("*");
902if (!pub->keytype ) pub->keytype = strdup("rsa");
903if (!pub->srvtype ) pub->srvtype = strdup("*");
80a47a2c 904
3045f050 905/* p= is required */
2592e6c0 906if (pub->key.data)
80a47a2c 907 return pub;
3045f050
JH
908
909pdkim_free_pubkey(pub);
910return NULL;
80a47a2c
TK
911}
912
913
914/* -------------------------------------------------------------------------- */
3045f050 915
f444c2c7 916static int
3045f050
JH
917pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len)
918{
919pdkim_signature *sig = ctx->sig;
920/* Cache relaxed version of data */
921char *relaxed_data = NULL;
922int relaxed_len = 0;
923
924/* Traverse all signatures, updating their hashes. */
925while (sig)
926 {
927 /* Defaults to simple canon (no further treatment necessary) */
928 const char *canon_data = data;
929 int canon_len = len;
930
931 if (sig->canon_body == PDKIM_CANON_RELAXED)
932 {
933 /* Relax the line if not done already */
934 if (!relaxed_data)
935 {
936 BOOL seen_wsp = FALSE;
937 const char *p;
938 int q = 0;
939
940 if (!(relaxed_data = malloc(len+1)))
941 return PDKIM_ERR_OOM;
942
943 for (p = data; *p; p++)
944 {
945 char c = *p;
946 if (c == '\r')
947 {
948 if (q > 0 && relaxed_data[q-1] == ' ')
949 q--;
950 }
951 else if (c == '\t' || c == ' ')
952 {
953 c = ' '; /* Turns WSP into SP */
954 if (seen_wsp)
955 continue;
956 seen_wsp = TRUE;
957 }
958 else
959 seen_wsp = FALSE;
960 relaxed_data[q++] = c;
961 }
962 relaxed_data[q] = '\0';
963 relaxed_len = q;
80a47a2c 964 }
3045f050
JH
965 canon_data = relaxed_data;
966 canon_len = relaxed_len;
80a47a2c
TK
967 }
968
3045f050
JH
969 /* Make sure we don't exceed the to-be-signed body length */
970 if ( sig->bodylength >= 0
971 && sig->signed_body_bytes + (unsigned long)canon_len > sig->bodylength
972 )
973 canon_len = sig->bodylength - sig->signed_body_bytes;
80a47a2c 974
3045f050
JH
975 if (canon_len > 0)
976 {
2592e6c0 977 exim_sha_update(&sig->body_hash, canon_data, canon_len);
3045f050 978 sig->signed_body_bytes += canon_len;
2592e6c0 979 DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
80a47a2c
TK
980 }
981
3045f050 982 sig = sig->next;
80a47a2c
TK
983 }
984
3045f050
JH
985if (relaxed_data) free(relaxed_data);
986return PDKIM_OK;
6ab02e3f 987}
80a47a2c
TK
988
989
990/* -------------------------------------------------------------------------- */
80a47a2c 991
f444c2c7 992static int
3045f050
JH
993pdkim_finish_bodyhash(pdkim_ctx *ctx)
994{
f4d091fb 995pdkim_signature *sig;
80a47a2c 996
3045f050 997/* Traverse all signatures */
f4d091fb 998for (sig = ctx->sig; sig; sig = sig->next)
3045f050 999 { /* Finish hashes */
2592e6c0
JH
1000 blob bh;
1001
1002 exim_sha_finish(&sig->body_hash, &bh);
3045f050 1003
0d04a285 1004 DEBUG(D_acl)
3045f050 1005 {
0d04a285
JH
1006 debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
1007 "PDKIM [%s] bh computed: ",
1008 sig->domain, sig->signed_body_bytes, sig->domain);
2592e6c0 1009 pdkim_hexprint(CS bh.data, bh.len);
80a47a2c 1010 }
3045f050
JH
1011
1012 /* SIGNING -------------------------------------------------------------- */
1013 if (ctx->mode == PDKIM_MODE_SIGN)
1014 {
2592e6c0 1015 sig->bodyhash = bh;
3045f050
JH
1016
1017 /* If bodylength limit is set, and we have received less bytes
1018 than the requested amount, effectively remove the limit tag. */
1019 if (sig->signed_body_bytes < sig->bodylength)
1020 sig->bodylength = -1;
80a47a2c 1021 }
3045f050
JH
1022
1023 /* VERIFICATION --------------------------------------------------------- */
1024 else
1025 {
1026 /* Compare bodyhash */
2592e6c0 1027 if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
3045f050 1028 {
0d04a285 1029 DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
80a47a2c 1030 }
3045f050
JH
1031 else
1032 {
0d04a285 1033 DEBUG(D_acl)
3045f050 1034 {
0d04a285 1035 debug_printf("PDKIM [%s] bh signature: ", sig->domain);
2592e6c0
JH
1036 pdkim_hexprint(sig->bodyhash.data,
1037 exim_sha_hashlen(&sig->body_hash));
0d04a285 1038 debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
3045f050 1039 }
3045f050
JH
1040 sig->verify_status = PDKIM_VERIFY_FAIL;
1041 sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
80a47a2c
TK
1042 }
1043 }
80a47a2c
TK
1044 }
1045
3045f050 1046return PDKIM_OK;
6ab02e3f 1047}
80a47a2c
TK
1048
1049
1050
1051/* -------------------------------------------------------------------------- */
1052/* Callback from pdkim_feed below for processing complete body lines */
3045f050 1053
0d04a285 1054static int
3045f050
JH
1055pdkim_bodyline_complete(pdkim_ctx *ctx)
1056{
1057char *p = ctx->linebuf;
1058int n = ctx->linebuf_offset;
c14470c3 1059pdkim_signature *sig = ctx->sig; /*XXX assumes only one sig */
3045f050
JH
1060
1061/* Ignore extra data if we've seen the end-of-data marker */
1062if (ctx->seen_eod) goto BAIL;
1063
1064/* We've always got one extra byte to stuff a zero ... */
0d04a285 1065ctx->linebuf[ctx->linebuf_offset] = '\0';
3045f050 1066
0d04a285
JH
1067/* Terminate on EOD marker */
1068if (memcmp(p, ".\r\n", 3) == 0)
3045f050 1069 {
0d04a285
JH
1070 /* In simple body mode, if any empty lines were buffered,
1071 replace with one. rfc 4871 3.4.3 */
1072 /*XXX checking the signed-body-bytes is a gross hack; I think
1073 it indicates that all linebreaks should be buffered, including
1074 the one terminating a text line */
1075 if ( sig && sig->canon_body == PDKIM_CANON_SIMPLE
1076 && sig->signed_body_bytes == 0
1077 && ctx->num_buffered_crlf > 0
1078 )
1079 pdkim_update_bodyhash(ctx, "\r\n", 2);
1080
1081 ctx->seen_eod = TRUE;
1082 goto BAIL;
1083 }
1084/* Unstuff dots */
1085if (memcmp(p, "..", 2) == 0)
1086 {
1087 p++;
1088 n--;
80a47a2c
TK
1089 }
1090
3045f050
JH
1091/* Empty lines need to be buffered until we find a non-empty line */
1092if (memcmp(p, "\r\n", 2) == 0)
1093 {
1094 ctx->num_buffered_crlf++;
1095 goto BAIL;
80a47a2c
TK
1096 }
1097
c14470c3 1098if (sig && sig->canon_body == PDKIM_CANON_RELAXED)
3045f050
JH
1099 {
1100 /* Lines with just spaces need to be buffered too */
1101 char *check = p;
1102 while (memcmp(check, "\r\n", 2) != 0)
1103 {
1104 char c = *check;
6a11a9e6 1105
3045f050
JH
1106 if (c != '\t' && c != ' ')
1107 goto PROCESS;
1108 check++;
6a11a9e6
JH
1109 }
1110
3045f050
JH
1111 ctx->num_buffered_crlf++;
1112 goto BAIL;
1113}
6a11a9e6 1114
3045f050
JH
1115PROCESS:
1116/* At this point, we have a non-empty line, so release the buffered ones. */
1117while (ctx->num_buffered_crlf)
1118 {
1119 pdkim_update_bodyhash(ctx, "\r\n", 2);
1120 ctx->num_buffered_crlf--;
80a47a2c
TK
1121 }
1122
3045f050 1123pdkim_update_bodyhash(ctx, p, n);
80a47a2c 1124
3045f050
JH
1125BAIL:
1126ctx->linebuf_offset = 0;
1127return PDKIM_OK;
80a47a2c
TK
1128}
1129
1130
1131/* -------------------------------------------------------------------------- */
1132/* Callback from pdkim_feed below for processing complete headers */
1133#define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
3045f050 1134
f444c2c7 1135static int
3045f050
JH
1136pdkim_header_complete(pdkim_ctx *ctx)
1137{
3045f050
JH
1138/* Special case: The last header can have an extra \r appended */
1139if ( (ctx->cur_header->len > 1) &&
1140 (ctx->cur_header->str[(ctx->cur_header->len)-1] == '\r') )
1141 {
1142 ctx->cur_header->str[(ctx->cur_header->len)-1] = '\0';
1143 ctx->cur_header->len--;
80a47a2c
TK
1144 }
1145
3045f050
JH
1146ctx->num_headers++;
1147if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
80a47a2c 1148
3045f050
JH
1149/* SIGNING -------------------------------------------------------------- */
1150if (ctx->mode == PDKIM_MODE_SIGN)
0d04a285
JH
1151 {
1152 pdkim_signature *sig;
1153
1154 for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */
3045f050 1155 if (header_name_match(ctx->cur_header->str,
8ef02a06 1156 sig->sign_headers) == PDKIM_OK)
3045f050 1157 {
37f8b554 1158 pdkim_stringlist *list;
80a47a2c 1159
37f8b554 1160 /* Add header to the signed headers list (in reverse order) */
3045f050
JH
1161 if (!(list = pdkim_prepend_stringlist(sig->headers,
1162 ctx->cur_header->str)))
1163 return PDKIM_ERR_OOM;
37f8b554 1164 sig->headers = list;
3045f050 1165 }
0d04a285 1166 }
94431adb 1167
0d04a285 1168/* VERIFICATION ----------------------------------------------------------- */
3045f050
JH
1169/* DKIM-Signature: headers are added to the verification list */
1170if (ctx->mode == PDKIM_MODE_VERIFY)
1171 {
1172 if (strncasecmp(ctx->cur_header->str,
1173 DKIM_SIGNATURE_HEADERNAME,
1174 strlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
1175 {
1176 pdkim_signature *new_sig;
80a47a2c 1177
3045f050 1178 /* Create and chain new signature block */
0d04a285 1179 DEBUG(D_acl) debug_printf(
3045f050 1180 "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
3045f050
JH
1181
1182 if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str)))
1183 {
1184 pdkim_signature *last_sig = ctx->sig;
1185 if (!last_sig)
1186 ctx->sig = new_sig;
1187 else
1188 {
1189 while (last_sig->next) last_sig = last_sig->next;
1190 last_sig->next = new_sig;
1191 }
80a47a2c 1192 }
abe1010c 1193 else
0d04a285
JH
1194 DEBUG(D_acl) debug_printf(
1195 "Error while parsing signature header\n"
3045f050 1196 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1197 }
37f8b554 1198
3045f050
JH
1199 /* every other header is stored for signature verification */
1200 else
1201 {
1202 pdkim_stringlist *list;
1203
abe1010c 1204 if (!(list = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->str)))
3045f050
JH
1205 return PDKIM_ERR_OOM;
1206 ctx->headers = list;
80a47a2c
TK
1207 }
1208 }
1209
3045f050
JH
1210BAIL:
1211pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
1212return PDKIM_OK;
6ab02e3f 1213}
80a47a2c
TK
1214
1215
1216
1217/* -------------------------------------------------------------------------- */
1218#define HEADER_BUFFER_FRAG_SIZE 256
3045f050
JH
1219
1220DLLEXPORT int
1221pdkim_feed (pdkim_ctx *ctx, char *data, int len)
1222{
1223int p;
1224
1225for (p = 0; p<len; p++)
1226 {
1227 char c = data[p];
1228
1229 if (ctx->past_headers)
1230 {
1231 /* Processing body byte */
0d04a285 1232 ctx->linebuf[ctx->linebuf_offset++] = c;
3045f050
JH
1233 if (c == '\n')
1234 {
1235 int rc = pdkim_bodyline_complete(ctx); /* End of line */
1236 if (rc != PDKIM_OK) return rc;
80a47a2c 1237 }
3045f050
JH
1238 if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1))
1239 return PDKIM_ERR_LONG_LINE;
80a47a2c 1240 }
3045f050
JH
1241 else
1242 {
1243 /* Processing header byte */
1244 if (c != '\r')
1245 {
1246 if (c == '\n')
1247 {
1248 if (ctx->seen_lf)
1249 {
1250 int rc = pdkim_header_complete(ctx); /* Seen last header line */
1251 if (rc != PDKIM_OK) return rc;
1252
0d04a285 1253 ctx->past_headers = TRUE;
3045f050 1254 ctx->seen_lf = 0;
0d04a285 1255 DEBUG(D_acl) debug_printf(
8ef02a06 1256 "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>\n");
3045f050
JH
1257 continue;
1258 }
1259 else
0d04a285 1260 ctx->seen_lf = TRUE;
3045f050
JH
1261 }
1262 else if (ctx->seen_lf)
1263 {
1264 if (!(c == '\t' || c == ' '))
1265 {
1266 int rc = pdkim_header_complete(ctx); /* End of header */
1267 if (rc != PDKIM_OK) return rc;
1268 }
0d04a285 1269 ctx->seen_lf = FALSE;
3045f050 1270 }
80a47a2c 1271 }
3045f050
JH
1272
1273 if (!ctx->cur_header)
1274 if (!(ctx->cur_header = pdkim_strnew(NULL)))
1275 return PDKIM_ERR_OOM;
1276
1277 if (ctx->cur_header->len < PDKIM_MAX_HEADER_LEN)
1278 if (!pdkim_strncat(ctx->cur_header, &data[p], 1))
1279 return PDKIM_ERR_OOM;
80a47a2c
TK
1280 }
1281 }
3045f050 1282return PDKIM_OK;
6ab02e3f 1283}
80a47a2c 1284
05b7d6de
JB
1285/*
1286 * RFC 5322 specifies that header line length SHOULD be no more than 78
1287 * lets make it so!
1288 * pdkim_headcat
1289 * returns char*
1290 *
1291 * col: this int holds and receives column number (octets since last '\n')
1292 * str: partial string to append to
94431adb 1293 * pad: padding, split line or space after before or after eg: ";"
05b7d6de
JB
1294 * intro: - must join to payload eg "h=", usually the tag name
1295 * payload: eg base64 data - long data can be split arbitrarily.
1296 *
1297 * this code doesn't fold the header in some of the places that RFC4871
1298 * allows: As per RFC5322(2.2.3) it only folds before or after tag-value
1299 * pairs and inside long values. it also always spaces or breaks after the
94431adb 1300 * "pad"
05b7d6de
JB
1301 *
1302 * no guarantees are made for output given out-of range input. like tag
f444c2c7 1303 * names longer than 78, or bogus col. Input is assumed to be free of line breaks.
05b7d6de
JB
1304 */
1305
3045f050
JH
1306static char *
1307pdkim_headcat(int *col, pdkim_str *str, const char * pad,
1308 const char *intro, const char *payload)
1309{
1310size_t l;
1311
1312if (pad)
05b7d6de 1313 {
3045f050
JH
1314 l = strlen(pad);
1315 if (*col + l > 78)
05b7d6de 1316 {
3045f050
JH
1317 pdkim_strcat(str, "\r\n\t");
1318 *col = 1;
05b7d6de 1319 }
3045f050
JH
1320 pdkim_strncat(str, pad, l);
1321 *col += l;
05b7d6de
JB
1322 }
1323
3045f050 1324l = (pad?1:0) + (intro?strlen(intro):0);
05b7d6de 1325
3045f050 1326if (*col + l > 78)
05b7d6de 1327 { /*can't fit intro - start a new line to make room.*/
3045f050
JH
1328 pdkim_strcat(str, "\r\n\t");
1329 *col = 1;
1330 l = intro?strlen(intro):0;
05b7d6de
JB
1331 }
1332
3045f050 1333l += payload ? strlen(payload):0 ;
05b7d6de 1334
3045f050 1335while (l>77)
05b7d6de 1336 { /* this fragment will not fit on a single line */
3045f050 1337 if (pad)
05b7d6de 1338 {
3045f050
JH
1339 pdkim_strcat(str, " ");
1340 *col += 1;
1341 pad = NULL; /* only want this once */
1342 l--;
05b7d6de 1343 }
3045f050
JH
1344
1345 if (intro)
05b7d6de 1346 {
3045f050
JH
1347 size_t sl = strlen(intro);
1348
abe1010c 1349 pdkim_strncat(str, intro, sl);
3045f050
JH
1350 *col += sl;
1351 l -= sl;
1352 intro = NULL; /* only want this once */
05b7d6de 1353 }
3045f050
JH
1354
1355 if (payload)
05b7d6de 1356 {
3045f050
JH
1357 size_t sl = strlen(payload);
1358 size_t chomp = *col+sl < 77 ? sl : 78-*col;
1359
abe1010c 1360 pdkim_strncat(str, payload, chomp);
3045f050
JH
1361 *col += chomp;
1362 payload += chomp;
1363 l -= chomp-1;
05b7d6de 1364 }
3045f050
JH
1365
1366 /* the while precondition tells us it didn't fit. */
1367 pdkim_strcat(str, "\r\n\t");
1368 *col = 1;
05b7d6de 1369 }
3045f050
JH
1370
1371if (*col + l > 78)
05b7d6de 1372 {
3045f050
JH
1373 pdkim_strcat(str, "\r\n\t");
1374 *col = 1;
1375 pad = NULL;
05b7d6de
JB
1376 }
1377
3045f050 1378if (pad)
05b7d6de 1379 {
3045f050
JH
1380 pdkim_strcat(str, " ");
1381 *col += 1;
1382 pad = NULL;
05b7d6de
JB
1383 }
1384
3045f050 1385if (intro)
05b7d6de 1386 {
3045f050
JH
1387 size_t sl = strlen(intro);
1388
1389 pdkim_strncat(str, intro, sl);
1390 *col += sl;
1391 l -= sl;
1392 intro = NULL;
05b7d6de 1393 }
3045f050
JH
1394
1395if (payload)
05b7d6de 1396 {
3045f050
JH
1397 size_t sl = strlen(payload);
1398
1399 pdkim_strncat(str, payload, sl);
1400 *col += sl;
05b7d6de
JB
1401 }
1402
3045f050 1403return str->str;
05b7d6de 1404}
80a47a2c 1405
3045f050 1406
80a47a2c 1407/* -------------------------------------------------------------------------- */
3045f050 1408
f444c2c7 1409static char *
2592e6c0 1410pdkim_create_header(pdkim_signature *sig, BOOL final)
3045f050
JH
1411{
1412char *rc = NULL;
1413char *base64_bh = NULL;
1414char *base64_b = NULL;
1415int col = 0;
1416pdkim_str *hdr;
1417pdkim_str *canon_all;
1418
1419if (!(hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION)))
1420 return NULL;
1421
1422if (!(canon_all = pdkim_strnew(pdkim_canons[sig->canon_headers])))
1423 goto BAIL;
1424
2592e6c0 1425if (!(base64_bh = pdkim_encode_base64(&sig->bodyhash)))
3045f050
JH
1426 goto BAIL;
1427
1428col = strlen(hdr->str);
1429
1430/* Required and static bits */
abe1010c
JH
1431if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
1432 && pdkim_headcat(&col, hdr, ";", "q=", pdkim_querymethods[sig->querymethod])
1433 && pdkim_strcat(canon_all, "/")
1434 && pdkim_strcat(canon_all, pdkim_canons[sig->canon_body])
1435 && pdkim_headcat(&col, hdr, ";", "c=", canon_all->str)
1436 && pdkim_headcat(&col, hdr, ";", "d=", sig->domain)
1437 && pdkim_headcat(&col, hdr, ";", "s=", sig->selector)
3045f050
JH
1438 )
1439 {
2592e6c0 1440 /* list of header names can be split between items. */
05b7d6de 1441 {
2592e6c0 1442 char *n = CS string_copy(sig->headernames);
3045f050
JH
1443 char *f = n;
1444 char *i = "h=";
1445 char *s = ";";
1446
1447 if (!n) goto BAIL;
1448 while (*n)
05b7d6de 1449 {
abe1010c 1450 char *c = strchr(n, ':');
05b7d6de 1451
3045f050 1452 if (c) *c ='\0';
05b7d6de 1453
3045f050 1454 if (!i)
abe1010c 1455 if (!pdkim_headcat(&col, hdr, NULL, NULL, ":"))
3045f050 1456 {
3045f050
JH
1457 goto BAIL;
1458 }
1459
abe1010c 1460 if (!pdkim_headcat(&col, hdr, s, i, n))
3045f050 1461 {
3045f050
JH
1462 goto BAIL;
1463 }
1464
1465 if (!c)
1466 break;
1467
1468 n = c+1;
1469 s = NULL;
1470 i = NULL;
80a47a2c
TK
1471 }
1472 }
05b7d6de 1473
3045f050
JH
1474 if(!pdkim_headcat(&col, hdr, ";", "bh=", base64_bh))
1475 goto BAIL;
1476
1477 /* Optional bits */
1478 if (sig->identity)
1479 if(!pdkim_headcat(&col, hdr, ";", "i=", sig->identity))
1480 goto BAIL;
1481
1482 if (sig->created > 0)
1483 {
1484 char minibuf[20];
1485
abe1010c 1486 snprintf(minibuf, 20, "%lu", sig->created);
3045f050
JH
1487 if(!pdkim_headcat(&col, hdr, ";", "t=", minibuf))
1488 goto BAIL;
1489 }
1490
1491 if (sig->expires > 0)
1492 {
1493 char minibuf[20];
1494
abe1010c 1495 snprintf(minibuf, 20, "%lu", sig->expires);
3045f050
JH
1496 if(!pdkim_headcat(&col, hdr, ";", "x=", minibuf))
1497 goto BAIL;
80a47a2c 1498 }
3045f050
JH
1499
1500 if (sig->bodylength >= 0)
1501 {
1502 char minibuf[20];
1503
abe1010c 1504 snprintf(minibuf, 20, "%lu", sig->bodylength);
3045f050
JH
1505 if(!pdkim_headcat(&col, hdr, ";", "l=", minibuf))
1506 goto BAIL;
80a47a2c
TK
1507 }
1508
3045f050
JH
1509 /* Preliminary or final version? */
1510 if (final)
1511 {
2592e6c0 1512 if (!(base64_b = pdkim_encode_base64(&sig->sigdata)))
3045f050
JH
1513 goto BAIL;
1514 if (!pdkim_headcat(&col, hdr, ";", "b=", base64_b))
1515 goto BAIL;
1516 }
1517 else
1518 if(!pdkim_headcat(&col, hdr, ";", "b=", ""))
1519 goto BAIL;
05b7d6de 1520
3045f050 1521 /* add trailing semicolon: I'm not sure if this is actually needed */
abe1010c 1522 if (!pdkim_headcat(&col, hdr, NULL, ";", ""))
3045f050 1523 goto BAIL;
80a47a2c
TK
1524 }
1525
3045f050 1526rc = strdup(hdr->str);
80a47a2c 1527
3045f050
JH
1528BAIL:
1529pdkim_strfree(hdr);
1530if (canon_all) pdkim_strfree(canon_all);
3045f050 1531return rc;
80a47a2c
TK
1532}
1533
1534
1535/* -------------------------------------------------------------------------- */
3045f050
JH
1536
1537DLLEXPORT int
1538pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures)
1539{
1540pdkim_signature *sig = ctx->sig;
1541pdkim_str *headernames = NULL; /* Collected signed header names */
1542
1543/* Check if we must still flush a (partial) header. If that is the
1544 case, the message has no body, and we must compute a body hash
1545 out of '<CR><LF>' */
1546if (ctx->cur_header && ctx->cur_header->len)
1547 {
1548 int rc = pdkim_header_complete(ctx);
1549 if (rc != PDKIM_OK) return rc;
1550 pdkim_update_bodyhash(ctx, "\r\n", 2);
80a47a2c 1551 }
3045f050 1552else
0d04a285 1553 DEBUG(D_acl) debug_printf(
3045f050 1554 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1555
3045f050
JH
1556/* Build (and/or evaluate) body hash */
1557if (pdkim_finish_bodyhash(ctx) != PDKIM_OK)
1558 return PDKIM_ERR_OOM;
80a47a2c 1559
3045f050
JH
1560/* SIGNING -------------------------------------------------------------- */
1561if (ctx->mode == PDKIM_MODE_SIGN)
1562 if (!(headernames = pdkim_strnew(NULL)))
1563 return PDKIM_ERR_OOM;
1564/* ---------------------------------------------------------------------- */
80a47a2c 1565
3045f050
JH
1566while (sig)
1567 {
2592e6c0
JH
1568 BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
1569 hctx hhash_ctx;
1570 char * sig_hdr;
1571 blob hhash;
1572 blob hdata;
cb224393 1573 int hdata_alloc = 0;
cb224393 1574
2592e6c0
JH
1575 hdata.data = NULL;
1576 hdata.len = 0;
1577
1578 exim_sha_init(&hhash_ctx, is_sha1);
80a47a2c 1579
0d04a285
JH
1580 DEBUG(D_acl) debug_printf(
1581 "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
3045f050
JH
1582
1583 /* SIGNING ---------------------------------------------------------------- */
1584 /* When signing, walk through our header list and add them to the hash. As we
8ef02a06
JH
1585 go, construct a list of the header's names to use for the h= parameter.
1586 Then append to that list any remaining header names for which there was no
1587 header to sign. */
3045f050
JH
1588
1589 if (ctx->mode == PDKIM_MODE_SIGN)
1590 {
1591 pdkim_stringlist *p;
8ef02a06
JH
1592 const uschar * l;
1593 uschar * s;
1594 int sep = 0;
3045f050
JH
1595
1596 for (p = sig->headers; p; p = p->next)
1597 {
2592e6c0 1598 uschar * rh;
3045f050 1599 /* Collect header names (Note: colon presence is guaranteed here) */
2592e6c0 1600 uschar * q = Ustrchr(p->value, ':');
3045f050
JH
1601
1602 if (!(pdkim_strncat(headernames, p->value,
8ef02a06 1603 (q - US p->value) + (p->next ? 1 : 0))))
3045f050
JH
1604 return PDKIM_ERR_OOM;
1605
1606 rh = sig->canon_headers == PDKIM_CANON_RELAXED
2592e6c0
JH
1607 ? US pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
1608 : string_copy(p->value); /* just copy it for simple canon */
3045f050
JH
1609 if (!rh)
1610 return PDKIM_ERR_OOM;
1611
1612 /* Feed header to the hash algorithm */
2592e6c0 1613 exim_sha_update(&hhash_ctx, rh, strlen(rh));
f444c2c7 1614
2592e6c0
JH
1615 /* Remember headers block for signing (when the library cannot do incremental) */
1616 (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
3045f050 1617
2592e6c0 1618 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
80a47a2c 1619 }
8ef02a06
JH
1620
1621 l = US sig->sign_headers;
1622 while((s = string_nextinlist(&l, &sep, NULL, 0)))
1623 if (*s != '_')
1624 {
1625 if (headernames->len > 0)
1626 if (!(pdkim_strncat(headernames, ":", 1)))
1627 return PDKIM_ERR_OOM;
1628 if (!(pdkim_strncat(headernames, CS s, Ustrlen(s))))
1629 return PDKIM_ERR_OOM;
1630 }
80a47a2c 1631 }
37f8b554 1632
3045f050
JH
1633 /* VERIFICATION ----------------------------------------------------------- */
1634 /* When verifying, walk through the header name list in the h= parameter and
1635 add the headers to the hash in that order. */
1636 else
1637 {
2592e6c0
JH
1638 uschar * b = string_copy(sig->headernames);
1639 uschar * p = b;
1640 uschar * q;
1641 pdkim_stringlist * hdrs;
3045f050
JH
1642
1643 if (!b) return PDKIM_ERR_OOM;
1644
1645 /* clear tags */
1646 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1647 hdrs->tag = 0;
1648
1649 while(1)
1650 {
2592e6c0 1651 if ((q = Ustrchr(p, ':')))
3045f050
JH
1652 *q = '\0';
1653
1654 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1655 if ( hdrs->tag == 0
2592e6c0
JH
1656 && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0
1657 && (hdrs->value)[Ustrlen(p)] == ':'
3045f050
JH
1658 )
1659 {
2592e6c0
JH
1660 uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
1661 ? US pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
1662 : string_copy(hdrs->value); /* just copy it for simple canon */
3045f050
JH
1663 if (!rh)
1664 return PDKIM_ERR_OOM;
1665
1666 /* Feed header to the hash algorithm */
2592e6c0 1667 exim_sha_update(&hhash_ctx, rh, strlen(rh));
3045f050 1668
2592e6c0 1669 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
3045f050
JH
1670 hdrs->tag = 1;
1671 break;
1672 }
1673
1674 if (!q) break;
1675 p = q+1;
80a47a2c 1676 }
80a47a2c
TK
1677 }
1678
0d04a285 1679 DEBUG(D_acl) debug_printf(
3045f050 1680 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1681
3045f050
JH
1682 /* SIGNING ---------------------------------------------------------------- */
1683 if (ctx->mode == PDKIM_MODE_SIGN)
1684 {
1685 /* Copy headernames to signature struct */
2592e6c0 1686 sig->headernames = string_copy(US headernames->str);
3045f050 1687 pdkim_strfree(headernames);
80a47a2c 1688
3045f050 1689 /* Create signature header with b= omitted */
8ef02a06 1690 sig_hdr = pdkim_create_header(sig, FALSE);
80a47a2c 1691 }
80a47a2c 1692
3045f050
JH
1693 /* VERIFICATION ----------------------------------------------------------- */
1694 else
1695 sig_hdr = strdup(sig->rawsig_no_b_val);
1696 /* ------------------------------------------------------------------------ */
1697
1698 if (!sig_hdr)
1699 return PDKIM_ERR_OOM;
80a47a2c 1700
3045f050
JH
1701 /* Relax header if necessary */
1702 if (sig->canon_headers == PDKIM_CANON_RELAXED)
1703 {
1704 char *relaxed_hdr = pdkim_relax_header(sig_hdr, 0);
1705
1706 free(sig_hdr);
1707 if (!relaxed_hdr)
1708 return PDKIM_ERR_OOM;
1709 sig_hdr = relaxed_hdr;
80a47a2c
TK
1710 }
1711
0d04a285 1712 DEBUG(D_acl)
3045f050 1713 {
0d04a285 1714 debug_printf(
3045f050 1715 "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
2592e6c0 1716 pdkim_quoteprint(sig_hdr, strlen(sig_hdr));
0d04a285 1717 debug_printf(
3045f050 1718 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
80a47a2c 1719 }
3045f050
JH
1720
1721 /* Finalize header hash */
2592e6c0
JH
1722 exim_sha_update(&hhash_ctx, sig_hdr, strlen(sig_hdr));
1723 exim_sha_finish(&hhash_ctx, &hhash);
3045f050 1724
f444c2c7
JH
1725 DEBUG(D_acl)
1726 {
1727 debug_printf("PDKIM [%s] hh computed: ", sig->domain);
2592e6c0 1728 pdkim_hexprint(hhash.data, hhash.len);
80a47a2c
TK
1729 }
1730
2592e6c0 1731 /* Remember headers block for signing (when the library cannot do incremental) */
f444c2c7 1732 if (ctx->mode == PDKIM_MODE_SIGN)
2592e6c0 1733 (void) exim_rsa_data_append(&hdata, &hdata_alloc, sig_hdr);
f444c2c7 1734
3045f050 1735 free(sig_hdr);
80a47a2c 1736
3045f050
JH
1737 /* SIGNING ---------------------------------------------------------------- */
1738 if (ctx->mode == PDKIM_MODE_SIGN)
1739 {
2592e6c0
JH
1740 es_ctx sctx;
1741 const uschar * errstr;
f444c2c7
JH
1742
1743 /* Import private key */
2592e6c0 1744 if ((errstr = exim_rsa_signing_init(sig->rsa_privkey, &sctx)))
f444c2c7 1745 {
2592e6c0 1746 DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
f444c2c7
JH
1747 return PDKIM_ERR_RSA_PRIVKEY;
1748 }
80a47a2c 1749
2592e6c0
JH
1750 /* Do signing. With OpenSSL we are signing the hash of headers just
1751 calculated, with GnuTLS we have to sign an entire block of headers
1752 (due to available interfaces) and it recalculates the hash internally. */
f444c2c7 1753
2592e6c0
JH
1754#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
1755 hdata = hhash;
f444c2c7
JH
1756#endif
1757
2592e6c0 1758 if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
f444c2c7 1759 {
2592e6c0 1760 DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
f444c2c7
JH
1761 return PDKIM_ERR_RSA_SIGNING;
1762 }
80a47a2c 1763
0d04a285 1764 DEBUG(D_acl)
3045f050 1765 {
0d04a285 1766 debug_printf( "PDKIM [%s] b computed: ", sig->domain);
2592e6c0 1767 pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
80a47a2c 1768 }
80a47a2c 1769
8ef02a06 1770 if (!(sig->signature_header = pdkim_create_header(sig, TRUE)))
3045f050 1771 return PDKIM_ERR_OOM;
80a47a2c 1772 }
80a47a2c 1773
3045f050
JH
1774 /* VERIFICATION ----------------------------------------------------------- */
1775 else
1776 {
2592e6c0
JH
1777 ev_ctx vctx;
1778 const uschar * errstr;
3045f050 1779
2592e6c0 1780 char *dns_txt_name, *dns_txt_reply;
f444c2c7
JH
1781
1782 /* Fetch public key for signing domain, from DNS */
3045f050
JH
1783
1784 if (!(dns_txt_name = malloc(PDKIM_DNS_TXT_MAX_NAMELEN)))
1785 return PDKIM_ERR_OOM;
1786
1787 if (!(dns_txt_reply = malloc(PDKIM_DNS_TXT_MAX_RECLEN)))
1788 {
1789 free(dns_txt_name);
1790 return PDKIM_ERR_OOM;
80a47a2c
TK
1791 }
1792
3045f050
JH
1793 memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
1794 memset(dns_txt_name , 0, PDKIM_DNS_TXT_MAX_NAMELEN);
1795
abe1010c 1796 if (snprintf(dns_txt_name, PDKIM_DNS_TXT_MAX_NAMELEN,
3045f050 1797 "%s._domainkey.%s.",
abe1010c 1798 sig->selector, sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN)
3045f050
JH
1799 {
1800 sig->verify_status = PDKIM_VERIFY_INVALID;
1801 sig->verify_ext_status = PDKIM_VERIFY_INVALID_BUFFER_SIZE;
1802 goto NEXT_VERIFY;
80a47a2c 1803 }
3045f050
JH
1804
1805 if ( ctx->dns_txt_callback(dns_txt_name, dns_txt_reply) != PDKIM_OK
1806 || dns_txt_reply[0] == '\0')
1807 {
1808 sig->verify_status = PDKIM_VERIFY_INVALID;
1809 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
1810 goto NEXT_VERIFY;
80a47a2c
TK
1811 }
1812
0d04a285 1813 DEBUG(D_acl)
3045f050 1814 {
0d04a285 1815 debug_printf(
2592e6c0
JH
1816 "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
1817 " Raw record: ");
1818 pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply));
80a47a2c 1819 }
3045f050 1820
abe1010c 1821 if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, dns_txt_reply)))
3045f050
JH
1822 {
1823 sig->verify_status = PDKIM_VERIFY_INVALID;
df3def24 1824 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
3045f050 1825
0d04a285
JH
1826 DEBUG(D_acl) debug_printf(
1827 " Error while parsing public key record\n"
3045f050 1828 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
3045f050 1829 goto NEXT_VERIFY;
80a47a2c
TK
1830 }
1831
0d04a285 1832 DEBUG(D_acl) debug_printf(
2592e6c0 1833 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
3045f050 1834
f444c2c7 1835 /* Import public key */
2592e6c0 1836 if ((errstr = exim_rsa_verify_init(&sig->pubkey->key, &vctx)))
3045f050 1837 {
2592e6c0 1838 DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
3045f050 1839 sig->verify_status = PDKIM_VERIFY_INVALID;
df3def24 1840 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
3045f050 1841 goto NEXT_VERIFY;
80a47a2c
TK
1842 }
1843
3045f050 1844 /* Check the signature */
2592e6c0 1845 if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
3045f050 1846 {
2592e6c0 1847 DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
3045f050
JH
1848 sig->verify_status = PDKIM_VERIFY_FAIL;
1849 sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
1850 goto NEXT_VERIFY;
ff7ddfd7
TK
1851 }
1852
2592e6c0 1853
3045f050
JH
1854 /* We have a winner! (if bodydhash was correct earlier) */
1855 if (sig->verify_status == PDKIM_VERIFY_NONE)
1856 sig->verify_status = PDKIM_VERIFY_PASS;
1857
1858NEXT_VERIFY:
1859
0d04a285 1860 DEBUG(D_acl)
3045f050 1861 {
0d04a285 1862 debug_printf("PDKIM [%s] signature status: %s",
3045f050
JH
1863 sig->domain, pdkim_verify_status_str(sig->verify_status));
1864 if (sig->verify_ext_status > 0)
0d04a285 1865 debug_printf(" (%s)\n",
3045f050
JH
1866 pdkim_verify_ext_status_str(sig->verify_ext_status));
1867 else
0d04a285 1868 debug_printf("\n");
80a47a2c 1869 }
80a47a2c 1870
3045f050
JH
1871 free(dns_txt_name);
1872 free(dns_txt_reply);
80a47a2c
TK
1873 }
1874
3045f050 1875 sig = sig->next;
80a47a2c
TK
1876 }
1877
3045f050
JH
1878/* If requested, set return pointer to signature(s) */
1879if (return_signatures)
1880 *return_signatures = ctx->sig;
80a47a2c 1881
3045f050 1882return PDKIM_OK;
80a47a2c
TK
1883}
1884
1885
1886/* -------------------------------------------------------------------------- */
3045f050
JH
1887
1888DLLEXPORT pdkim_ctx *
0d04a285 1889pdkim_init_verify(int(*dns_txt_callback)(char *, char *))
3045f050
JH
1890{
1891pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx));
1892
1893if (!ctx)
1894 return NULL;
abe1010c 1895memset(ctx, 0, sizeof(pdkim_ctx));
3045f050
JH
1896
1897if (!(ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN)))
1898 {
1899 free(ctx);
1900 return NULL;
80a47a2c
TK
1901 }
1902
3045f050 1903ctx->mode = PDKIM_MODE_VERIFY;
3045f050 1904ctx->dns_txt_callback = dns_txt_callback;
80a47a2c 1905
3045f050 1906return ctx;
80a47a2c
TK
1907}
1908
1909
1910/* -------------------------------------------------------------------------- */
80a47a2c 1911
3045f050 1912DLLEXPORT pdkim_ctx *
f444c2c7 1913pdkim_init_sign(char *domain, char *selector, char *rsa_privkey, int algo)
3045f050
JH
1914{
1915pdkim_ctx *ctx;
1916pdkim_signature *sig;
80a47a2c 1917
3045f050
JH
1918if (!domain || !selector || !rsa_privkey)
1919 return NULL;
80a47a2c 1920
3045f050
JH
1921if (!(ctx = malloc(sizeof(pdkim_ctx))))
1922 return NULL;
abe1010c 1923memset(ctx, 0, sizeof(pdkim_ctx));
80a47a2c 1924
3045f050
JH
1925if (!(ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN)))
1926 {
1927 free(ctx);
1928 return NULL;
80a47a2c
TK
1929 }
1930
3045f050
JH
1931if (!(sig = malloc(sizeof(pdkim_signature))))
1932 {
1933 free(ctx->linebuf);
1934 free(ctx);
1935 return NULL;
80a47a2c 1936 }
abe1010c 1937memset(sig, 0, sizeof(pdkim_signature));
80a47a2c 1938
3045f050 1939sig->bodylength = -1;
80a47a2c 1940
3045f050 1941ctx->mode = PDKIM_MODE_SIGN;
3045f050 1942ctx->sig = sig;
80a47a2c 1943
2592e6c0
JH
1944sig->domain = strdup(domain);
1945sig->selector = strdup(selector);
1946sig->rsa_privkey = strdup(rsa_privkey);
1947sig->algo = algo;
cb224393 1948
2592e6c0 1949if (!sig->domain || !sig->selector || !sig->rsa_privkey)
cb224393 1950 goto BAIL;
3045f050 1951
2592e6c0 1952exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1);
3045f050
JH
1953return ctx;
1954
1955BAIL:
1956 pdkim_free_ctx(ctx);
1957 return NULL;
6ab02e3f 1958}
80a47a2c 1959
f444c2c7 1960
80a47a2c 1961/* -------------------------------------------------------------------------- */
3045f050
JH
1962
1963DLLEXPORT int
1964pdkim_set_optional(pdkim_ctx *ctx,
80a47a2c
TK
1965 char *sign_headers,
1966 char *identity,
1967 int canon_headers,
1968 int canon_body,
1969 long bodylength,
80a47a2c 1970 unsigned long created,
3045f050
JH
1971 unsigned long expires)
1972{
8ef02a06
JH
1973pdkim_signature * sig = ctx->sig;
1974
3045f050 1975if (identity)
8ef02a06 1976 if (!(sig->identity = strdup(identity)))
3045f050 1977 return PDKIM_ERR_OOM;
80a47a2c 1978
8ef02a06
JH
1979if (!(sig->sign_headers = strdup(sign_headers
1980 ? sign_headers : PDKIM_DEFAULT_SIGN_HEADERS)))
1981 return PDKIM_ERR_OOM;
80a47a2c 1982
8ef02a06
JH
1983sig->canon_headers = canon_headers;
1984sig->canon_body = canon_body;
1985sig->bodylength = bodylength;
1986sig->created = created;
1987sig->expires = expires;
80a47a2c 1988
3045f050 1989return PDKIM_OK;
6ab02e3f 1990}
3045f050 1991
3045f050 1992
2592e6c0
JH
1993void
1994pdkim_init(void)
1995{
1996exim_rsa_init();
1997}
1998
1999
2000
f444c2c7 2001#endif /*DISABLE_DKIM*/