Gitweb:
https://git.exim.org/exim.git/commitdiff/9a0f997bac85d8f234238162f3cee4524b6f989c
Commit: 9a0f997bac85d8f234238162f3cee4524b6f989c
Parent: e89891fa5e27a1a6f895f45d324f2b407c41539b
Author: Jeremy Harris <jgh146exb@???>
AuthorDate: Wed Sep 4 21:46:26 2024 +0100
Committer: Jeremy Harris <jgh146exb@???>
CommitDate: Wed Sep 4 21:46:26 2024 +0100
dkim dynamic module
---
doc/doc-txt/NewStuff | 6 +-
src/OS/Makefile-Base | 35 +-
src/scripts/Configure-Makefile | 2 +-
src/scripts/MakeLinks | 25 +-
src/scripts/drivers-Makefile | 29 +-
src/src/EDITME | 4 +
src/src/acl.c | 45 ++-
src/src/arc.c | 365 +++++++++++-------
src/src/config.h.defaults | 3 +
src/src/daemon.c | 13 -
src/src/dkim.h | 33 --
src/src/drtables.c | 61 ++-
src/src/exim.c | 6 +-
src/src/exim.h | 3 +-
src/src/expand.c | 78 ++--
src/src/functions.h | 15 +-
src/src/globals.c | 19 -
src/src/globals.h | 19 -
src/src/hash.c | 43 ---
src/src/hash.h | 10 -
src/src/miscmods/Makefile | 26 +-
src/src/miscmods/README | 11 +-
src/src/{ => miscmods}/dkim.c | 595 ++++++++++++++++++++++++++----
src/src/miscmods/dkim.h | 47 +++
src/src/miscmods/dkim_api.h | 36 ++
src/src/{ => miscmods}/dkim_transport.c | 7 +-
src/src/miscmods/dmarc.c | 35 +-
src/src/miscmods/dmarc_api.h | 3 +-
src/src/{ => miscmods}/pdkim/Makefile | 0
src/src/{ => miscmods}/pdkim/README | 0
src/src/{ => miscmods}/pdkim/crypt_ver.h | 0
src/src/{ => miscmods}/pdkim/pdkim.c | 15 +-
src/src/{ => miscmods}/pdkim/pdkim.h | 2 +-
src/src/{ => miscmods}/pdkim/pdkim_hash.h | 0
src/src/{ => miscmods}/pdkim/signing.c | 33 +-
src/src/{ => miscmods}/pdkim/signing.h | 10 +-
src/src/miscmods/spf.c | 2 +-
src/src/miscmods/spf_api.h | 3 +-
src/src/pdkim/config.h | 4 -
src/src/readconf.c | 12 +-
src/src/receive.c | 133 ++-----
src/src/smtp_in.c | 53 +--
src/src/spool_in.c | 11 +-
src/src/string.c | 2 +-
src/src/structs.h | 1 +
src/src/tls-gnu.c | 4 +-
src/src/tls-openssl.c | 4 +-
src/src/transports/smtp.c | 22 +-
test/runtest | 5 +-
49 files changed, 1234 insertions(+), 656 deletions(-)
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index 640bd58cd..1189ce3f3 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -14,9 +14,9 @@ Version 4.98
3. Events smtp:fail:protocol and smtp:fail:syntax
- 4. JSON and LDAP lookup support, SPF support, all the router and authenticator
- drivers, and all the transport drivers except smtp, can now be built as
- loadable modules
+ 4. JSON and LDAP lookup support, SPF, DKIM and DMARC support, all the router
+ and authenticator drivers, and all the transport drivers except smtp, can
+ now be built as loadable modules
Version 4.98
------------
diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base
index 591b4261c..12319967e 100644
--- a/src/OS/Makefile-Base
+++ b/src/OS/Makefile-Base
@@ -221,15 +221,15 @@ macro-spa.o : auths/spa.c
macro-authtls.o: auths/tls.c
@echo "$(CC) -DMACRO_PREDEF auths/tls.c"
$(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ auths/tls.c
-macro-dkim.o: dkim.c
- @echo "$(CC) -DMACRO_PREDEF dkim.c"
- $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ dkim.c
+macro-dkim.o: miscmods/dkim.c
+ @echo "$(CC) -DMACRO_PREDEF miscmods/dkim.c"
+ $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ miscmods/dkim.c
macro-malware.o: malware.c
@echo "$(CC) -DMACRO_PREDEF malware.c"
$(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ malware.c
-macro-signing.o: pdkim/signing.c
- @echo "$(CC) -DMACRO_PREDEF pdkim/signing.c"
- $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ pdkim/signing.c
+macro-signing.o: miscmods/signing.c
+ @echo "$(CC) -DMACRO_PREDEF miscmods/signing.c"
+ $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ miscmods/signing.c
macro_predef: $(OBJ_MACRO)
@echo "$(LNCC) -o $@"
@@ -244,7 +244,7 @@ macro.c: macro_predef
# problem, but it does no harm. Other make programs will just ignore this.
.PHONY: all config utils \
- buildauths buildlookups buildpdkim buildrouters \
+ buildauths buildlookups buildrouters \
buildtransports buildmisc dynmodules checklocalmake clean
@@ -515,7 +515,7 @@ OBJ_AUTHS = call_pam.o call_pwcheck.o call_radius.o check_serv_cond.o \
OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
- filtertest.o globals.o dkim.o dkim_transport.o dnsbl.o hash.o \
+ filtertest.o globals.o dnsbl.o hash.o \
header.o host.o host_address.o ip.o log.o lss.o match.o md5.o moan.o \
os.o parse.o priv.o proxy.o queue.o \
rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o regex_cache.o \
@@ -526,13 +526,13 @@ OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
local_scan.o $(EXIM_PERL) $(OBJ_WITH_CONTENT_SCAN) \
$(OBJ_EXPERIMENTAL)
-exim: buildlookups buildauths pdkim/pdkim.a \
+exim: buildlookups buildauths \
buildrouters buildtransports buildmisc \
$(OBJ_EXIM) version.o
@echo "$(LNCC) -o exim"
$(FE)$(PURIFY) $(LNCC) -o exim $(LFLAGS) $(OBJ_EXIM) version.o \
routers/routers.a transports/transports.a lookups/lookups.a \
- auths/auths.a pdkim/pdkim.a miscmods/miscmods.a \
+ auths/auths.a miscmods/miscmods.a \
$(LIBRESOLV) $(LIBS) $(LIBS_EXIM) $(IPV6_LIBS) $(EXTRALIBS) \
$(EXTRALIBS_EXIM) $(DBMLIB) $(LOOKUP_LIBS) $(AUTH_LIBS) \
$(PERL_LIBS) $(TLS_LIBS) $(PCRE_LIBS) $(LDFLAGS)
@@ -685,6 +685,7 @@ HDRS = blob.h \
hintsdb/hints_tdb.h \
local_scan.h \
macros.h \
+ miscmods/dkim_api.h \
miscmods/dmarc_api.h \
miscmods/spf_api.h \
mytypes.h \
@@ -706,6 +707,7 @@ PHDRS = ../config.h \
../hintsdb/hints_tdb.h \
../local_scan.h \
../macros.h \
+ ../miscmods/dkim_api.h \
../miscmods/dmarc_api.h \
../miscmods/spf_api.h \
../mytypes.h \
@@ -886,8 +888,6 @@ transport.o: $(HDRS) transport.c
tree.o: $(HDRS) tree.c
verify.o: $(HDRS) transports/smtp.h verify.c
xtextencode.o: $(HDRS) xtextencode.c
-dkim.o: $(HDRS) pdkim/pdkim.h dkim.c
-dkim_transport.o: $(HDRS) dkim_transport.c
# Dependencies for WITH_CONTENT_SCAN modules
@@ -900,7 +900,7 @@ spool_mbox.o: $(HDRS) spool_mbox.c
# Dependencies for EXPERIMENTAL_* modules
-arc.o: $(HDRS) pdkim/pdkim.h arc.c
+arc.o: $(HDRS) miscmods/pdkim.h arc.c
bmi_spam.o: $(HDRS) bmi_spam.c
dane.o: $(HDRS) dane.c dane-openssl.c
dcc.o: $(HDRS) dcc.h dcc.c
@@ -1065,15 +1065,6 @@ buildauths: config
INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"
@echo " "
-# The PDKIM library
-
-buildpdkim: pdkim/pdkim.a
-pdkim/pdkim.a: config
- @cd pdkim && $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" CFLAGS="$(CFLAGS)" \
- FE="$(FE)" RANLIB="$(RANLIB)" RM_COMMAND="$(RM_COMMAND)" HDRS="$(PHDRS)" \
- INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"
- @echo " "
-
buildmisc: config
@cd miscmods && $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) \
CC="$(CC)" CFLAGS="$(CFLAGS)" \
diff --git a/src/scripts/Configure-Makefile b/src/scripts/Configure-Makefile
index c3019f846..12f0ddd9c 100755
--- a/src/scripts/Configure-Makefile
+++ b/src/scripts/Configure-Makefile
@@ -311,7 +311,7 @@ done <<-END
routers ROUTER ACCEPT DNSLOOKUP IPLITERAL IPLOOKUP MANUALROUTE QUERYPROGRAM REDIRECT
transports TRANSPORT APPENDFILE AUTOREPLY LMTP PIPE QUEUEFILE SMTP
auths AUTH CRAM_MD5 CYRUS_SASL DOVECOT EXTERNAL GSASL HEIMDAL_GSSAPI PLAINTEXT SPA TLS
- miscmods SUPPORT SPF DMARC
+ miscmods SUPPORT _DKIM DMARC SPF
END
# See if there is a definition of EXIM_PERL in what we have built so far.
diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks
index f657abd5b..a6521a95e 100755
--- a/src/scripts/MakeLinks
+++ b/src/scripts/MakeLinks
@@ -90,13 +90,20 @@ done
cd ..
# miscellaneous modules
+# Note that the file in the miscmods/pdkim/ source subdir get linked to the
+# destination miscmods/ dir
d="miscmods"
mkdir $d
cd $d
# Makefile is generated
-for f in dmarc.c dmarc.h dmarc_api.h dummy.c spf.c spf.h spf_api.h
+for f in dummy.c \
+ dkim.c dkim_transport.c dkim.h dkim_api.h \
+ pdkim/crypt_ver.h pdkim/pdkim.c pdkim/pdkim.h \
+ pdkim/pdkim_hash.h pdkim/signing.c pdkim/signing.h \
+ dmarc.c dmarc.h dmarc_api.h \
+ spf.c spf.h spf_api.h
do
- ln -s ../../src/$d/$f $f
+ ln -s ../../src/$d/$f `basename $f`
done
cd ..
@@ -110,17 +117,6 @@ do
done
cd ..
-# Likewise for the code for the PDKIM library
-d="pdkim"
-mkdir $d
-cd $d
-for f in README Makefile crypt_ver.h pdkim.c \
- pdkim.h hash.c hash.h signing.c signing.h blob.h
-do
- ln -s ../../src/$d/$f $f
-done
-cd ..
-
# The basic source files for Exim and utilities. NB local_scan.h gets linked,
# but local_scan.c does not, because its location is taken from the build-time
# configuration. Likewise for the os.c file, which gets build dynamically.
@@ -140,7 +136,6 @@ for f in blob.h dbfunctions.h exim.h functions.h globals.h \
string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-cipher-stdname.c \
tls-gnu.c tls-openssl.c \
tod.c transport.c tree.c verify.c version.c xtextencode.c \
- dkim.c dkim.h dkim_transport.c \
valgrind.h memcheck.h \
macro_predef.c macro_predef.h
do
@@ -155,7 +150,7 @@ done
# EXPERIMENTAL_*
for f in arc.c bmi_spam.c bmi_spam.h dcc.c dcc.h dane.c dane-openssl.c \
- danessl.h imap_utf7.c spf.c spf.h utf8.c xclient.c
+ danessl.h imap_utf7.c utf8.c xclient.c
do
ln -s ../src/$f $f
done
diff --git a/src/scripts/drivers-Makefile b/src/scripts/drivers-Makefile
index 2dd958043..085eedbda 100755
--- a/src/scripts/drivers-Makefile
+++ b/src/scripts/drivers-Makefile
@@ -95,13 +95,25 @@ fi
# command-line, not just check the Makefile.
want_dynamic() {
- local dyn_name="$1"
+ local dyn_name="${1#_}"
local re="(${classdef}|EXPERIMENTAL)_${dyn_name}[ $tab]*=[ $tab]*2"
+ #XXX Solaris does not support -E on grep. Must use egrep.
env | grep -E -q "^$re"
if [ $? -eq 0 ]; then return 0; fi
grep -E -q "^[ $tab]*$re" "$defs_source"
}
+want_not_disabled() {
+ local want_name="${1#_}"
+ [ "$local_want_name" = "$1" ] && return 0;
+ local re="DISABLED_${want_name}[ $tab]*=[ $tab]*."
+ env | grep -E -q "^$re"
+ [ $? -ne 0 ] && return 0
+ grep -E -q "^[ $tab]*$re" "$defs_source"
+ [ $? -ne 0 ] && return 0
+ return 1
+}
+
want_at_all() {
local want_name="$1"
local re="(${classdef}|EXPERIMENTAL)_${want_name}[ $tab]*=[ $tab]*."
@@ -135,20 +147,21 @@ emit_module_rule() {
echo >&2 "Missing CFLAGS_DYNAMIC prevents building dynamic $name"
exit 1
fi
- MODS="${MODS} ${mod_name}.so"
+ MODS="${MODS} ${mod_name#_}.so"
grep "^${classdef}_${name}_PC" "$defs_source" 1>&2
pkgconf=$(grep "^${classdef}_${name}_PC" "$defs_source")
if [ $? -eq 0 ]; then
pkgconf=$(echo $pkgconf | sed 's/^.*= *//')
- echo "${classdef}_${mod_name}_INCLUDE = $(pkg-config --cflags $pkgconf)"
- echo "${classdef}_${mod_name}_LIBS = $(pkg-config --libs $pkgconf)"
+ echo "${classdef}_${mod_name#_}_INCLUDE = $(pkg-config --cflags $pkgconf)"
+ echo "${classdef}_${mod_name#_}_LIBS = $(pkg-config --libs $pkgconf)"
else
grep "^${classdef}_${name}_" "$defs_source"
- echo "${classdef}_${mod_name}_INCLUDE = \$(${classdef}_${name}_INCLUDE)"
- echo "${classdef}_${mod_name}_LIBS = \$(${classdef}_${name}_LIBS)"
+ echo "${classdef}_${mod_name#_}_INCLUDE = \$(${classdef}_${name}_INCLUDE)"
+ echo "${classdef}_${mod_name#_}_LIBS = \$(${classdef}_${name}_LIBS)"
fi
- elif want_at_all "$name"
- then
+ elif want_not_disabled "$name"; then
+ OBJ="${OBJ} ${mod_name#_}.o"
+ elif want_at_all "$name"; then
OBJ="${OBJ} ${mod_name}.o"
fi
}
diff --git a/src/src/EDITME b/src/src/EDITME
index 35c497697..9d458842a 100644
--- a/src/src/EDITME
+++ b/src/src/EDITME
@@ -586,6 +586,10 @@ DISABLE_MAL_MKS=yes
# turned on by default. See the spec for information on conditionally
# disabling it. To disable the inclusion of the entire feature, set
# DISABLE_DKIM to "yes"
+#
+# It is possible to build the support as a dynamic-load module. In addition
+# to not defining DISABLE_DKIM, define SUPPORT_DKIM=2. The usual rules on
+# defines for includes and libs apply.
# DISABLE_DKIM=yes
diff --git a/src/src/acl.c b/src/src/acl.c
index 023ac2ff6..878278313 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -3934,23 +3934,19 @@ for (; cb; cb = cb->next)
#ifndef DISABLE_DKIM
case ACLC_DKIM_SIGNER:
- if (dkim_cur_signer)
- rc = match_isinlist(dkim_cur_signer,
- &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL);
- else
- rc = FAIL;
- break;
-
case ACLC_DKIM_STATUS:
- { /* return good for any match */
- const uschar * s = dkim_verify_status ? dkim_verify_status : US"none";
- int sep = 0;
- for (uschar * ss; ss = string_nextinlist(&s, &sep, NULL, 0); )
- if ( (rc = match_isinlist(ss, &arg,
- 0, NULL, NULL, MCL_STRING, TRUE, NULL))
- == OK) break;
- }
+ /* See comment on ACLC_SPF wrt. coding issues */
+ {
+ misc_module_info * mi = misc_mod_find(US"dkim", &log_message);
+ typedef int (*fn_t)(const uschar *);
+ rc = mi
+ ? (((fn_t *) mi->functions)
+ [cb->type == ACLC_DKIM_SIGNER
+ ? DKIM_SIGNER_ISINLIST
+ : DKIM_STATUS_LISTMATCH]) (arg)
+ : DEFER;
break;
+ }
#endif
#ifdef SUPPORT_DMARC
@@ -4183,11 +4179,19 @@ for (; cb; cb = cb->next)
#endif
)
store_pool = POOL_PERM;
+
#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);
+ if ( Ustrcmp(cb->u.varname, "dkim_verify_status") == 0
+ || Ustrcmp(cb->u.varname, "dkim_verify_reason") == 0
+ )
+ {
+ misc_module_info * mi = misc_mod_findonly(US"dkim");
+ typedef void (*fn_t)(const uschar *, void *);
+
+ if (mi)
+ (((fn_t *) mi->functions)[DKIM_SETVAR])
+ (cb->u.varname, string_copy(arg));
+ }
else
#endif
acl_var_create(cb->u.varname)->data.ptr = string_copy(arg);
@@ -4216,7 +4220,8 @@ for (; cb; cb = cb->next)
case ACLC_SPF:
case ACLC_SPF_GUESS:
/* We have hardwired function-call numbers, and also prototypes for the
- functions. We could do a function name table search for the number
+ functions. We could do a function name table search or (simpler)
+ a module include file with defines for the numbers
but I can't see how to deal with prototypes. Is a K&R non-prototyped
function still usable with today's compilers (but we would lose on
type-checking)? We could macroize the typedef, and even the function
diff --git a/src/src/arc.c b/src/src/arc.c
index d24b61114..a065ca8e3 100644
--- a/src/src/arc.c
+++ b/src/src/arc.c
@@ -15,16 +15,13 @@
# else
# include "functions.h"
-# include "pdkim/pdkim.h"
-# include "pdkim/signing.h"
+# include "miscmods/pdkim.h"
+# include "miscmods/signing.h"
# ifdef SUPPORT_DMARC
# include "miscmods/dmarc.h"
# endif
-extern pdkim_ctx * dkim_verify_ctx;
-extern pdkim_ctx dkim_sign_ctx;
-
#define ARC_SIGN_OPT_TSTAMP BIT(0)
#define ARC_SIGN_OPT_EXPIRE BIT(1)
@@ -100,6 +97,8 @@ typedef enum line_extract {
le_all
} line_extract_t;
+static misc_module_info * arc_dkim_mod_info;
+
static time_t now;
static time_t expire;
static hdr_rlist * headers_rlist;
@@ -132,6 +131,23 @@ arc_parse_line() gathering only the 'i' tag (instance) information.
*/
+/******************************************************************************/
+
+/* We need a module init function, to check on the dkim module being present
+(and we may as well stack it's modinfo ptr)
+
+For now (until we do an arc module), called from exim.c main().
+*/
+BOOL
+arc_init(void)
+{
+uschar * errstr = NULL;
+if ((arc_dkim_mod_info = misc_mod_find(US"dkim", &errstr)))
+ return TRUE;
+log_write(0, LOG_MAIN|LOG_PANIC, "arc: %s", errstr);
+return FALSE;
+}
+
/******************************************************************************/
@@ -586,6 +602,39 @@ return Ustrncmp(s, al->cv.data, al->cv.len) == 0;
}
/******************************************************************************/
+/* Service routines provided by the dkim module */
+
+static int
+arc_dkim_hashname_blob_to_type(const blob * name)
+{
+typedef int (*fn_t)(const blob *);
+return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HASHNAME_TO_TYPE]) (name);
+}
+static hashmethod
+arc_dkim_hashtype_to_method(int hashtype)
+{
+typedef hashmethod (*fn_t)(int);
+return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HASHTYPE_TO_METHOD]) (hashtype);
+}
+static hashmethod
+arc_dkim_hashname_blob_to_method(const blob * name)
+{
+typedef hashmethod (*fn_t)(const blob *);
+return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HASHNAME_TO_METHOD]) (name);
+}
+
+/******************************************************************************/
+
+/* Do a "relaxed" canonicalization of a header */
+static uschar *
+arc_relax_header_n(const uschar * text, int len, BOOL append_crlf)
+{
+typedef uschar * (*fn_t)(const uschar *, int, BOOL);
+return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_HEADER_RELAX])
+ (text, len, append_crlf);
+}
+
+
/* Return the hash of headers from the message that the AMS claims it
signed.
@@ -599,14 +648,12 @@ const uschar * hn;
int sep = ':';
hdr_rlist * r;
BOOL relaxed = Ustrncmp(US"relaxed", ams->c_head.data, ams->c_head.len) == 0;
-int hashtype = pdkim_hashname_to_hashtype(
- ams->a_hash.data, ams->a_hash.len);
+hashmethod hm = arc_dkim_hashname_blob_to_method(&ams->a_hash);
hctx hhash_ctx;
const uschar * s;
int len;
-if ( hashtype == -1
- || !exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
+if (hm < 0 || !exim_sha_init(&hhash_ctx, hm))
{
DEBUG(D_acl)
debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
@@ -628,7 +675,7 @@ while ((hn = string_nextinlist(&headernames, &sep, NULL, 0)))
&& strncasecmp(CCS (s = r->h->text), CCS hn, Ustrlen(hn)) == 0
)
{
- if (relaxed) s = pdkim_relax_header_n(s, r->h->slen, TRUE);
+ if (relaxed) s = arc_relax_header_n(s, r->h->slen, TRUE);
DEBUG(D_acl) debug_printf("%Z\n", s);
exim_sha_update_string(&hhash_ctx, s);
@@ -640,7 +687,7 @@ while ((hn = string_nextinlist(&headernames, &sep, NULL, 0)))
s = ams->rawsig_no_b_val.data, len = ams->rawsig_no_b_val.len;
if (relaxed)
- len = Ustrlen(s = pdkim_relax_header_n(s, len, FALSE));
+ len = Ustrlen(s = arc_relax_header_n(s, len, FALSE));
DEBUG(D_acl) debug_printf("%.*Z\n", len, s);
exim_sha_update(&hhash_ctx, s, len);
@@ -653,33 +700,34 @@ return;
-static pdkim_pubkey *
-arc_line_to_pubkey(arc_line * al)
+static blob *
+arc_line_to_pubkey(arc_line * al, const uschar ** errstr)
{
-uschar * dns_txt;
-pdkim_pubkey * p;
-
-if (!(dns_txt = dkim_exim_query_dns_txt(string_sprintf("%.*s._domainkey.%.*s",
- (int)al->s.len, al->s.data, (int)al->d.len, al->d.data))))
- {
- DEBUG(D_acl) debug_printf("pubkey dns lookup fail\n");
- return NULL;
- }
-
-if ( !(p = pdkim_parse_pubkey_record(dns_txt))
- || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0)
- )
+typedef const uschar * (*fn_t)(const uschar *, blob **, const uschar **);
+blob * pubkey;
+const uschar * hashes;
+const uschar * srvtype =
+ (((fn_t *) arc_dkim_mod_info->functions)[DKIM_DNS_PUBKEY])
+ (string_sprintf("%.*s._domainkey.%.*s",
+ (int)al->s.len, al->s.data, (int)al->d.len, al->d.data),
+ &pubkey, &hashes);
+
+/*XXX do we need a blob-string printf %handler? Other types of blob? */
+
+if (!srvtype)
+ { *errstr = US"pubkey dns lookup fail"; return NULL; }
+if ((Ustrcmp(srvtype, "*") != 0 && Ustrcmp(srvtype, "email") != 0))
{
- DEBUG(D_acl) debug_printf("pubkey dns lookup format error\n");
+ *errstr = string_sprintf("pubkey format error: srvtype '%s'", srvtype);
return NULL;
}
/* If the pubkey limits use to specified hashes, reject unusable
signatures. XXX should we have looked for multiple dns records? */
-if (p->hashes)
+if (hashes)
{
- const uschar * list = p->hashes, * ele;
+ const uschar * list = hashes, * ele;
int sep = ':';
while ((ele = string_nextinlist(&list, &sep, NULL, 0)))
@@ -687,11 +735,38 @@ if (p->hashes)
if (!ele)
{
DEBUG(D_acl) debug_printf("pubkey h=%s vs sig a=%.*s\n",
- p->hashes, (int)al->a.len, al->a.data);
+ hashes, (int)al->a.len, al->a.data);
+ *errstr = US"no usable sig for this pubkey hash list";
return NULL;
}
}
-return p;
+return pubkey;
+}
+
+
+
+
+/* Set up a body hashing method on the given signature-context
+(creates a new one if needed, or uses an already-present one).
+
+Arguments:
+ signing TRUE for signing, FALSE for verification
+ c canonicalization spec, text form
+ ah hash, text form
+ bodylen byte count for message body
+
+Return: pointer to hashing method struct
+*/
+
+static pdkim_bodyhash *
+arc_set_bodyhash(BOOL signing,
+ const blob * c, const blob * ah, long bodylen)
+{
+typedef pdkim_bodyhash * (*fn_t)(BOOL,
+ const blob * canon, const blob * hash, long bodylen);
+
+return (((fn_t *) arc_dkim_mod_info->functions)[DKIM_SET_BODYHASH])
+ (signing, c, ah, bodylen);
}
@@ -700,22 +775,72 @@ return p;
static pdkim_bodyhash *
arc_ams_setup_vfy_bodyhash(arc_line * ams)
{
-int canon_head = -1, canon_body = -1;
-long bodylen;
-
-if (!ams->c.data) ams->c.data = US"simple"; /* RFC 6376 (DKIM) default */
-pdkim_cstring_to_canons(ams->c.data, ams->c.len, &canon_head, &canon_body);
-bodylen = ams->l.data
- ? strtol(CS string_copyn(ams->l.data, ams->l.len), NULL, 10) : -1;
-
-return pdkim_set_bodyhash(dkim_verify_ctx,
- pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len),
- canon_body,
- bodylen);
+blob * c = &ams->c;
+long bodylen = ams->l.data
+ ? strtol(CS string_copyn(ams->l.data, ams->l.len), NULL, 10)
+ : -1;
+
+if (!c->data)
+ {
+ c->data = US"simple"; /* RFC 6376 (DKIM) default */
+ c->len = 6;
+ }
+
+return arc_set_bodyhash(FALSE, c, &ams->a_hash, bodylen);
}
+static void
+arc_decode_base64(const uschar * str, blob * b)
+{
+int dlen = b64decode(str, &b->data, str);
+if (dlen < 0) b->data = NULL;
+b->len = dlen;
+}
+
+
+
+static int
+arc_sig_verify(arc_set * as, arc_line * al, hashmethod hm,
+ blob * hhash_computed, blob * sighash,
+ const uschar * why, const uschar ** errstr_p)
+{
+blob * pubkey;
+const uschar * errstr = NULL;
+int rc;
+typedef int (*fn_t)
+ (const blob *, const blob *, hashmethod, const blob *, const uschar **);
+
+/* Get the public key from DNS */
+
+/*XXX dkim module */
+if (!(pubkey = arc_line_to_pubkey(al, &errstr)))
+ {
+ *errstr_p = string_sprintf("%s (%s)", errstr, why);
+ return ERROR;
+ }
+
+rc = (((fn_t *) arc_dkim_mod_info->functions)[DKIM_SIG_VERIFY])
+ (sighash, hhash_computed, hm, pubkey, &errstr);
+switch (rc)
+ {
+ case OK:
+ break;
+ case FAIL:
+ DEBUG(D_acl)
+ debug_printf("ARC i=%d %s verify %s\n", as->instance, why, errstr);
+ break;
+ case ERROR:
+ DEBUG(D_acl) debug_printf("ARC verify %s init: %s\n", why, errstr);
+ break;
+ }
+return rc;
+}
+
+
+
+
/* Verify an AMS. This is a DKIM-sig header, but with an ARC i= tag
and without a DKIM v= tag.
*/
@@ -725,12 +850,11 @@ arc_ams_verify(arc_ctx * ctx, arc_set * as)
{
arc_line * ams = as->hdr_ams;
pdkim_bodyhash * b;
-pdkim_pubkey * p;
blob sighash;
-blob hhash;
-ev_ctx vctx;
-int hashtype;
+blob hhash_computed;
+hashmethod hm;
const uschar * errstr;
+int rc;
as->ams_verify_done = US"in-progress";
@@ -771,7 +895,7 @@ DEBUG(D_acl)
/* We know the bh-tag blob is of a nul-term string, so safe as a string */
if ( !ams->bh.data
- || (pdkim_decode_base64(ams->bh.data, &sighash), sighash.len != b->bh.len)
+ || (arc_decode_base64(ams->bh.data, &sighash), sighash.len != b->bh.len)
|| memcmp(sighash.data, b->bh.data, b->bh.len) != 0
)
{
@@ -786,38 +910,21 @@ if ( !ams->bh.data
DEBUG(D_acl) debug_printf("ARC i=%d AMS Body hash compared OK\n", as->instance);
-/* Get the public key from DNS */
-
-if (!(p = arc_line_to_pubkey(ams)))
- return as->ams_verify_done = arc_state_reason = US"pubkey problem";
-
/* We know the b-tag blob is of a nul-term string, so safe as a string */
-pdkim_decode_base64(ams->b.data, &sighash);
+arc_decode_base64(ams->b.data, &sighash);
-arc_get_verify_hhash(ctx, ams, &hhash);
+arc_get_verify_hhash(ctx, ams, &hhash_computed);
-/* Setup the interface to the signing library */
-
-if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
- {
- DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
- as->ams_verify_done = arc_state_reason = US"internal sigverify init error";
- return US"fail";
- }
-
-hashtype = pdkim_hashname_to_hashtype(ams->a_hash.data, ams->a_hash.len);
-if (hashtype == -1)
+if ((hm = arc_dkim_hashname_blob_to_method(&ams->a_hash)) < 0)
{
DEBUG(D_acl) debug_printf("ARC i=%d AMS verify bad a_hash\n", as->instance);
return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
}
-if ((errstr = exim_dkim_verify(&vctx,
- pdkim_hashes[hashtype].exim_hashmethod, &hhash, &sighash)))
- {
- DEBUG(D_acl) debug_printf("ARC i=%d AMS verify %s\n", as->instance, errstr);
- return as->ams_verify_done = arc_state_reason = US"AMS sig nonverify";
- }
+rc = arc_sig_verify(as, ams, hm, &hhash_computed, &sighash, US"AMS", &errstr);
+if (rc != OK)
+ return as->ams_verify_done = arc_state_reason =
+ rc == FAIL ? US"AMS sig nonverify" : errstr;
DEBUG(D_acl) debug_printf("ARC i=%d AMS verify pass\n", as->instance);
as->ams_verify_passed = TRUE;
@@ -901,13 +1008,12 @@ arc_seal_verify(arc_ctx * ctx, arc_set * as)
{
arc_line * hdr_as = as->hdr_as;
arc_set * as2;
-int hashtype;
+hashmethod hm;
hctx hhash_ctx;
blob hhash_computed;
blob sighash;
-ev_ctx vctx;
-pdkim_pubkey * p;
const uschar * errstr;
+int rc;
DEBUG(D_acl) debug_printf("ARC: AS vfy i=%d\n", as->instance);
/*
@@ -935,10 +1041,9 @@ if ( as->instance == 1 && !arc_cv_match(hdr_as, US"none")
the ARC-Seal.
*/
-hashtype = pdkim_hashname_to_hashtype(hdr_as->a_hash.data, hdr_as->a_hash.len);
+hm = arc_dkim_hashname_blob_to_method(&hdr_as->a_hash);
-if ( hashtype == -1
- || !exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod))
+if (hm < 0 || !exim_sha_init(&hhash_ctx, hm))
{
DEBUG(D_acl)
debug_printf("ARC: hash setup error, possibly nonhandled hashtype\n");
@@ -967,7 +1072,8 @@ for (as2 = ctx->arcset_chain;
al = as2->hdr_aar;
if (!(s = al->relaxed))
- al->relaxed = s = pdkim_relax_header_n(al->complete->text,
+ /*XXX dkim module */
+ al->relaxed = s = arc_relax_header_n(al->complete->text,
al->complete->slen, TRUE);
len = Ustrlen(s);
DEBUG(D_acl) debug_printf("%Z\n", s);
@@ -975,7 +1081,8 @@ for (as2 = ctx->arcset_chain;
al = as2->hdr_ams;
if (!(s = al->relaxed))
- al->relaxed = s = pdkim_relax_header_n(al->complete->text,
+ /*XXX dkim module */
+ al->relaxed = s = arc_relax_header_n(al->complete->text,
al->complete->slen, TRUE);
len = Ustrlen(s);
DEBUG(D_acl) debug_printf("%Z\n", s);
@@ -983,10 +1090,12 @@ for (as2 = ctx->arcset_chain;
al = as2->hdr_as;
if (as2->instance == as->instance)
- s = pdkim_relax_header_n(al->rawsig_no_b_val.data,
+ /*XXX dkim module */
+ s = arc_relax_header_n(al->rawsig_no_b_val.data,
al->rawsig_no_b_val.len, FALSE);
else if (!(s = al->relaxed))
- al->relaxed = s = pdkim_relax_header_n(al->complete->text,
+ /*XXX dkim module */
+ al->relaxed = s = arc_relax_header_n(al->complete->text,
al->complete->slen, TRUE);
len = Ustrlen(s);
DEBUG(D_acl) debug_printf("%Z\n", s);
@@ -1009,12 +1118,9 @@ DEBUG(D_acl)
/*
6. Retrieve the public key identified by the "s" and "d" tags in
the ARC-Seal, as described in Section 4.1.6.
-*/
-if (!(p = arc_line_to_pubkey(hdr_as)))
- return US"pubkey problem";
+Done below, in arc_sig_verify().
-/*
7. Determine whether the signature portion ("b" tag) of the ARC-
Seal and the digest computed above are valid according to the
public key. (See also Section Section 8.4 for failure case
@@ -1025,21 +1131,12 @@ if (!(p = arc_line_to_pubkey(hdr_as)))
*/
/* We know the b-tag blob is of a nul-term string, so safe as a string */
-pdkim_decode_base64(hdr_as->b.data, &sighash);
-
-if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL)))
- {
- DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr);
- return US"fail";
- }
+arc_decode_base64(hdr_as->b.data, &sighash);
-if ((errstr = exim_dkim_verify(&vctx,
- pdkim_hashes[hashtype].exim_hashmethod,
- &hhash_computed, &sighash)))
+rc = arc_sig_verify(as, hdr_as, hm, &hhash_computed, &sighash, US"AS", &errstr);
+if (rc != OK)
{
- DEBUG(D_acl)
- debug_printf("ARC i=%d AS headers verify: %s\n", as->instance, errstr);
- arc_state_reason = US"seal sigverify error";
+ if (rc == FAIL) arc_state_reason = US"seal sigverify error";
return US"fail";
}
@@ -1076,12 +1173,6 @@ const uschar * res;
memset(&arc_verify_ctx, 0, sizeof(arc_verify_ctx));
-if (!dkim_verify_ctx)
- {
- DEBUG(D_acl) debug_printf("ARC: no DKIM verify context\n");
- return NULL;
- }
-
/* AS evaluation, per
https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-10#section-6
*/
@@ -1285,10 +1376,13 @@ arc_sig_from_pseudoheader(gstring * hdata, int hashtype, const uschar * privkey,
blob * sig, const uschar * why)
{
hashmethod hm = /*sig->keytype == KEYTYPE_ED25519*/ FALSE
- ? HASH_SHA2_512 : pdkim_hashes[hashtype].exim_hashmethod;
+ ? HASH_SHA2_512
+ : arc_dkim_hashtype_to_method(hashtype);
+
blob hhash;
-es_ctx sctx;
const uschar * errstr;
+typedef const uschar * (*fn_t)
+ (const blob *, hashmethod, const uschar *, blob *);
DEBUG(D_transport)
{
@@ -1296,7 +1390,7 @@ DEBUG(D_transport)
debug_printf("ARC: %s header data for signing:\n", why);
debug_printf("%.*Z\n", hdata->ptr, hdata->s);
- (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
+ (void) exim_sha_init(&hhash_ctx, hm);
exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
exim_sha_finish(&hhash_ctx, &hhash);
debug_printf("ARC: header hash: %.*H\n", hhash.len, hhash.data);
@@ -1305,7 +1399,7 @@ DEBUG(D_transport)
if (FALSE /*need hash for Ed25519 or GCrypt signing*/ )
{
hctx hhash_ctx;
- (void) exim_sha_init(&hhash_ctx, pdkim_hashes[hashtype].exim_hashmethod);
+ (void) exim_sha_init(&hhash_ctx, arc_dkim_hashtype_to_method(hashtype));
exim_sha_update(&hhash_ctx, hdata->s, hdata->ptr);
exim_sha_finish(&hhash_ctx, &hhash);
}
@@ -1315,8 +1409,9 @@ else
hhash.len = hdata->ptr;
}
-if ( (errstr = exim_dkim_signing_init(privkey, &sctx))
- || (errstr = exim_dkim_sign(&sctx, hm, &hhash, sig)))
+errstr = (((fn_t *) arc_dkim_mod_info->functions)[DKIM_SIGN_DATA])
+ (&hhash, hm, privkey, sig);
+if (errstr)
{
log_write(0, LOG_MAIN, "ARC: %s signing: %s\n", why, errstr);
DEBUG(D_transport)
@@ -1324,6 +1419,7 @@ if ( (errstr = exim_dkim_signing_init(privkey, &sctx))
privkey);
return FALSE;
}
+
return TRUE;
}
@@ -1333,7 +1429,7 @@ static gstring *
arc_sign_append_sig(gstring * g, blob * sig)
{
/*debug_printf("%s: raw sig %.*H\n", __FUNCTION__, sig->len, sig->data);*/
-sig->data = pdkim_encode_base64(sig);
+sig->data = b64encode(sig->data, sig->len);
sig->len = Ustrlen(sig->data);
for (;;)
{
@@ -1360,7 +1456,8 @@ arc_sign_append_ams(gstring * g, arc_ctx * ctx, int instance,
uschar * s;
gstring * hdata = NULL;
int col;
-int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6); /*XXX hardwired */
+const blob ams_h = {.data = US"sha256", .len = 6}; /*XXX hardwired */
+int hashtype = arc_dkim_hashname_blob_to_type(&ams_h);
blob sig;
int ams_off;
arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED);
@@ -1378,7 +1475,7 @@ if (options & ARC_SIGN_OPT_TSTAMP)
if (options & ARC_SIGN_OPT_EXPIRE)
g = string_fmt_append(g, "; x=%lu", (u_long)expire);
g = string_fmt_append(g, ";\r\n\tbh=%s;\r\n\th=",
- pdkim_encode_base64(bodyhash));
+ b64encode(bodyhash->data, bodyhash->len));
for(col = 3; rheaders; rheaders = rheaders->prev)
{
@@ -1406,7 +1503,8 @@ for(col = 3; rheaders; rheaders = rheaders->prev)
/* Accumulate header for hashing/signing */
hdata = string_cat(hdata,
- pdkim_relax_header_n(htext, rheaders->h->slen, TRUE)); /*XXX hardwired */
+ /*XXX dkim module */
+ arc_relax_header_n(htext, rheaders->h->slen, TRUE)); /*XXX hardwired */
break;
}
}
@@ -1420,7 +1518,8 @@ g = string_catn(g, US";\r\n\tb=;", 7);
/* Include the pseudo-header in the accumulation */
-s = pdkim_relax_header_n(g->s + ams_off, g->ptr - ams_off, FALSE);
+/*XXX dkim module */
+s = arc_relax_header_n(g->s + ams_off, g->ptr - ams_off, FALSE);
hdata = string_cat(hdata, s);
/* Calculate the signature from the accumulation */
@@ -1483,7 +1582,8 @@ header_line * h = (header_line *)(al+1);
uschar * badline_str;
gstring * hdata = NULL;
-int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6); /*XXX hardwired */
+const blob as_h = {.data = US"sha256", .len = 6}; /*XXX hardwired */
+int hashtype = arc_dkim_hashname_blob_to_type(&as_h);
blob sig;
/*
@@ -1533,15 +1633,18 @@ for (arc_set * as = Ustrcmp(status, US"fail") == 0
badline_str = US"aar";
if (!(l = as->hdr_aar)) goto badline;
h = l->complete;
- hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
+ /*XXX dkim module */
+ hdata = string_cat(hdata, arc_relax_header_n(h->text, h->slen, TRUE));
badline_str = US"ams";
if (!(l = as->hdr_ams)) goto badline;
h = l->complete;
- hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, TRUE));
+ /*XXX dkim module */
+ hdata = string_cat(hdata, arc_relax_header_n(h->text, h->slen, TRUE));
badline_str = US"as";
if (!(l = as->hdr_as)) goto badline;
h = l->complete;
- hdata = string_cat(hdata, pdkim_relax_header_n(h->text, h->slen, !!as->next));
+ /*XXX dkim module */
+ hdata = string_cat(hdata, arc_relax_header_n(h->text, h->slen, !!as->next));
}
/* Calculate the signature from the accumulation */
@@ -1567,21 +1670,19 @@ badline:
/**************************************/
-/* Return pointer to pdkim_bodyhash for given hash method, creating new
-method if needed.
-*/
+/*XXX not static currently as the smtp tpt calls us */
+/* Really returns pdkim_bodyhash* - but there's an ordering
+problem for functions.h so call it void* */
void *
arc_ams_setup_sign_bodyhash(void)
{
-int canon_head, canon_body;
+blob canon = {.data = US"relaxed", .len = 7}; /*XXX hardwired */
+blob hash = {.data = US"sha256", .len = 6}; /*XXX hardwired */
DEBUG(D_transport) debug_printf("ARC: requesting bodyhash\n");
-pdkim_cstring_to_canons(US"relaxed", 7, &canon_head, &canon_body); /*XXX hardwired */
-return pdkim_set_bodyhash(&dkim_sign_ctx,
- pdkim_hashname_to_hashtype(US"sha256", 6), /*XXX hardwired */
- canon_body,
- -1);
+
+return arc_set_bodyhash(TRUE, &canon, &hash, -1);
}
@@ -1767,6 +1868,10 @@ g = arc_sign_append_aar(g, &arc_sign_ctx, identity, instance, &ar);
- ? oversigning?
- Covers the data
- we must have requested a suitable bodyhash previously
+XXX so where was that done? I don't see it!
+XXX ah, ok - the smtp tpt calls arc_ams_setup_sign_bodyhash() directly, early
+ -> should pref use a better named call to make the point, but that
+ can wait until arc becomes a module
*/
b = arc_ams_setup_sign_bodyhash();
@@ -1827,8 +1932,6 @@ arc_line al;
pdkim_bodyhash * b;
uschar * errstr;
-if (!dkim_verify_ctx) return US"no dkim context";
-
if (strncmpic(ARC_HDR_AMS, g->s, ARC_HDRLEN_AMS) != 0) return US"not AMS";
DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n");
diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
index 13b203e80..d602886a0 100644
--- a/src/src/config.h.defaults
+++ b/src/src/config.h.defaults
@@ -167,6 +167,9 @@ Do not put spaces between # and the 'define'.
#define SUPPORT_SRS
#define SUPPORT_TRANSLATE_IP_ADDRESS
+/* Required to support dynamic-module build */
+#define SUPPORT_DKIM
+
#define SYSLOG_LOG_PID
#define SYSLOG_LONG_LINES
diff --git a/src/src/daemon.c b/src/src/daemon.c
index 456c586da..fc8c7fdd2 100644
--- a/src/src/daemon.c
+++ b/src/src/daemon.c
@@ -2562,19 +2562,6 @@ else /* no listening sockets, only queue-runs */
dns_pattern_init();
smtp_deliver_init(); /* Used for callouts */
-#ifndef DISABLE_DKIM
- {
-# ifdef MEASURE_TIMING
- struct timeval t0;
- gettimeofday(&t0, NULL);
-# endif
- dkim_exim_init();
-# ifdef MEASURE_TIMING
- report_time_since(&t0, US"dkim_exim_init (delta)");
-# endif
- }
-#endif
-
#ifdef WITH_CONTENT_SCAN
malware_init();
#endif
diff --git a/src/src/dkim.h b/src/src/dkim.h
deleted file mode 100644
index 915c6c739..000000000
--- a/src/src/dkim.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*************************************************
-* Exim - an Internet mail transport agent *
-*************************************************/
-
-/* Copyright (c) University of Cambridge, 1995 - 2018 */
-/* See the file NOTICE for conditions of use and distribution. */
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-void dkim_exim_init(void);
-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_all(void);
-int dkim_exim_acl_run(uschar *, gstring **, uschar **, uschar **);
-uschar *dkim_exim_expand_query(int);
-
-#define DKIM_ALGO 1
-#define DKIM_BODYLENGTH 2
-#define DKIM_CANON_BODY 3
-#define DKIM_CANON_HEADERS 4
-#define DKIM_COPIEDHEADERS 5
-#define DKIM_CREATED 6
-#define DKIM_EXPIRES 7
-#define DKIM_HEADERNAMES 8
-#define DKIM_IDENTITY 9
-#define DKIM_KEY_GRANULARITY 10
-#define DKIM_KEY_SRVTYPE 11
-#define DKIM_KEY_NOTES 12
-#define DKIM_KEY_TESTING 13
-#define DKIM_NOSUBDOMAINS 14
-#define DKIM_VERIFY_STATUS 15
-#define DKIM_VERIFY_REASON 16
diff --git a/src/src/drtables.c b/src/src/drtables.c
index 9ed55e29a..61ced3e6a 100644
--- a/src/src/drtables.c
+++ b/src/src/drtables.c
@@ -431,12 +431,16 @@ misc_module_info * misc_module_list = NULL;
static void
misc_mod_add(misc_module_info * mi)
{
-if (mi->init) mi->init(mi);
-DEBUG(D_any) if (mi->lib_vers_report)
- debug_printf_indent("%Y", mi->lib_vers_report(NULL));
+if (mi->init && mi->init(mi))
+ {
+ DEBUG(D_any) if (mi->lib_vers_report)
+ debug_printf_indent("%Y", mi->lib_vers_report(NULL));
-mi->next = misc_module_list;
-misc_module_list = mi;
+ mi->next = misc_module_list;
+ misc_module_list = mi;
+ }
+else DEBUG(D_any)
+ debug_printf_indent("module init call failed for %s\n", mi->name);
}
@@ -453,7 +457,10 @@ const char * errormsg;
DEBUG(D_any) debug_printf_indent("loading module '%s'\n", name);
if (!(dl = mod_open(name, US"miscmod", errstr)))
+ {
+ DEBUG(D_any) debug_printf_indent(" mod_open: %s\n", *errstr);
return NULL;
+ }
mi = (struct misc_module_info *) dlsym(dl,
CS string_sprintf("%s_module_info", name));
@@ -546,6 +553,39 @@ for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
return OK;
}
+/* Ditto, authres. Having to sort the responses (mainly for the testsuite)
+is pretty painful - maybe we should sort the modules on insertion to
+the list? */
+
+gstring *
+misc_mod_authres(gstring * g)
+{
+typedef struct {
+ const uschar * name;
+ gstring * res;
+} pref;
+pref prefs[] = {
+ {US"spf", NULL}, {US"dkim", NULL}, {US"dmarc", NULL}, {US"arc", NULL}
+};
+gstring * others = NULL;
+
+for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
+ if (mi->authres)
+ {
+ pref * p;
+ for (p = prefs; p < prefs + nelem(prefs); p++)
+ if (Ustrcmp(p->name, mi->name) == 0) break;
+
+ if (p) p->res = (mi->authres)(NULL);
+ else others = (mi->authres)(others);
+ }
+
+for (pref * p = prefs; p < prefs + nelem(prefs); p++)
+ g = gstring_append(g, p->res);
+return gstring_append(g, others);
+}
+
+
@@ -697,6 +737,9 @@ DEBUG(D_lookup) debug_printf("Loaded %d lookup modules\n", countmodules);
}
+#if !defined(DISABLE_DKIM) && (!defined(SUPPORT_DKIM) || SUPPORT_DKIM!=2)
+extern misc_module_info dkim_module_info;
+#endif
#if defined(SUPPORT_DMARC) && SUPPORT_DMARC!=2
extern misc_module_info dmarc_module_info;
#endif
@@ -709,16 +752,18 @@ init_misc_mod_list(void)
{
static BOOL onetime = FALSE;
if (onetime) return;
+onetime = TRUE;
+#if !defined(DISABLE_DKIM) && (!defined(SUPPORT_DKIM) || SUPPORT_DKIM!=2)
+misc_mod_add(&dkim_module_info);
+#endif
#if defined(SUPPORT_SPF) && SUPPORT_SPF!=2
-/* dmarc depends on spf so this add must go first, for the dmarc-static case */
misc_mod_add(&spf_module_info);
#endif
#if defined(SUPPORT_DMARC) && SUPPORT_DMARC!=2
+/* dmarc depends on spf so this add must go after, for the both-static case */
misc_mod_add(&dmarc_module_info);
#endif
-
-onetime = TRUE;
}
diff --git a/src/src/exim.c b/src/src/exim.c
index 5ad54ffc1..ca98e25de 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -4211,6 +4211,9 @@ is equivalent to the ability to modify a setuid binary!
This needs to happen before we read the main configuration. */
init_lookup_list();
init_misc_mod_list();
+#ifdef EXPERIMENTAL_ARC
+arc_init(); /*XXX temporary, until we do an arc module */
+#endif
/*XXX this excrescence could move to the testsuite standard config setup file */
#ifdef SUPPORT_I18N
@@ -5610,9 +5613,6 @@ if (host_checking)
return_path = sender_address = NULL;
dnslist_domain = dnslist_matched = NULL;
-#ifndef DISABLE_DKIM
- dkim_cur_signer = NULL;
-#endif
acl_var_m = NULL;
deliver_localpart_orig = NULL;
deliver_domain_orig = NULL;
diff --git a/src/src/exim.h b/src/src/exim.h
index c996a2f8c..8260dc75f 100644
--- a/src/src/exim.h
+++ b/src/src/exim.h
@@ -547,7 +547,8 @@ config.h, mytypes.h, and store.h, so we don't need to mention them explicitly.
# include "miscmods/spf_api.h"
#endif
#ifndef DISABLE_DKIM
-# include "dkim.h"
+# include "miscmods/dkim.h"
+# include "miscmods/dkim_api.h"
#endif
#ifdef SUPPORT_DMARC
# include "miscmods/dmarc.h"
diff --git a/src/src/expand.c b/src/src/expand.c
index af3816051..02680771f 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -490,27 +490,28 @@ static var_entry var_table[] = {
{ "dcc_result", vtype_stringptr, &dcc_result },
#endif
#ifndef DISABLE_DKIM
- { "dkim_algo", vtype_dkim, (void *)DKIM_ALGO },
- { "dkim_bodylength", vtype_dkim, (void *)DKIM_BODYLENGTH },
- { "dkim_canon_body", vtype_dkim, (void *)DKIM_CANON_BODY },
- { "dkim_canon_headers", vtype_dkim, (void *)DKIM_CANON_HEADERS },
- { "dkim_copiedheaders", vtype_dkim, (void *)DKIM_COPIEDHEADERS },
- { "dkim_created", vtype_dkim, (void *)DKIM_CREATED },
- { "dkim_cur_signer", vtype_stringptr, &dkim_cur_signer },
- { "dkim_domain", vtype_stringptr, &dkim_signing_domain },
- { "dkim_expires", vtype_dkim, (void *)DKIM_EXPIRES },
- { "dkim_headernames", vtype_dkim, (void *)DKIM_HEADERNAMES },
- { "dkim_identity", vtype_dkim, (void *)DKIM_IDENTITY },
- { "dkim_key_granularity",vtype_dkim, (void *)DKIM_KEY_GRANULARITY },
- { "dkim_key_length", vtype_int, &dkim_key_length },
- { "dkim_key_nosubdomains",vtype_dkim, (void *)DKIM_NOSUBDOMAINS },
- { "dkim_key_notes", vtype_dkim, (void *)DKIM_KEY_NOTES },
- { "dkim_key_srvtype", vtype_dkim, (void *)DKIM_KEY_SRVTYPE },
- { "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_stringptr, &dkim_verify_reason },
- { "dkim_verify_status", vtype_stringptr, &dkim_verify_status },
+ { "dkim_algo", vtype_module, US"dkim" },
+ { "dkim_bodylength", vtype_module, US"dkim" },
+ { "dkim_canon_body", vtype_module, US"dkim" },
+ { "dkim_canon_headers", vtype_module, US"dkim" },
+ { "dkim_copiedheaders", vtype_module, US"dkim" },
+ { "dkim_created", vtype_module, US"dkim" },
+ { "dkim_cur_signer", vtype_module, US"dkim" },
+ { "dkim_domain", vtype_module, US"dkim" },
+ { "dkim_expires", vtype_module, US"dkim" },
+ { "dkim_headernames", vtype_module, US"dkim" },
+ { "dkim_identity", vtype_module, US"dkim" },
+ { "dkim_key_granularity",vtype_module, US"dkim" },
+ { "dkim_key_length", vtype_module, US"dkim" },
+ { "dkim_key_nosubdomains",vtype_module, US"dkim" },
+ { "dkim_key_notes", vtype_module, US"dkim" },
+ { "dkim_key_srvtype", vtype_module, US"dkim" },
+ { "dkim_key_testing", vtype_module, US"dkim" },
+ { "dkim_selector", vtype_module, US"dkim" },
+ { "dkim_signers", vtype_module, US"dkim" },
+ { "dkim_verify_reason", vtype_module, US"dkim" },
+ { "dkim_verify_signers", vtype_module, US"dkim" },
+ { "dkim_verify_status", vtype_module, US"dkim" },
#endif
#ifdef SUPPORT_DMARC
{ "dmarc_domain_policy", vtype_module, US"dmarc" },
@@ -2131,7 +2132,13 @@ switch (vp->type)
#ifndef DISABLE_DKIM
case vtype_dkim:
- return dkim_exim_expand_query((int)(long)val);
+ {
+ misc_module_info * mi = misc_mod_findonly(US"dkim");
+ typedef uschar * (*fn_t)(int);
+ return mi
+ ? (((fn_t *) mi->functions)[DKIM_EXPAND_QUERY]) ((int)(long)val)
+ : US"";
+ }
#endif
case vtype_module:
@@ -4882,32 +4889,7 @@ while (*s)
yield = authres_local(yield, sub_arg[0]);
yield = authres_iprev(yield);
yield = authres_smtpauth(yield);
-#ifdef SUPPORT_SPF
- {
- misc_module_info * mi = misc_mod_findonly(US"spf");
- if (mi)
- {
- typedef gstring * (*fn_t)(gstring *);
- fn_t fn = ((fn_t *) mi->functions)[SPF_AUTHRES];
- yield = fn(yield);
- }
- }
-#endif
-#ifndef DISABLE_DKIM
- yield = authres_dkim(yield);
-#endif
-#ifdef SUPPORT_DMARC
- {
- misc_module_info * mi = misc_mod_findonly(US"dmarc");
- if (mi)
- {
- /*XXX is authres common enough to be generic? */
- typedef gstring * (*fn_t)(gstring *);
- fn_t fn = ((fn_t *) mi->functions)[DMARC_AUTHRES];
- yield = fn(yield);
- }
- }
-#endif
+ yield = misc_mod_authres(yield);
#ifdef EXPERIMENTAL_ARC
yield = authres_arc(yield);
#endif
diff --git a/src/src/functions.h b/src/src/functions.h
index 3a980318f..fba5ec688 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -1,3 +1,4 @@
+extern BOOL arc_init(void);
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
@@ -144,9 +145,6 @@ extern uschar *authenticator_current_name(void);
#ifdef EXPERIMENTAL_ARC
extern gstring *authres_arc(gstring *);
#endif
-#ifndef DISABLE_DKIM
-extern gstring *authres_dkim(gstring *);
-#endif
extern gstring *authres_smtpauth(gstring *);
extern uschar *b64encode(const uschar *, int);
@@ -222,13 +220,6 @@ extern void delivery_re_exec(int);
extern void die_tainted(const uschar *, const uschar *, int);
extern BOOL directory_make(const uschar *, const uschar *, int, BOOL);
-#ifndef DISABLE_DKIM
-extern uschar *dkim_exim_query_dns_txt(const uschar *);
-extern void dkim_exim_sign_init(void);
-
-extern BOOL dkim_transport_write_message(transport_ctx *,
- struct ob_dkim *, const uschar ** errstr);
-#endif
extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
extern int dns_basic_lookup(dns_answer *, const uschar *, int);
extern uschar *dns_build_reverse(const uschar *);
@@ -375,6 +366,7 @@ extern int mime_regex(const uschar **, BOOL);
extern void mime_set_anomaly(int);
#endif
+extern gstring *misc_mod_authres(gstring *);
extern int misc_mod_conn_init(const uschar *, const uschar *);
extern misc_module_info * misc_mod_find(const uschar * modname, uschar **);
extern misc_module_info * misc_mod_findonly(const uschar * modname);
@@ -552,6 +544,7 @@ extern int smtp_setup_msg(void);
extern int smtp_sock_connect(smtp_connect_args *, int, const blob *);
extern BOOL smtp_start_session(void);
extern int smtp_ungetc(int);
+extern void smtp_verify_feed(const uschar *, unsigned);
extern BOOL smtp_verify_helo(void);
extern int smtp_write_command(void *, int, const char *, ...) PRINTF_FUNCTION(3,4);
#ifdef WITH_CONTENT_SCAN
@@ -1112,7 +1105,7 @@ g->s = s;
static inline gstring *
gstring_append(gstring * dest, gstring * item)
{
-return string_catn(dest, item->s, item->ptr);
+return item ? string_catn(dest, item->s, item->ptr) : dest;
}
diff --git a/src/src/globals.c b/src/src/globals.c
index cfa75f1d7..6fae1582f 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -864,25 +864,6 @@ address_item *deliver_recipients = NULL;
uschar *deliver_selectstring = NULL;
uschar *deliver_selectstring_sender = NULL;
-#ifndef DISABLE_DKIM
-unsigned dkim_collect_input = 0;
-uschar *dkim_cur_signer = NULL;
-int dkim_key_length = 0;
-void *dkim_signatures = NULL;
-uschar *dkim_signers = NULL;
-uschar *dkim_signing_domain = NULL;
-uschar *dkim_signing_selector = NULL;
-gstring *dkim_signing_record = NULL;
-uschar *dkim_verify_hashes = US"sha256:sha512";
-uschar *dkim_verify_keytypes = US"ed25519:rsa";
-uschar *dkim_verify_min_keysizes = US"rsa=1024 ed25519=250";
-BOOL dkim_verify_minimal = FALSE;
-uschar *dkim_verify_overall = NULL;
-uschar *dkim_verify_signers = US"$dkim_signers";
-uschar *dkim_verify_status = NULL;
-uschar *dkim_verify_reason = NULL;
-#endif
-
uschar *dns_again_means_nonexist = NULL;
int dns_csa_search_limit = 5;
int dns_cname_loops = 1;
diff --git a/src/src/globals.h b/src/src/globals.h
index 8173d771e..1f03cefee 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -545,25 +545,6 @@ extern BOOL disable_fsync; /* Not for normal use */
#endif
extern BOOL disable_ipv6; /* Don't do any IPv6 things */
-#ifndef DISABLE_DKIM
-extern unsigned dkim_collect_input; /* Runtime count of dkim signtures; tracks whether SMTP input is fed to DKIM validation */
-extern uschar *dkim_cur_signer; /* Expansion variable, holds the current "signer" domain or identity during a acl_smtp_dkim run */
-extern int dkim_key_length; /* Expansion variable, length of signing key in bits */
-extern void *dkim_signatures; /* Actually a (pdkim_signature *) but most files do not need to know */
-extern uschar *dkim_signers; /* Expansion variable, holds colon-separated list of domains and identities that have signed a message */
-extern gstring *dkim_signing_record; /* domains+selectors used */
-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_hashes; /* Preference order for signatures */
-extern uschar *dkim_verify_keytypes; /* Preference order for signatures */
-extern uschar *dkim_verify_min_keysizes; /* list of minimum key sizes, keyed by algo */
-extern BOOL dkim_verify_minimal; /* Shortcircuit signature verification */
-extern uschar *dkim_verify_overall; /* First successful domain verified, or null */
-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
-
extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */
extern int dns_csa_search_limit; /* How deep to search for CSA SRV records */
extern BOOL dns_csa_use_reverse; /* Check CSA in reverse DNS? (non-standard) */
diff --git a/src/src/hash.c b/src/src/hash.c
index 17a52fe43..d629a52bd 100644
--- a/src/src/hash.c
+++ b/src/src/hash.c
@@ -227,49 +227,6 @@ memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen);
-#elif defined(SHA_POLARSSL)
-# define HAVE_PARTIAL_SHA
-/******************************************************************************/
-
-BOOL
-exim_sha_init(hctx * h, hashmethod m)
-{
-/*XXX extend for sha512 */
-switch (h->method = m)
- {
- case HASH_SHA1: h->hashlen = 20; sha1_starts(&h->u.sha1); break;
- case HASH_SHA2_256: h->hashlen = 32; sha2_starts(&h->u.sha2, 0); break;
- default: h->hashlen = 0; return FALSE;
- }
-return TRUE;
-}
-
-
-void
-exim_sha_update(hctx * h, const uschar * data, int len)
-{
-switch (h->method)
- {
- case HASH_SHA1: sha1_update(h->u.sha1, US data, len); break;
- case HASH_SHA2_256: sha2_update(h->u.sha2, US data, len); break;
- }
-}
-
-
-void
-exim_sha_finish(hctx * h, blob * b)
-{
-b->data = store_get(b->len = h->hashlen, GET_INTAINTED);
-switch (h->method)
- {
- case HASH_SHA1: sha1_finish(h->u.sha1, b->data); break;
- case HASH_SHA2_256: sha2_finish(h->u.sha2, b->data); break;
- }
-}
-
-
-
-
#elif defined(SHA_NATIVE)
/******************************************************************************/
/* Only sha-1 supported */
diff --git a/src/src/hash.h b/src/src/hash.h
index 788c9f0ad..9ad837b62 100644
--- a/src/src/hash.h
+++ b/src/src/hash.h
@@ -19,10 +19,6 @@
# include <gnutls/crypto.h>
#elif defined(SHA_GCRYPT)
# include <gcrypt.h>
-#elif defined(SHA_POLARSSL)
-# include "pdkim/pdkim.h" /*XXX ugly */
-# include "pdkim/polarssl/sha1.h"
-# include "pdkim/polarssl/sha2.h"
#endif
@@ -63,12 +59,6 @@ typedef struct {
#elif defined(SHA_GCRYPT)
gcry_md_hd_t sha; /* Either SHA1 or SHA256 block */
-#elif defined(SHA_POLARSSL)
- union {
- sha1_context sha1; /* SHA1 block */
- sha2_context sha2; /* SHA256 block */
- } u;
-
#elif defined(SHA_NATIVE)
sha1 sha1;
#endif
diff --git a/src/src/miscmods/Makefile b/src/src/miscmods/Makefile
index 3bc535720..d25d5ede0 100644
--- a/src/src/miscmods/Makefile
+++ b/src/src/miscmods/Makefile
@@ -25,10 +25,28 @@ miscmods.a: $(OBJ)
$(FE)$(CC) -c $(CFLAGS) $(INCLUDE) $*.c
.c.so:; @echo "$(CC) -shared $*.c"
- $(FE)$(CC) $(SUPPORT_$*_INCLUDE) $(SUPPORT_$*_LIBS) -DDYNLOOKUP $(CFLAGS_DYNAMIC) $(CFLAGS) $(INCLUDE) $(DLFLAGS) $*.c -o $@
-
-dmarc.o dmarc.so: $(HDRS) ../pdkim/pdkim.h dmarc.h dmarc.c
+ $(FE)$(CC) $(SUPPORT_$*_INCLUDE) $(SUPPORT_$*_LIBS) \
+ -DDYNLOOKUP $(CFLAGS_DYNAMIC) $(CFLAGS) $(INCLUDE) \
+ $(DLFLAGS) $*.c -o $@
+
+# Note that the sources from pdkim/ are linked into the build.../miscmods/ dir
+# by scripts/Makelinks.
+dkim.o dkim.so: $(HDRS) dkim.h dkim.c dkim_transport.c \
+ crypt_ver.h pdkim.h pdkim_hash.h pdkim.c \
+ signing.h signing.c
+dmarc.o dmarc.so: $(HDRS) pdkim.h dmarc.h dmarc.c
dummy.o: dummy.c
-spf.o spf.so: $(HDRS) spf.h spf.c
+spf.o spf.so: $(HDRS) spf.h spf.c
+
+dkim.o:
+ @echo "$(CC) dkim.c dkim_transport.c pdkim.c signing.c"
+ $(FE)$(CC) -r $(CFLAGS) $(INCLUDE) \
+ dkim.c dkim_transport.c pdkim.c signing.c -o $@
+
+dkim.so:
+ @echo "$(CC) -shared dkim.c dkim_transport.c pdkim.c signing.c"
+ $(FE)$(CC) $(SUPPORT_$*_INCLUDE) $(SUPPORT_$*_LIBS) \
+ -DDYNLOOKUP $(CFLAGS_DYNAMIC) $(CFLAGS) $(INCLUDE) \
+ $(DLFLAGS) dkim.c dkim_transport.c pdkim.c signing.c -o $@
# End
diff --git a/src/src/miscmods/README b/src/src/miscmods/README
index e534a4eed..d1b7a1632 100644
--- a/src/src/miscmods/README
+++ b/src/src/miscmods/README
@@ -49,6 +49,7 @@ The variables table defins $variables for expansion, using the same
definition entry struct as the main var_table in expand.c;
entries here should have their proper vtype_<type> and should be duplicated
in the main table but with vtype_module and the module name.
+Entries must be in order by the variable name.
There are service functions to locate and to locate-or-load modules
by name; these hide the static/dynamic aspect of a module. Most
@@ -79,6 +80,14 @@ Write an include file with anything callers need to know, in particular
and add it to HDRS and PHDRS in OS/Makefile-Base.
Add a SUPPORT_<foo> line to Local/Makefile, and (if dynamic) any
SUPPORT_<foo>_INCLUDE or SUPPORT_<foo>_LIBS required.
-Add the capitalised module name <foo> to the "miscmods" like in
+Add the capitalised module name <foo> to the "miscmods" line in
scripts/Configure-Makefile.
Add all the filenames to the "miscmods" list in scripts/Makelinks
+
+For statically-linked modules the SUPPORT_<foo> line should say "yes",
+for dynamic: "2".
+
+If include-by-default is wanted for the module, use DISABLE_<foo> instead
+of SUPPORT_<foo> (and leave it commented out as appropriate), and prefix
+the name in the "miscmods" line with an underbar ("_").
+For dynamic builds, a SUPPORT_ line is still needed.
diff --git a/src/src/dkim.c b/src/src/miscmods/dkim.c
similarity index 65%
rename from src/src/dkim.c
rename to src/src/miscmods/dkim.c
index 68f074889..38677097b 100644
--- a/src/src/dkim.c
+++ b/src/src/miscmods/dkim.c
@@ -10,14 +10,15 @@
/* Code for DKIM support. Other DKIM relevant code is in
receive.c, transport.c and transports/smtp.c */
-#include "exim.h"
+#include "../exim.h"
#ifndef DISABLE_DKIM
-# include "pdkim/pdkim.h"
+# include "pdkim.h"
+# include "signing.h"
# ifdef MACRO_PREDEF
-# include "macro_predef.h"
+# include "../macro_predef.h"
void
params_dkim(void)
@@ -27,25 +28,56 @@ builtin_macro_create_var(US"_DKIM_OVERSIGN_HEADERS", US PDKIM_OVERSIGN_HEADERS);
}
# else /*!MACRO_PREDEF*/
+/* Options */
+uschar *dkim_verify_hashes = US"sha256:sha512";
+uschar *dkim_verify_keytypes = US"ed25519:rsa";
+uschar *dkim_verify_min_keysizes = US"rsa=1024 ed25519=250";
+BOOL dkim_verify_minimal = FALSE;
+uschar *dkim_verify_signers = US"$dkim_signers";
+
+/* $variables */
+
+uschar *dkim_cur_signer = NULL;
+int dkim_key_length = 0;
+uschar *dkim_signers = NULL;
+uschar *dkim_signing_domain = NULL;
+uschar *dkim_signing_selector = NULL;
+uschar *dkim_verify_reason = NULL;
+uschar *dkim_verify_status = NULL;
+
+/* Working variables */
+
+unsigned dkim_collect_input = 0;
+void *dkim_signatures = NULL;
+gstring *dkim_signing_record = NULL;
+uschar *dkim_vdom_firstpass = NULL;
+
+
+extern BOOL dkim_transport_write_message(transport_ctx *,
+ struct ob_dkim *, const uschar ** errstr);
+
+/****************************************/
pdkim_ctx dkim_sign_ctx;
int dkim_verify_oldpool;
-pdkim_ctx *dkim_verify_ctx = NULL;
+pdkim_ctx * dkim_verify_ctx = NULL;
pdkim_signature *dkim_cur_sig = NULL;
static const uschar * dkim_collect_error = NULL;
#define DKIM_MAX_SIGNATURES 20
+static void dkim_exim_verify_pause(BOOL pause);
+/****************************************/
/* Look up the DKIM record in DNS for the given hostname.
Will use the first found if there are multiple.
The return string is tainted, having come from off-site.
*/
-uschar *
+static uschar *
dkim_exim_query_dns_txt(const uschar * name)
{
dns_answer * dnsa = store_get_dns_answer();
@@ -93,20 +125,93 @@ return NULL; /*XXX better error detail? logging? */
}
-void
-dkim_exim_init(void)
+
+/* Module API: Lookup a DNS DKIM record and parse the pubkey.
+
+Arguments:
+ dnsname record to lookup in DNS
+ pubkey_p pointer for return of pubkey
+ hashes_p pointer for return of hashes
+
+Return: srvtype, or NULL on error
+*/
+
+static const uschar *
+dkim_exim_parse_dns_pubkey(const uschar * dnsname, blob ** pubkey_p,
+ const uschar ** hashes_p)
+{
+const uschar * dnstxt = dkim_exim_query_dns_txt(dnsname);
+pdkim_pubkey * p;
+
+if (!dnstxt)
+ {
+ DEBUG(D_acl) debug_printf_indent("pubkey dns lookup fail\n");
+ return NULL;
+ }
+if (!(p = pdkim_parse_pubkey_record(dnstxt)))
+ {
+ DEBUG(D_acl) debug_printf_indent("pubkey dns record format error\n");
+ return NULL;
+ }
+*pubkey_p = &p->key;
+*hashes_p = p->hashes;
+return p->srvtype;
+}
+
+
+
+
+/* Return:
+ OK verify succesful
+ FAIL verify did not pass
+ ERROR problem setting up the pubkey
+*/
+
+static int
+dkim_exim_sig_verify(const blob * sighash, const blob * data_hash,
+ hashmethod hash, const blob * pubkey, const uschar ** errstr_p)
{
-if (f.dkim_init_done) return;
+ev_ctx vctx;
+const uschar * errstr;
+int rc = OK;
+
+if ((errstr = exim_dkim_verify_init(pubkey, KEYFMT_DER, &vctx, NULL)))
+ rc = ERROR;
+else if ((errstr = exim_dkim_verify(&vctx, hash, data_hash, sighash)))
+ rc = FAIL;
+
+*errstr_p = errstr;
+return rc;
+}
+
+
+
+/****************************************/
+
+static BOOL
+dkim_exim_init(void * dummy)
+{
+if (f.dkim_init_done) return TRUE;
f.dkim_init_done = TRUE;
pdkim_init();
+return TRUE;
}
-void
-dkim_exim_verify_init(BOOL dot_stuffing)
+/* Module API: Set up for verification of a message being received.
+Always returns OK.
+*/
+
+static int
+dkim_exim_verify_init(void)
{
-dkim_exim_init();
+BOOL dot_stuffing = chunking_state <= CHUNKING_OFFERED;
+
+if (!smtp_input || smtp_batched_input || f.dkim_disable_verify)
+ return OK;
+
+dkim_exim_init(NULL);
/* There is a store-reset between header & body reception for the main pool
(actually, after every header line) so cannot use that as we need the data we
@@ -126,6 +231,7 @@ if (dkim_verify_ctx)
/* Create new context */
dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing);
+dkim_exim_verify_pause(FALSE);
dkim_collect_input = dkim_verify_ctx ? DKIM_MAX_SIGNATURES : 0;
dkim_collect_error = NULL;
@@ -134,18 +240,21 @@ receive_get_cache(chunking_state == CHUNKING_LAST
? chunking_data_left : GETC_BUFFER_UNLIMITED);
store_pool = dkim_verify_oldpool;
+return OK;
}
-/* Submit a chunk of data for verification input.
+/* Module API : Submit a chunk of data for verification input.
+A NULL data pointer indicates end-of-message.
Only use the data when the feed is activated. */
-void
-dkim_exim_verify_feed(uschar * data, int len)
+
+static void
+dkim_exim_verify_feed(const uschar * data, unsigned len)
{
int rc;
store_pool = POOL_MESSAGE;
-if ( dkim_collect_input
+if ( (dkim_collect_input || !data)
&& (rc = pdkim_feed(dkim_verify_ctx, data, len)) != PDKIM_OK)
{
dkim_collect_error = pdkim_errstr(rc);
@@ -157,6 +266,74 @@ store_pool = dkim_verify_oldpool;
}
+/* Module API: pause/resume the verification data feed */
+
+static void
+dkim_exim_verify_pause(BOOL pause)
+{
+static unsigned save = 0;
+static BOOL paused = FALSE;
+
+if (!pause)
+ {
+ if (paused)
+ { dkim_collect_input = save; paused = FALSE; }
+ }
+else
+ if (!paused)
+ { save = dkim_collect_input; dkim_collect_input = 0; paused = TRUE; }
+}
+
+/* Module API: Finish off the body hashes, calculate sigs and do compares */
+
+static void
+dkim_exim_verify_finish(void)
+{
+int rc;
+gstring * g = NULL;
+const uschar * errstr = NULL;
+
+store_pool = POOL_MESSAGE;
+
+/* 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);
+ f.dkim_disable_verify = TRUE;
+ goto out;
+ }
+
+dkim_collect_input = 0;
+
+/* Finish DKIM operation and fetch link to signatures chain */
+
+rc = pdkim_feed_finish(dkim_verify_ctx, (pdkim_signature **)&dkim_signatures,
+ &errstr);
+if (rc != PDKIM_OK && errstr)
+ log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
+
+/* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
+
+for (pdkim_signature * 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);
+ }
+gstring_release_unused(g);
+dkim_signers = string_from_gstring(g);
+
+out:
+store_pool = dkim_verify_oldpool;
+}
+
+
+
/* Log the result for the given signature */
static void
dkim_exim_verify_log_sig(pdkim_signature * sig)
@@ -168,12 +345,12 @@ if (!sig) return;
/* Remember the domain for the first pass result */
-if ( !dkim_verify_overall
+if ( !dkim_vdom_firstpass
&& dkim_verify_status
? Ustrcmp(dkim_verify_status, US"pass") == 0
: sig->verify_status == PDKIM_VERIFY_PASS
)
- dkim_verify_overall = string_copy(sig->domain);
+ dkim_vdom_firstpass= string_copy(sig->domain);
/* Rewrite the sig result if the ACL overrode it. This is only
needed because the DMARC code (sigh) peeks at the dkim sigs.
@@ -294,7 +471,8 @@ return;
}
-/* Log a line for each signature */
+/* Module API: Log a line for each signature */
+
void
dkim_exim_verify_log_all(void)
{
@@ -303,55 +481,22 @@ for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
}
-void
-dkim_exim_verify_finish(void)
-{
-int rc;
-gstring * g = NULL;
-const uschar * errstr = NULL;
-
-store_pool = POOL_MESSAGE;
-
-/* 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);
- f.dkim_disable_verify = TRUE;
- goto out;
- }
-
-dkim_collect_input = 0;
-
-/* Finish DKIM operation and fetch link to signatures chain */
-
-rc = pdkim_feed_finish(dkim_verify_ctx, (pdkim_signature **)&dkim_signatures,
- &errstr);
-if (rc != PDKIM_OK && errstr)
- log_write(0, LOG_MAIN, "DKIM: validation error: %s", errstr);
-
-/* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */
-
-for (pdkim_signature * 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);
- }
-gstring_release_unused(g);
-dkim_signers = string_from_gstring(g);
+/* Module API: append a log element with domain for the first passing sig */
-out:
-store_pool = dkim_verify_oldpool;
+gstring *
+dkim_exim_vdom_firstpass(gstring * g)
+{
+if (dkim_vdom_firstpass)
+ g = string_append(g, 2, US" DKIM=", dkim_vdom_firstpass);
+return g;
}
+/* For one signature, run the DKIM ACL, log the sig result,
+and append ths sig status to the status list.
+
+Args as per dkim_exim_acl_run() below */
-/* Args as per dkim_exim_acl_run() below */
static int
dkim_acl_call(uschar * id, gstring ** res_ptr,
uschar ** user_msgptr, uschar ** log_msgptr)
@@ -387,7 +532,7 @@ Returns: OK access is granted by an ACCEPT verb
ERROR disaster
*/
-int
+static int
dkim_exim_acl_run(uschar * id, gstring ** res_ptr,
uschar ** user_msgptr, uschar ** log_msgptr)
{
@@ -442,6 +587,146 @@ return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr);
}
+/* Module API:
+Loop over dkim_verify_signers option doing ACL calls. If one return any
+non-OK value stop and return that, else return OK.
+*/
+
+int
+ /*XXX need a user_msgptr */
+dkim_exim_acl_entry(uschar ** user_msgptr, uschar ** log_msgptr)
+{
+int rc = OK;
+
+GET_OPTION("dkim_verify_signers");
+if (dkim_verify_signers && *dkim_verify_signers)
+ {
+ const uschar * dkim_verify_signers_expanded =
+ expand_cstring(dkim_verify_signers);
+ gstring * results = NULL, * seen_items = NULL;
+ int signer_sep = 0, old_pool = store_pool;
+
+ if (!dkim_verify_signers_expanded)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "expansion of dkim_verify_signers option failed: %s",
+ expand_string_message);
+ return DEFER;
+ }
+
+ store_pool = POOL_PERM; /* Allow created variables to live to data ACL */
+
+ /* Loop over signers we want to verify, calling ACL. Default to OK
+ when no signers are present. Each call from here expands to an ACL
+ call per matching sig in the message. */
+
+ for (uschar * item;
+ item = string_nextinlist(&dkim_verify_signers_expanded,
+ &signer_sep, NULL, 0); )
+ {
+ /* Prevent running ACL for an empty item */
+ if (!item || !*item) continue;
+
+ /* Only run ACL once for each domain or identity,
+ no matter how often it appears in the expanded list. */
+ if (seen_items)
+ {
+ uschar * seen_item;
+ const uschar * seen_items_list = string_from_gstring(seen_items);
+ int seen_sep = ':';
+ BOOL seen_this_item = FALSE;
+
+ while ((seen_item = string_nextinlist(&seen_items_list, &seen_sep,
+ NULL, 0)))
+ if (Ustrcmp(seen_item, item) == 0)
+ {
+ seen_this_item = TRUE;
+ break;
+ }
+
+ if (seen_this_item)
+ {
+ DEBUG(D_receive)
+ debug_printf("acl_smtp_dkim: skipping signer %s, "
+ "already seen\n", item);
+ continue;
+ }
+
+ seen_items = string_catn(seen_items, US":", 1);
+ }
+ seen_items = string_cat(seen_items, item);
+
+ if ((rc = dkim_exim_acl_run(item, &results, user_msgptr, log_msgptr)) != OK)
+ {
+ DEBUG(D_receive)
+ debug_printf("acl_smtp_dkim: acl_check returned %d on %s, "
+ "skipping remaining items\n", rc, item);
+ break;
+ }
+ if (dkim_verify_minimal && Ustrcmp(dkim_verify_status, "pass") == 0)
+ break;
+ } /* signers loop */
+
+ dkim_verify_status = string_from_gstring(results);
+ store_pool = old_pool;
+ }
+else
+ dkim_exim_verify_log_all();
+
+return rc;
+}
+
+/******************************************************************************/
+
+/* Module API */
+
+static int
+dkim_exim_signer_isinlist(const uschar * l)
+{
+return dkim_cur_signer
+ ? match_isinlist(dkim_cur_signer, &l, 0, NULL, NULL, MCL_STRING, TRUE, NULL)
+ : FAIL;
+}
+
+/* Module API */
+
+static int
+dkim_exim_status_listmatch(const uschar * l)
+{ /* return good for any match */
+const uschar * s = dkim_verify_status ? dkim_verify_status : US"none";
+int sep = 0, rc = FAIL;
+for (uschar * ss; ss = string_nextinlist(&s, &sep, NULL, 0); )
+ if ( (rc = match_isinlist(ss, &l, 0, NULL, NULL, MCL_STRING, TRUE, NULL))
+ == OK) break;
+return rc;
+}
+
+/* Module API: Overwriteable dkim result variables */
+
+static void
+dkim_exim_setvar(const uschar * name, void * val)
+{
+if (Ustrcmp(name, "dkim_verify_status") == 0)
+ dkim_verify_status = val;
+else if (Ustrcmp(name, "dkim_verify_reason") == 0)
+ dkim_verify_reason = val;
+}
+
+/******************************************************************************/
+
+static void
+dkim_smtp_reset(void)
+{
+dkim_cur_signer = dkim_signers =
+dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
+f.dkim_disable_verify = FALSE;
+dkim_collect_input = 0;
+dkim_vdom_firstpass = dkim_verify_status = dkim_verify_reason = NULL;
+dkim_key_length = 0;
+}
+
+/******************************************************************************/
+
static uschar *
dkim_exim_expand_defaults(int what)
{
@@ -468,6 +753,8 @@ switch (what)
}
+/* Module API: return a computed value for a variable expansion */
+
uschar *
dkim_exim_expand_query(int what)
{
@@ -585,12 +872,14 @@ switch (what)
}
-void
+/* Module API */
+
+static void
dkim_exim_sign_init(void)
{
int old_pool = store_pool;
-dkim_exim_init();
+dkim_exim_init(NULL);
store_pool = POOL_MAIN;
pdkim_init_context(&dkim_sign_ctx, FALSE, &dkim_exim_query_dns_txt);
store_pool = old_pool;
@@ -834,6 +1123,95 @@ expand_bad:
+#ifdef SUPPORT_DMARC
+
+/* Module API */
+
+static const pdkim_signature *
+dkim_sigs_list(void)
+{
+return dkim_signatures;
+}
+#endif
+
+#ifdef EXPERIMENTAL_ARC
+
+/* Module API */
+static int
+dkim_hashname_to_type(const blob * name)
+{
+return pdkim_hashname_to_hashtype(name->data, name->len);
+}
+
+/* Module API */
+hashmethod
+dkim_hashtype_to_method(int hashtype)
+{
+return hashtype >= 0 ? pdkim_hashes[hashtype].exim_hashmethod : -1;
+}
+
+/* Module API */
+hashmethod
+dkim_hashname_to_method(const blob * name)
+{
+return dkim_hashtype_to_method(dkim_hashname_to_type(name));
+}
+
+/* Module API: Set up a body hashing method on the given signature-context
+(creates a new one if needed, or uses an already-present one).
+
+Arguments:
+ signing TRUE to use dkim's signing context, else dkim_verify_ctx
+ canon canonicalization spec, text form
+ hash hash spec, text form
+ bodylen byte count for message body
+
+Return: pointer to hashing method struct
+*/
+
+static pdkim_bodyhash *
+dkim_set_bodyhash(BOOL signing,
+ const blob * canon, const blob * hashname, long bodylen)
+{
+int canon_head = -1, canon_body = -1;
+
+pdkim_cstring_to_canons(canon->data, canon->len, &canon_head, &canon_body);
+return pdkim_set_bodyhash(signing ? &dkim_sign_ctx: dkim_verify_ctx,
+ dkim_hashname_to_type(hashname),
+ canon_body,
+ bodylen);
+}
+
+/* Module API: Sign a blob of data (which might already be a hash, if
+Ed25519 or GCrypt signing).
+
+Arguments:
+ data to be signed
+ hm hash to be applied to the data
+ privkey private key for siging, PEM format
+ signature pointer for result blob
+
+Return: NULL, or error string on failure
+*/
+
+static const uschar *
+dkim_sign_blob(const blob * data, hashmethod hm, const uschar * privkey,
+ blob * signature)
+{
+es_ctx sctx;
+const uschar * errstr;
+
+if ((errstr = exim_dkim_signing_init(privkey, &sctx)))
+ { DEBUG(D_transport) debug_printf("signing key setup: %s\n", errstr); }
+else errstr = exim_dkim_sign(&sctx, hm, data, signature);
+
+return errstr;
+}
+
+#endif /*EXPERIMENTAL_ARC*/
+
+
+/* Module API */
gstring *
authres_dkim(gstring * g)
@@ -909,6 +1287,93 @@ DEBUG(D_acl)
return g;
}
+/******************************************************************************/
+/* Module API */
+
+static optionlist dkim_options[] = {
+ { "acl_smtp_dkim", opt_stringptr, {&acl_smtp_dkim} },
+ { "dkim_verify_hashes", opt_stringptr, {&dkim_verify_hashes} },
+ { "dkim_verify_keytypes", opt_stringptr, {&dkim_verify_keytypes} },
+ { "dkim_verify_min_keysizes", opt_stringptr, {&dkim_verify_min_keysizes} },
+ { "dkim_verify_minimal", opt_bool, {&dkim_verify_minimal} },
+ { "dkim_verify_signers", opt_stringptr, {&dkim_verify_signers} },
+};
+
+static void * dkim_functions[] = {
+ [DKIM_VERIFY_FEED] = dkim_exim_verify_feed,
+ [DKIM_VERIFY_PAUSE] = dkim_exim_verify_pause,
+ [DKIM_VERIFY_FINISH] = dkim_exim_verify_finish,
+ [DKIM_ACL_ENTRY] = dkim_exim_acl_entry,
+ [DKIM_VERIFY_LOG_ALL] = dkim_exim_verify_log_all,
+ [DKIM_VDOM_FIRSTPASS] = dkim_exim_vdom_firstpass,
+
+ [DKIM_SIGNER_ISINLIST] = dkim_exim_signer_isinlist,
+ [DKIM_STATUS_LISTMATCH] = dkim_exim_status_listmatch,
+
+ [DKIM_SETVAR] = dkim_exim_setvar,
+ [DKIM_EXPAND_QUERY] = dkim_exim_expand_query,
+
+ [DKIM_TRANSPORT_INIT] = dkim_exim_sign_init,
+ [DKIM_TRANSPORT_WRITE] = dkim_transport_write_message,
+
+#ifdef SUPPORT_DMARC
+ [DKIM_SIGS_LIST] = dkim_sigs_list,
+#endif
+#ifdef EXPERIMENTAL_ARC
+ [DKIM_HASHNAME_TO_TYPE] = dkim_hashname_to_type,
+ [DKIM_HASHTYPE_TO_METHOD] = dkim_hashtype_to_method,
+ [DKIM_HASHNAME_TO_METHOD] = dkim_hashname_to_method,
+ [DKIM_SET_BODYHASH] = dkim_set_bodyhash,
+ [DKIM_DNS_PUBKEY] = dkim_exim_parse_dns_pubkey,
+ [DKIM_SIG_VERIFY] = dkim_exim_sig_verify,
+ [DKIM_HEADER_RELAX] = pdkim_relax_header_n,
+ [DKIM_SIGN_DATA] = dkim_sign_blob,
+#endif
+};
+
+static var_entry dkim_variables[] = {
+ { "dkim_algo", vtype_dkim, (void *)DKIM_ALGO },
+ { "dkim_bodylength", vtype_dkim, (void *)DKIM_BODYLENGTH },
+ { "dkim_canon_body", vtype_dkim, (void *)DKIM_CANON_BODY },
+ { "dkim_canon_headers", vtype_dkim, (void *)DKIM_CANON_HEADERS },
+ { "dkim_copiedheaders", vtype_dkim, (void *)DKIM_COPIEDHEADERS },
+ { "dkim_created", vtype_dkim, (void *)DKIM_CREATED },
+ { "dkim_cur_signer", vtype_stringptr, &dkim_cur_signer },
+ { "dkim_domain", vtype_stringptr, &dkim_signing_domain },
+ { "dkim_expires", vtype_dkim, (void *)DKIM_EXPIRES },
+ { "dkim_headernames", vtype_dkim, (void *)DKIM_HEADERNAMES },
+ { "dkim_identity", vtype_dkim, (void *)DKIM_IDENTITY },
+ { "dkim_key_granularity",vtype_dkim, (void *)DKIM_KEY_GRANULARITY },
+ { "dkim_key_length", vtype_int, &dkim_key_length },
+ { "dkim_key_nosubdomains",vtype_dkim, (void *)DKIM_NOSUBDOMAINS },
+ { "dkim_key_notes", vtype_dkim, (void *)DKIM_KEY_NOTES },
+ { "dkim_key_srvtype", vtype_dkim, (void *)DKIM_KEY_SRVTYPE },
+ { "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_stringptr, &dkim_verify_reason },
+ { "dkim_verify_status", vtype_stringptr, &dkim_verify_status },
+};
+
+misc_module_info dkim_module_info = {
+ .name = US"dkim",
+# if SUPPORT_DKIM==2
+ .dyn_magic = MISC_MODULE_MAGIC,
+# endif
+ .init = dkim_exim_init,
+ .msg_init = dkim_exim_verify_init,
+ .authres = authres_dkim,
+ .smtp_reset = dkim_smtp_reset,
+
+ .options = dkim_options,
+ .options_count = nelem(dkim_options),
+
+ .functions = dkim_functions,
+ .functions_count = nelem(dkim_functions),
+
+ .variables = dkim_variables,
+ .variables_count = nelem(dkim_variables),
+};
# endif /*!MACRO_PREDEF*/
#endif /*!DISABLE_DKIM*/
diff --git a/src/src/miscmods/dkim.h b/src/src/miscmods/dkim.h
new file mode 100644
index 000000000..aa14d58d2
--- /dev/null
+++ b/src/src/miscmods/dkim.h
@@ -0,0 +1,47 @@
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* Copyright (c) University of Cambridge, 1995 - 2018 */
+/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+gstring * dkim_exim_sign(int, off_t, uschar *, struct ob_dkim *, const uschar **);
+uschar *dkim_exim_expand_query(int);
+
+
+#define DKIM_ALGO 1
+#define DKIM_BODYLENGTH 2
+#define DKIM_CANON_BODY 3
+#define DKIM_CANON_HEADERS 4
+#define DKIM_COPIEDHEADERS 5
+#define DKIM_CREATED 6
+#define DKIM_EXPIRES 7
+#define DKIM_HEADERNAMES 8
+#define DKIM_IDENTITY 9
+#define DKIM_KEY_GRANULARITY 10
+#define DKIM_KEY_SRVTYPE 11
+#define DKIM_KEY_NOTES 12
+#define DKIM_KEY_TESTING 13
+#define DKIM_NOSUBDOMAINS 14
+#define DKIM_VERIFY_STATUS 15
+#define DKIM_VERIFY_REASON 16
+
+
+extern unsigned dkim_collect_input; /* Runtime count of dkim signtures; tracks whether SMTP input is fed to DKIM validation */
+extern uschar *dkim_cur_signer; /* Expansion variable, holds the current "signer" domain or identity during a acl_smtp_dkim run */
+extern int dkim_key_length; /* Expansion variable, length of signing key in bits */
+extern void *dkim_signatures; /* Actually a (pdkim_signature *) but most files do not need to know */
+extern uschar *dkim_signers; /* Expansion variable, holds colon-separated list of domains and identities that have signed a message */
+extern gstring *dkim_signing_record; /* domains+selectors used */
+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_hashes; /* Preference order for signatures */
+extern uschar *dkim_verify_keytypes; /* Preference order for signatures */
+extern uschar *dkim_verify_min_keysizes; /* list of minimum key sizes, keyed by algo */
+extern BOOL dkim_verify_minimal; /* Shortcircuit signature verification */
+extern uschar *dkim_vdom_firstpass; /* First successful domain verified, or null */
+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 */
+
diff --git a/src/src/miscmods/dkim_api.h b/src/src/miscmods/dkim_api.h
new file mode 100644
index 000000000..54e1141ff
--- /dev/null
+++ b/src/src/miscmods/dkim_api.h
@@ -0,0 +1,36 @@
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* Copyright (c) The Exim Maintainers 2024 */
+/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* API definitions for the dkim module */
+
+
+/* Function table entry numbers */
+
+#define DKIM_VERIFY_FEED 0
+#define DKIM_VERIFY_PAUSE 1
+#define DKIM_VERIFY_FINISH 2
+#define DKIM_ACL_ENTRY 3
+#define DKIM_VERIFY_LOG_ALL 4
+#define DKIM_VDOM_FIRSTPASS 5
+#define DKIM_SIGNER_ISINLIST 6
+#define DKIM_STATUS_LISTMATCH 7
+#define DKIM_SETVAR 8
+#define DKIM_EXPAND_QUERY 9
+#define DKIM_TRANSPORT_INIT 10
+#define DKIM_TRANSPORT_WRITE 11
+
+#define DKIM_SIGS_LIST 12
+
+#define DKIM_HASHNAME_TO_TYPE 13
+#define DKIM_HASHTYPE_TO_METHOD 14
+#define DKIM_HASHNAME_TO_METHOD 15
+#define DKIM_SET_BODYHASH 16
+#define DKIM_DNS_PUBKEY 17
+#define DKIM_SIG_VERIFY 18
+#define DKIM_HEADER_RELAX 19
+#define DKIM_SIGN_DATA 20
diff --git a/src/src/dkim_transport.c b/src/src/miscmods/dkim_transport.c
similarity index 98%
rename from src/src/dkim_transport.c
rename to src/src/miscmods/dkim_transport.c
index 63870c57f..0500da2be 100644
--- a/src/src/dkim_transport.c
+++ b/src/src/miscmods/dkim_transport.c
@@ -10,7 +10,7 @@
/* Transport shim for dkim signing */
-#include "exim.h"
+#include "../exim.h"
#ifndef DISABLE_DKIM /* rest of file */
@@ -154,7 +154,8 @@ if (!rc) return FALSE;
/* Get signatures for headers plus spool data file */
#ifdef EXPERIMENTAL_ARC
-arc_sign_init();
+arc_sign_init(); /*XXX perhaps move this call back to the smtp tpt
+ around where it currently calls arc_ams_setup_sign_bodyhash() ? */
#endif
/* The dotstuffed status of the datafile depends on whether it was stored
@@ -395,7 +396,7 @@ if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
/* If there is no filter command set up, construct the message and calculate
a dkim signature of it, send the signature and a reconstructed message. This
-avoids using a temprary file. */
+avoids using a temporary file. */
if ( !transport_filter_argv
|| !*transport_filter_argv
diff --git a/src/src/miscmods/dmarc.c b/src/src/miscmods/dmarc.c
index 37648d045..4a8beab66 100644
--- a/src/src/miscmods/dmarc.c
+++ b/src/src/miscmods/dmarc.c
@@ -22,7 +22,7 @@
# include "../functions.h"
# include "dmarc.h"
-# include "../pdkim/pdkim.h"
+# include "pdkim.h"
OPENDMARC_LIB_T dmarc_ctx;
DMARC_POLICY_T *dmarc_pctx = NULL;
@@ -32,7 +32,8 @@ BOOL dmarc_abort = FALSE;
uschar *dmarc_pass_fail = US"skipped";
header_line *from_header = NULL;
-misc_module_info * spf_mod_info;
+static misc_module_info * dmarc_dkim_mod_info;
+static misc_module_info * dmarc_spf_mod_info;
SPF_response_t *spf_response_p;
int dmarc_spf_ares_result = 0;
uschar *spf_sender_domain = NULL;
@@ -73,9 +74,19 @@ static BOOL
dmarc_init(void *)
{
uschar * errstr;
-if (!(spf_mod_info = misc_mod_find(US"spf", &errstr)))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "dmarc: failed to find SPF module: %s", errstr);
+if (!(dmarc_spf_mod_info = misc_mod_find(US"spf", &errstr)))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "dmarc: %s", errstr);
+ return FALSE;
+ }
+
+/*XXX not yet used, but will be */
+if (!(dmarc_dkim_mod_info = misc_mod_find(US"dkim", &errstr)))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "dmarc: %s", errstr);
+ return FALSE;
+ }
+
return TRUE;
}
@@ -188,6 +199,8 @@ return OK;
static void
dmarc_smtp_reset(void)
{
+f.dmarc_has_been_checked = f.dmarc_disable_verify =
+f.dmarc_enable_forensic = FALSE;
dmarc_domain_policy = dmarc_status = dmarc_status_text =
dmarc_used_domain = NULL;
}
@@ -394,7 +407,6 @@ dmarc_process(void)
int sr, origin; /* used in SPF section */
int dmarc_spf_result = 0; /* stores spf into dmarc conn ctx */
int tmp_ans, c;
-pdkim_signature * sig = dkim_signatures;
uschar * rr;
BOOL has_dmarc_record = TRUE;
u_char ** ruf; /* forensic report addressees, if called for */
@@ -453,6 +465,7 @@ if (!dmarc_abort && !sender_host_authenticated)
{
uschar * dmarc_domain;
gstring * dkim_history_buffer = NULL;
+ typedef const pdkim_signature * (*sigs_fn_t)(void);
/* Use the envelope sender domain for this part of DMARC */
@@ -460,9 +473,9 @@ if (!dmarc_abort && !sender_host_authenticated)
{
typedef SPF_response_t * (*fn_t)(void);
- if (spf_mod_info)
+ if (dmarc_spf_mod_info)
/*XXX ugly use of a pointer */
- spf_response_p = ((fn_t *) spf_mod_info->functions)[SPF_GET_RESPONSE]();
+ spf_response_p = ((fn_t *) dmarc_spf_mod_info->functions)[SPF_GET_RESPONSE]();
}
if (!spf_response_p)
@@ -519,7 +532,9 @@ if (!dmarc_abort && !sender_host_authenticated)
/* Now we cycle through the dkim signature results and put into
the opendmarc context, further building the DMARC reply. */
- for(pdkim_signature * sig = dkim_signatures; sig; sig = sig->next)
+ for(const pdkim_signature * sig =
+ (((sigs_fn_t *)dmarc_dkim_mod_info->functions)[DKIM_SIGS_LIST])();
+ sig; sig = sig->next)
{
int dkim_result, dkim_ares_result, vs, ves;
@@ -749,7 +764,6 @@ static optionlist dmarc_options[] = {
static void * dmarc_functions[] = {
[DMARC_PROCESS] = dmarc_process,
[DMARC_EXPAND_QUERY] = dmarc_exim_expand_query,
- [DMARC_AUTHRES] = authres_dmarc,
[DMARC_STORE_DATA] = dmarc_store_data,
};
@@ -775,6 +789,7 @@ misc_module_info dmarc_module_info =
.lib_vers_report = dmarc_version_report,
.smtp_reset = dmarc_smtp_reset,
.msg_init = dmarc_msg_init,
+ .authres = authres_dmarc,
.options = dmarc_options,
.options_count = nelem(dmarc_options),
diff --git a/src/src/miscmods/dmarc_api.h b/src/src/miscmods/dmarc_api.h
index 6ba8a5060..9d9ef62da 100644
--- a/src/src/miscmods/dmarc_api.h
+++ b/src/src/miscmods/dmarc_api.h
@@ -13,5 +13,4 @@
#define DMARC_PROCESS 0
#define DMARC_EXPAND_QUERY 1
-#define DMARC_AUTHRES 2
-#define DMARC_STORE_DATA 3
+#define DMARC_STORE_DATA 2
diff --git a/src/src/pdkim/Makefile b/src/src/miscmods/pdkim/Makefile
similarity index 100%
rename from src/src/pdkim/Makefile
rename to src/src/miscmods/pdkim/Makefile
diff --git a/src/src/pdkim/README b/src/src/miscmods/pdkim/README
similarity index 100%
rename from src/src/pdkim/README
rename to src/src/miscmods/pdkim/README
diff --git a/src/src/pdkim/crypt_ver.h b/src/src/miscmods/pdkim/crypt_ver.h
similarity index 100%
rename from src/src/pdkim/crypt_ver.h
rename to src/src/miscmods/pdkim/crypt_ver.h
diff --git a/src/src/pdkim/pdkim.c b/src/src/miscmods/pdkim/pdkim.c
similarity index 99%
rename from src/src/pdkim/pdkim.c
rename to src/src/miscmods/pdkim/pdkim.c
index 42e67e6aa..cdbdfc5e0 100644
--- a/src/src/pdkim/pdkim.c
+++ b/src/src/miscmods/pdkim/pdkim.c
@@ -298,6 +298,7 @@ return PDKIM_FAIL;
/* -------------------------------------------------------------------------- */
+/* Module API */
/* Performs "relaxed" canonicalization of a header. */
uschar *
@@ -422,7 +423,7 @@ b->len = dlen;
uschar *
pdkim_encode_base64(blob * b)
{
-return b64encode(CUS b->data, b->len);
+return b64encode(b->data, b->len);
}
@@ -1005,13 +1006,13 @@ return PDKIM_OK;
#define HEADER_BUFFER_FRAG_SIZE 256
DLLEXPORT int
-pdkim_feed(pdkim_ctx * ctx, uschar * data, int len)
+pdkim_feed(pdkim_ctx * ctx, const uschar * data, unsigned len)
{
/* Alternate EOD signal, used in non-dotstuffing mode */
if (!data)
pdkim_body_complete(ctx);
-else for (int p = 0; p < len; p++)
+else for (unsigned p = 0; p < len; p++)
{
uschar c = data[p];
int rc;
@@ -2008,6 +2009,12 @@ pdkim_bodyhash * b;
if (hashtype == -1 || canon_method == -1) return NULL;
+if (!ctx)
+ {
+ DEBUG(D_receive) debug_printf("pdkim_set_bodyhash: null context\n");
+ return NULL;
+ }
+
for (b = ctx->bodyhash; b; b = b->next)
if ( hashtype == b->hashtype
&& canon_method == b->canon_method
@@ -2073,7 +2080,7 @@ DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback;
void
pdkim_init(void)
{
-exim_dkim_init();
+exim_dkim_signers_init();
}
diff --git a/src/src/pdkim/pdkim.h b/src/src/miscmods/pdkim/pdkim.h
similarity index 99%
rename from src/src/pdkim/pdkim.h
rename to src/src/miscmods/pdkim/pdkim.h
index 5f91d3bc7..b86014f8f 100644
--- a/src/src/pdkim/pdkim.h
+++ b/src/src/miscmods/pdkim/pdkim.h
@@ -344,7 +344,7 @@ pdkim_bodyhash *pdkim_set_bodyhash(pdkim_ctx *, int, int, long);
pdkim_bodyhash *pdkim_set_sig_bodyhash(pdkim_ctx *, pdkim_signature *);
DLLEXPORT
-int pdkim_feed (pdkim_ctx *, uschar *, int);
+int pdkim_feed (pdkim_ctx *, const uschar *, unsigned);
DLLEXPORT
int pdkim_feed_finish (pdkim_ctx *, pdkim_signature **, const uschar **);
diff --git a/src/src/pdkim/pdkim_hash.h b/src/src/miscmods/pdkim/pdkim_hash.h
similarity index 100%
rename from src/src/pdkim/pdkim_hash.h
rename to src/src/miscmods/pdkim/pdkim_hash.h
diff --git a/src/src/pdkim/signing.c b/src/src/miscmods/pdkim/signing.c
similarity index 95%
rename from src/src/pdkim/signing.c
rename to src/src/miscmods/pdkim/signing.c
index b564fb929..44f2e12ac 100644
--- a/src/src/pdkim/signing.c
+++ b/src/src/miscmods/pdkim/signing.c
@@ -64,7 +64,7 @@ DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s%s", level, message,
void
-exim_dkim_init(void)
+exim_dkim_signers_init(void)
{
#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
DEBUG(D_tls)
@@ -125,10 +125,16 @@ return NULL;
/* allocate mem for signature (when signing) */
/* hash & sign data. No way to do incremental.
+Arguments:
+ sign_ctx library-specific context for a signature (incl. key)
+ hash hash method to apply to data
+ data data to be signed
+ sig returned signature
+
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, const blob * data, blob * sig)
{
gnutls_datum_t k_data = { .data = data->data, .size = data->len };
gnutls_digest_algorithm_t dig;
@@ -159,7 +165,7 @@ return NULL;
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx,
+exim_dkim_verify_init(const blob * pubkey, keyformat fmt, ev_ctx * verify_ctx,
unsigned * bits)
{
gnutls_datum_t k;
@@ -197,7 +203,8 @@ return ret;
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, const blob * data_hash,
+ const blob * sig)
{
gnutls_datum_t k = { .data = data_hash->data, .size = data_hash->len };
gnutls_datum_t s = { .data = sig->data, .size = sig->len };
@@ -301,7 +308,7 @@ return NULL;
void
-exim_dkim_init(void)
+exim_dkim_signers_init(void)
{
/* Version check should be the very first call because it
makes sure that important subsystems are initialized. */
@@ -484,7 +491,7 @@ asn_err: return US asn1_strerror(rc);
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, const blob * data, blob * sig)
{
char * sexp_hash;
gcry_sexp_t s_hash = NULL, s_key = NULL, s_sig = NULL;
@@ -559,7 +566,7 @@ return NULL;
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx,
+exim_dkim_verify_init(const blob * pubkey, keyformat fmt, ev_ctx * verify_ctx,
unsigned * bits)
{
/*
@@ -648,7 +655,8 @@ XXX though we appear to be doing a hash, too!
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data_hash, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, const blob * data_hash,
+ const blob * sig)
{
/*
cf. libgnutls 2.8.5 _wrap_gcry_pk_verify()
@@ -702,7 +710,7 @@ return NULL;
/******************************************************************************/
void
-exim_dkim_init(void)
+exim_dkim_signers_init(void)
{
ERR_load_crypto_strings();
}
@@ -747,7 +755,7 @@ return NULL;
Return: NULL for success with the signaature in the sig blob, or an error string */
const uschar *
-exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, blob * data, blob * sig)
+exim_dkim_sign(es_ctx * sign_ctx, hashmethod hash, const blob * data, blob * sig)
{
const EVP_MD * md;
EVP_MD_CTX * ctx;
@@ -804,7 +812,7 @@ return US ERR_error_string(ERR_get_error(), NULL);
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx,
+exim_dkim_verify_init(const blob * pubkey, keyformat fmt, ev_ctx * verify_ctx,
unsigned * bits)
{
const uschar * s = pubkey->data;
@@ -841,7 +849,8 @@ return ret;
Return: NULL for success, or an error string */
const uschar *
-exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, blob * data, blob * sig)
+exim_dkim_verify(ev_ctx * verify_ctx, hashmethod hash, const blob * data,
+ const blob * sig)
{
const EVP_MD * md;
diff --git a/src/src/pdkim/signing.h b/src/src/miscmods/pdkim/signing.h
similarity index 83%
rename from src/src/pdkim/signing.h
rename to src/src/miscmods/pdkim/signing.h
index 7760ce73f..ce801a395 100644
--- a/src/src/pdkim/signing.h
+++ b/src/src/miscmods/pdkim/signing.h
@@ -86,13 +86,15 @@ typedef struct {
#endif
-extern void exim_dkim_init(void);
+extern void exim_dkim_signers_init(void);
extern gstring * exim_dkim_data_append(gstring *, uschar *);
extern const uschar * exim_dkim_signing_init(const uschar *, es_ctx *);
-extern const uschar * exim_dkim_sign(es_ctx *, hashmethod, blob *, blob *);
-extern const uschar * exim_dkim_verify_init(blob *, keyformat, ev_ctx *, unsigned *);
-extern const uschar * exim_dkim_verify(ev_ctx *, hashmethod, blob *, blob *);
+extern const uschar * exim_dkim_sign(es_ctx *, hashmethod, const blob *, blob *);
+extern const uschar * exim_dkim_verify_init(const blob *, keyformat, ev_ctx *,
+ unsigned *);
+extern const uschar * exim_dkim_verify(ev_ctx *, hashmethod, const blob *,
+ const blob *);
#endif /*DISABLE_DKIM*/
/* End of File */
diff --git a/src/src/miscmods/spf.c b/src/src/miscmods/spf.c
index ea23c1c65..14937e799 100644
--- a/src/src/miscmods/spf.c
+++ b/src/src/miscmods/spf.c
@@ -566,7 +566,6 @@ static optionlist spf_options[] = {
static void * spf_functions[] = {
[SPF_PROCESS] = spf_process,
- [SPF_AUTHRES] = authres_spf,
[SPF_GET_RESPONSE] = spf_get_response, /* ugly; for dmarc */
[SPF_OPEN] = spf_lookup_open,
@@ -593,6 +592,7 @@ misc_module_info spf_module_info =
.lib_vers_report = spf_lib_version_report,
.conn_init = spf_conn_init,
.smtp_reset = spf_smtp_reset,
+ .authres = authres_spf,
.options = spf_options,
.options_count = nelem(spf_options),
diff --git a/src/src/miscmods/spf_api.h b/src/src/miscmods/spf_api.h
index 3801e3e3f..117a7ae6a 100644
--- a/src/src/miscmods/spf_api.h
+++ b/src/src/miscmods/spf_api.h
@@ -6,13 +6,12 @@
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* API definitions for the spfmodule */
+/* API definitions for the spf module */
/* Function table entry numbers */
#define SPF_PROCESS 0
-#define SPF_AUTHRES 1
#define SPF_GET_RESPONSE 2
#define SPF_OPEN 3
#define SPF_CLOSE 4
diff --git a/src/src/pdkim/config.h b/src/src/pdkim/config.h
deleted file mode 100644
index fdd4cfe4f..000000000
--- a/src/src/pdkim/config.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#define POLARSSL_BASE64_C
-
-
-
diff --git a/src/src/readconf.c b/src/src/readconf.c
index ae7073229..5aabd0194 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -48,7 +48,7 @@ static optionlist optionlist_config[] = {
{ "acl_smtp_data_prdr", opt_stringptr, {&acl_smtp_data_prdr} },
#endif
#ifndef DISABLE_DKIM
- { "acl_smtp_dkim", opt_stringptr, {&acl_smtp_dkim} },
+ { "acl_smtp_dkim", opt_module, {US"dkim"} },
#endif
{ "acl_smtp_etrn", opt_stringptr, {&acl_smtp_etrn} },
{ "acl_smtp_expn", opt_stringptr, {&acl_smtp_expn} },
@@ -122,11 +122,11 @@ static optionlist optionlist_config[] = {
#endif
{ "disable_ipv6", opt_bool, {&disable_ipv6} },
#ifndef DISABLE_DKIM
- { "dkim_verify_hashes", opt_stringptr, {&dkim_verify_hashes} },
- { "dkim_verify_keytypes", opt_stringptr, {&dkim_verify_keytypes} },
- { "dkim_verify_min_keysizes", opt_stringptr, {&dkim_verify_min_keysizes} },
- { "dkim_verify_minimal", opt_bool, {&dkim_verify_minimal} },
- { "dkim_verify_signers", opt_stringptr, {&dkim_verify_signers} },
+ { "dkim_verify_hashes", opt_module, {US"dkim"} },
+ { "dkim_verify_keytypes", opt_module, {US"dkim"} },
+ { "dkim_verify_min_keysizes", opt_module, {US"dkim"} },
+ { "dkim_verify_minimal", opt_module, {US"dkim"} },
+ { "dkim_verify_signers", opt_module, {US"dkim"} },
#endif
#ifdef SUPPORT_DMARC
{ "dmarc_forensic_sender", opt_module, {US"dmarc"} },
diff --git a/src/src/receive.c b/src/src/receive.c
index a6b7722bf..541e9320d 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -1828,13 +1828,6 @@ mime_is_rfc822 = 0;
mime_part_count = -1;
#endif
-#ifndef DISABLE_DKIM
-/* Call into DKIM to set up the context. In CHUNKING mode
-we clear the dot-stuffing flag */
-if (smtp_input && !smtp_batched_input && !f.dkim_disable_verify)
- dkim_exim_verify_init(chunking_state <= CHUNKING_OFFERED);
-#endif
-
if (misc_mod_msg_init() != OK)
goto TIDYUP;
@@ -3517,100 +3510,47 @@ else
#ifndef DISABLE_DKIM
if (!f.dkim_disable_verify)
{
- /* Finish off the body hashes, calculate sigs and do compares */
- dkim_exim_verify_finish();
+ misc_module_info * mi = misc_mod_findonly(US"dkim");
+ if (mi)
+ {
+ typedef void (*vfin_fn_t)(void);
+ typedef int (*vacl_fn_t)(uschar **, uschar**);
+ typedef void (*vlog_fn_t)(void);
- /* Check if we must run the DKIM ACL */
- GET_OPTION("acl_smtp_dkim");
- if (acl_smtp_dkim && dkim_verify_signers && *dkim_verify_signers)
- {
- uschar * dkim_verify_signers_expanded =
- expand_string(dkim_verify_signers);
- gstring * results = NULL, * seen_items = NULL;
- int signer_sep = 0, old_pool = store_pool;
- const uschar * ptr;
- uschar * item;
-
- store_pool = POOL_PERM; /* Allow created variables to live to data ACL */
-
- if (!(ptr = dkim_verify_signers_expanded))
- log_write(0, LOG_MAIN|LOG_PANIC,
- "expansion of dkim_verify_signers option failed: %s",
- expand_string_message);
-
- /* Loop over signers we want to verify, calling ACL. Default to OK
- when no signers are present. Each call from here expands to a n ACL
- call per matching sig in the message. */
-
- rc = OK;
- while ((item = string_nextinlist(&ptr, &signer_sep, NULL, 0)))
- {
- /* Prevent running ACL for an empty item */
- if (!item || !*item) continue;
+ /* Finish off the body hashes, calculate sigs and do compares */
- /* Only run ACL once for each domain or identity,
- no matter how often it appears in the expanded list. */
- if (seen_items)
- {
- uschar * seen_item;
- const uschar * seen_items_list = string_from_gstring(seen_items);
- int seen_sep = ':';
- BOOL seen_this_item = FALSE;
-
- while ((seen_item = string_nextinlist(&seen_items_list, &seen_sep,
- NULL, 0)))
- if (Ustrcmp(seen_item,item) == 0)
- {
- seen_this_item = TRUE;
- break;
- }
-
- if (seen_this_item)
- {
- DEBUG(D_receive)
- debug_printf("acl_smtp_dkim: skipping signer %s, "
- "already seen\n", item);
- continue;
- }
+ (((vfin_fn_t *) mi->functions)[DKIM_VERIFY_FINISH]) ();
- seen_items = string_catn(seen_items, US":", 1);
- }
- seen_items = string_cat(seen_items, item);
+ /* Check if we must run the DKIM ACL */
+
+ GET_OPTION("acl_smtp_dkim");
+ if (acl_smtp_dkim)
+ {
+ rc = (((vacl_fn_t *) mi->functions)[DKIM_ACL_ENTRY])
+ (&user_msg, &log_msg);
+ add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
- rc = dkim_exim_acl_run(item, &results, &user_msg, &log_msg);
if (rc != OK)
{
- DEBUG(D_receive)
- debug_printf("acl_smtp_dkim: acl_check returned %d on %s, "
- "skipping remaining items\n", rc, item);
cancel_cutthrough_connection(TRUE, US"dkim acl not ok");
- break;
+
+ if (rc != DISCARD)
+ {
+ Uunlink(spool_name);
+ if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0)
+ smtp_yield = FALSE; /* No more msgs after dropped conn */
+ smtp_reply = US""; /* Indicate reply already sent */
+ goto NOT_ACCEPTED; /* Skip to end of function */
+ }
+ recipients_count = 0;
+ blackholed_by = US"DKIM ACL";
+ if (log_msg)
+ blackhole_log_msg = string_sprintf(": %s", log_msg);
}
- else
- if (dkim_verify_minimal && Ustrcmp(dkim_verify_status, "pass") == 0)
- break;
- }
- dkim_verify_status = string_from_gstring(results);
- store_pool = old_pool;
- add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
- if (rc == DISCARD)
- {
- recipients_count = 0;
- blackholed_by = US"DKIM ACL";
- if (log_msg)
- blackhole_log_msg = string_sprintf(": %s", log_msg);
- }
- else if (rc != OK)
- {
- Uunlink(spool_name);
- if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0)
- smtp_yield = FALSE; /* No more messages after dropped connection */
- smtp_reply = US""; /* Indicate reply already sent */
- goto NOT_ACCEPTED; /* Skip to end of function */
}
- }
- else /* No acl or no wanted signers */
- dkim_exim_verify_log_all();
+ else /* No ACL; just log */
+ (((vlog_fn_t *) mi->functions)[DKIM_VERIFY_LOG_ALL]) ();
+ }
}
#endif /* DISABLE_DKIM */
@@ -4189,8 +4129,13 @@ if (LOGGING(8bitmime))
g = string_fmt_append(g, " M8S=%d", body_8bitmime);
#ifndef DISABLE_DKIM
-if (LOGGING(dkim) && dkim_verify_overall)
- g = string_append(g, 2, US" DKIM=", dkim_verify_overall);
+if (LOGGING(dkim))
+ {
+ misc_module_info * mi = misc_mod_findonly(US"dkim");
+ typedef gstring * (*fn_t)(gstring *);
+ if (mi)
+ g = (((fn_t *) mi->functions)[DKIM_VDOM_FIRSTPASS]) (g);
+ }
# ifdef EXPERIMENTAL_ARC
if (LOGGING(dkim) && arc_state && Ustrcmp(arc_state, "pass") == 0)
g = string_catn(g, US" ARC", 4);
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index adf6c59cb..e75894850 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -464,6 +464,23 @@ smtp_had_eof = smtp_had_error = 0;
+#ifndef DISABLE_DKIM
+/* Feed received message data to the dkim module */
+/*XXX maybe a global dkim_info? */
+void
+smtp_verify_feed(const uschar * s, unsigned n)
+{
+static misc_module_info * dkim_mi = NULL;
+typedef void (*fn_t)(const uschar *, int);
+
+if (!dkim_mi && !(dkim_mi = misc_mod_findonly(US"dkim")))
+ return;
+
+(((fn_t *) dkim_mi->functions)[DKIM_VERIFY_FEED]) (s, n);
+}
+#endif
+
+
/* Refill the buffer, and notify DKIM verification code.
Return false for error or EOF.
*/
@@ -507,7 +524,7 @@ if (rc <= 0)
return FALSE;
}
#ifndef DISABLE_DKIM
-dkim_exim_verify_feed(smtp_inbuffer, rc);
+smtp_verify_feed(smtp_inbuffer, rc);
#endif
smtp_inend = smtp_inbuffer + rc;
smtp_inptr = smtp_inbuffer;
@@ -570,7 +587,7 @@ int n = smtp_inend - smtp_inptr;
if (n > lim)
n = lim;
if (n > 0)
- dkim_exim_verify_feed(smtp_inptr, n);
+ smtp_verify_feed(smtp_inptr, n);
#endif
}
@@ -726,19 +743,24 @@ bdat_getc(unsigned lim)
uschar * user_msg = NULL;
uschar * log_msg;
-for(;;)
- {
#ifndef DISABLE_DKIM
- unsigned dkim_save;
+misc_module_info * dkim_info = misc_mod_findonly(US"dkim");
+typedef void (*dkim_pause_t)(BOOL);
+dkim_pause_t dkim_pause;
+
+dkim_pause = dkim_info
+ ? ((dkim_pause_t *) dkim_info->functions)[DKIM_VERIFY_PAUSE] : NULL;
#endif
+for(;;)
+ {
+
if (chunking_data_left > 0)
return lwr_receive_getc(chunking_data_left--);
bdat_pop_receive_functions();
#ifndef DISABLE_DKIM
- dkim_save = dkim_collect_input;
- dkim_collect_input = 0;
+ if (dkim_pause) dkim_pause(TRUE);
#endif
/* Unless PIPELINING was offered, there should be no next command
@@ -767,9 +789,7 @@ for(;;)
if (chunking_state == CHUNKING_LAST)
{
#ifndef DISABLE_DKIM
- dkim_collect_input = dkim_save;
- dkim_exim_verify_feed(NULL, 0); /* notify EOD */
- dkim_collect_input = 0;
+ smtp_verify_feed(NULL, 0); /* notify EOD */
#endif
return EOD;
}
@@ -843,7 +863,7 @@ next_cmd:
bdat_push_receive_functions();
#ifndef DISABLE_DKIM
- dkim_collect_input = dkim_save;
+ if (dkim_pause) dkim_pause(FALSE);
#endif
break; /* to top of main loop */
}
@@ -1681,17 +1701,6 @@ bmi_run = 0;
bmi_verdicts = NULL;
#endif
dnslist_domain = dnslist_matched = NULL;
-#ifndef DISABLE_DKIM
-dkim_cur_signer = dkim_signers =
-dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
-f.dkim_disable_verify = FALSE;
-dkim_collect_input = 0;
-dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
-dkim_key_length = 0;
-#endif
-#ifdef SUPPORT_DMARC
-f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
-#endif
#ifdef EXPERIMENTAL_ARC
arc_state = arc_state_reason = NULL;
arc_received_instance = 0;
diff --git a/src/src/spool_in.c b/src/src/spool_in.c
index bb54571be..43b30986d 100644
--- a/src/src/spool_in.c
+++ b/src/src/spool_in.c
@@ -269,9 +269,18 @@ bmi_verdicts = NULL;
#endif
#ifndef DISABLE_DKIM
-dkim_signers = NULL;
f.dkim_disable_verify = FALSE;
+# ifdef COMPILE_UTILITY
+dkim_signers = NULL;
dkim_collect_input = 0;
+#else
+ {
+ misc_module_info * mi = misc_mod_findonly(US"dkim");
+ /* We used to clear only dkim_signers, dkim_collect_input. This does more
+ but I think it is safe. */
+ if (mi) mi->smtp_reset();
+ }
+# endif
#endif
#ifndef DISABLE_TLS
diff --git a/src/src/string.c b/src/src/string.c
index 2b62233d8..dfda2d405 100644
--- a/src/src/string.c
+++ b/src/src/string.c
@@ -1688,7 +1688,7 @@ while (*fp)
case '}' : zg = string_catn(zg, US"{BC}", 4); break;
default:
{
- unsigned char u = *s;
+ uschar u = *s;
if ( (u < 32) || (u > 127) )
zg = string_fmt_append(zg, "{%02x}", u);
else
diff --git a/src/src/structs.h b/src/src/structs.h
index 46abac728..ef311b677 100644
--- a/src/src/structs.h
+++ b/src/src/structs.h
@@ -1026,6 +1026,7 @@ typedef struct misc_module_info {
int (*conn_init)(const uschar *, const uschar *);
void (*smtp_reset)(void);
int (*msg_init)(void);
+ gstring * (*authres)(gstring *);
void * options;
unsigned options_count;
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 06cd4a5f8..7963e2c97 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -3904,7 +3904,7 @@ else if (inbytes < 0)
return FALSE;
}
#ifndef DISABLE_DKIM
-dkim_exim_verify_feed(state->xfer_buffer, inbytes);
+smtp_verify_feed(state->xfer_buffer, inbytes);
#endif
state->xfer_buffer_hwm = (int) inbytes;
state->xfer_buffer_lwm = 0;
@@ -3980,7 +3980,7 @@ int n = state->xfer_buffer_hwm - state->xfer_buffer_lwm;
if (n > lim)
n = lim;
if (n > 0)
- dkim_exim_verify_feed(state->xfer_buffer+state->xfer_buffer_lwm, n);
+ smtp_verify_feed(state->xfer_buffer+state->xfer_buffer_lwm, n);
#endif
}
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 033bd0e10..302404b6c 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -4545,7 +4545,7 @@ switch(error)
}
#ifndef DISABLE_DKIM
-dkim_exim_verify_feed(ssl_xfer_buffer, inbytes);
+smtp_verify_feed(ssl_xfer_buffer, inbytes);
#endif
ssl_xfer_buffer_hwm = inbytes;
ssl_xfer_buffer_lwm = 0;
@@ -4615,7 +4615,7 @@ int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm;
if (n > lim)
n = lim;
if (n > 0)
- dkim_exim_verify_feed(ssl_xfer_buffer+ssl_xfer_buffer_lwm, n);
+ smtp_verify_feed(ssl_xfer_buffer+ssl_xfer_buffer_lwm, n);
#endif
}
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index d25a2b1f6..cdd2a404f 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -46,6 +46,7 @@ optionlist smtp_transport_options[] = {
{ "data_timeout", opt_time, LOFF(data_timeout) },
{ "delay_after_cutoff", opt_bool, LOFF(delay_after_cutoff) },
#ifndef DISABLE_DKIM
+ /*XXX dkim module */
{ "dkim_canon", opt_stringptr, LOFF(dkim.dkim_canon) },
{ "dkim_domain", opt_stringptr, LOFF(dkim.dkim_domain) },
{ "dkim_hash", opt_stringptr, LOFF(dkim.dkim_hash) },
@@ -4103,13 +4104,18 @@ else
#ifndef DISABLE_DKIM
{
+ typedef void (*fn_t)(void);
+ misc_module_info * mi;
# ifdef MEASURE_TIMING
struct timeval t0;
gettimeofday(&t0, NULL);
# endif
- dkim_exim_sign_init();
-# ifdef EXPERIMENTAL_ARC
+
+ if ((mi = misc_mod_find(US"dkim", NULL)))
{
+ (((fn_t *) mi->functions)[DKIM_TRANSPORT_INIT]) ();
+
+# ifdef EXPERIMENTAL_ARC
uschar * s = ob->arc_sign;
if (s)
{
@@ -4129,8 +4135,8 @@ else
ob->dkim.force_bodyhash = TRUE;
}
}
+# endif /*ARC*/
}
-# endif
# ifdef MEASURE_TIMING
report_time_since(&t0, US"dkim_exim_sign_init (delta)");
# endif
@@ -4175,7 +4181,15 @@ else
}
#ifndef DISABLE_DKIM
- sx->ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
+ {
+ misc_module_info * mi = misc_mod_find(US"dkim", NULL);
+ typedef BOOL (*fn_t)(transport_ctx *, struct ob_dkim *, const uschar **);
+
+ sx->ok = mi
+ ? (((fn_t *) mi->functions)[DKIM_TRANSPORT_WRITE])
+ (&tctx, &ob->dkim, CUSS &message)
+ : transport_write_message(&tctx, 0);
+ }
#else
sx->ok = transport_write_message(&tctx, 0);
#endif
diff --git a/test/runtest b/test/runtest
index 70499312d..83659ea19 100755
--- a/test/runtest
+++ b/test/runtest
@@ -1560,8 +1560,8 @@ RESET_AFTER_EXTRA_LINE_READ:
# Not all platforms build with SPF enabled
next if /(^$time_pid?spf_conn_init|spf_compile\.c)/;
next if /try option spf_smtp_comment_template$/;
- next if /loading module '(?:dmarc|spf)'$/;
- next if /^$time_pid?Loaded "(?:dmarc|spf)"$/;
+ next if /loading module '(?:dkim|dmarc|spf)'$/;
+ next if /^$time_pid?Loaded "(?:dkim|dmarc|spf)"$/;
# Not all platforms have sendfile support
next if /^cannot use sendfile for body: no support$/;
@@ -4147,7 +4147,6 @@ system("sudo cp eximdir/exim eximdir/exim_exim;" .
"sudo chmod 06755 eximdir/exim_exim");
# Copy any libraries that were built for dynamic load
-# Currently this is only for lookup methods
($parm_exim_dir) = $parm_exim =~ m?^(.*)/exim?;
--
## subscription configuration (requires account):
##
https://lists.exim.org/mailman3/postorius/lists/exim-cvs.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-cvs-unsubscribe@???
## Exim details at
http://www.exim.org/
## Please use the Wiki with this list -
http://wiki.exim.org/