summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2017-11-07 19:01:42 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2017-11-07 19:01:42 +0000
commita79d883474c84fa2a286b7797a7664b599912fcd (patch)
treedba2c8be5c8c9f90504ad157c2e2623f142de1a4 /src
parentba86e143c7aeb0d70ea4c9d73a617a98f06f6baa (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.c51
-rw-r--r--src/src/dkim.c193
-rw-r--r--src/src/dkim.h2
-rw-r--r--src/src/expand.c4
-rw-r--r--src/src/globals.c2
-rw-r--r--src/src/globals.h2
-rw-r--r--src/src/receive.c9
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 */