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