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