update to pre-4.87 master
[exim.git] / src / src / dkim.c
CommitLineData
80a47a2c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
bfe645c1 5/* Copyright (c) University of Cambridge, 1995 - 2015 */
80a47a2c
TK
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
bfe645c1 17pdkim_ctx *dkim_verify_ctx = NULL;
80a47a2c 18pdkim_signature *dkim_signatures = NULL;
bfe645c1
JH
19pdkim_signature *dkim_cur_sig = NULL;
20
21static int
22dkim_exim_query_dns_txt(char *name, char *answer)
23{
24dns_answer dnsa;
25dns_scan dnss;
26dns_record *rr;
27
28lookup_dnssec_authenticated = NULL;
29if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
30 return PDKIM_FAIL;
31
32/* Search for TXT record */
33
34for (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 {
80a47a2c
TK
39 int rr_offset = 0;
40 int answer_offset = 0;
bfe645c1
JH
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;
4263f395 54 }
bfe645c1 55 return PDKIM_OK;
80a47a2c 56 }
80a47a2c 57
bfe645c1 58return PDKIM_FAIL;
80a47a2c
TK
59}
60
61
bfe645c1
JH
62void
63dkim_exim_verify_init(void)
64{
65/* Free previous context if there is one */
80a47a2c 66
bfe645c1
JH
67if (dkim_verify_ctx)
68 pdkim_free_ctx(dkim_verify_ctx);
80a47a2c 69
bfe645c1 70/* Create new context */
80a47a2c 71
bfe645c1
JH
72dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt);
73dkim_collect_input = !!dkim_verify_ctx;
80a47a2c
TK
74}
75
76
bfe645c1
JH
77void
78dkim_exim_verify_feed(uschar * data, int len)
79{
80if ( dkim_collect_input
81 && pdkim_feed(dkim_verify_ctx, (char *)data, len) != PDKIM_OK)
82 dkim_collect_input = FALSE;
80a47a2c
TK
83}
84
85
bfe645c1
JH
86void
87dkim_exim_verify_finish(void)
88{
89pdkim_signature *sig = NULL;
90int dkim_signers_size = 0;
91int dkim_signers_ptr = 0;
92dkim_signers = NULL;
80a47a2c 93
bfe645c1 94/* Delete eventual previous signature chain */
80a47a2c 95
bfe645c1
JH
96dkim_signatures = NULL;
97
98/* If we have arrived here with dkim_collect_input == FALSE, it
99means there was a processing error somewhere along the way.
100Log the incident and disable futher verification. */
101
102if (!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;
80a47a2c 109 }
80a47a2c 110
bfe645c1
JH
111dkim_collect_input = FALSE;
112
113/* Finish DKIM operation and fetch link to signatures chain */
114
115if (pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures) != PDKIM_OK)
116 return;
117
118for (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]");
80a47a2c 145 break;
bfe645c1
JH
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 }
80a47a2c 170 break;
bfe645c1
JH
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 }
80a47a2c 190 break;
bfe645c1
JH
191
192 case PDKIM_VERIFY_PASS:
193 logmsg =
194 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
80a47a2c
TK
195 break;
196 }
197
bfe645c1
JH
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, ":");
80a47a2c 206
bfe645c1 207 if (sig->identity)
9e5d6b55 208 dkim_signers = string_append(dkim_signers,
bfe645c1
JH
209 &dkim_signers_size,
210 &dkim_signers_ptr, 2, sig->identity, ":");
80a47a2c 211
bfe645c1 212 /* Process next signature */
80a47a2c
TK
213 }
214
bfe645c1
JH
215/* NULL-terminate and chop the last colon from the domain list */
216
217if (dkim_signers)
218 {
219 dkim_signers[dkim_signers_ptr] = '\0';
220 if (Ustrlen(dkim_signers) > 0)
221 dkim_signers[Ustrlen(dkim_signers) - 1] = '\0';
0d2b278d 222 }
80a47a2c
TK
223}
224
225
bfe645c1
JH
226void
227dkim_exim_acl_setup(uschar * id)
228{
229pdkim_signature * sig;
230uschar * cmp_val;
231
232dkim_cur_sig = NULL;
233dkim_cur_signer = id;
234
235if (dkim_disable_verify || !id || !dkim_verify_ctx)
236 return;
237
238/* Find signature to run ACL on */
239
240for (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;
80a47a2c 259 }
80a47a2c
TK
260}
261
262
bfe645c1
JH
263static uschar *
264dkim_exim_expand_defaults(int what)
265{
266switch (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}
80a47a2c 287
80a47a2c 288
bfe645c1
JH
289uschar *
290dkim_exim_expand_query(int what)
291{
292if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig)
293 return dkim_exim_expand_defaults(what);
294
295switch (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";
da5dfc3a 303 }
bfe645c1
JH
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";
da5dfc3a 316 }
bfe645c1
JH
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";
80a47a2c 391 }
bfe645c1
JH
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";
80a47a2c 401 }
bfe645c1
JH
402
403 default:
404 return US"";
80a47a2c
TK
405 }
406}
407
408
bfe645c1
JH
409uschar *
410dkim_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{
414int sep = 0;
415uschar *seen_items = NULL;
416int seen_items_size = 0;
417int seen_items_offset = 0;
418uschar itembuf[256];
419uschar *dkim_canon_expanded;
420uschar *dkim_sign_headers_expanded;
421uschar *dkim_private_key_expanded;
422pdkim_ctx *ctx = NULL;
423uschar *rc = NULL;
424uschar *sigbuf = NULL;
425int sigsize = 0;
426int sigptr = 0;
427pdkim_signature *signature;
428int pdkim_canon;
429int pdkim_rc;
430int sread;
431char buf[4096];
432int save_errno = 0;
433int old_pool = store_pool;
434
435store_pool = POOL_MAIN;
436
437if (!(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;
80a47a2c 444 }
80a47a2c 445
bfe645c1
JH
446/* Set $dkim_domain expansion variable to each unique domain in list. */
447
448while ((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. */
80a47a2c 475
bfe645c1
JH
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 {
80a47a2c 489 /* expansion error, do not send message. */
bfe645c1
JH
490 log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand "
491 "dkim_canon: %s", expand_string_message);
80a47a2c
TK
492 rc = NULL;
493 goto CLEANUP;
bfe645c1 494 }
80a47a2c 495
bfe645c1
JH
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;
a8e1eeba 506 }
bfe645c1
JH
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);
a8e1eeba
MH
514 rc = NULL;
515 goto CLEANUP;
bfe645c1
JH
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;
a8e1eeba 527 }
80a47a2c 528
bfe645c1
JH
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);
80a47a2c
TK
548 rc = NULL;
549 goto CLEANUP;
db4d0902 550 }
80a47a2c 551
bfe645c1
JH
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);
cf745305
TK
556 rc = NULL;
557 goto CLEANUP;
1ac6b2e7 558 }
80a47a2c 559
bfe645c1
JH
560 (void) close(privkey_fd);
561 dkim_private_key_expanded = big_buffer;
a8e1eeba 562 }
bfe645c1
JH
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 {
80a47a2c
TK
578 rc = NULL;
579 goto CLEANUP;
bfe645c1
JH
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;
80a47a2c 589 }
80a47a2c 590
bfe645c1
JH
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;
a8e1eeba 596 }
80a47a2c 597
bfe645c1
JH
598 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
599 US signature->signature_header, US"\r\n");
80a47a2c 600
bfe645c1
JH
601 pdkim_free_ctx(ctx);
602 ctx = NULL;
80a47a2c 603 }
a8e1eeba 604
bfe645c1
JH
605if (sigbuf)
606 {
607 sigbuf[sigptr] = '\0';
608 rc = sigbuf;
609 }
610else
611 rc = US"";
612
613CLEANUP:
614if (ctx)
615 pdkim_free_ctx(ctx);
616store_pool = old_pool;
617errno = save_errno;
618return rc;
93f2d376 619}
80a47a2c
TK
620
621#endif