DKIM: replace pdkim module debugging trace facility with Exim standard
[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_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 dkim_cur_sig = NULL;
233 dkim_cur_signer = id;
234
235 if (dkim_disable_verify || !id || !dkim_verify_ctx)
236 return;
237
238 /* Find signature to run ACL on */
239
240 for (sig = dkim_signatures; sig; sig = sig->next)
241 if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain)
242 && strcmpic(cmp_val, id) == 0
243 )
244 {
245 dkim_cur_sig = sig;
246
247 /* The "dkim_domain" and "dkim_selector" expansion variables have
248 related globals, since they are used in the signing code too.
249 Instead of inventing separate names for verification, we set
250 them here. This is easy since a domain and selector is guaranteed
251 to be in a signature. The other dkim_* expansion items are
252 dynamically fetched from dkim_cur_sig at expansion time (see
253 function below). */
254
255 dkim_signing_domain = US sig->domain;
256 dkim_signing_selector = US sig->selector;
257 dkim_key_length = sig->sigdata_len * 8;
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( (char *) dkim_signing_domain,
565 (char *) dkim_signing_selector,
566 (char *) dkim_private_key_expanded);
567 pdkim_set_optional(ctx,
568 (char *) dkim_sign_headers_expanded,
569 NULL,
570 pdkim_canon,
571 pdkim_canon, -1, PDKIM_ALGO_RSA_SHA256, 0, 0);
572
573 lseek(dkim_fd, 0, SEEK_SET);
574
575 while ((sread = read(dkim_fd, &buf, 4096)) > 0)
576 if (pdkim_feed(ctx, buf, sread) != PDKIM_OK)
577 {
578 rc = NULL;
579 goto CLEANUP;
580 }
581
582 /* Handle failed read above. */
583 if (sread == -1)
584 {
585 debug_printf("DKIM: Error reading -K file.\n");
586 save_errno = errno;
587 rc = NULL;
588 goto CLEANUP;
589 }
590
591 if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK)
592 {
593 log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
594 rc = NULL;
595 goto CLEANUP;
596 }
597
598 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
599 US signature->signature_header, US"\r\n");
600
601 pdkim_free_ctx(ctx);
602 ctx = NULL;
603 }
604
605 if (sigbuf)
606 {
607 sigbuf[sigptr] = '\0';
608 rc = sigbuf;
609 }
610 else
611 rc = US"";
612
613 CLEANUP:
614 if (ctx)
615 pdkim_free_ctx(ctx);
616 store_pool = old_pool;
617 errno = save_errno;
618 return rc;
619 }
620
621 #endif