1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge, 1995 - 2015 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* Code for DKIM support. Other DKIM relevant code is in
9 receive.c, transport.c and transports/smtp.c */
15 #include "pdkim/pdkim.h"
17 pdkim_ctx *dkim_verify_ctx = NULL;
18 pdkim_signature *dkim_signatures = NULL;
19 pdkim_signature *dkim_cur_sig = NULL;
22 dkim_exim_query_dns_txt(char *name, char *answer)
28 lookup_dnssec_authenticated = NULL;
29 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
32 /* Search for TXT record */
34 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
36 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
37 if (rr->type == T_TXT)
40 int answer_offset = 0;
42 /* Copy record content to the answer buffer */
44 while (rr_offset < rr->size)
46 uschar len = rr->data[rr_offset++];
47 snprintf(answer + answer_offset,
48 PDKIM_DNS_TXT_MAX_RECLEN - answer_offset,
49 "%.*s", (int)len, (char *) (rr->data + rr_offset));
52 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
63 dkim_exim_verify_init(void)
65 /* Free previous context if there is one */
68 pdkim_free_ctx(dkim_verify_ctx);
70 /* Create new context */
72 dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP, &dkim_exim_query_dns_txt);
73 dkim_collect_input = !!dkim_verify_ctx;
75 if (dkim_collect_input)
76 pdkim_set_debug_stream(dkim_verify_ctx, debug_file);
82 dkim_exim_verify_feed(uschar * data, int len)
84 if ( dkim_collect_input
85 && pdkim_feed(dkim_verify_ctx, (char *)data, len) != PDKIM_OK)
86 dkim_collect_input = FALSE;
91 dkim_exim_verify_finish(void)
93 pdkim_signature *sig = NULL;
94 int dkim_signers_size = 0;
95 int dkim_signers_ptr = 0;
98 /* Delete eventual previous signature chain */
100 dkim_signatures = NULL;
102 /* If we have arrived here with dkim_collect_input == FALSE, it
103 means there was a processing error somewhere along the way.
104 Log the incident and disable futher verification. */
106 if (!dkim_collect_input)
108 log_write(0, LOG_MAIN,
109 "DKIM: Error while running this message through validation,"
110 " disabling signature verification.");
111 dkim_disable_verify = TRUE;
115 dkim_collect_input = FALSE;
117 /* Finish DKIM operation and fetch link to signatures chain */
119 if (pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures) != PDKIM_OK)
122 for (sig = dkim_signatures; sig; sig = sig->next)
127 /* Log a line for each signature */
129 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
130 string_sprintf("d=%s s=%s c=%s/%s a=%s ",
133 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
134 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
135 sig->algo == PDKIM_ALGO_RSA_SHA256 ? "rsa-sha256" : "rsa-sha1"),
137 sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
138 sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"",
139 sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"",
140 sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US""
143 switch (sig->verify_status)
145 case PDKIM_VERIFY_NONE:
146 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
149 case PDKIM_VERIFY_INVALID:
150 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
151 switch (sig->verify_ext_status)
153 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
154 logmsg = string_append(logmsg, &size, &ptr, 1,
155 "public key record (currently?) unavailable]");
158 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
159 logmsg = string_append(logmsg, &size, &ptr, 1,
160 "overlong public key record]");
163 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
164 logmsg = string_append(logmsg, &size, &ptr, 1,
165 "syntax error in public key record]");
169 logmsg = string_append(logmsg, &size, &ptr, 1,
170 "unspecified problem]");
174 case PDKIM_VERIFY_FAIL:
176 string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
177 switch (sig->verify_ext_status)
179 case PDKIM_VERIFY_FAIL_BODY:
180 logmsg = string_append(logmsg, &size, &ptr, 1,
181 "body hash mismatch (body probably modified in transit)]");
184 case PDKIM_VERIFY_FAIL_MESSAGE:
185 logmsg = string_append(logmsg, &size, &ptr, 1,
186 "signature did not verify (headers probably modified in transit)]");
190 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
194 case PDKIM_VERIFY_PASS:
196 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
201 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
203 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
205 dkim_signers = string_append(dkim_signers,
207 &dkim_signers_ptr, 2, sig->domain, ":");
210 dkim_signers = string_append(dkim_signers,
212 &dkim_signers_ptr, 2, sig->identity, ":");
214 /* Process next signature */
217 /* NULL-terminate and chop the last colon from the domain list */
221 dkim_signers[dkim_signers_ptr] = '\0';
222 if (Ustrlen(dkim_signers) > 0)
223 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
229 dkim_exim_acl_setup(uschar * id)
231 pdkim_signature * sig;
236 dkim_cur_signer = id;
238 if (dkim_disable_verify || !id || !dkim_verify_ctx)
241 /* Find signature to run ACL on */
243 for (sig = dkim_signatures; sig; sig = sig->next)
244 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
245 && strcmpic(cmp_val, id) == 0
250 /* The "dkim_domain" and "dkim_selector" expansion variables have
251 related globals, since they are used in the signing code too.
252 Instead of inventing separate names for verification, we set
253 them here. This is easy since a domain and selector is guaranteed
254 to be in a signature. The other dkim_* expansion items are
255 dynamically fetched from dkim_cur_sig at expansion time (see
258 dkim_signing_domain = US sig->domain;
259 dkim_signing_selector = US sig->selector;
266 dkim_exim_expand_defaults(int what)
270 case DKIM_ALGO: return US"";
271 case DKIM_BODYLENGTH: return US"9999999999999";
272 case DKIM_CANON_BODY: return US"";
273 case DKIM_CANON_HEADERS: return US"";
274 case DKIM_COPIEDHEADERS: return US"";
275 case DKIM_CREATED: return US"0";
276 case DKIM_EXPIRES: return US"9999999999999";
277 case DKIM_HEADERNAMES: return US"";
278 case DKIM_IDENTITY: return US"";
279 case DKIM_KEY_GRANULARITY: return US"*";
280 case DKIM_KEY_SRVTYPE: return US"*";
281 case DKIM_KEY_NOTES: return US"";
282 case DKIM_KEY_TESTING: return US"0";
283 case DKIM_NOSUBDOMAINS: return US"0";
284 case DKIM_VERIFY_STATUS: return US"none";
285 case DKIM_VERIFY_REASON: return US"";
286 default: return US"";
292 dkim_exim_expand_query(int what)
294 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
295 return dkim_exim_expand_defaults(what);
300 switch (dkim_cur_sig->algo)
302 case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
303 case PDKIM_ALGO_RSA_SHA256:
304 default: return US"rsa-sha256";
307 case DKIM_BODYLENGTH:
308 return dkim_cur_sig->bodylength >= 0
309 ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
310 : dkim_exim_expand_defaults(what);
312 case DKIM_CANON_BODY:
313 switch (dkim_cur_sig->canon_body)
315 case PDKIM_CANON_RELAXED: return US"relaxed";
316 case PDKIM_CANON_SIMPLE:
317 default: return US"simple";
320 case DKIM_CANON_HEADERS:
321 switch (dkim_cur_sig->canon_headers)
323 case PDKIM_CANON_RELAXED: return US"relaxed";
324 case PDKIM_CANON_SIMPLE:
325 default: return US"simple";
328 case DKIM_COPIEDHEADERS:
329 return dkim_cur_sig->copiedheaders
330 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
333 return dkim_cur_sig->created > 0
334 ? string_sprintf("%llu", dkim_cur_sig->created)
335 : dkim_exim_expand_defaults(what);
338 return dkim_cur_sig->expires > 0
339 ? string_sprintf("%llu", dkim_cur_sig->expires)
340 : dkim_exim_expand_defaults(what);
342 case DKIM_HEADERNAMES:
343 return dkim_cur_sig->headernames
344 ? US dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
347 return dkim_cur_sig->identity
348 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
350 case DKIM_KEY_GRANULARITY:
351 return dkim_cur_sig->pubkey
352 ? dkim_cur_sig->pubkey->granularity
353 ? US dkim_cur_sig->pubkey->granularity
354 : dkim_exim_expand_defaults(what)
355 : dkim_exim_expand_defaults(what);
357 case DKIM_KEY_SRVTYPE:
358 return dkim_cur_sig->pubkey
359 ? dkim_cur_sig->pubkey->srvtype
360 ? US dkim_cur_sig->pubkey->srvtype
361 : dkim_exim_expand_defaults(what)
362 : dkim_exim_expand_defaults(what);
365 return dkim_cur_sig->pubkey
366 ? dkim_cur_sig->pubkey->notes
367 ? US dkim_cur_sig->pubkey->notes
368 : dkim_exim_expand_defaults(what)
369 : dkim_exim_expand_defaults(what);
371 case DKIM_KEY_TESTING:
372 return dkim_cur_sig->pubkey
373 ? dkim_cur_sig->pubkey->testing
375 : dkim_exim_expand_defaults(what)
376 : dkim_exim_expand_defaults(what);
378 case DKIM_NOSUBDOMAINS:
379 return dkim_cur_sig->pubkey
380 ? dkim_cur_sig->pubkey->no_subdomaining
382 : dkim_exim_expand_defaults(what)
383 : dkim_exim_expand_defaults(what);
385 case DKIM_VERIFY_STATUS:
386 switch (dkim_cur_sig->verify_status)
388 case PDKIM_VERIFY_INVALID: return US"invalid";
389 case PDKIM_VERIFY_FAIL: return US"fail";
390 case PDKIM_VERIFY_PASS: return US"pass";
391 case PDKIM_VERIFY_NONE:
392 default: return US"none";
395 case DKIM_VERIFY_REASON:
396 switch (dkim_cur_sig->verify_ext_status)
398 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
399 return US"pubkey_unavailable";
400 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: return US"pubkey_syntax";
401 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
402 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
412 dkim_exim_sign(int dkim_fd, uschar * dkim_private_key,
413 const uschar * dkim_domain, uschar * dkim_selector,
414 uschar * dkim_canon, uschar * dkim_sign_headers)
417 uschar *seen_items = NULL;
418 int seen_items_size = 0;
419 int seen_items_offset = 0;
421 uschar *dkim_canon_expanded;
422 uschar *dkim_sign_headers_expanded;
423 uschar *dkim_private_key_expanded;
424 pdkim_ctx *ctx = NULL;
426 uschar *sigbuf = NULL;
429 pdkim_signature *signature;
435 int old_pool = store_pool;
437 store_pool = POOL_MAIN;
439 if (!(dkim_domain = expand_cstring(dkim_domain)))
441 /* expansion error, do not send message. */
442 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
443 "dkim_domain: %s", expand_string_message);
448 /* Set $dkim_domain expansion variable to each unique domain in list. */
450 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
451 itembuf, sizeof(itembuf))))
453 if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
456 /* Only sign once for each domain, no matter how often it
457 appears in the expanded list. */
461 const uschar *seen_items_list = seen_items;
462 if (match_isinlist(dkim_signing_domain,
463 &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
468 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
472 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
473 dkim_signing_domain);
474 seen_items[seen_items_offset] = '\0';
476 /* Set up $dkim_selector expansion variable. */
478 if (!(dkim_signing_selector = expand_string(dkim_selector)))
480 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
481 "dkim_selector: %s", expand_string_message);
486 /* Get canonicalization to use */
488 dkim_canon_expanded = dkim_canon ? expand_string(dkim_canon) : US"relaxed";
489 if (!dkim_canon_expanded)
491 /* expansion error, do not send message. */
492 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
493 "dkim_canon: %s", expand_string_message);
498 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
499 pdkim_canon = PDKIM_CANON_RELAXED;
500 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
501 pdkim_canon = PDKIM_CANON_SIMPLE;
504 log_write(0, LOG_MAIN,
505 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
506 dkim_canon_expanded);
507 pdkim_canon = PDKIM_CANON_RELAXED;
510 dkim_sign_headers_expanded = NULL;
511 if (dkim_sign_headers)
512 if (!(dkim_sign_headers_expanded = expand_string(dkim_sign_headers)))
514 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
515 "dkim_sign_headers: %s", expand_string_message);
519 /* else pass NULL, which means default header list */
521 /* Get private key to use. */
523 if (!(dkim_private_key_expanded = expand_string(dkim_private_key)))
525 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
526 "dkim_private_key: %s", expand_string_message);
531 if ( Ustrlen(dkim_private_key_expanded) == 0
532 || Ustrcmp(dkim_private_key_expanded, "0") == 0
533 || Ustrcmp(dkim_private_key_expanded, "false") == 0
535 continue; /* don't sign, but no error */
537 if (dkim_private_key_expanded[0] == '/')
541 /* Looks like a filename, load the private key. */
543 memset(big_buffer, 0, big_buffer_size);
544 privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY);
547 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
548 "private key file for reading: %s",
549 dkim_private_key_expanded);
554 if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
556 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
557 dkim_private_key_expanded);
562 (void) close(privkey_fd);
563 dkim_private_key_expanded = big_buffer;
566 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
567 (char *) dkim_signing_domain,
568 (char *) dkim_signing_selector,
569 (char *) dkim_private_key_expanded);
571 pdkim_set_debug_stream(ctx, debug_file);
573 pdkim_set_optional(ctx,
574 (char *) dkim_sign_headers_expanded,
577 pdkim_canon, -1, PDKIM_ALGO_RSA_SHA256, 0, 0);
579 lseek(dkim_fd, 0, SEEK_SET);
581 while ((sread = read(dkim_fd, &buf, 4096)) > 0)
582 if (pdkim_feed(ctx, buf, sread) != PDKIM_OK)
588 /* Handle failed read above. */
591 debug_printf("DKIM: Error reading -K file.\n");
597 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
599 log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
604 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
605 US signature->signature_header, US"\r\n");
613 sigbuf[sigptr] = '\0';
622 store_pool = old_pool;