X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/cf7453055ad53ee3d2ee7b790ee83168ce58412d..6f5d1ca3b1563d0ed580a43ba711b3534b19234e:/src/src/dkim.c diff --git a/src/src/dkim.c b/src/src/dkim.c index 3109168a3..171fcccdb 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/dkim.c,v 1.4 2009/10/13 18:32:05 tom Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 2009 */ +/* Copyright (c) University of Cambridge, 1995 - 2007 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for DKIM support. Other DKIM relevant code is in @@ -25,6 +23,7 @@ int dkim_exim_query_dns_txt(char *name, char *answer) { dns_scan dnss; dns_record *rr; + lookup_dnssec_authenticated = NULL; if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL; /* Search for TXT record */ @@ -44,6 +43,9 @@ int dkim_exim_query_dns_txt(char *name, char *answer) { "%.*s", (int)len, (char *)((rr->data)+rr_offset)); rr_offset+=len; answer_offset+=len; + if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN) { + return PDKIM_FAIL; + } } } else return PDKIM_FAIL; @@ -81,9 +83,9 @@ void dkim_exim_verify_feed(uschar *data, int len) { void dkim_exim_verify_finish(void) { pdkim_signature *sig = NULL; - int dkim_signing_domains_size = 0; - int dkim_signing_domains_ptr = 0; - dkim_signing_domains = NULL; + int dkim_signers_size = 0; + int dkim_signers_ptr = 0; + dkim_signers = NULL; /* Delete eventual previous signature chain */ dkim_signatures = NULL; @@ -92,7 +94,7 @@ void dkim_exim_verify_finish(void) { means there was a processing error somewhere along the way. Log the incident and disable futher verification. */ if (!dkim_collect_input) { - log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: Error while running this message through validation, disabling signature verification."); + log_write(0, LOG_MAIN, "DKIM: Error while running this message through validation, disabling signature verification."); dkim_disable_verify = TRUE; return; } @@ -108,7 +110,7 @@ void dkim_exim_verify_finish(void) { /* Log a line for each signature */ uschar *logmsg = string_append(NULL, &size, &ptr, 5, - string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ", + string_sprintf( "d=%s s=%s c=%s/%s a=%s ", sig->domain, sig->selector, (sig->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed", @@ -176,34 +178,46 @@ void dkim_exim_verify_finish(void) { } logmsg[ptr] = '\0'; - log_write(0, LOG_MAIN, (char *)logmsg); - - /* Build a colon-separated list of signing domains in dkim_signing_domains */ - dkim_signing_domains = string_append(dkim_signing_domains, - &dkim_signing_domains_size, - &dkim_signing_domains_ptr, - 2, - sig->domain, - ":" - ); + log_write(0, LOG_MAIN, "DKIM: %s", logmsg); + + /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */ + dkim_signers = string_append(dkim_signers, + &dkim_signers_size, + &dkim_signers_ptr, + 2, + sig->domain, + ":" + ); + + if (sig->identity != NULL) { + dkim_signers = string_append(dkim_signers, + &dkim_signers_size, + &dkim_signers_ptr, + 2, + sig->identity, + ":" + ); + } /* Process next signature */ sig = sig->next; } - /* Chop the last colon from the domain list */ - if ((dkim_signing_domains != NULL) && - (Ustrlen(dkim_signing_domains) > 0)) - dkim_signing_domains[Ustrlen(dkim_signing_domains)-1] = '\0'; + /* NULL-terminate and chop the last colon from the domain list */ + if (dkim_signers != NULL) { + dkim_signers[dkim_signers_ptr] = '\0'; + if (Ustrlen(dkim_signers) > 0) + dkim_signers[Ustrlen(dkim_signers)-1] = '\0'; + } } void dkim_exim_acl_setup(uschar *id) { pdkim_signature *sig = dkim_signatures; dkim_cur_sig = NULL; + dkim_cur_signer = id; if (dkim_disable_verify || - !id || !sig || - !dkim_verify_ctx) return; + !id || !dkim_verify_ctx) return; /* Find signature to run ACL on */ while (sig != NULL) { uschar *cmp_val = NULL; @@ -375,15 +389,29 @@ uschar *dkim_exim_sign(int dkim_fd, uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_sign_headers) { + int sep = 0; + uschar *seen_items = NULL; + int seen_items_size = 0; + int seen_items_offset = 0; + uschar itembuf[256]; + uschar *dkim_canon_expanded; + uschar *dkim_sign_headers_expanded; + uschar *dkim_private_key_expanded; pdkim_ctx *ctx = NULL; uschar *rc = NULL; + uschar *sigbuf = NULL; + int sigsize = 0; + int sigptr = 0; pdkim_signature *signature; int pdkim_canon; + int pdkim_rc; int sread; char buf[4096]; int save_errno = 0; int old_pool = store_pool; + store_pool = POOL_MAIN; + dkim_domain = expand_string(dkim_domain); if (dkim_domain == NULL) { /* expansion error, do not send message. */ @@ -392,127 +420,161 @@ uschar *dkim_exim_sign(int dkim_fd, rc = NULL; goto CLEANUP; } - /* Set up $dkim_domain expansion variable. */ - dkim_signing_domain = dkim_domain; - /* Get selector to use. */ - dkim_selector = expand_string(dkim_selector); - if (dkim_selector == NULL) { - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_selector: %s", expand_string_message); - rc = NULL; - goto CLEANUP; - } - /* Set up $dkim_selector expansion variable. */ - dkim_signing_selector = dkim_selector; + /* Set $dkim_domain expansion variable to each unique domain in list. */ + while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, + itembuf, + sizeof(itembuf))) != NULL) { + if (!dkim_signing_domain || (dkim_signing_domain[0] == '\0')) continue; + /* Only sign once for each domain, no matter how often it + appears in the expanded list. */ + if (seen_items != NULL) { + uschar *seen_items_list = seen_items; + if (match_isinlist(dkim_signing_domain, + &seen_items_list,0,NULL,NULL,MCL_STRING,TRUE,NULL) == OK) + continue; + seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":"); + } + seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,dkim_signing_domain); + seen_items[seen_items_offset] = '\0'; - /* Get canonicalization to use */ - dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed"); - if (dkim_canon == NULL) { - /* expansion error, do not send message. */ - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_canon: %s", expand_string_message); - rc = NULL; - goto CLEANUP; - } - if (Ustrcmp(dkim_canon, "relaxed") == 0) - pdkim_canon = PDKIM_CANON_RELAXED; - else if (Ustrcmp(dkim_canon, "simple") == 0) - pdkim_canon = PDKIM_CANON_RELAXED; - else { - log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon); - pdkim_canon = PDKIM_CANON_RELAXED; - } + /* Set up $dkim_selector expansion variable. */ + dkim_signing_selector = expand_string(dkim_selector); + if (dkim_signing_selector == NULL) { + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " + "dkim_selector: %s", expand_string_message); + rc = NULL; + goto CLEANUP; + } - /* Expand signing headers once */ - if (dkim_sign_headers != NULL) { - dkim_sign_headers = expand_string(dkim_sign_headers); - if (dkim_sign_headers == NULL) { + /* Get canonicalization to use */ + dkim_canon_expanded = expand_string(dkim_canon?dkim_canon:US"relaxed"); + if (dkim_canon_expanded == NULL) { + /* expansion error, do not send message. */ log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_sign_headers: %s", expand_string_message); + "dkim_canon: %s", expand_string_message); rc = NULL; goto CLEANUP; } - } + if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0) + pdkim_canon = PDKIM_CANON_RELAXED; + else if (Ustrcmp(dkim_canon_expanded, "simple") == 0) + pdkim_canon = PDKIM_CANON_SIMPLE; + else { + log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon_expanded); + pdkim_canon = PDKIM_CANON_RELAXED; + } - /* Get private key to use. */ - dkim_private_key = expand_string(dkim_private_key); - if (dkim_private_key == NULL) { - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_private_key: %s", expand_string_message); - rc = NULL; - goto CLEANUP; - } - if ( (Ustrlen(dkim_private_key) == 0) || - (Ustrcmp(dkim_private_key,"0") == 0) || - (Ustrcmp(dkim_private_key,"false") == 0) ) { - /* don't sign, but no error */ - rc = US""; - goto CLEANUP; - } + if (dkim_sign_headers) { + dkim_sign_headers_expanded = expand_string(dkim_sign_headers); + if (dkim_sign_headers_expanded == NULL) { + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " + "dkim_sign_headers: %s", expand_string_message); + rc = NULL; + goto CLEANUP; + } + } + else { + /* pass NULL, which means default header list */ + dkim_sign_headers_expanded = NULL; + } - if (dkim_private_key[0] == '/') { - int privkey_fd = 0; - /* Looks like a filename, load the private key. */ - memset(big_buffer,0,big_buffer_size); - privkey_fd = open(CS dkim_private_key,O_RDONLY); - if (privkey_fd < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, "unable to open " - "private key file for reading: %s", dkim_private_key); + /* Get private key to use. */ + dkim_private_key_expanded = expand_string(dkim_private_key); + if (dkim_private_key_expanded == NULL) { + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " + "dkim_private_key: %s", expand_string_message); rc = NULL; goto CLEANUP; } - (void)read(privkey_fd,big_buffer,(big_buffer_size-2)); - (void)close(privkey_fd); - dkim_private_key = big_buffer; - } + if ( (Ustrlen(dkim_private_key_expanded) == 0) || + (Ustrcmp(dkim_private_key_expanded,"0") == 0) || + (Ustrcmp(dkim_private_key_expanded,"false") == 0) ) { + /* don't sign, but no error */ + continue; + } - ctx = pdkim_init_sign(PDKIM_INPUT_SMTP, - (char *)dkim_signing_domain, - (char *)dkim_signing_selector, - (char *)dkim_private_key - ); - - pdkim_set_debug_stream(ctx,debug_file); - - pdkim_set_optional(ctx, - (char *)dkim_sign_headers, - NULL, - pdkim_canon, - pdkim_canon, - -1, - PDKIM_ALGO_RSA_SHA256, - 0, - 0); - - while((sread = read(dkim_fd,&buf,4096)) > 0) { - if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) { + if (dkim_private_key_expanded[0] == '/') { + int privkey_fd = 0; + /* Looks like a filename, load the private key. */ + memset(big_buffer,0,big_buffer_size); + privkey_fd = open(CS dkim_private_key_expanded,O_RDONLY); + if (privkey_fd < 0) { + log_write(0, LOG_MAIN|LOG_PANIC, "unable to open " + "private key file for reading: %s", dkim_private_key_expanded); + rc = NULL; + goto CLEANUP; + } + if (read(privkey_fd,big_buffer,(big_buffer_size-2)) < 0) { + log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s", + dkim_private_key_expanded); + rc = NULL; + goto CLEANUP; + } + (void)close(privkey_fd); + dkim_private_key_expanded = big_buffer; + } + + ctx = pdkim_init_sign(PDKIM_INPUT_SMTP, + (char *)dkim_signing_domain, + (char *)dkim_signing_selector, + (char *)dkim_private_key_expanded + ); + + pdkim_set_debug_stream(ctx,debug_file); + + pdkim_set_optional(ctx, + (char *)dkim_sign_headers_expanded, + NULL, + pdkim_canon, + pdkim_canon, + -1, + PDKIM_ALGO_RSA_SHA256, + 0, + 0); + + lseek(dkim_fd, 0, SEEK_SET); + while((sread = read(dkim_fd,&buf,4096)) > 0) { + if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) { + rc = NULL; + goto CLEANUP; + } + } + /* Handle failed read above. */ + if (sread == -1) { + debug_printf("DKIM: Error reading -K file.\n"); + save_errno = errno; rc = NULL; goto CLEANUP; } - } - /* Handle failed read above. */ - if (sread == -1) { - debug_printf("DKIM: Error reading -K file.\n"); - save_errno = errno; - rc = NULL; - goto CLEANUP; - } - if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK) - goto CLEANUP; + pdkim_rc = pdkim_feed_finish(ctx,&signature); + if (pdkim_rc != PDKIM_OK) { + log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc); + rc = NULL; + goto CLEANUP; + } - rc = store_get(strlen(signature->signature_header)+3); - Ustrcpy(rc,US signature->signature_header); - Ustrcat(rc,US"\r\n"); + sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2, + US signature->signature_header, + US"\r\n"); - CLEANUP: - if (ctx != NULL) { pdkim_free_ctx(ctx); + ctx = NULL; } + + if (sigbuf != NULL) { + sigbuf[sigptr] = '\0'; + rc = sigbuf; + } else + rc = US""; + + CLEANUP: + if (ctx != NULL) + pdkim_free_ctx(ctx); store_pool = old_pool; errno = save_errno; return rc; -}; +} #endif