diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2017-11-07 19:01:42 +0000 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2017-11-07 19:01:42 +0000 |
commit | a79d883474c84fa2a286b7797a7664b599912fcd (patch) | |
tree | dba2c8be5c8c9f90504ad157c2e2623f142de1a4 /src | |
parent | ba86e143c7aeb0d70ea4c9d73a617a98f06f6baa (diff) |
DKIM: Allow the DKIM ACL to override verification results. Bug 2186
This provides generic support, though is covers the need introduced
by https://datatracker.ietf.org/doc/draft-ietf-dcrup-dkim-usage/?include_text=1
(deprecating sha-1 and RSA keys shorter than 1024 bits).
Diffstat (limited to 'src')
-rw-r--r-- | src/src/acl.c | 51 | ||||
-rw-r--r-- | src/src/dkim.c | 193 | ||||
-rw-r--r-- | src/src/dkim.h | 2 | ||||
-rw-r--r-- | src/src/expand.c | 4 | ||||
-rw-r--r-- | src/src/globals.c | 2 | ||||
-rw-r--r-- | src/src/globals.h | 2 | ||||
-rw-r--r-- | src/src/receive.c | 9 |
7 files changed, 171 insertions, 92 deletions
diff --git a/src/src/acl.c b/src/src/acl.c index 640989997..739cd91ae 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -852,6 +852,26 @@ while ((s = (*func)()) != NULL) compatibility. */ if (c == ACLC_SET) +#ifndef DISABLE_DKIM + if ( Ustrncmp(s, "dkim_verify_status", 18) == 0 + || Ustrncmp(s, "dkim_verify_reason", 18) == 0) + { + uschar * endptr = s+18; + + if (isalnum(*endptr)) + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" " + "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)", + s); + return NULL; + } + cond->u.varname = string_copyn(s, 18); + s = endptr; + while (isspace(*s)) s++; + } + else +#endif { uschar *endptr; @@ -2899,8 +2919,19 @@ for (; cb != NULL; cb = cb->next) if (cb->type == ACLC_SET) { - debug_printf("acl_%s ", cb->u.varname); - lhswidth += 5 + Ustrlen(cb->u.varname); +#ifndef DISABLE_DKIM + if ( Ustrcmp(cb->u.varname, "dkim_verify_status") == 0 + || Ustrcmp(cb->u.varname, "dkim_verify_reason") == 0) + { + debug_printf("%s ", cb->u.varname); + lhswidth += 19; + } + else +#endif + { + debug_printf("acl_%s ", cb->u.varname); + lhswidth += 5 + Ustrlen(cb->u.varname); + } } debug_printf("= %s\n", cb->arg); @@ -3402,7 +3433,7 @@ for (; cb != NULL; cb = cb->next) #ifndef DISABLE_DKIM case ACLC_DKIM_SIGNER: - if (dkim_cur_signer != NULL) + if (dkim_cur_signer) rc = match_isinlist(dkim_cur_signer, &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); else @@ -3410,7 +3441,7 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_DKIM_STATUS: - rc = match_isinlist(dkim_exim_expand_query(DKIM_VERIFY_STATUS), + rc = match_isinlist(dkim_verify_status, &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); break; #endif @@ -3609,12 +3640,22 @@ for (; cb != NULL; cb = cb->next) { int old_pool = store_pool; if ( cb->u.varname[0] == 'c' +#ifndef DISABLE_DKIM + || cb->u.varname[0] == 'd' +#endif #ifndef DISABLE_EVENT || event_name /* An event is being delivered */ #endif ) store_pool = POOL_PERM; - acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); +#ifndef DISABLE_DKIM /* Overwriteable dkim result variables */ + if (Ustrcmp(cb->u.varname, "dkim_verify_status") == 0) + dkim_verify_status = string_copy(arg); + else if (Ustrcmp(cb->u.varname, "dkim_verify_reason") == 0) + dkim_verify_reason = string_copy(arg); + else +#endif + acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); store_pool = old_pool; } break; diff --git a/src/src/dkim.c b/src/src/dkim.c index d31cae9c7..9a13e2a80 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -134,67 +134,35 @@ store_pool = dkim_verify_oldpool; } -void -dkim_exim_verify_finish(void) +/* Log the result for the given signature */ +static void +dkim_exim_verify_log_sig(pdkim_signature * sig) { -pdkim_signature * sig = NULL; -int rc; -gstring * g = NULL; -const uschar * errstr; - -store_pool = POOL_PERM; - -/* Delete eventual previous signature chain */ - -dkim_signers = NULL; -dkim_signatures = NULL; - -if (dkim_collect_error) - { - log_write(0, LOG_MAIN, - "DKIM: Error during validation, disabling signature verification: %.100s", - dkim_collect_error); - dkim_disable_verify = TRUE; - goto out; - } - -dkim_collect_input = FALSE; - -/* Finish DKIM operation and fetch link to signatures chain */ - -rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr); -if (rc != PDKIM_OK) - { - log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc), - errstr ? ": " : "", errstr ? errstr : US""); - goto out; - } - -for (sig = dkim_signatures; sig; sig = sig->next) - { - uschar * s; - gstring * logmsg; - - /* Log a line for each signature */ - - if (!(s = sig->domain)) s = US"<UNSET>"; - logmsg = string_append(NULL, 2, "d=", s); - if (!(s = sig->selector)) s = US"<UNSET>"; - logmsg = string_append(logmsg, 2, " s=", s); - logmsg = string_append(logmsg, 7, - " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", - "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", - " a=", dkim_sig_to_a_tag(sig), - string_sprintf(" b=" SIZE_T_FMT, - (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0)); - if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s); - if (sig->created > 0) logmsg = string_cat(logmsg, - string_sprintf(" t=%lu", sig->created)); - if (sig->expires > 0) logmsg = string_cat(logmsg, - string_sprintf(" x=%lu", sig->expires)); - if (sig->bodylength > -1) logmsg = string_cat(logmsg, - string_sprintf(" l=%lu", sig->bodylength)); - +gstring * logmsg = string_catn(NULL, "DKIM: ", 6); +uschar * s; + +if (!(s = sig->domain)) s = US"<UNSET>"; +logmsg = string_append(logmsg, 2, "d=", s); +if (!(s = sig->selector)) s = US"<UNSET>"; +logmsg = string_append(logmsg, 2, " s=", s); +logmsg = string_append(logmsg, 7, +" c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", +"/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", +" a=", dkim_sig_to_a_tag(sig), +string_sprintf(" b=" SIZE_T_FMT, + (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0)); +if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s); +if (sig->created > 0) logmsg = string_cat(logmsg, + string_sprintf(" t=%lu", sig->created)); +if (sig->expires > 0) logmsg = string_cat(logmsg, + string_sprintf(" x=%lu", sig->expires)); +if (sig->bodylength > -1) logmsg = string_cat(logmsg, + string_sprintf(" l=%lu", sig->bodylength)); + +if ( !dkim_verify_status + || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS) + && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON) + ) ) switch (sig->verify_status) { case PDKIM_VERIFY_NONE: @@ -207,7 +175,7 @@ for (sig = dkim_signatures; sig; sig = sig->next) { case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: logmsg = string_cat(logmsg, - "public key record (currently?) unavailable]"); + "public key record (currently?) unavailable]"); break; case PDKIM_VERIFY_INVALID_BUFFER_SIZE: @@ -219,13 +187,13 @@ for (sig = dkim_signatures; sig; sig = sig->next) logmsg = string_cat(logmsg, "syntax error in public key record]"); break; - case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR: - logmsg = string_cat(logmsg, "signature tag missing or invalid]"); - break; + case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR: + logmsg = string_cat(logmsg, "signature tag missing or invalid]"); + break; - case PDKIM_VERIFY_INVALID_DKIM_VERSION: - logmsg = string_cat(logmsg, "unsupported DKIM version]"); - break; + case PDKIM_VERIFY_INVALID_DKIM_VERSION: + logmsg = string_cat(logmsg, "unsupported DKIM version]"); + break; default: logmsg = string_cat(logmsg, "unspecified problem]"); @@ -233,8 +201,7 @@ for (sig = dkim_signatures; sig; sig = sig->next) break; case PDKIM_VERIFY_FAIL: - logmsg = - string_cat(logmsg, " [verification failed - "); + logmsg = string_cat(logmsg, " [verification failed - "); switch (sig->verify_ext_status) { case PDKIM_VERIFY_FAIL_BODY: @@ -256,18 +223,74 @@ for (sig = dkim_signatures; sig; sig = sig->next) logmsg = string_cat(logmsg, " [verification succeeded]"); break; } +else + logmsg = string_append(logmsg, 5, + US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]"); + +log_write(0, LOG_MAIN, string_from_gstring(logmsg)); +return; +} + + +/* Log a line for "the current" signature */ +void +dkim_exim_verify_log_item(void) +{ +dkim_exim_verify_log_sig(dkim_cur_sig); +} + + +/* Log a line for each signature */ +void +dkim_exim_verify_log_all(void) +{ +pdkim_signature * sig; +for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig); +} - log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg)); - /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */ +void +dkim_exim_verify_finish(void) +{ +pdkim_signature * sig; +int rc; +gstring * g = NULL; +const uschar * errstr; - if (sig->domain) - g = string_append_listele(g, ':', sig->domain); +store_pool = POOL_PERM; - if (sig->identity) - g = string_append_listele(g, ':', sig->identity); +/* Delete eventual previous signature chain */ - /* Process next signature */ +dkim_signers = NULL; +dkim_signatures = NULL; + +if (dkim_collect_error) + { + log_write(0, LOG_MAIN, + "DKIM: Error during validation, disabling signature verification: %.100s", + dkim_collect_error); + dkim_disable_verify = TRUE; + goto out; + } + +dkim_collect_input = FALSE; + +/* Finish DKIM operation and fetch link to signatures chain */ + +rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr); +if (rc != PDKIM_OK) + { + log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc), + errstr ? ": " : "", errstr ? errstr : US""); + goto out; + } + +/* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */ + +for (sig = dkim_signatures; sig; sig = sig->next) + { + if (sig->domain) g = string_append_listele(g, ':', sig->domain); + if (sig->identity) g = string_append_listele(g, ':', sig->identity); } if (g) dkim_signers = g->s; @@ -309,6 +332,12 @@ for (sig = dkim_signatures; sig; sig = sig->next) dkim_signing_domain = US sig->domain; dkim_signing_selector = US sig->selector; dkim_key_length = sig->sighash.len * 8; + + /* These two return static strings, so we can compare the addr + later to see if the ACL overwrote them. Check that when logging */ + + dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS); + dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON); return; } } @@ -365,12 +394,12 @@ switch (what) } case DKIM_CANON_HEADERS: - switch (dkim_cur_sig->canon_headers) - { - case PDKIM_CANON_RELAXED: return US"relaxed"; - case PDKIM_CANON_SIMPLE: - default: return US"simple"; - } + 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 diff --git a/src/src/dkim.h b/src/src/dkim.h index 735a1162a..f32aa6635 100644 --- a/src/src/dkim.h +++ b/src/src/dkim.h @@ -10,6 +10,8 @@ gstring * dkim_exim_sign(int, off_t, uschar *, struct ob_dkim *, const uschar ** void dkim_exim_verify_init(BOOL); void dkim_exim_verify_feed(uschar *, int); void dkim_exim_verify_finish(void); +void dkim_exim_verify_log_item(void); +void dkim_exim_verify_log_all(void); void dkim_exim_acl_setup(uschar *); uschar *dkim_exim_expand_query(int); diff --git a/src/src/expand.c b/src/src/expand.c index 782467ff7..f44ddf8b8 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -508,8 +508,8 @@ static var_entry var_table[] = { { "dkim_key_testing", vtype_dkim, (void *)DKIM_KEY_TESTING }, { "dkim_selector", vtype_stringptr, &dkim_signing_selector }, { "dkim_signers", vtype_stringptr, &dkim_signers }, - { "dkim_verify_reason", vtype_dkim, (void *)DKIM_VERIFY_REASON }, - { "dkim_verify_status", vtype_dkim, (void *)DKIM_VERIFY_STATUS}, + { "dkim_verify_reason", vtype_stringptr, &dkim_verify_reason }, + { "dkim_verify_status", vtype_stringptr, &dkim_verify_status }, #endif #ifdef EXPERIMENTAL_DMARC { "dmarc_ar_header", vtype_stringptr, &dmarc_ar_header }, diff --git a/src/src/globals.c b/src/src/globals.c index 2a53835e9..5df84bd10 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -668,6 +668,8 @@ uschar *dkim_signers = NULL; uschar *dkim_signing_domain = NULL; uschar *dkim_signing_selector = NULL; uschar *dkim_verify_signers = US"$dkim_signers"; +uschar *dkim_verify_status = NULL; +uschar *dkim_verify_reason = NULL; #endif #ifdef EXPERIMENTAL_DMARC BOOL dmarc_has_been_checked = FALSE; diff --git a/src/src/globals.h b/src/src/globals.h index 62336c275..37d4cada3 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -393,6 +393,8 @@ extern uschar *dkim_signers; /* Expansion variable, holds colon-separa extern uschar *dkim_signing_domain; /* Expansion variable, domain used for signing a message. */ extern uschar *dkim_signing_selector; /* Expansion variable, selector used for signing a message. */ extern uschar *dkim_verify_signers; /* Colon-separated list of domains for each of which we call the DKIM ACL */ +extern uschar *dkim_verify_status; /* result for this signature */ +extern uschar *dkim_verify_reason; /* result for this signature */ #endif #ifdef EXPERIMENTAL_DMARC extern BOOL dmarc_has_been_checked; /* Global variable to check if test has been called yet */ diff --git a/src/src/receive.c b/src/src/receive.c index 31402925d..f181f1a51 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3405,8 +3405,8 @@ else else { int sep = 0; - const uschar *ptr = dkim_verify_signers_expanded; - uschar *item = NULL; + const uschar * ptr = dkim_verify_signers_expanded; + uschar * item = NULL; gstring * seen_items = NULL; /* Default to OK when no items are present */ @@ -3452,6 +3452,7 @@ else dkim_exim_acl_setup(item); rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, &user_msg, &log_msg); + dkim_exim_verify_log_item(); if (rc != OK) { @@ -3467,7 +3468,7 @@ else { recipients_count = 0; blackholed_by = US"DKIM ACL"; - if (log_msg != NULL) + if (log_msg) blackhole_log_msg = string_sprintf(": %s", log_msg); } else if (rc != OK) @@ -3481,6 +3482,8 @@ else } } } + else + dkim_exim_verify_log_all(); } #endif /* DISABLE_DKIM */ |