2 * PDKIM - a RFC4871 (DKIM) implementation
4 * Copyright (C) 2009 - 2016 Tom Kistner <tom@duncanthrax.net>
5 * Copyright (C) 2016 Jeremy Harris <jgh@exim.org>
7 * http://duncanthrax.net/pdkim/
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.
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.
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.
27 #ifndef DISABLE_DKIM /* entire file */
30 # error Need SUPPORT_TLS for DKIM
33 #include "crypt_ver.h"
36 # include <openssl/rsa.h>
37 # include <openssl/ssl.h>
38 # include <openssl/err.h>
39 #elif defined(RSA_GNUTLS)
40 # include <gnutls/gnutls.h>
41 # include <gnutls/x509.h>
47 #define PDKIM_SIGNATURE_VERSION "1"
48 #define PDKIM_PUB_RECORD_VERSION US "DKIM1"
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"
63 /* -------------------------------------------------------------------------- */
64 struct pdkim_stringlist {
70 /* -------------------------------------------------------------------------- */
71 /* A bunch of list constants */
72 const uschar * pdkim_querymethods[] = {
76 const uschar * pdkim_algos[] = {
81 const uschar * pdkim_canons[] = {
86 const uschar * pdkim_hashes[] = {
91 const uschar * pdkim_keytypes[] = {
96 typedef struct pdkim_combined_canon_entry {
100 } pdkim_combined_canon_entry;
102 pdkim_combined_canon_entry pdkim_combined_canons[] = {
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 },
113 /* -------------------------------------------------------------------------- */
116 pdkim_verify_status_str(int status)
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";
129 pdkim_verify_ext_status_str(int ext_status)
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";
146 pdkim_errstr(int status)
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)";
163 /* -------------------------------------------------------------------------- */
164 /* Print debugging functions */
166 pdkim_quoteprint(const uschar *data, int len)
169 for (i = 0; i < len; i++)
171 const int c = data[i];
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;
181 if ( (c < 32) || (c > 127) )
182 debug_printf("{%02x}", c);
184 debug_printf("%c", c);
192 pdkim_hexprint(const uschar *data, int len)
195 if (data) for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
196 else debug_printf("<NULL>");
202 static pdkim_stringlist *
203 pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str)
205 pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist));
207 memset(new_entry, 0, sizeof(pdkim_stringlist));
208 new_entry->value = string_copy(str);
209 if (base) new_entry->next = base;
215 /* Trim whitespace fore & aft */
218 pdkim_strtrim(uschar * str)
222 while (*p == '\t' || *p == ' ') p++; /* skip whitespace */
223 while (*p) {*q = *p; q++; p++;} /* dump the leading whitespace */
225 while (q != str && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) )
226 { /* dump trailing whitespace */
234 /* -------------------------------------------------------------------------- */
237 pdkim_free_ctx(pdkim_ctx *ctx)
242 /* -------------------------------------------------------------------------- */
243 /* Matches the name of the passed raw "header" against
244 the passed colon-separated "tick", and invalidates
245 the entry in tick. Returns OK or fail-code */
246 /*XXX might be safer done using a pdkim_stringlist for "tick" */
249 header_name_match(const uschar * header, uschar * tick)
255 uschar * hcolon = Ustrchr(header, ':'); /* Get header name */
258 return PDKIM_FAIL; /* This isn't a header */
260 /* if we had strncmpic() we wouldn't need this copy */
261 hname = string_copyn(header, hcolon-header);
263 /* Copy tick-off list locally, so we can punch zeroes into it */
264 p = lcopy = string_copy(tick);
266 for (q = Ustrchr(p, ':'); q; q = Ustrchr(p, ':'))
269 if (strcmpic(p, hname) == 0)
275 if (strcmpic(p, hname) == 0)
281 /* Invalidate header name instance in tick-off list */
287 /* -------------------------------------------------------------------------- */
288 /* Performs "relaxed" canonicalization of a header. */
291 pdkim_relax_header(const uschar * header, int crlf)
293 BOOL past_field_name = FALSE;
294 BOOL seen_wsp = FALSE;
296 uschar * relaxed = store_get(Ustrlen(header)+3);
297 uschar * q = relaxed;
299 for (p = header; *p; p++)
303 if (c == '\r' || c == '\n')
305 if (c == '\t' || c == ' ')
309 c = ' '; /* Turns WSP into SP */
313 if (!past_field_name && c == ':')
315 if (seen_wsp) q--; /* This removes WSP before the colon */
316 seen_wsp = TRUE; /* This removes WSP after the colon */
317 past_field_name = TRUE;
322 /* Lowercase header name */
323 if (!past_field_name) c = tolower(c);
327 if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */
329 if (crlf) { *q++ = '\r'; *q++ = '\n'; }
335 /* -------------------------------------------------------------------------- */
336 #define PDKIM_QP_ERROR_DECODE -1
339 pdkim_decode_qp_char(uschar *qp_p, int *c)
341 uschar *initial_pos = qp_p;
343 /* Advance one char */
346 /* Check for two hex digits and decode them */
347 if (isxdigit(*qp_p) && isxdigit(qp_p[1]))
349 /* Do hex conversion */
350 *c = (isdigit(*qp_p) ? *qp_p - '0' : toupper(*qp_p) - 'A' + 10) << 4;
351 *c |= isdigit(qp_p[1]) ? qp_p[1] - '0' : toupper(qp_p[1]) - 'A' + 10;
355 /* Illegal char here */
356 *c = PDKIM_QP_ERROR_DECODE;
361 /* -------------------------------------------------------------------------- */
364 pdkim_decode_qp(uschar * str)
369 uschar * n = store_get(Ustrlen(str)+1);
377 p = pdkim_decode_qp_char(p, &nchar);
393 /* -------------------------------------------------------------------------- */
396 pdkim_decode_base64(uschar *str, blob * b)
399 dlen = b64decode(str, &b->data);
400 if (dlen < 0) b->data = NULL;
405 pdkim_encode_base64(blob * b)
407 return b64encode(b->data, b->len);
411 /* -------------------------------------------------------------------------- */
412 #define PDKIM_HDR_LIMBO 0
413 #define PDKIM_HDR_TAG 1
414 #define PDKIM_HDR_VALUE 2
416 static pdkim_signature *
417 pdkim_parse_sig_header(pdkim_ctx *ctx, uschar * raw_hdr)
419 pdkim_signature *sig ;
421 uschar * cur_tag = NULL; int ts = 0, tl = 0;
422 uschar * cur_val = NULL; int vs = 0, vl = 0;
423 BOOL past_hname = FALSE;
424 BOOL in_b_val = FALSE;
425 int where = PDKIM_HDR_LIMBO;
428 sig = store_get(sizeof(pdkim_signature));
429 memset(sig, 0, sizeof(pdkim_signature));
430 sig->bodylength = -1;
432 /* Set so invalid/missing data error display is accurate */
436 q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
438 for (p = raw_hdr; ; p++)
443 if (c == '\r' || c == '\n')
446 /* Fast-forward through header name */
449 if (c == ':') past_hname = TRUE;
453 if (where == PDKIM_HDR_LIMBO)
455 /* In limbo, just wait for a tag-char to appear */
456 if (!(c >= 'a' && c <= 'z'))
459 where = PDKIM_HDR_TAG;
462 if (where == PDKIM_HDR_TAG)
464 if (c >= 'a' && c <= 'z')
465 cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
470 if (Ustrcmp(cur_tag, "b") == 0)
475 where = PDKIM_HDR_VALUE;
480 if (where == PDKIM_HDR_VALUE)
482 if (c == '\r' || c == '\n' || c == ' ' || c == '\t')
485 if (c == ';' || c == '\0')
490 pdkim_strtrim(cur_val);
492 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val);
497 pdkim_decode_base64(cur_val,
498 cur_tag[1] == 'h' ? &sig->bodyhash : &sig->sighash);
501 /* We only support version 1, and that is currently the
502 only version there is. */
504 Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1;
507 for (i = 0; pdkim_algos[i]; i++)
508 if (Ustrcmp(cur_val, pdkim_algos[i]) == 0)
515 for (i = 0; pdkim_combined_canons[i].str; i++)
516 if (Ustrcmp(cur_val, pdkim_combined_canons[i].str) == 0)
518 sig->canon_headers = pdkim_combined_canons[i].canon_headers;
519 sig->canon_body = pdkim_combined_canons[i].canon_body;
524 for (i = 0; pdkim_querymethods[i]; i++)
525 if (Ustrcmp(cur_val, pdkim_querymethods[i]) == 0)
527 sig->querymethod = i;
532 sig->selector = string_copy(cur_val); break;
534 sig->domain = string_copy(cur_val); break;
536 sig->identity = pdkim_decode_qp(cur_val); break;
538 sig->created = strtoul(CS cur_val, NULL, 10); break;
540 sig->expires = strtoul(CS cur_val, NULL, 10); break;
542 sig->bodylength = strtol(CS cur_val, NULL, 10); break;
544 sig->headernames = string_copy(cur_val); break;
546 sig->copiedheaders = pdkim_decode_qp(cur_val); break;
548 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
555 where = PDKIM_HDR_LIMBO;
558 cur_val = string_catn(cur_val, &vs, &vl, p, 1);
570 /* Chomp raw header. The final newline must not be added to the signature. */
571 while (--q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n'))
577 "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
578 pdkim_quoteprint(US sig->rawsig_no_b_val, Ustrlen(sig->rawsig_no_b_val));
580 "PDKIM >> Sig size: %4u bits\n", (unsigned) sig->sighash.len*8);
582 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
585 exim_sha_init(&sig->body_hash_ctx,
586 sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
591 /* -------------------------------------------------------------------------- */
593 static pdkim_pubkey *
594 pdkim_parse_pubkey_record(pdkim_ctx *ctx, const uschar *raw_record)
598 uschar * cur_tag = NULL; int ts = 0, tl = 0;
599 uschar * cur_val = NULL; int vs = 0, vl = 0;
600 int where = PDKIM_HDR_LIMBO;
602 pub = store_get(sizeof(pdkim_pubkey));
603 memset(pub, 0, sizeof(pdkim_pubkey));
605 for (p = raw_record; ; p++)
610 if (c != '\r' && c != '\n') switch (where)
612 case PDKIM_HDR_LIMBO: /* In limbo, just wait for a tag-char to appear */
613 if (!(c >= 'a' && c <= 'z'))
615 where = PDKIM_HDR_TAG;
619 if (c >= 'a' && c <= 'z')
620 cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
625 where = PDKIM_HDR_VALUE;
629 case PDKIM_HDR_VALUE:
630 if (c == ';' || c == '\0')
635 pdkim_strtrim(cur_val);
636 DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val);
641 pub->version = string_copy(cur_val); break;
644 /* This field appears to never be used. Also, unclear why
645 a '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);
651 pub->granularity = string_copy(cur_val); break;
653 pub->notes = pdkim_decode_qp(cur_val); break;
655 pdkim_decode_base64(US cur_val, &pub->key); break;
657 pub->srvtype = string_copy(cur_val); break;
659 if (Ustrchr(cur_val, 'y') != NULL) pub->testing = 1;
660 if (Ustrchr(cur_val, 's') != NULL) pub->no_subdomaining = 1;
663 DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
669 where = PDKIM_HDR_LIMBO;
672 cur_val = string_catn(cur_val, &vs, &vl, p, 1);
676 if (c == '\0') break;
679 /* Set fallback defaults */
680 if (!pub->version ) pub->version = string_copy(PDKIM_PUB_RECORD_VERSION);
681 else if (Ustrcmp(pub->version, PDKIM_PUB_RECORD_VERSION) != 0) return NULL;
683 if (!pub->granularity) pub->granularity = string_copy(US"*");
685 if (!pub->keytype ) pub->keytype = string_copy(US"rsa");
687 if (!pub->srvtype ) pub->srvtype = string_copy(US"*");
697 /* -------------------------------------------------------------------------- */
700 pdkim_update_bodyhash(pdkim_ctx * ctx, const char * data, int len)
702 pdkim_signature * sig;
703 uschar * relaxed_data = NULL; /* Cache relaxed version of data */
706 /* Traverse all signatures, updating their hashes. */
707 for (sig = ctx->sig; sig; sig = sig->next)
709 /* Defaults to simple canon (no further treatment necessary) */
710 const uschar *canon_data = CUS data;
713 if (sig->canon_body == PDKIM_CANON_RELAXED)
715 /* Relax the line if not done already */
718 BOOL seen_wsp = FALSE;
722 /* We want to be able to free this else we allocate
723 for the entire message which could be many MB. Since
724 we don't know what allocations the SHA routines might
725 do, not safe to use store_get()/store_reset(). */
727 relaxed_data = store_malloc(len+1);
729 for (p = data; *p; p++)
734 if (q > 0 && relaxed_data[q-1] == ' ')
737 else if (c == '\t' || c == ' ')
739 c = ' '; /* Turns WSP into SP */
746 relaxed_data[q++] = c;
748 relaxed_data[q] = '\0';
751 canon_data = relaxed_data;
752 canon_len = relaxed_len;
755 /* Make sure we don't exceed the to-be-signed body length */
756 if ( sig->bodylength >= 0
757 && sig->signed_body_bytes + (unsigned long)canon_len > sig->bodylength
759 canon_len = sig->bodylength - sig->signed_body_bytes;
763 exim_sha_update(&sig->body_hash_ctx, CUS canon_data, canon_len);
764 sig->signed_body_bytes += canon_len;
765 DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
769 if (relaxed_data) store_free(relaxed_data);
774 /* -------------------------------------------------------------------------- */
777 pdkim_finish_bodyhash(pdkim_ctx *ctx)
779 pdkim_signature *sig;
781 /* Traverse all signatures */
782 for (sig = ctx->sig; sig; sig = sig->next)
783 { /* Finish hashes */
786 exim_sha_finish(&sig->body_hash_ctx, &bh);
790 debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
791 "PDKIM [%s] Body hash computed: ",
792 sig->domain, sig->signed_body_bytes, sig->domain);
793 pdkim_hexprint(CUS bh.data, bh.len);
796 /* SIGNING -------------------------------------------------------------- */
797 if (ctx->flags & PDKIM_MODE_SIGN)
801 /* If bodylength limit is set, and we have received less bytes
802 than the requested amount, effectively remove the limit tag. */
803 if (sig->signed_body_bytes < sig->bodylength)
804 sig->bodylength = -1;
808 /* VERIFICATION --------------------------------------------------------- */
809 /* Be careful that the header sig included a bodyash */
811 if (sig->bodyhash.data && memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
813 DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
819 debug_printf("PDKIM [%s] Body hash signature from headers: ", sig->domain);
820 pdkim_hexprint(sig->bodyhash.data, sig->bodyhash.len);
821 debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
823 sig->verify_status = PDKIM_VERIFY_FAIL;
824 sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
832 pdkim_body_complete(pdkim_ctx * ctx)
834 pdkim_signature * sig = ctx->sig; /*XXX assumes only one sig */
836 /* In simple body mode, if any empty lines were buffered,
837 replace with one. rfc 4871 3.4.3 */
838 /*XXX checking the signed-body-bytes is a gross hack; I think
839 it indicates that all linebreaks should be buffered, including
840 the one terminating a text line */
842 if ( sig && sig->canon_body == PDKIM_CANON_SIMPLE
843 && sig->signed_body_bytes == 0
844 && ctx->num_buffered_crlf > 0
846 pdkim_update_bodyhash(ctx, "\r\n", 2);
848 ctx->flags |= PDKIM_SEEN_EOD;
849 ctx->linebuf_offset = 0;
855 /* -------------------------------------------------------------------------- */
856 /* Call from pdkim_feed below for processing complete body lines */
859 pdkim_bodyline_complete(pdkim_ctx *ctx)
861 char *p = ctx->linebuf;
862 int n = ctx->linebuf_offset;
863 pdkim_signature *sig = ctx->sig; /*XXX assumes only one sig */
865 /* Ignore extra data if we've seen the end-of-data marker */
866 if (ctx->flags & PDKIM_SEEN_EOD) goto BAIL;
868 /* We've always got one extra byte to stuff a zero ... */
869 ctx->linebuf[ctx->linebuf_offset] = '\0';
871 /* Terminate on EOD marker */
872 if (ctx->flags & PDKIM_DOT_TERM)
874 if (memcmp(p, ".\r\n", 3) == 0)
875 return pdkim_body_complete(ctx);
878 if (memcmp(p, "..", 2) == 0)
885 /* Empty lines need to be buffered until we find a non-empty line */
886 if (memcmp(p, "\r\n", 2) == 0)
888 ctx->num_buffered_crlf++;
892 if (sig && sig->canon_body == PDKIM_CANON_RELAXED)
894 /* Lines with just spaces need to be buffered too */
896 while (memcmp(check, "\r\n", 2) != 0)
900 if (c != '\t' && c != ' ')
905 ctx->num_buffered_crlf++;
910 /* At this point, we have a non-empty line, so release the buffered ones. */
911 while (ctx->num_buffered_crlf)
913 pdkim_update_bodyhash(ctx, "\r\n", 2);
914 ctx->num_buffered_crlf--;
917 pdkim_update_bodyhash(ctx, p, n);
920 ctx->linebuf_offset = 0;
925 /* -------------------------------------------------------------------------- */
926 /* Callback from pdkim_feed below for processing complete headers */
927 #define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
930 pdkim_header_complete(pdkim_ctx *ctx)
932 /* Special case: The last header can have an extra \r appended */
933 if ( (ctx->cur_header_len > 1) &&
934 (ctx->cur_header[(ctx->cur_header_len)-1] == '\r') )
935 --ctx->cur_header_len;
936 ctx->cur_header[ctx->cur_header_len] = '\0';
939 if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
941 /* SIGNING -------------------------------------------------------------- */
942 if (ctx->flags & PDKIM_MODE_SIGN)
944 pdkim_signature *sig;
946 for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */
948 /* Add header to the signed headers list (in reverse order) */
949 sig->headers = pdkim_prepend_stringlist(sig->headers,
953 /* VERIFICATION ----------------------------------------------------------- */
954 /* DKIM-Signature: headers are added to the verification list */
959 debug_printf("PDKIM >> raw hdr: ");
960 pdkim_quoteprint(CUS ctx->cur_header, Ustrlen(ctx->cur_header));
962 if (strncasecmp(CCS ctx->cur_header,
963 DKIM_SIGNATURE_HEADERNAME,
964 Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
966 pdkim_signature * new_sig, * last_sig;
968 /* Create and chain new signature block. We could error-check for all
969 required tags here, but prefer to create the internal sig and expicitly
970 fail verification of it later. */
972 DEBUG(D_acl) debug_printf(
973 "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
975 new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header);
977 if (!(last_sig = ctx->sig))
981 while (last_sig->next) last_sig = last_sig->next;
982 last_sig->next = new_sig;
986 /* all headers are stored for signature verification */
987 ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header);
991 *ctx->cur_header = '\0';
992 ctx->cur_header_len = 0; /* leave buffer for reuse */
998 /* -------------------------------------------------------------------------- */
999 #define HEADER_BUFFER_FRAG_SIZE 256
1002 pdkim_feed(pdkim_ctx *ctx, char *data, int len)
1006 /* Alternate EOD signal, used in non-dotstuffing mode */
1008 pdkim_body_complete(ctx);
1010 else for (p = 0; p<len; p++)
1014 if (ctx->flags & PDKIM_PAST_HDRS)
1016 if (c == '\n' && !(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
1018 ctx->linebuf[ctx->linebuf_offset++] = '\r';
1019 if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
1020 return PDKIM_ERR_LONG_LINE;
1023 /* Processing body byte */
1024 ctx->linebuf[ctx->linebuf_offset++] = c;
1026 ctx->flags |= PDKIM_SEEN_CR;
1029 ctx->flags &= ~PDKIM_SEEN_CR;
1030 if ((rc = pdkim_bodyline_complete(ctx)) != PDKIM_OK)
1034 if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
1035 return PDKIM_ERR_LONG_LINE;
1039 /* Processing header byte */
1041 ctx->flags |= PDKIM_SEEN_CR;
1044 if (!(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
1045 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
1046 &ctx->cur_header_len, CUS "\r", 1);
1048 if (ctx->flags & PDKIM_SEEN_LF) /* Seen last header line */
1050 if ((rc = pdkim_header_complete(ctx)) != PDKIM_OK)
1053 ctx->flags = ctx->flags & ~(PDKIM_SEEN_LF|PDKIM_SEEN_CR) | PDKIM_PAST_HDRS;
1054 DEBUG(D_acl) debug_printf(
1055 "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
1059 ctx->flags = ctx->flags & ~PDKIM_SEEN_CR | PDKIM_SEEN_LF;
1061 else if (ctx->flags & PDKIM_SEEN_LF)
1063 if (!(c == '\t' || c == ' ')) /* End of header */
1064 if ((rc = pdkim_header_complete(ctx)) != PDKIM_OK)
1066 ctx->flags &= ~PDKIM_SEEN_LF;
1069 if (ctx->cur_header_len < PDKIM_MAX_HEADER_LEN)
1070 ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
1071 &ctx->cur_header_len, CUS &data[p], 1);
1079 /* Extend a grwong header with a continuation-linebreak */
1081 pdkim_hdr_cont(uschar * str, int * size, int * ptr, int * col)
1084 return string_catn(str, size, ptr, US"\r\n\t", 3);
1090 * RFC 5322 specifies that header line length SHOULD be no more than 78
1094 * returns uschar * (not nul-terminated)
1096 * col: this int holds and receives column number (octets since last '\n')
1097 * str: partial string to append to
1098 * size: current buffer size for str
1099 * ptr: current tail-pointer for str
1100 * pad: padding, split line or space after before or after eg: ";"
1101 * intro: - must join to payload eg "h=", usually the tag name
1102 * payload: eg base64 data - long data can be split arbitrarily.
1104 * this code doesn't fold the header in some of the places that RFC4871
1105 * allows: As per RFC5322(2.2.3) it only folds before or after tag-value
1106 * pairs and inside long values. it also always spaces or breaks after the
1109 * no guarantees are made for output given out-of range input. like tag
1110 * names longer than 78, or bogus col. Input is assumed to be free of line breaks.
1114 pdkim_headcat(int * col, uschar * str, int * size, int * ptr,
1115 const uschar * pad, const uschar * intro, const uschar * payload)
1123 str = pdkim_hdr_cont(str, size, ptr, col);
1124 str = string_catn(str, size, ptr, pad, l);
1128 l = (pad?1:0) + (intro?Ustrlen(intro):0);
1131 { /*can't fit intro - start a new line to make room.*/
1132 str = pdkim_hdr_cont(str, size, ptr, col);
1133 l = intro?Ustrlen(intro):0;
1136 l += payload ? Ustrlen(payload):0 ;
1139 { /* this fragment will not fit on a single line */
1142 str = string_catn(str, size, ptr, US" ", 1);
1144 pad = NULL; /* only want this once */
1150 size_t sl = Ustrlen(intro);
1152 str = string_catn(str, size, ptr, intro, sl);
1155 intro = NULL; /* only want this once */
1160 size_t sl = Ustrlen(payload);
1161 size_t chomp = *col+sl < 77 ? sl : 78-*col;
1163 str = string_catn(str, size, ptr, payload, chomp);
1169 /* the while precondition tells us it didn't fit. */
1170 str = pdkim_hdr_cont(str, size, ptr, col);
1175 str = pdkim_hdr_cont(str, size, ptr, col);
1181 str = string_catn(str, size, ptr, US" ", 1);
1188 size_t sl = Ustrlen(intro);
1190 str = string_catn(str, size, ptr, intro, sl);
1198 size_t sl = Ustrlen(payload);
1200 str = string_catn(str, size, ptr, payload, sl);
1208 /* -------------------------------------------------------------------------- */
1211 pdkim_create_header(pdkim_signature *sig, BOOL final)
1216 uschar * hdr; int hdr_size = 0, hdr_len = 0;
1217 uschar * canon_all; int can_size = 0, can_len = 0;
1219 canon_all = string_cat (NULL, &can_size, &can_len,
1220 pdkim_canons[sig->canon_headers]);
1221 canon_all = string_catn(canon_all, &can_size, &can_len, US"/", 1);
1222 canon_all = string_cat (canon_all, &can_size, &can_len,
1223 pdkim_canons[sig->canon_body]);
1224 canon_all[can_len] = '\0';
1226 hdr = string_cat(NULL, &hdr_size, &hdr_len,
1227 US"DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
1230 /* Required and static bits */
1231 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"a=",
1232 pdkim_algos[sig->algo]);
1233 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"q=",
1234 pdkim_querymethods[sig->querymethod]);
1235 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"c=",
1237 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"d=",
1239 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"s=",
1242 /* list of header names can be split between items. */
1244 uschar * n = string_copy(sig->headernames);
1245 uschar * i = US"h=";
1250 uschar * c = Ustrchr(n, ':');
1255 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, NULL, US":");
1257 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, s, i, n);
1268 base64_bh = pdkim_encode_base64(&sig->bodyhash);
1269 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"bh=", base64_bh);
1273 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"i=", sig->identity);
1275 if (sig->created > 0)
1279 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->created);
1280 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"t=", minibuf);
1283 if (sig->expires > 0)
1287 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->expires);
1288 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"x=", minibuf);
1291 if (sig->bodylength >= 0)
1295 snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->bodylength);
1296 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"l=", minibuf);
1299 /* Preliminary or final version? */
1300 base64_b = final ? pdkim_encode_base64(&sig->sighash) : US"";
1301 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
1303 /* add trailing semicolon: I'm not sure if this is actually needed */
1304 hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US"");
1306 hdr[hdr_len] = '\0';
1311 /* -------------------------------------------------------------------------- */
1313 static pdkim_pubkey *
1314 pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx)
1316 uschar * dns_txt_name, * dns_txt_reply;
1318 const uschar * errstr;
1320 /* Fetch public key for signing domain, from DNS */
1322 dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain);
1324 dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN);
1325 memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
1327 if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK
1328 || dns_txt_reply[0] == '\0'
1331 sig->verify_status = PDKIM_VERIFY_INVALID;
1332 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE;
1339 "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
1341 pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
1344 if ( !(p = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply))
1345 || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
1348 sig->verify_status = PDKIM_VERIFY_INVALID;
1349 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
1354 debug_printf(" Invalid public key service type '%s'\n", p->srvtype);
1356 debug_printf(" Error while parsing public key record\n");
1358 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1363 DEBUG(D_acl) debug_printf(
1364 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1366 /* Import public key */
1367 if ((errstr = exim_rsa_verify_init(&p->key, vctx)))
1369 DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
1370 sig->verify_status = PDKIM_VERIFY_INVALID;
1371 sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
1379 /* -------------------------------------------------------------------------- */
1382 pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures)
1384 pdkim_signature *sig = ctx->sig;
1386 /* Check if we must still flush a (partial) header. If that is the
1387 case, the message has no body, and we must compute a body hash
1388 out of '<CR><LF>' */
1389 if (ctx->cur_header && ctx->cur_header_len)
1391 int rc = pdkim_header_complete(ctx);
1392 if (rc != PDKIM_OK) return rc;
1393 pdkim_update_bodyhash(ctx, "\r\n", 2);
1396 DEBUG(D_acl) debug_printf(
1397 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1399 /* Build (and/or evaluate) body hash */
1400 pdkim_finish_bodyhash(ctx);
1404 BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
1406 uschar * sig_hdr = US"";
1409 int hdata_alloc = 0;
1414 exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256);
1416 DEBUG(D_acl) debug_printf(
1417 "PDKIM >> Header data for hash, canonicalized, in sequence >>>>>>>>>>>>>>\n");
1419 /* SIGNING ---------------------------------------------------------------- */
1420 /* When signing, walk through our header list and add them to the hash. As we
1421 go, construct a list of the header's names to use for the h= parameter.
1422 Then append to that list any remaining header names for which there was no
1425 if (ctx->flags & PDKIM_MODE_SIGN)
1427 uschar * headernames = NULL; /* Collected signed header names */
1429 pdkim_stringlist *p;
1434 for (p = sig->headers; p; p = p->next)
1435 if (header_name_match(p->value, sig->sign_headers) == PDKIM_OK)
1438 /* Collect header names (Note: colon presence is guaranteed here) */
1439 uschar * q = Ustrchr(p->value, ':');
1441 headernames = string_catn(headernames, &hs, &hl,
1442 p->value, (q - US p->value) + (p->next ? 1 : 0));
1444 rh = sig->canon_headers == PDKIM_CANON_RELAXED
1445 ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
1446 : string_copy(CUS p->value); /* just copy it for simple canon */
1448 /* Feed header to the hash algorithm */
1449 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
1451 /* Remember headers block for signing (when the library cannot do incremental) */
1452 (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
1454 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1457 l = sig->sign_headers;
1458 while((s = string_nextinlist(&l, &sep, NULL, 0)))
1460 { /*SSS string_append_listele() */
1461 if (hl > 0 && headernames[hl-1] != ':')
1462 headernames = string_catn(headernames, &hs, &hl, US":", 1);
1464 headernames = string_cat(headernames, &hs, &hl, s);
1466 headernames[hl] = '\0';
1468 /* Copy headernames to signature struct */
1469 sig->headernames = headernames;
1471 /* Create signature header with b= omitted */
1472 sig_hdr = pdkim_create_header(sig, FALSE);
1475 /* VERIFICATION ----------------------------------------------------------- */
1476 /* When verifying, walk through the header name list in the h= parameter and
1477 add the headers to the hash in that order. */
1480 uschar * p = sig->headernames;
1482 pdkim_stringlist * hdrs;
1487 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1493 if ((q = Ustrchr(p, ':')))
1496 /*XXX walk the list of headers in same order as received. */
1497 for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
1499 && strncasecmp(CCS hdrs->value, CCS p, Ustrlen(p)) == 0
1500 && (hdrs->value)[Ustrlen(p)] == ':'
1503 /* cook header for relaxed canon, or just copy it for simple */
1505 uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
1506 ? pdkim_relax_header(hdrs->value, 1)
1507 : string_copy(CUS hdrs->value);
1509 /* Feed header to the hash algorithm */
1510 exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
1512 DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
1521 sig_hdr = string_copy(sig->rawsig_no_b_val);
1525 DEBUG(D_acl) debug_printf(
1526 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1528 /* Relax header if necessary */
1529 if (sig->canon_headers == PDKIM_CANON_RELAXED)
1530 sig_hdr = pdkim_relax_header(sig_hdr, 0);
1535 "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
1536 pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
1538 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1541 /* Finalize header hash */
1542 exim_sha_update(&hhash_ctx, CUS sig_hdr, Ustrlen(sig_hdr));
1543 exim_sha_finish(&hhash_ctx, &hhash);
1547 debug_printf("PDKIM [%s] Header hash computed: ", sig->domain);
1548 pdkim_hexprint(hhash.data, hhash.len);
1551 /* Remember headers block for signing (when the library cannot do incremental) */
1552 if (ctx->flags & PDKIM_MODE_SIGN)
1553 (void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
1555 /* SIGNING ---------------------------------------------------------------- */
1556 if (ctx->flags & PDKIM_MODE_SIGN)
1559 const uschar * errstr;
1561 /* Import private key */
1562 if ((errstr = exim_rsa_signing_init(US sig->rsa_privkey, &sctx)))
1564 DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
1565 return PDKIM_ERR_RSA_PRIVKEY;
1568 /* Do signing. With OpenSSL we are signing the hash of headers just
1569 calculated, with GnuTLS we have to sign an entire block of headers
1570 (due to available interfaces) and it recalculates the hash internally. */
1572 #if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
1576 if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sighash)))
1578 DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
1579 return PDKIM_ERR_RSA_SIGNING;
1584 debug_printf( "PDKIM [%s] b computed: ", sig->domain);
1585 pdkim_hexprint(sig->sighash.data, sig->sighash.len);
1588 sig->signature_header = pdkim_create_header(sig, TRUE);
1591 /* VERIFICATION ----------------------------------------------------------- */
1595 const uschar * errstr;
1598 /* Make sure we have all required signature tags */
1599 if (!( sig->domain && *sig->domain
1600 && sig->selector && *sig->selector
1601 && sig->headernames && *sig->headernames
1602 && sig->bodyhash.data
1603 && sig->sighash.data
1608 sig->verify_status = PDKIM_VERIFY_INVALID;
1609 sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
1611 DEBUG(D_acl) debug_printf(
1612 " Error in DKIM-Signature header: tags missing or invalid\n"
1613 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1617 /* Make sure sig uses supported DKIM version (only v1) */
1618 if (sig->version != 1)
1620 sig->verify_status = PDKIM_VERIFY_INVALID;
1621 sig->verify_ext_status = PDKIM_VERIFY_INVALID_DKIM_VERSION;
1623 DEBUG(D_acl) debug_printf(
1624 " Error in DKIM-Signature header: unsupported DKIM version\n"
1625 "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1629 if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx)))
1632 /* Check the signature */
1633 if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sighash)))
1635 DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
1636 sig->verify_status = PDKIM_VERIFY_FAIL;
1637 sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
1642 /* We have a winner! (if bodyhash was correct earlier) */
1643 if (sig->verify_status == PDKIM_VERIFY_NONE)
1644 sig->verify_status = PDKIM_VERIFY_PASS;
1650 debug_printf("PDKIM [%s] signature status: %s",
1651 sig->domain, pdkim_verify_status_str(sig->verify_status));
1652 if (sig->verify_ext_status > 0)
1653 debug_printf(" (%s)\n",
1654 pdkim_verify_ext_status_str(sig->verify_ext_status));
1663 /* If requested, set return pointer to signature(s) */
1664 if (return_signatures)
1665 *return_signatures = ctx->sig;
1671 /* -------------------------------------------------------------------------- */
1673 DLLEXPORT pdkim_ctx *
1674 pdkim_init_verify(int(*dns_txt_callback)(char *, char *), BOOL dot_stuffing)
1678 ctx = store_get(sizeof(pdkim_ctx));
1679 memset(ctx, 0, sizeof(pdkim_ctx));
1681 if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM;
1682 ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
1683 ctx->dns_txt_callback = dns_txt_callback;
1689 /* -------------------------------------------------------------------------- */
1691 DLLEXPORT pdkim_ctx *
1692 pdkim_init_sign(char * domain, char * selector, char * rsa_privkey, int algo,
1693 BOOL dot_stuffed, int(*dns_txt_callback)(char *, char *))
1696 pdkim_signature * sig;
1698 if (!domain || !selector || !rsa_privkey)
1701 ctx = store_get(sizeof(pdkim_ctx) + PDKIM_MAX_BODY_LINE_LEN + sizeof(pdkim_signature));
1702 memset(ctx, 0, sizeof(pdkim_ctx));
1704 ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;
1705 ctx->linebuf = CS (ctx+1);
1707 DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback;
1709 sig = (pdkim_signature *)(ctx->linebuf + PDKIM_MAX_BODY_LINE_LEN);
1710 memset(sig, 0, sizeof(pdkim_signature));
1712 sig->bodylength = -1;
1715 sig->domain = string_copy(US domain);
1716 sig->selector = string_copy(US selector);
1717 sig->rsa_privkey = string_copy(US rsa_privkey);
1720 exim_sha_init(&sig->body_hash_ctx,
1721 algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
1724 pdkim_signature s = *sig;
1727 debug_printf("PDKIM (checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1728 if (!pdkim_key_from_dns(ctx, &s, &vctx))
1729 debug_printf("WARNING: bad dkim key in dns\n");
1730 debug_printf("PDKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
1736 /* -------------------------------------------------------------------------- */
1739 pdkim_set_optional(pdkim_ctx *ctx,
1745 unsigned long created,
1746 unsigned long expires)
1748 pdkim_signature * sig = ctx->sig;
1751 sig->identity = string_copy(US identity);
1753 sig->sign_headers = string_copy(sign_headers
1754 ? US sign_headers : US PDKIM_DEFAULT_SIGN_HEADERS);
1756 sig->canon_headers = canon_headers;
1757 sig->canon_body = canon_body;
1758 sig->bodylength = bodylength;
1759 sig->created = created;
1760 sig->expires = expires;
1774 #endif /*DISABLE_DKIM*/