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