1 /* $Cambridge: exim/src/src/dkim.c,v 1.15 2010/06/12 13:54:38 jetmore Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge, 1995 - 2007 */
8 /* See the file NOTICE for conditions of use and distribution. */
10 /* Code for DKIM support. Other DKIM relevant code is in
11 receive.c, transport.c and transports/smtp.c */
17 #include "pdkim/pdkim.h"
19 pdkim_ctx *dkim_verify_ctx = NULL;
20 pdkim_signature *dkim_signatures = NULL;
21 pdkim_signature *dkim_cur_sig = NULL;
23 int dkim_exim_query_dns_txt(char *name, char *answer) {
28 if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL;
30 /* Search for TXT record */
31 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
33 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
34 if (rr->type == T_TXT) break;
36 /* Copy record content to the answer buffer */
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));
49 else return PDKIM_FAIL;
55 void dkim_exim_verify_init(void) {
57 /* Free previous context if there is one */
58 if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
60 /* Create new context */
61 dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
62 &dkim_exim_query_dns_txt
65 if (dkim_verify_ctx != NULL) {
66 dkim_collect_input = TRUE;
67 pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
69 else dkim_collect_input = FALSE;
74 void dkim_exim_verify_feed(uschar *data, int len) {
75 if (dkim_collect_input &&
76 pdkim_feed(dkim_verify_ctx,
78 len) != PDKIM_OK) dkim_collect_input = FALSE;
82 void dkim_exim_verify_finish(void) {
83 pdkim_signature *sig = NULL;
84 int dkim_signers_size = 0;
85 int dkim_signers_ptr = 0;
88 /* Delete eventual previous signature chain */
89 dkim_signatures = NULL;
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, "DKIM: Error while running this message through validation, disabling signature verification.");
96 dkim_disable_verify = TRUE;
99 dkim_collect_input = FALSE;
101 /* Finish DKIM operation and fetch link to signatures chain */
102 if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return;
104 sig = dkim_signatures;
105 while (sig != NULL) {
108 /* Log a line for each signature */
109 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
111 string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ",
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"
118 ((sig->identity != NULL)?
119 string_sprintf("i=%s ", sig->identity)
124 string_sprintf("t=%lu ", sig->created)
129 string_sprintf("x=%lu ", sig->expires)
133 ((sig->bodylength > -1)?
134 string_sprintf("l=%lu ", sig->bodylength)
140 switch(sig->verify_status) {
141 case PDKIM_VERIFY_NONE:
142 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
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]");
150 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
151 logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]");
153 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
154 logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]");
157 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]");
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)]");
166 case PDKIM_VERIFY_FAIL_MESSAGE:
167 logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]");
170 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
173 case PDKIM_VERIFY_PASS:
174 logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
179 log_write(0, LOG_MAIN, (char *)logmsg);
181 /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
182 dkim_signers = string_append(dkim_signers,
190 if (sig->identity != NULL) {
191 dkim_signers = string_append(dkim_signers,
200 /* Process next signature */
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';
213 void dkim_exim_acl_setup(uschar *id) {
214 pdkim_signature *sig = dkim_signatures;
216 dkim_cur_signer = id;
217 if (dkim_disable_verify ||
218 !id || !dkim_verify_ctx) return;
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)) {
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
233 dkim_signing_domain = (uschar *)sig->domain;
234 dkim_signing_selector = (uschar *)sig->selector;
242 uschar *dkim_exim_expand_query(int what) {
244 if (!dkim_verify_ctx ||
245 dkim_disable_verify ||
246 !dkim_cur_sig) return dkim_exim_expand_defaults(what);
250 switch(dkim_cur_sig->algo) {
251 case PDKIM_ALGO_RSA_SHA1:
253 case PDKIM_ALGO_RSA_SHA256:
255 return US"rsa-sha256";
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:
262 switch(dkim_cur_sig->canon_body) {
263 case PDKIM_CANON_RELAXED:
265 case PDKIM_CANON_SIMPLE:
269 case DKIM_CANON_HEADERS:
270 switch(dkim_cur_sig->canon_headers) {
271 case PDKIM_CANON_RELAXED:
273 case PDKIM_CANON_SIMPLE:
277 case DKIM_COPIEDHEADERS:
278 return dkim_cur_sig->copiedheaders?
279 (uschar *)(dkim_cur_sig->copiedheaders)
280 :dkim_exim_expand_defaults(what);
282 return (dkim_cur_sig->created > 0)?
283 (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
284 :dkim_exim_expand_defaults(what);
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);
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)
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)
310 :dkim_exim_expand_defaults(what);
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)
317 :dkim_exim_expand_defaults(what);
318 case DKIM_KEY_TESTING:
319 return dkim_cur_sig->pubkey?
320 (dkim_cur_sig->pubkey->testing?
322 :dkim_exim_expand_defaults(what)
324 :dkim_exim_expand_defaults(what);
325 case DKIM_NOSUBDOMAINS:
326 return dkim_cur_sig->pubkey?
327 (dkim_cur_sig->pubkey->no_subdomaining?
329 :dkim_exim_expand_defaults(what)
331 :dkim_exim_expand_defaults(what);
332 case DKIM_VERIFY_STATUS:
333 switch(dkim_cur_sig->verify_status) {
334 case PDKIM_VERIFY_INVALID:
336 case PDKIM_VERIFY_FAIL:
338 case PDKIM_VERIFY_PASS:
340 case PDKIM_VERIFY_NONE:
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";
361 uschar *dkim_exim_expand_defaults(int 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"";
384 uschar *dkim_exim_sign(int dkim_fd,
385 uschar *dkim_private_key,
387 uschar *dkim_selector,
389 uschar *dkim_sign_headers) {
391 uschar *seen_items = NULL;
392 int seen_items_size = 0;
393 int seen_items_offset = 0;
395 uschar *dkim_canon_expanded;
396 uschar *dkim_sign_headers_expanded;
397 uschar *dkim_private_key_expanded;
398 pdkim_ctx *ctx = NULL;
400 pdkim_signature *signature;
406 int old_pool = store_pool;
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);
417 /* Set $dkim_domain expansion variable to each unique domain in list. */
418 while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
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)
429 seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":");
431 seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,dkim_signing_domain);
432 seen_items[seen_items_offset] = '\0';
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);
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. */
447 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
448 "dkim_canon: %s", expand_string_message);
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;
457 log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon_expanded);
458 pdkim_canon = PDKIM_CANON_RELAXED;
461 if (dkim_sign_headers) {
462 dkim_sign_headers_expanded = expand_string(dkim_sign_headers);
463 if (dkim_sign_headers_expanded == NULL) {
464 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
465 "dkim_sign_headers: %s", expand_string_message);
471 /* pass NULL, which means default header list */
472 dkim_sign_headers_expanded = NULL;
475 /* Get private key to use. */
476 dkim_private_key_expanded = expand_string(dkim_private_key);
477 if (dkim_private_key_expanded == NULL) {
478 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
479 "dkim_private_key: %s", expand_string_message);
483 if ( (Ustrlen(dkim_private_key_expanded) == 0) ||
484 (Ustrcmp(dkim_private_key_expanded,"0") == 0) ||
485 (Ustrcmp(dkim_private_key_expanded,"false") == 0) ) {
486 /* don't sign, but no error */
491 if (dkim_private_key_expanded[0] == '/') {
493 /* Looks like a filename, load the private key. */
494 memset(big_buffer,0,big_buffer_size);
495 privkey_fd = open(CS dkim_private_key_expanded,O_RDONLY);
496 if (privkey_fd < 0) {
497 log_write(0, LOG_MAIN|LOG_PANIC, "unable to open "
498 "private key file for reading: %s", dkim_private_key_expanded);
502 (void)read(privkey_fd,big_buffer,(big_buffer_size-2));
503 (void)close(privkey_fd);
504 dkim_private_key_expanded = big_buffer;
507 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
508 (char *)dkim_signing_domain,
509 (char *)dkim_signing_selector,
510 (char *)dkim_private_key_expanded
513 pdkim_set_debug_stream(ctx,debug_file);
515 pdkim_set_optional(ctx,
516 (char *)dkim_sign_headers_expanded,
521 PDKIM_ALGO_RSA_SHA256,
525 while((sread = read(dkim_fd,&buf,4096)) > 0) {
526 if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
531 /* Handle failed read above. */
533 debug_printf("DKIM: Error reading -K file.\n");
539 pdkim_rc = pdkim_feed_finish(ctx,&signature);
540 if (pdkim_rc != PDKIM_OK) {
541 log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
545 rc = store_get(strlen(signature->signature_header)+3);
546 Ustrcpy(rc,US signature->signature_header);
547 Ustrcat(rc,US"\r\n");
556 store_pool = old_pool;