db2eb4948cd587f56f1a5f8a29a11fc1f39a7b12
[exim.git] / src / src / dkim.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge, 1995 - 2015 */
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 pdkim_ctx *dkim_verify_ctx = NULL;
18 pdkim_signature *dkim_signatures = NULL;
19 pdkim_signature *dkim_cur_sig = NULL;
20
21 static int
22 dkim_exim_query_dns_txt(char *name, char *answer)
23 {
24 dns_answer dnsa;
25 dns_scan dnss;
26 dns_record *rr;
27
28 lookup_dnssec_authenticated = NULL;
29 if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
30 return PDKIM_FAIL;
31
32 /* Search for TXT record */
33
34 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
35 rr;
36 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
37 if (rr->type == T_TXT)
38 {
39 int rr_offset = 0;
40 int answer_offset = 0;
41
42 /* Copy record content to the answer buffer */
43
44 while (rr_offset < rr->size)
45 {
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));
50 rr_offset += len;
51 answer_offset += len;
52 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN)
53 return PDKIM_FAIL;
54 }
55 return PDKIM_OK;
56 }
57
58 return PDKIM_FAIL;
59 }
60
61
62 void
63 dkim_exim_verify_init(void)
64 {
65 /* Free previous context if there is one */
66
67 if (dkim_verify_ctx)
68 pdkim_free_ctx(dkim_verify_ctx);
69
70 /* Create new context */
71
72 dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP, &dkim_exim_query_dns_txt);
73
74 if ((dkim_collect_input = !!dkim_verify_ctx))
75 pdkim_set_debug_stream(dkim_verify_ctx, debug_file);
76 }
77
78
79 void
80 dkim_exim_verify_feed(uschar * data, int len)
81 {
82 if ( dkim_collect_input
83 && pdkim_feed(dkim_verify_ctx, (char *)data, len) != PDKIM_OK)
84 dkim_collect_input = FALSE;
85 }
86
87
88 void
89 dkim_exim_verify_finish(void)
90 {
91 pdkim_signature *sig = NULL;
92 int dkim_signers_size = 0;
93 int dkim_signers_ptr = 0;
94 dkim_signers = NULL;
95
96 /* Delete eventual previous signature chain */
97
98 dkim_signatures = NULL;
99
100 /* If we have arrived here with dkim_collect_input == FALSE, it
101 means there was a processing error somewhere along the way.
102 Log the incident and disable futher verification. */
103
104 if (!dkim_collect_input)
105 {
106 log_write(0, LOG_MAIN,
107 "DKIM: Error while running this message through validation,"
108 " disabling signature verification.");
109 dkim_disable_verify = TRUE;
110 return;
111 }
112
113 dkim_collect_input = FALSE;
114
115 /* Finish DKIM operation and fetch link to signatures chain */
116
117 if (pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures) != PDKIM_OK)
118 return;
119
120 for (sig = dkim_signatures; sig; sig = sig->next)
121 {
122 int size = 0;
123 int ptr = 0;
124
125 /* Log a line for each signature */
126
127 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
128 string_sprintf("d=%s s=%s c=%s/%s a=%s ",
129 sig->domain,
130 sig->selector,
131 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
132 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
133 sig->algo == PDKIM_ALGO_RSA_SHA256 ? "rsa-sha256" : "rsa-sha1"),
134
135 sig->identity ? string_sprintf("i=%s ", sig->identity) : US"",
136 sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"",
137 sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"",
138 sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US""
139 );
140
141 switch (sig->verify_status)
142 {
143 case PDKIM_VERIFY_NONE:
144 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
145 break;
146
147 case PDKIM_VERIFY_INVALID:
148 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
149 switch (sig->verify_ext_status)
150 {
151 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
152 logmsg = string_append(logmsg, &size, &ptr, 1,
153 "public key record (currently?) unavailable]");
154 break;
155
156 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
157 logmsg = string_append(logmsg, &size, &ptr, 1,
158 "overlong public key record]");
159 break;
160
161 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
162 logmsg = string_append(logmsg, &size, &ptr, 1,
163 "syntax error in public key record]");
164 break;
165
166 default:
167 logmsg = string_append(logmsg, &size, &ptr, 1,
168 "unspecified problem]");
169 }
170 break;
171
172 case PDKIM_VERIFY_FAIL:
173 logmsg =
174 string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
175 switch (sig->verify_ext_status)
176 {
177 case PDKIM_VERIFY_FAIL_BODY:
178 logmsg = string_append(logmsg, &size, &ptr, 1,
179 "body hash mismatch (body probably modified in transit)]");
180 break;
181
182 case PDKIM_VERIFY_FAIL_MESSAGE:
183 logmsg = string_append(logmsg, &size, &ptr, 1,
184 "signature did not verify (headers probably modified in transit)]");
185 break;
186
187 default:
188 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
189 }
190 break;
191
192 case PDKIM_VERIFY_PASS:
193 logmsg =
194 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
195 break;
196 }
197
198 logmsg[ptr] = '\0';
199 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
200
201 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
202
203 dkim_signers = string_append(dkim_signers,
204 &dkim_signers_size,
205 &dkim_signers_ptr, 2, sig->domain, ":");
206
207 if (sig->identity)
208 dkim_signers = string_append(dkim_signers,
209 &dkim_signers_size,
210 &dkim_signers_ptr, 2, sig->identity, ":");
211
212 /* Process next signature */
213 }
214
215 /* NULL-terminate and chop the last colon from the domain list */
216
217 if (dkim_signers)
218 {
219 dkim_signers[dkim_signers_ptr] = '\0';
220 if (Ustrlen(dkim_signers) > 0)
221 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
222 }
223 }
224
225
226 void
227 dkim_exim_acl_setup(uschar * id)
228 {
229 pdkim_signature * sig;
230 uschar * cmp_val;
231
232
233 dkim_cur_sig = NULL;
234 dkim_cur_signer = id;
235
236 if (dkim_disable_verify || !id || !dkim_verify_ctx)
237 return;
238
239 /* Find signature to run ACL on */
240
241 for (sig = dkim_signatures; sig; sig = sig->next)
242 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
243 && strcmpic(cmp_val, id) == 0
244 )
245 {
246 dkim_cur_sig = sig;
247
248 /* The "dkim_domain" and "dkim_selector" expansion variables have
249 related globals, since they are used in the signing code too.
250 Instead of inventing separate names for verification, we set
251 them here. This is easy since a domain and selector is guaranteed
252 to be in a signature. The other dkim_* expansion items are
253 dynamically fetched from dkim_cur_sig at expansion time (see
254 function below). */
255
256 dkim_signing_domain = US sig->domain;
257 dkim_signing_selector = US sig->selector;
258 return;
259 }
260 }
261
262
263 static uschar *
264 dkim_exim_expand_defaults(int what)
265 {
266 switch (what)
267 {
268 case DKIM_ALGO: return US"";
269 case DKIM_BODYLENGTH: return US"9999999999999";
270 case DKIM_CANON_BODY: return US"";
271 case DKIM_CANON_HEADERS: return US"";
272 case DKIM_COPIEDHEADERS: return US"";
273 case DKIM_CREATED: return US"0";
274 case DKIM_EXPIRES: return US"9999999999999";
275 case DKIM_HEADERNAMES: return US"";
276 case DKIM_IDENTITY: return US"";
277 case DKIM_KEY_GRANULARITY: return US"*";
278 case DKIM_KEY_SRVTYPE: return US"*";
279 case DKIM_KEY_NOTES: return US"";
280 case DKIM_KEY_TESTING: return US"0";
281 case DKIM_NOSUBDOMAINS: return US"0";
282 case DKIM_VERIFY_STATUS: return US"none";
283 case DKIM_VERIFY_REASON: return US"";
284 default: return US"";
285 }
286 }
287
288
289 uschar *
290 dkim_exim_expand_query(int what)
291 {
292 if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
293 return dkim_exim_expand_defaults(what);
294
295 switch (what)
296 {
297 case DKIM_ALGO:
298 switch (dkim_cur_sig->algo)
299 {
300 case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1";
301 case PDKIM_ALGO_RSA_SHA256:
302 default: return US"rsa-sha256";
303 }
304
305 case DKIM_BODYLENGTH:
306 return dkim_cur_sig->bodylength >= 0
307 ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength)
308 : dkim_exim_expand_defaults(what);
309
310 case DKIM_CANON_BODY:
311 switch (dkim_cur_sig->canon_body)
312 {
313 case PDKIM_CANON_RELAXED: return US"relaxed";
314 case PDKIM_CANON_SIMPLE:
315 default: return US"simple";
316 }
317
318 case DKIM_CANON_HEADERS:
319 switch (dkim_cur_sig->canon_headers)
320 {
321 case PDKIM_CANON_RELAXED: return US"relaxed";
322 case PDKIM_CANON_SIMPLE:
323 default: return US"simple";
324 }
325
326 case DKIM_COPIEDHEADERS:
327 return dkim_cur_sig->copiedheaders
328 ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what);
329
330 case DKIM_CREATED:
331 return dkim_cur_sig->created > 0
332 ? string_sprintf("%llu", dkim_cur_sig->created)
333 : dkim_exim_expand_defaults(what);
334
335 case DKIM_EXPIRES:
336 return dkim_cur_sig->expires > 0
337 ? string_sprintf("%llu", dkim_cur_sig->expires)
338 : dkim_exim_expand_defaults(what);
339
340 case DKIM_HEADERNAMES:
341 return dkim_cur_sig->headernames
342 ? US dkim_cur_sig->headernames : dkim_exim_expand_defaults(what);
343
344 case DKIM_IDENTITY:
345 return dkim_cur_sig->identity
346 ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what);
347
348 case DKIM_KEY_GRANULARITY:
349 return dkim_cur_sig->pubkey
350 ? dkim_cur_sig->pubkey->granularity
351 ? US dkim_cur_sig->pubkey->granularity
352 : dkim_exim_expand_defaults(what)
353 : dkim_exim_expand_defaults(what);
354
355 case DKIM_KEY_SRVTYPE:
356 return dkim_cur_sig->pubkey
357 ? dkim_cur_sig->pubkey->srvtype
358 ? US dkim_cur_sig->pubkey->srvtype
359 : dkim_exim_expand_defaults(what)
360 : dkim_exim_expand_defaults(what);
361
362 case DKIM_KEY_NOTES:
363 return dkim_cur_sig->pubkey
364 ? dkim_cur_sig->pubkey->notes
365 ? US dkim_cur_sig->pubkey->notes
366 : dkim_exim_expand_defaults(what)
367 : dkim_exim_expand_defaults(what);
368
369 case DKIM_KEY_TESTING:
370 return dkim_cur_sig->pubkey
371 ? dkim_cur_sig->pubkey->testing
372 ? US"1"
373 : dkim_exim_expand_defaults(what)
374 : dkim_exim_expand_defaults(what);
375
376 case DKIM_NOSUBDOMAINS:
377 return dkim_cur_sig->pubkey
378 ? dkim_cur_sig->pubkey->no_subdomaining
379 ? US"1"
380 : dkim_exim_expand_defaults(what)
381 : dkim_exim_expand_defaults(what);
382
383 case DKIM_VERIFY_STATUS:
384 switch (dkim_cur_sig->verify_status)
385 {
386 case PDKIM_VERIFY_INVALID: return US"invalid";
387 case PDKIM_VERIFY_FAIL: return US"fail";
388 case PDKIM_VERIFY_PASS: return US"pass";
389 case PDKIM_VERIFY_NONE:
390 default: return US"none";
391 }
392
393 case DKIM_VERIFY_REASON:
394 switch (dkim_cur_sig->verify_ext_status)
395 {
396 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
397 return US"pubkey_unavailable";
398 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: return US"pubkey_syntax";
399 case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch";
400 case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect";
401 }
402
403 default:
404 return US"";
405 }
406 }
407
408
409 uschar *
410 dkim_exim_sign(int dkim_fd, uschar * dkim_private_key,
411 const uschar * dkim_domain, uschar * dkim_selector,
412 uschar * dkim_canon, uschar * dkim_sign_headers)
413 {
414 int sep = 0;
415 uschar *seen_items = NULL;
416 int seen_items_size = 0;
417 int seen_items_offset = 0;
418 uschar itembuf[256];
419 uschar *dkim_canon_expanded;
420 uschar *dkim_sign_headers_expanded;
421 uschar *dkim_private_key_expanded;
422 pdkim_ctx *ctx = NULL;
423 uschar *rc = NULL;
424 uschar *sigbuf = NULL;
425 int sigsize = 0;
426 int sigptr = 0;
427 pdkim_signature *signature;
428 int pdkim_canon;
429 int pdkim_rc;
430 int sread;
431 char buf[4096];
432 int save_errno = 0;
433 int old_pool = store_pool;
434
435 store_pool = POOL_MAIN;
436
437 if (!(dkim_domain = expand_cstring(dkim_domain)))
438 {
439 /* expansion error, do not send message. */
440 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
441 "dkim_domain: %s", expand_string_message);
442 rc = NULL;
443 goto CLEANUP;
444 }
445
446 /* Set $dkim_domain expansion variable to each unique domain in list. */
447
448 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
449 itembuf, sizeof(itembuf))))
450 {
451 if (!dkim_signing_domain || dkim_signing_domain[0] == '\0')
452 continue;
453
454 /* Only sign once for each domain, no matter how often it
455 appears in the expanded list. */
456
457 if (seen_items)
458 {
459 const uschar *seen_items_list = seen_items;
460 if (match_isinlist(dkim_signing_domain,
461 &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE,
462 NULL) == OK)
463 continue;
464
465 seen_items =
466 string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":");
467 }
468
469 seen_items =
470 string_append(seen_items, &seen_items_size, &seen_items_offset, 1,
471 dkim_signing_domain);
472 seen_items[seen_items_offset] = '\0';
473
474 /* Set up $dkim_selector expansion variable. */
475
476 if (!(dkim_signing_selector = expand_string(dkim_selector)))
477 {
478 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
479 "dkim_selector: %s", expand_string_message);
480 rc = NULL;
481 goto CLEANUP;
482 }
483
484 /* Get canonicalization to use */
485
486 dkim_canon_expanded = dkim_canon ? expand_string(dkim_canon) : US"relaxed";
487 if (!dkim_canon_expanded)
488 {
489 /* expansion error, do not send message. */
490 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
491 "dkim_canon: %s", expand_string_message);
492 rc = NULL;
493 goto CLEANUP;
494 }
495
496 if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
497 pdkim_canon = PDKIM_CANON_RELAXED;
498 else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
499 pdkim_canon = PDKIM_CANON_SIMPLE;
500 else
501 {
502 log_write(0, LOG_MAIN,
503 "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",
504 dkim_canon_expanded);
505 pdkim_canon = PDKIM_CANON_RELAXED;
506 }
507
508 dkim_sign_headers_expanded = NULL;
509 if (dkim_sign_headers)
510 if (!(dkim_sign_headers_expanded = expand_string(dkim_sign_headers)))
511 {
512 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
513 "dkim_sign_headers: %s", expand_string_message);
514 rc = NULL;
515 goto CLEANUP;
516 }
517 /* else pass NULL, which means default header list */
518
519 /* Get private key to use. */
520
521 if (!(dkim_private_key_expanded = expand_string(dkim_private_key)))
522 {
523 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
524 "dkim_private_key: %s", expand_string_message);
525 rc = NULL;
526 goto CLEANUP;
527 }
528
529 if ( Ustrlen(dkim_private_key_expanded) == 0
530 || Ustrcmp(dkim_private_key_expanded, "0") == 0
531 || Ustrcmp(dkim_private_key_expanded, "false") == 0
532 )
533 continue; /* don't sign, but no error */
534
535 if (dkim_private_key_expanded[0] == '/')
536 {
537 int privkey_fd = 0;
538
539 /* Looks like a filename, load the private key. */
540
541 memset(big_buffer, 0, big_buffer_size);
542 privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY);
543 if (privkey_fd < 0)
544 {
545 log_write(0, LOG_MAIN | LOG_PANIC, "unable to open "
546 "private key file for reading: %s",
547 dkim_private_key_expanded);
548 rc = NULL;
549 goto CLEANUP;
550 }
551
552 if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0)
553 {
554 log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
555 dkim_private_key_expanded);
556 rc = NULL;
557 goto CLEANUP;
558 }
559
560 (void) close(privkey_fd);
561 dkim_private_key_expanded = big_buffer;
562 }
563
564 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
565 (char *) dkim_signing_domain,
566 (char *) dkim_signing_selector,
567 (char *) dkim_private_key_expanded);
568 pdkim_set_debug_stream(ctx, debug_file);
569 pdkim_set_optional(ctx,
570 (char *) dkim_sign_headers_expanded,
571 NULL,
572 pdkim_canon,
573 pdkim_canon, -1, PDKIM_ALGO_RSA_SHA256, 0, 0);
574
575 lseek(dkim_fd, 0, SEEK_SET);
576
577 while ((sread = read(dkim_fd, &buf, 4096)) > 0)
578 if (pdkim_feed(ctx, buf, sread) != PDKIM_OK)
579 {
580 rc = NULL;
581 goto CLEANUP;
582 }
583
584 /* Handle failed read above. */
585 if (sread == -1)
586 {
587 debug_printf("DKIM: Error reading -K file.\n");
588 save_errno = errno;
589 rc = NULL;
590 goto CLEANUP;
591 }
592
593 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
594 {
595 log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
596 rc = NULL;
597 goto CLEANUP;
598 }
599
600 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
601 US signature->signature_header, US"\r\n");
602
603 pdkim_free_ctx(ctx);
604 ctx = NULL;
605 }
606
607 if (sigbuf)
608 {
609 sigbuf[sigptr] = '\0';
610 rc = sigbuf;
611 }
612 else
613 rc = US"";
614
615 CLEANUP:
616 if (ctx)
617 pdkim_free_ctx(ctx);
618 store_pool = old_pool;
619 errno = save_errno;
620 return rc;
621 }
622
623 #endif