SECURITY: DKIM DNS buffer overflow protection
[exim.git] / src / src / dkim.c
CommitLineData
80a47a2c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
0a49a7a4 5/* Copyright (c) University of Cambridge, 1995 - 2007 */
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
26 if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL;
27
28 /* Search for TXT record */
29 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
30 rr != NULL;
31 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
32 if (rr->type == T_TXT) break;
33
34 /* Copy record content to the answer buffer */
35 if (rr != NULL) {
36 int rr_offset = 0;
37 int answer_offset = 0;
38 while (rr_offset < rr->size) {
39 uschar len = (rr->data)[rr_offset++];
40 snprintf(answer+(answer_offset),
41 PDKIM_DNS_TXT_MAX_RECLEN-(answer_offset),
42 "%.*s", (int)len, (char *)((rr->data)+rr_offset));
43 rr_offset+=len;
44 answer_offset+=len;
4263f395
PP
45 if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN) {
46 return PDKIM_FAIL;
47 }
80a47a2c
TK
48 }
49 }
50 else return PDKIM_FAIL;
51
52 return PDKIM_OK;
53}
54
55
56void dkim_exim_verify_init(void) {
57
58 /* Free previous context if there is one */
59 if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
60
61 /* Create new context */
62 dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
63 &dkim_exim_query_dns_txt
64 );
65
66 if (dkim_verify_ctx != NULL) {
67 dkim_collect_input = TRUE;
68 pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
69 }
70 else dkim_collect_input = FALSE;
71
72}
73
74
75void dkim_exim_verify_feed(uschar *data, int len) {
76 if (dkim_collect_input &&
77 pdkim_feed(dkim_verify_ctx,
78 (char *)data,
79 len) != PDKIM_OK) dkim_collect_input = FALSE;
80}
81
82
83void dkim_exim_verify_finish(void) {
84 pdkim_signature *sig = NULL;
9e5d6b55
TK
85 int dkim_signers_size = 0;
86 int dkim_signers_ptr = 0;
87 dkim_signers = NULL;
80a47a2c
TK
88
89 /* Delete eventual previous signature chain */
90 dkim_signatures = NULL;
91
92 /* If we have arrived here with dkim_collect_input == FALSE, it
93 means there was a processing error somewhere along the way.
94 Log the incident and disable futher verification. */
95 if (!dkim_collect_input) {
d4f333f7 96 log_write(0, LOG_MAIN, "DKIM: Error while running this message through validation, disabling signature verification.");
80a47a2c
TK
97 dkim_disable_verify = TRUE;
98 return;
99 }
100 dkim_collect_input = FALSE;
101
102 /* Finish DKIM operation and fetch link to signatures chain */
103 if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return;
104
105 sig = dkim_signatures;
106 while (sig != NULL) {
107 int size = 0;
108 int ptr = 0;
109 /* Log a line for each signature */
110 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
111
337e3505 112 string_sprintf( "d=%s s=%s c=%s/%s a=%s ",
80a47a2c
TK
113 sig->domain,
114 sig->selector,
115 (sig->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
116 (sig->canon_body == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
117 (sig->algo == PDKIM_ALGO_RSA_SHA256)?"rsa-sha256":"rsa-sha1"
118 ),
119 ((sig->identity != NULL)?
120 string_sprintf("i=%s ", sig->identity)
121 :
122 US""
123 ),
124 ((sig->created > 0)?
125 string_sprintf("t=%lu ", sig->created)
126 :
127 US""
128 ),
129 ((sig->expires > 0)?
130 string_sprintf("x=%lu ", sig->expires)
131 :
132 US""
133 ),
134 ((sig->bodylength > -1)?
135 string_sprintf("l=%lu ", sig->bodylength)
136 :
137 US""
138 )
139 );
140
141 switch(sig->verify_status) {
142 case PDKIM_VERIFY_NONE:
143 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
144 break;
145 case PDKIM_VERIFY_INVALID:
146 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
147 switch (sig->verify_ext_status) {
148 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
149 logmsg = string_append(logmsg, &size, &ptr, 1, "public key record (currently?) unavailable]");
150 break;
151 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
152 logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]");
153 break;
154 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
155 logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]");
156 break;
157 default:
158 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]");
159 }
160 break;
161 case PDKIM_VERIFY_FAIL:
162 logmsg = string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
163 switch (sig->verify_ext_status) {
164 case PDKIM_VERIFY_FAIL_BODY:
165 logmsg = string_append(logmsg, &size, &ptr, 1, "body hash mismatch (body probably modified in transit)]");
166 break;
167 case PDKIM_VERIFY_FAIL_MESSAGE:
168 logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]");
169 break;
170 default:
171 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
172 }
173 break;
174 case PDKIM_VERIFY_PASS:
175 logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
176 break;
177 }
178
179 logmsg[ptr] = '\0';
337e3505 180 log_write(0, LOG_MAIN, "DKIM: %s", logmsg);
80a47a2c 181
9e5d6b55
TK
182 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
183 dkim_signers = string_append(dkim_signers,
184 &dkim_signers_size,
185 &dkim_signers_ptr,
186 2,
187 sig->domain,
188 ":"
189 );
190
191 if (sig->identity != NULL) {
192 dkim_signers = string_append(dkim_signers,
193 &dkim_signers_size,
194 &dkim_signers_ptr,
195 2,
196 sig->identity,
197 ":"
198 );
199 }
80a47a2c
TK
200
201 /* Process next signature */
202 sig = sig->next;
203 }
204
0d2b278d
TK
205 /* NULL-terminate and chop the last colon from the domain list */
206 if (dkim_signers != NULL) {
207 dkim_signers[dkim_signers_ptr] = '\0';
208 if (Ustrlen(dkim_signers) > 0)
209 dkim_signers[Ustrlen(dkim_signers)-1] = '\0';
210 }
80a47a2c
TK
211}
212
213
214void dkim_exim_acl_setup(uschar *id) {
215 pdkim_signature *sig = dkim_signatures;
216 dkim_cur_sig = NULL;
9e5d6b55 217 dkim_cur_signer = id;
80a47a2c 218 if (dkim_disable_verify ||
9e5d6b55 219 !id || !dkim_verify_ctx) return;
80a47a2c
TK
220 /* Find signature to run ACL on */
221 while (sig != NULL) {
222 uschar *cmp_val = NULL;
223 if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity;
224 else cmp_val = (uschar *)sig->domain;
225 if (cmp_val && (strcmpic(cmp_val,id) == 0)) {
226 dkim_cur_sig = sig;
227 /* The "dkim_domain" and "dkim_selector" expansion variables have
228 related globals, since they are used in the signing code too.
229 Instead of inventing separate names for verification, we set
230 them here. This is easy since a domain and selector is guaranteed
231 to be in a signature. The other dkim_* expansion items are
232 dynamically fetched from dkim_cur_sig at expansion time (see
233 function below). */
234 dkim_signing_domain = (uschar *)sig->domain;
235 dkim_signing_selector = (uschar *)sig->selector;
236 return;
237 }
238 sig = sig->next;
239 }
240}
241
242
243uschar *dkim_exim_expand_query(int what) {
244
245 if (!dkim_verify_ctx ||
246 dkim_disable_verify ||
247 !dkim_cur_sig) return dkim_exim_expand_defaults(what);
248
249 switch(what) {
250 case DKIM_ALGO:
da5dfc3a
TK
251 switch(dkim_cur_sig->algo) {
252 case PDKIM_ALGO_RSA_SHA1:
253 return US"rsa-sha1";
254 case PDKIM_ALGO_RSA_SHA256:
255 default:
256 return US"rsa-sha256";
257 }
80a47a2c
TK
258 case DKIM_BODYLENGTH:
259 return (dkim_cur_sig->bodylength >= 0)?
260 (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
261 :dkim_exim_expand_defaults(what);
262 case DKIM_CANON_BODY:
da5dfc3a
TK
263 switch(dkim_cur_sig->canon_body) {
264 case PDKIM_CANON_RELAXED:
265 return US"relaxed";
266 case PDKIM_CANON_SIMPLE:
267 default:
268 return US"simple";
269 }
80a47a2c 270 case DKIM_CANON_HEADERS:
da5dfc3a
TK
271 switch(dkim_cur_sig->canon_headers) {
272 case PDKIM_CANON_RELAXED:
273 return US"relaxed";
274 case PDKIM_CANON_SIMPLE:
275 default:
276 return US"simple";
277 }
80a47a2c
TK
278 case DKIM_COPIEDHEADERS:
279 return dkim_cur_sig->copiedheaders?
280 (uschar *)(dkim_cur_sig->copiedheaders)
281 :dkim_exim_expand_defaults(what);
282 case DKIM_CREATED:
283 return (dkim_cur_sig->created > 0)?
284 (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
285 :dkim_exim_expand_defaults(what);
286 case DKIM_EXPIRES:
287 return (dkim_cur_sig->expires > 0)?
288 (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
289 :dkim_exim_expand_defaults(what);
290 case DKIM_HEADERNAMES:
291 return dkim_cur_sig->headernames?
292 (uschar *)(dkim_cur_sig->headernames)
293 :dkim_exim_expand_defaults(what);
294 case DKIM_IDENTITY:
295 return dkim_cur_sig->identity?
296 (uschar *)(dkim_cur_sig->identity)
297 :dkim_exim_expand_defaults(what);
298 case DKIM_KEY_GRANULARITY:
299 return dkim_cur_sig->pubkey?
300 (dkim_cur_sig->pubkey->granularity?
301 (uschar *)(dkim_cur_sig->pubkey->granularity)
302 :dkim_exim_expand_defaults(what)
303 )
304 :dkim_exim_expand_defaults(what);
305 case DKIM_KEY_SRVTYPE:
306 return dkim_cur_sig->pubkey?
307 (dkim_cur_sig->pubkey->srvtype?
308 (uschar *)(dkim_cur_sig->pubkey->srvtype)
309 :dkim_exim_expand_defaults(what)
310 )
311 :dkim_exim_expand_defaults(what);
312 case DKIM_KEY_NOTES:
313 return dkim_cur_sig->pubkey?
314 (dkim_cur_sig->pubkey->notes?
315 (uschar *)(dkim_cur_sig->pubkey->notes)
316 :dkim_exim_expand_defaults(what)
317 )
318 :dkim_exim_expand_defaults(what);
319 case DKIM_KEY_TESTING:
320 return dkim_cur_sig->pubkey?
321 (dkim_cur_sig->pubkey->testing?
322 US"1"
323 :dkim_exim_expand_defaults(what)
324 )
325 :dkim_exim_expand_defaults(what);
326 case DKIM_NOSUBDOMAINS:
327 return dkim_cur_sig->pubkey?
328 (dkim_cur_sig->pubkey->no_subdomaining?
329 US"1"
330 :dkim_exim_expand_defaults(what)
331 )
332 :dkim_exim_expand_defaults(what);
333 case DKIM_VERIFY_STATUS:
334 switch(dkim_cur_sig->verify_status) {
335 case PDKIM_VERIFY_INVALID:
336 return US"invalid";
337 case PDKIM_VERIFY_FAIL:
338 return US"fail";
339 case PDKIM_VERIFY_PASS:
340 return US"pass";
341 case PDKIM_VERIFY_NONE:
342 default:
343 return US"none";
344 }
345 case DKIM_VERIFY_REASON:
346 switch (dkim_cur_sig->verify_ext_status) {
347 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
348 return US"pubkey_unavailable";
349 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
350 return US"pubkey_syntax";
351 case PDKIM_VERIFY_FAIL_BODY:
352 return US"bodyhash_mismatch";
353 case PDKIM_VERIFY_FAIL_MESSAGE:
354 return US"signature_incorrect";
355 }
356 default:
357 return US"";
358 }
359}
360
361
362uschar *dkim_exim_expand_defaults(int what) {
363 switch(what) {
364 case DKIM_ALGO: return US"";
365 case DKIM_BODYLENGTH: return US"9999999999999";
366 case DKIM_CANON_BODY: return US"";
367 case DKIM_CANON_HEADERS: return US"";
368 case DKIM_COPIEDHEADERS: return US"";
369 case DKIM_CREATED: return US"0";
370 case DKIM_EXPIRES: return US"9999999999999";
371 case DKIM_HEADERNAMES: return US"";
372 case DKIM_IDENTITY: return US"";
373 case DKIM_KEY_GRANULARITY: return US"*";
374 case DKIM_KEY_SRVTYPE: return US"*";
375 case DKIM_KEY_NOTES: return US"";
376 case DKIM_KEY_TESTING: return US"0";
377 case DKIM_NOSUBDOMAINS: return US"0";
378 case DKIM_VERIFY_STATUS: return US"none";
379 case DKIM_VERIFY_REASON: return US"";
380 default: return US"";
381 }
382}
383
384
385uschar *dkim_exim_sign(int dkim_fd,
386 uschar *dkim_private_key,
387 uschar *dkim_domain,
388 uschar *dkim_selector,
389 uschar *dkim_canon,
390 uschar *dkim_sign_headers) {
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
80a47a2c
TK
414 dkim_domain = expand_string(dkim_domain);
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) {
431 uschar *seen_items_list = seen_items;
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 }
507 (void)read(privkey_fd,big_buffer,(big_buffer_size-2));
508 (void)close(privkey_fd);
509 dkim_private_key_expanded = big_buffer;
510 }
80a47a2c 511
a8e1eeba
MH
512 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
513 (char *)dkim_signing_domain,
514 (char *)dkim_signing_selector,
515 (char *)dkim_private_key_expanded
516 );
517
518 pdkim_set_debug_stream(ctx,debug_file);
519
520 pdkim_set_optional(ctx,
521 (char *)dkim_sign_headers_expanded,
522 NULL,
523 pdkim_canon,
524 pdkim_canon,
525 -1,
526 PDKIM_ALGO_RSA_SHA256,
527 0,
528 0);
529
bc4bc4c5 530 lseek(dkim_fd, 0, SEEK_SET);
a8e1eeba
MH
531 while((sread = read(dkim_fd,&buf,4096)) > 0) {
532 if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
533 rc = NULL;
534 goto CLEANUP;
535 }
536 }
537 /* Handle failed read above. */
538 if (sread == -1) {
539 debug_printf("DKIM: Error reading -K file.\n");
540 save_errno = errno;
80a47a2c
TK
541 rc = NULL;
542 goto CLEANUP;
543 }
80a47a2c 544
a8e1eeba
MH
545 pdkim_rc = pdkim_feed_finish(ctx,&signature);
546 if (pdkim_rc != PDKIM_OK) {
547 log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
bc4bc4c5 548 rc = NULL;
a8e1eeba
MH
549 goto CLEANUP;
550 }
80a47a2c 551
bc4bc4c5
PP
552 sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
553 US signature->signature_header,
554 US"\r\n");
80a47a2c 555
80a47a2c 556 pdkim_free_ctx(ctx);
a8e1eeba 557 ctx = NULL;
80a47a2c 558 }
a8e1eeba 559
bc4bc4c5
PP
560 if (sigbuf != NULL) {
561 sigbuf[sigptr] = '\0';
562 rc = sigbuf;
563 } else
564 rc = US"";
565
a8e1eeba
MH
566 CLEANUP:
567 if (ctx != NULL)
568 pdkim_free_ctx(ctx);
80a47a2c
TK
569 store_pool = old_pool;
570 errno = save_errno;
571 return rc;
93f2d376 572}
80a47a2c
TK
573
574#endif