-/* $Cambridge: exim/src/src/dkim.c,v 1.2 2009/06/10 07:34:04 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
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 */
"%.*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;
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;
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;
}
/* 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",
}
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;
switch(what) {
case DKIM_ALGO:
- return dkim_cur_sig->algo?
- (uschar *)(dkim_cur_sig->algo)
- :dkim_exim_expand_defaults(what);
+ switch(dkim_cur_sig->algo) {
+ case PDKIM_ALGO_RSA_SHA1:
+ return US"rsa-sha1";
+ case PDKIM_ALGO_RSA_SHA256:
+ default:
+ return US"rsa-sha256";
+ }
case DKIM_BODYLENGTH:
return (dkim_cur_sig->bodylength >= 0)?
(uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
:dkim_exim_expand_defaults(what);
case DKIM_CANON_BODY:
- return dkim_cur_sig->canon_body?
- (uschar *)(dkim_cur_sig->canon_body)
- :dkim_exim_expand_defaults(what);
+ switch(dkim_cur_sig->canon_body) {
+ case PDKIM_CANON_RELAXED:
+ return US"relaxed";
+ case PDKIM_CANON_SIMPLE:
+ default:
+ return US"simple";
+ }
case DKIM_CANON_HEADERS:
- return dkim_cur_sig->canon_headers?
- (uschar *)(dkim_cur_sig->canon_headers)
- :dkim_exim_expand_defaults(what);
+ switch(dkim_cur_sig->canon_headers) {
+ case PDKIM_CANON_RELAXED:
+ return US"relaxed";
+ case PDKIM_CANON_SIMPLE:
+ default:
+ return US"simple";
+ }
case DKIM_COPIEDHEADERS:
return dkim_cur_sig->copiedheaders?
(uschar *)(dkim_cur_sig->copiedheaders)
}
-uschar *dkim_exim_sign(int dkim_fd,
- uschar *dkim_private_key,
- uschar *dkim_domain,
- uschar *dkim_selector,
- uschar *dkim_canon,
- uschar *dkim_sign_headers) {
+uschar *
+dkim_exim_sign(int dkim_fd, uschar *dkim_private_key,
+ const uschar *dkim_domain, 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;
- dkim_domain = expand_string(dkim_domain);
+ store_pool = POOL_MAIN;
+
+ dkim_domain = expand_cstring(dkim_domain);
if (dkim_domain == NULL) {
/* expansion error, do not send message. */
log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
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) {
+ const 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);
- (void)read(privkey_fd,big_buffer,16383);
- (void)close(privkey_fd);
- dkim_private_key = big_buffer;
- }
+ /* 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;
+ }
+ 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;
+ }
+
+ 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
- );
-
- 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) {
+ 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