DMARC: fix result reporting when a DKIM ACL overrides the verify result. Bug...
[exim.git] / src / src / dkim.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge, 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* Code for DKIM support. Other DKIM relevant code is in
9 receive.c, transport.c and transports/smtp.c */
10
11 #include "exim.h"
12
13 #ifndef DISABLE_DKIM
14
15 # include "pdkim/pdkim.h"
16
17 # ifdef MACRO_PREDEF
18 # include "macro_predef.h"
19
20 void
21 params_dkim(void)
22 {
23 builtin_macro_create_var(US"_DKIM_SIGN_HEADERS", US PDKIM_DEFAULT_SIGN_HEADERS);
24 }
25 # else /*!MACRO_PREDEF*/
26
27
28
29
30 int dkim_verify_oldpool;
31 pdkim_ctx *dkim_verify_ctx = NULL;
32 pdkim_signature *dkim_signatures = NULL;
33 pdkim_signature *dkim_cur_sig = NULL;
34 static const uschar * dkim_collect_error = NULL;
35
36
37
38 /*XXX the caller only uses the first record if we return multiple.
39 */
40
41 static uschar *
42 dkim_exim_query_dns_txt(char * name)
43 {
44 dns_answer dnsa;
45 dns_scan dnss;
46 dns_record *rr;
47 gstring * g = NULL;
48
49 lookup_dnssec_authenticated = NULL;
50 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
51 return NULL; /*XXX better error detail? logging? */
52
53 /* Search for TXT record */
54
55 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
56 rr;
57 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
58 if (rr->type == T_TXT)
59 {
60 int rr_offset = 0;
61
62 /* Copy record content to the answer buffer */
63
64 while (rr_offset < rr->size)
65 {
66 uschar len = rr->data[rr_offset++];
67
68 g = string_catn(g, US(rr->data + rr_offset), len);
69 if (g->ptr >= PDKIM_DNS_TXT_MAX_RECLEN)
70 goto bad;
71
72 rr_offset += len;
73 }
74
75 /* check if this looks like a DKIM record */
76 if (Ustrncmp(g->s, "v=", 2) != 0 || strncasecmp(CS g->s, "v=dkim", 6) == 0)
77 {
78 store_reset(g->s + g->ptr + 1);
79 return string_from_gstring(g);
80 }
81
82 if (g) g->ptr = 0; /* overwrite previous record */
83 }
84
85 bad:
86 if (g) store_reset(g);
87 return NULL; /*XXX better error detail? logging? */
88 }
89
90
91 void
92 dkim_exim_init(void)
93 {
94 pdkim_init();
95 }
96
97
98
99 void
100 dkim_exim_verify_init(BOOL dot_stuffing)
101 {
102 /* There is a store-reset between header & body reception
103 so cannot use the main pool. Any allocs done by Exim
104 memory-handling must use the perm pool. */
105
106 dkim_verify_oldpool = store_pool;
107 store_pool = POOL_PERM;
108
109 /* Free previous context if there is one */
110
111 if (dkim_verify_ctx)
112 pdkim_free_ctx(dkim_verify_ctx);
113
114 /* Create new context */
115
116 dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
117 dkim_collect_input = !!dkim_verify_ctx;
118 dkim_collect_error = NULL;
119
120 /* Start feed up with any cached data */
121 receive_get_cache();
122
123 store_pool = dkim_verify_oldpool;
124 }
125
126
127 void
128 dkim_exim_verify_feed(uschar * data, int len)
129 {
130 int rc;
131
132 store_pool = POOL_PERM;
133 if ( dkim_collect_input
134 && (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
135 {
136 dkim_collect_error = pdkim_errstr(rc);
137 log_write(0, LOG_MAIN,
138 "DKIM: validation error: %.100s", dkim_collect_error);
139 dkim_collect_input = FALSE;
140 }
141 store_pool = dkim_verify_oldpool;
142 }
143
144
145 /* Log the result for the given signature */
146 static void
147 dkim_exim_verify_log_sig(pdkim_signature * sig)
148 {
149 gstring * logmsg;
150 uschar * s;
151
152 if (!sig) return;
153
154 /* Remember the domain for the first pass result */
155
156 if ( !dkim_verify_overall
157 && dkim_verify_status
158 ? Ustrcmp(dkim_verify_status, US"pass") == 0
159 : sig->verify_status == PDKIM_VERIFY_PASS
160 )
161 dkim_verify_overall = string_copy(sig->domain);
162
163 /* Rewrite the sig result if the ACL overrode it. This is only
164 needed because the DMARC code (sigh) peeks at the dkim sigs.
165 Mark the sig for this having been done. */
166
167 if ( dkim_verify_status
168 && ( dkim_verify_status != dkim_exim_expand_query(DKIM_VERIFY_STATUS)
169 || dkim_verify_reason != dkim_exim_expand_query(DKIM_VERIFY_REASON)
170 ) )
171 { /* overridden by ACL */
172 sig->verify_ext_status = -1;
173 if (Ustrcmp(dkim_verify_status, US"fail") == 0)
174 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_FAIL;
175 else if (Ustrcmp(dkim_verify_status, US"invalid") == 0)
176 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_INVALID;
177 else if (Ustrcmp(dkim_verify_status, US"none") == 0)
178 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_NONE;
179 else if (Ustrcmp(dkim_verify_status, US"pass") == 0)
180 sig->verify_status = PDKIM_VERIFY_POLICY | PDKIM_VERIFY_PASS;
181 else
182 sig->verify_status = -1;
183 }
184
185 if (!LOGGING(dkim_verbose)) return;
186
187
188 logmsg = string_catn(NULL, US"DKIM: ", 6);
189 if (!(s = sig->domain)) s = US"<UNSET>";
190 logmsg = string_append(logmsg, 2, "d=", s);
191 if (!(s = sig->selector)) s = US"<UNSET>";
192 logmsg = string_append(logmsg, 2, " s=", s);
193 logmsg = string_append(logmsg, 7,
194 " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
195 "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
196 " a=", dkim_sig_to_a_tag(sig),
197 string_sprintf(" b=" SIZE_T_FMT,
198 (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0));
199 if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s);
200 if (sig->created > 0) logmsg = string_cat(logmsg,
201 string_sprintf(" t=%lu", sig->created));
202 if (sig->expires > 0) logmsg = string_cat(logmsg,
203 string_sprintf(" x=%lu", sig->expires));
204 if (sig->bodylength > -1) logmsg = string_cat(logmsg,
205 string_sprintf(" l=%lu", sig->bodylength));
206
207 if (sig->verify_status & PDKIM_VERIFY_POLICY)
208 logmsg = string_append(logmsg, 5,
209 US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]");
210 else
211 switch (sig->verify_status)
212 {
213 case PDKIM_VERIFY_NONE:
214 logmsg = string_cat(logmsg, US" [not verified]");
215 break;
216
217 case PDKIM_VERIFY_INVALID:
218 logmsg = string_cat(logmsg, US" [invalid - ");
219 switch (sig->verify_ext_status)
220 {
221 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
222 logmsg = string_cat(logmsg,
223 US"public key record (currently?) unavailable]");
224 break;
225
226 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
227 logmsg = string_cat(logmsg, US"overlong public key record]");
228 break;
229
230 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
231 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
232 logmsg = string_cat(logmsg, US"syntax error in public key record]");
233 break;
234
235 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
236 logmsg = string_cat(logmsg, US"signature tag missing or invalid]");
237 break;
238
239 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
240 logmsg = string_cat(logmsg, US"unsupported DKIM version]");
241 break;
242
243 default:
244 logmsg = string_cat(logmsg, US"unspecified problem]");
245 }
246 break;
247
248 case PDKIM_VERIFY_FAIL:
249 logmsg = string_cat(logmsg, US" [verification failed - ");
250 switch (sig->verify_ext_status)
251 {
252 case PDKIM_VERIFY_FAIL_BODY:
253 logmsg = string_cat(logmsg,
254 US"body hash mismatch (body probably modified in transit)]");
255 break;
256
257 case PDKIM_VERIFY_FAIL_MESSAGE:
258 logmsg = string_cat(logmsg,
259 US"signature did not verify "
260 "(headers probably modified in transit)]");
261 break;
262
263 default:
264 logmsg = string_cat(logmsg, US"unspecified reason]");
265 }
266 break;
267
268 case PDKIM_VERIFY_PASS:
269 logmsg = string_cat(logmsg, US" [verification succeeded]");
270 break;
271 }
272
273 log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg));
274 return;
275 }
276
277
278 /* Log a line for each signature */
279 void
280 dkim_exim_verify_log_all(void)
281 {
282 pdkim_signature * sig;
283 for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig);
284 }
285
286
287 void
288 dkim_exim_verify_finish(void)
289 {
290 pdkim_signature * sig;
291 int rc;
292 gstring * g = NULL;
293 const uschar * errstr = NULL;
294
295 store_pool = POOL_PERM;
296
297 /* Delete eventual previous signature chain */
298
299 dkim_signers = NULL;
300 dkim_signatures = NULL;
301
302 if (dkim_collect_error)
303 {
304 log_write(0, LOG_MAIN,
305 "DKIM: Error during validation, disabling signature verification: %.100s",
306 dkim_collect_error);
307 dkim_disable_verify = TRUE;
308 goto out;
309 }
310
311 dkim_collect_input = FALSE;
312
313 /* Finish DKIM operation and fetch link to signatures chain */
314
315 rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr);
316 if (rc != PDKIM_OK && errstr)
317 log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
318
319 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
320
321 for (sig = dkim_signatures; sig; sig = sig->next)
322 {
323 if (sig->domain) g = string_append_listele(g, ':', sig->domain);
324 if (sig->identity) g = string_append_listele(g, ':', sig->identity);
325 }
326
327 if (g) dkim_signers = g->s;
328
329 out:
330 store_pool = dkim_verify_oldpool;
331 }
332
333
334
335 /* Args as per dkim_exim_acl_run() below */
336 static int
337 dkim_acl_call(uschar * id, gstring ** res_ptr,
338 uschar ** user_msgptr, uschar ** log_msgptr)
339 {
340 int rc;
341 DEBUG(D_receive)
342 debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id);
343
344 rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr);
345 dkim_exim_verify_log_sig(dkim_cur_sig);
346 *res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status);
347 return rc;
348 }
349
350
351
352 /* For the given identity, run the DKIM ACL once for each matching signature.
353
354 Arguments
355 id Identity to look for in dkim signatures
356 res_ptr ptr to growable string-list of status results,
357 appended to per ACL run
358 user_msgptr where to put a user error (for SMTP response)
359 log_msgptr where to put a logging message (not for SMTP response)
360
361 Returns: OK access is granted by an ACCEPT verb
362 DISCARD access is granted by a DISCARD verb
363 FAIL access is denied
364 FAIL_DROP access is denied; drop the connection
365 DEFER can't tell at the moment
366 ERROR disaster
367 */
368
369 int
370 dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
371 uschar ** user_msgptr, uschar ** log_msgptr)
372 {
373 pdkim_signature * sig;
374 uschar * cmp_val;
375 int rc = -1;
376
377 dkim_verify_status = US"none";
378 dkim_verify_reason = US"";
379 dkim_cur_signer = id;
380
381 if (dkim_disable_verify || !id || !dkim_verify_ctx)
382 return OK;
383
384 /* Find signatures to run ACL on */
385
386 for (sig = dkim_signatures; sig; sig = sig->next)
387 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
388 && strcmpic(cmp_val, id) == 0
389 )
390 {
391 /* The "dkim_domain" and "dkim_selector" expansion variables have
392 related globals, since they are used in the signing code too.
393 Instead of inventing separate names for verification, we set
394 them here. This is easy since a domain and selector is guaranteed
395 to be in a signature. The other dkim_* expansion items are
396 dynamically fetched from dkim_cur_sig at expansion time (see
397 function below). */
398
399 dkim_cur_sig = sig;
400 dkim_signing_domain = US sig->domain;
401 dkim_signing_selector = US sig->selector;
402 dkim_key_length = sig->sighash.len * 8;
403
404 /* These two return static strings, so we can compare the addr
405 later to see if the ACL overwrote them. Check that when logging */
406
407 dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS);
408 dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON);
409
410 if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK)
411 return rc;
412 }
413
414 if (rc != -1)
415 return rc;
416
417 /* No matching sig found. Call ACL once anyway. */
418
419 dkim_cur_sig = NULL;
420 return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
421 }
422
423
424 static uschar *
425 dkim_exim_expand_defaults(int what)
426 {
427 switch (what)
428 {
429 case DKIM_ALGO: return US"";
430 case DKIM_BODYLENGTH: return US"9999999999999";
431 case DKIM_CANON_BODY: return US"";
432 case DKIM_CANON_HEADERS: return US"";
433 case DKIM_COPIEDHEADERS: return US"";
434 case DKIM_CREATED: return US"0";
435 case DKIM_EXPIRES: return US"9999999999999";
436 case DKIM_HEADERNAMES: return US"";
437 case DKIM_IDENTITY: return US"";
438 case DKIM_KEY_GRANULARITY: return US"*";
439 case DKIM_KEY_SRVTYPE: return US"*";
440 case DKIM_KEY_NOTES: return US"";
441 case DKIM_KEY_TESTING: return US"0";
442 case DKIM_NOSUBDOMAINS: return US"0";
443 case DKIM_VERIFY_STATUS: return US"none";
444 case DKIM_VERIFY_REASON: return US"";
445 default: return US"";
446 }
447 }
448
449
450 uschar *
451 dkim_exim_expand_query(int what)
452 {
453 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
454 return dkim_exim_expand_defaults(what);
455
456 switch (what)
457 {
458 case DKIM_ALGO:
459 return dkim_sig_to_a_tag(dkim_cur_sig);
460
461 case DKIM_BODYLENGTH:
462 return dkim_cur_sig->bodylength >= 0
463 ? string_sprintf("%ld", dkim_cur_sig->bodylength)
464 : dkim_exim_expand_defaults(what);
465
466 case DKIM_CANON_BODY:
467 switch (dkim_cur_sig->canon_body)
468 {
469 case PDKIM_CANON_RELAXED: return US"relaxed";
470 case PDKIM_CANON_SIMPLE:
471 default: return US"simple";
472 }
473
474 case DKIM_CANON_HEADERS:
475 switch (dkim_cur_sig->canon_headers)
476 {
477 case PDKIM_CANON_RELAXED: return US"relaxed";
478 case PDKIM_CANON_SIMPLE:
479 default: return US"simple";
480 }
481
482 case DKIM_COPIEDHEADERS:
483 return dkim_cur_sig->copiedheaders
484 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
485
486 case DKIM_CREATED:
487 return dkim_cur_sig->created > 0
488 ? string_sprintf("%lu", dkim_cur_sig->created)
489 : dkim_exim_expand_defaults(what);
490
491 case DKIM_EXPIRES:
492 return dkim_cur_sig->expires > 0
493 ? string_sprintf("%lu", dkim_cur_sig->expires)
494 : dkim_exim_expand_defaults(what);
495
496 case DKIM_HEADERNAMES:
497 return dkim_cur_sig->headernames
498 ? dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
499
500 case DKIM_IDENTITY:
501 return dkim_cur_sig->identity
502 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
503
504 case DKIM_KEY_GRANULARITY:
505 return dkim_cur_sig->pubkey
506 ? dkim_cur_sig->pubkey->granularity
507 ? US dkim_cur_sig->pubkey->granularity
508 : dkim_exim_expand_defaults(what)
509 : dkim_exim_expand_defaults(what);
510
511 case DKIM_KEY_SRVTYPE:
512 return dkim_cur_sig->pubkey
513 ? dkim_cur_sig->pubkey->srvtype
514 ? US dkim_cur_sig->pubkey->srvtype
515 : dkim_exim_expand_defaults(what)
516 : dkim_exim_expand_defaults(what);
517
518 case DKIM_KEY_NOTES:
519 return dkim_cur_sig->pubkey
520 ? dkim_cur_sig->pubkey->notes
521 ? US dkim_cur_sig->pubkey->notes
522 : dkim_exim_expand_defaults(what)
523 : dkim_exim_expand_defaults(what);
524
525 case DKIM_KEY_TESTING:
526 return dkim_cur_sig->pubkey
527 ? dkim_cur_sig->pubkey->testing
528 ? US"1"
529 : dkim_exim_expand_defaults(what)
530 : dkim_exim_expand_defaults(what);
531
532 case DKIM_NOSUBDOMAINS:
533 return dkim_cur_sig->pubkey
534 ? dkim_cur_sig->pubkey->no_subdomaining
535 ? US"1"
536 : dkim_exim_expand_defaults(what)
537 : dkim_exim_expand_defaults(what);
538
539 case DKIM_VERIFY_STATUS:
540 switch (dkim_cur_sig->verify_status)
541 {
542 case PDKIM_VERIFY_INVALID: return US"invalid";
543 case PDKIM_VERIFY_FAIL: return US"fail";
544 case PDKIM_VERIFY_PASS: return US"pass";
545 case PDKIM_VERIFY_NONE:
546 default: return US"none";
547 }
548
549 case DKIM_VERIFY_REASON:
550 switch (dkim_cur_sig->verify_ext_status)
551 {
552 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
553 return US"pubkey_unavailable";
554 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:return US"pubkey_dns_syntax";
555 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return US"pubkey_der_syntax";
556 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
557 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
558 }
559
560 default:
561 return US"";
562 }
563 }
564
565
566 /* Generate signatures for the given file.
567 If a prefix is given, prepend it to the file for the calculations.
568
569 Return:
570 NULL: error; error string written
571 string: signature header(s), or a zero-length string (not an error)
572 */
573
574 gstring *
575 dkim_exim_sign(int fd, off_t off, uschar * prefix,
576 struct ob_dkim * dkim, const uschar ** errstr)
577 {
578 const uschar * dkim_domain;
579 int sep = 0;
580 gstring * seen_doms = NULL;
581 pdkim_ctx ctx;
582 pdkim_signature * sig;
583 gstring * sigbuf;
584 int pdkim_rc;
585 int sread;
586 uschar buf[4096];
587 int save_errno = 0;
588 int old_pool = store_pool;
589 uschar * errwhen;
590
591 store_pool = POOL_MAIN;
592
593 pdkim_init_context(&ctx, dkim->dot_stuffed, &dkim_exim_query_dns_txt);
594
595 if (!(dkim_domain = expand_cstring(dkim->dkim_domain)))
596 /* expansion error, do not send message. */
597 { errwhen = US"dkim_domain"; goto expand_bad; }
598
599 /* Set $dkim_domain expansion variable to each unique domain in list. */
600
601 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, NULL, 0)))
602 {
603 const uschar * dkim_sel;
604 int sel_sep = 0;
605
606 if (dkim_signing_domain[0] == '\0')
607 continue;
608
609 /* Only sign once for each domain, no matter how often it
610 appears in the expanded list. */
611
612 if (match_isinlist(dkim_signing_domain, CUSS &seen_doms,
613 0, NULL, NULL, MCL_STRING, TRUE, NULL) == OK)
614 continue;
615
616 seen_doms = string_append_listele(seen_doms, ':', dkim_signing_domain);
617
618 /* Set $dkim_selector expansion variable to each selector in list,
619 for this domain. */
620
621 if (!(dkim_sel = expand_string(dkim->dkim_selector)))
622 if (!(dkim_signing_selector = expand_string(dkim->dkim_selector)))
623 { errwhen = US"dkim_selector"; goto expand_bad; }
624
625 while ((dkim_signing_selector = string_nextinlist(&dkim_sel, &sel_sep,
626 NULL, 0)))
627 {
628 uschar * dkim_canon_expanded;
629 int pdkim_canon;
630 uschar * dkim_sign_headers_expanded = NULL;
631 uschar * dkim_private_key_expanded;
632 uschar * dkim_hash_expanded;
633 uschar * dkim_identity_expanded = NULL;
634
635 /* Get canonicalization to use */
636
637 dkim_canon_expanded = dkim->dkim_canon
638 ? expand_string(dkim->dkim_canon) : US"relaxed";
639 if (!dkim_canon_expanded) /* expansion error, do not send message. */
640 { errwhen = US"dkim_canon"; goto expand_bad; }
641
642 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
643 pdkim_canon = PDKIM_CANON_RELAXED;
644 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
645 pdkim_canon = PDKIM_CANON_SIMPLE;
646 else
647 {
648 log_write(0, LOG_MAIN,
649 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
650 dkim_canon_expanded);
651 pdkim_canon = PDKIM_CANON_RELAXED;
652 }
653
654 if ( dkim->dkim_sign_headers
655 && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers)))
656 { errwhen = US"dkim_sign_header"; goto expand_bad; }
657 /* else pass NULL, which means default header list */
658
659 /* Get private key to use. */
660
661 if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key)))
662 { errwhen = US"dkim_private_key"; goto expand_bad; }
663
664 if ( Ustrlen(dkim_private_key_expanded) == 0
665 || Ustrcmp(dkim_private_key_expanded, "0") == 0
666 || Ustrcmp(dkim_private_key_expanded, "false") == 0
667 )
668 continue; /* don't sign, but no error */
669
670 if (dkim_private_key_expanded[0] == '/')
671 {
672 int privkey_fd, off = 0, len;
673
674 /* Looks like a filename, load the private key. */
675
676 memset(big_buffer, 0, big_buffer_size);
677
678 if ((privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY)) < 0)
679 {
680 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
681 "private key file for reading: %s",
682 dkim_private_key_expanded);
683 goto bad;
684 }
685
686 do
687 {
688 if ((len = read(privkey_fd, big_buffer + off, big_buffer_size - 2 - off)) < 0)
689 {
690 (void) close(privkey_fd);
691 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
692 dkim_private_key_expanded);
693 goto bad;
694 }
695 off += len;
696 }
697 while (len > 0);
698
699 (void) close(privkey_fd);
700 big_buffer[off] = '\0';
701 dkim_private_key_expanded = big_buffer;
702 }
703
704 if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash)))
705 { errwhen = US"dkim_hash"; goto expand_bad; }
706
707 if (dkim->dkim_identity)
708 if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity)))
709 { errwhen = US"dkim_identity"; goto expand_bad; }
710 else if (!*dkim_identity_expanded)
711 dkim_identity_expanded = NULL;
712
713 /*XXX so we currently nail signing to RSA + this hash.
714 Need to extract algo from privkey and check for disallowed combos. */
715
716 if (!(sig = pdkim_init_sign(&ctx, dkim_signing_domain,
717 dkim_signing_selector,
718 dkim_private_key_expanded,
719 dkim_hash_expanded,
720 errstr
721 )))
722 goto bad;
723 dkim_private_key_expanded[0] = '\0';
724
725 pdkim_set_optional(sig,
726 CS dkim_sign_headers_expanded,
727 CS dkim_identity_expanded,
728 pdkim_canon,
729 pdkim_canon, -1, 0, 0);
730
731 if (!pdkim_set_bodyhash(&ctx, sig))
732 goto bad;
733
734 if (!ctx.sig) /* link sig to context chain */
735 ctx.sig = sig;
736 else
737 {
738 pdkim_signature * n = ctx.sig;
739 while (n->next) n = n->next;
740 n->next = sig;
741 }
742 }
743 }
744 if (!ctx.sig)
745 {
746 DEBUG(D_transport) debug_printf("DKIM: no viable signatures to use\n");
747 sigbuf = string_get(1); /* return a zero-len string */
748 goto CLEANUP;
749 }
750
751 if (prefix && (pdkim_rc = pdkim_feed(&ctx, prefix, Ustrlen(prefix))) != PDKIM_OK)
752 goto pk_bad;
753
754 if (lseek(fd, off, SEEK_SET) < 0)
755 sread = -1;
756 else
757 while ((sread = read(fd, &buf, sizeof(buf))) > 0)
758 if ((pdkim_rc = pdkim_feed(&ctx, buf, sread)) != PDKIM_OK)
759 goto pk_bad;
760
761 /* Handle failed read above. */
762 if (sread == -1)
763 {
764 debug_printf("DKIM: Error reading -K file.\n");
765 save_errno = errno;
766 goto bad;
767 }
768
769 /* Build string of headers, one per signature */
770
771 if ((pdkim_rc = pdkim_feed_finish(&ctx, &sig, errstr)) != PDKIM_OK)
772 goto pk_bad;
773
774 for (sigbuf = NULL; sig; sig = sig->next)
775 sigbuf = string_append(sigbuf, 2, US sig->signature_header, US"\r\n");
776
777 CLEANUP:
778 (void) string_from_gstring(sigbuf);
779 store_pool = old_pool;
780 errno = save_errno;
781 return sigbuf;
782
783 pk_bad:
784 log_write(0, LOG_MAIN|LOG_PANIC,
785 "DKIM: signing failed: %.100s", pdkim_errstr(pdkim_rc));
786 bad:
787 sigbuf = NULL;
788 goto CLEANUP;
789
790 expand_bad:
791 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand %s: %s",
792 errwhen, expand_string_message);
793 goto bad;
794 }
795
796
797
798
799 gstring *
800 authres_dkim(gstring * g)
801 {
802 pdkim_signature * sig;
803
804 for (sig = dkim_signatures; sig; sig = sig->next)
805 {
806 g = string_catn(g, US";\\n\\tdkim=", 10);
807
808 if (sig->verify_status & PDKIM_VERIFY_POLICY)
809 g = string_append(g, 5,
810 US"policy (", dkim_verify_status, US" - ", dkim_verify_reason, US")");
811 else switch(sig->verify_status)
812 {
813 case PDKIM_VERIFY_NONE: g = string_cat(g, US"none"); break;
814 case PDKIM_VERIFY_INVALID:
815 switch (sig->verify_ext_status)
816 {
817 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
818 g = string_cat(g, US"tmperror (pubkey unavailable)"); break;
819 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
820 g = string_cat(g, US"permerror (overlong public key record)"); break;
821 case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD:
822 case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT:
823 g = string_cat(g, US"neutral (syntax error in public key record)");
824 break;
825 case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR:
826 g = string_cat(g, US"neutral (signature tag missing or invalid)");
827 break;
828 case PDKIM_VERIFY_INVALID_DKIM_VERSION:
829 g = string_cat(g, US"neutral (unsupported DKIM version)");
830 break;
831 default:
832 g = string_cat(g, US"permerror (unspecified problem)"); break;
833 }
834 break;
835 case PDKIM_VERIFY_FAIL:
836 switch (sig->verify_ext_status)
837 {
838 case PDKIM_VERIFY_FAIL_BODY:
839 g = string_cat(g,
840 US"fail (body hash mismatch; body probably modified in transit)");
841 break;
842 case PDKIM_VERIFY_FAIL_MESSAGE:
843 g = string_cat(g,
844 US"fail (signature did not verify; headers probably modified in transit)");
845 break;
846 default:
847 g = string_cat(g, US"fail (unspecified reason)");
848 break;
849 }
850 break;
851 case PDKIM_VERIFY_PASS: g = string_cat(g, US"pass"); break;
852 default: g = string_cat(g, US"permerror"); break;
853 }
854 if (sig->domain) g = string_append(g, 2, US" header.d=", sig->domain);
855 if (sig->identity) g = string_append(g, 2, US" header.i=", sig->identity);
856 if (sig->selector) g = string_append(g, 2, US" header.s=", sig->selector);
857 g = string_append(g, 2, US" header.a=", dkim_sig_to_a_tag(sig));
858 }
859 return g;
860 }
861
862
863 # endif /*!MACRO_PREDEF*/
864 #endif /*!DISABLE_DKIM*/