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