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