fix no-ssl build
[exim.git] / src / src / dkim.c
CommitLineData
80a47a2c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
3386088d 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
1cfe5c1c 17pdkim_ctx *dkim_verify_ctx = NULL;
80a47a2c 18pdkim_signature *dkim_signatures = NULL;
1cfe5c1c 19pdkim_signature *dkim_cur_sig = NULL;
80a47a2c 20
1cfe5c1c
JH
21static int
22dkim_exim_query_dns_txt(char *name, char *answer)
23{
24dns_answer dnsa;
25dns_scan dnss;
26dns_record *rr;
80a47a2c 27
1cfe5c1c
JH
28lookup_dnssec_authenticated = NULL;
29if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED)
30 return PDKIM_FAIL;
80a47a2c 31
1cfe5c1c 32/* Search for TXT record */
80a47a2c 33
1cfe5c1c
JH
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;
1cfe5c1c
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 }
1cfe5c1c 55 return PDKIM_OK;
80a47a2c 56 }
80a47a2c 57
1cfe5c1c 58return PDKIM_FAIL;
80a47a2c
TK
59}
60
61
1cfe5c1c
JH
62void
63dkim_exim_verify_init(void)
64{
65/* Free previous context if there is one */
80a47a2c 66
1cfe5c1c
JH
67if (dkim_verify_ctx)
68 pdkim_free_ctx(dkim_verify_ctx);
80a47a2c 69
1cfe5c1c 70/* Create new context */
80a47a2c 71
0d04a285 72dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt);
3b957582 73dkim_collect_input = !!dkim_verify_ctx;
80a47a2c
TK
74}
75
76
1cfe5c1c
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
1cfe5c1c
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;
93
94/* Delete eventual previous signature chain */
95
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;
109 }
80a47a2c 110
1cfe5c1c 111dkim_collect_input = FALSE;
80a47a2c 112
1cfe5c1c 113/* Finish DKIM operation and fetch link to signatures chain */
80a47a2c 114
1cfe5c1c
JH
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,
abe1010c 126 string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ",
1cfe5c1c
JH
127 sig->domain,
128 sig->selector,
129 sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
130 sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed",
abe1010c
JH
131 sig->algo == PDKIM_ALGO_RSA_SHA256 ? "rsa-sha256" : "rsa-sha1",
132 sig->sigdata_len * 8
133 ),
1cfe5c1c
JH
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;
1cfe5c1c
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;
1cfe5c1c
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;
1cfe5c1c
JH
191
192 case PDKIM_VERIFY_PASS:
193 logmsg =
194 string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
80a47a2c
TK
195 break;
196 }
197
1cfe5c1c
JH
198 logmsg[ptr] = '\0';
199 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
80a47a2c 200
1cfe5c1c
JH
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)
9e5d6b55 208 dkim_signers = string_append(dkim_signers,
1cfe5c1c
JH
209 &dkim_signers_size,
210 &dkim_signers_ptr, 2, sig->identity, ":");
80a47a2c 211
1cfe5c1c 212 /* Process next signature */
80a47a2c
TK
213 }
214
1cfe5c1c
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
1cfe5c1c
JH
226void
227dkim_exim_acl_setup(uschar * id)
228{
229pdkim_signature * sig;
230uschar * cmp_val;
231
1cfe5c1c
JH
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;
abe1010c 257 dkim_key_length = sig->sigdata_len * 8;
1cfe5c1c 258 return;
80a47a2c 259 }
80a47a2c
TK
260}
261
262
1cfe5c1c
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
1cfe5c1c
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 }
1cfe5c1c
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";
80a47a2c 316 }
1cfe5c1c
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 }
80a47a2c 392
1cfe5c1c
JH
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 }
80a47a2c 402
1cfe5c1c
JH
403 default:
404 return US"";
80a47a2c
TK
405 }
406}
407
408
55414b25 409uschar *
1cfe5c1c
JH
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)
55414b25 413{
1cfe5c1c
JH
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;
444 }
445
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. */
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 {
80a47a2c 489 /* expansion error, do not send message. */
1cfe5c1c
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;
1cfe5c1c 494 }
80a47a2c 495
1cfe5c1c
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 }
1cfe5c1c
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;
1cfe5c1c
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
1cfe5c1c
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
1cfe5c1c
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
1cfe5c1c
JH
560 (void) close(privkey_fd);
561 dkim_private_key_expanded = big_buffer;
a8e1eeba 562 }
1cfe5c1c 563
0d04a285 564 ctx = pdkim_init_sign( (char *) dkim_signing_domain,
1cfe5c1c
JH
565 (char *) dkim_signing_selector,
566 (char *) dkim_private_key_expanded);
1cfe5c1c
JH
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;
1cfe5c1c
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
1cfe5c1c
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
1cfe5c1c
JH
598 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
599 US signature->signature_header, US"\r\n");
80a47a2c 600
1cfe5c1c
JH
601 pdkim_free_ctx(ctx);
602 ctx = NULL;
80a47a2c 603 }
a8e1eeba 604
1cfe5c1c
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