From 9a0f997bac85d8f234238162f3cee4524b6f989c Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 4 Sep 2024 21:46:26 +0100 Subject: [PATCH] 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(-) delete mode 100644 src/src/dkim.h rename src/src/{ => miscmods}/dkim.c (65%) create mode 100644 src/src/miscmods/dkim.h create mode 100644 src/src/miscmods/dkim_api.h rename src/src/{ => miscmods}/dkim_transport.c (98%) rename src/src/{ => miscmods}/pdkim/Makefile (100%) rename src/src/{ => miscmods}/pdkim/README (100%) rename src/src/{ => miscmods}/pdkim/crypt_ver.h (100%) rename src/src/{ => miscmods}/pdkim/pdkim.c (99%) rename src/src/{ => miscmods}/pdkim/pdkim.h (99%) rename src/src/{ => miscmods}/pdkim/pdkim_hash.h (100%) rename src/src/{ => miscmods}/pdkim/signing.c (95%) rename src/src/{ => miscmods}/pdkim/signing.h (83%) delete mode 100644 src/src/pdkim/config.h 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 #elif defined(SHA_GCRYPT) # include -#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_ 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_ line to Local/Makefile, and (if dynamic) any SUPPORT__INCLUDE or SUPPORT__LIBS required. -Add the capitalised module name to the "miscmods" like in +Add the capitalised module name 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_ line should say "yes", +for dynamic: "2". + +If include-by-default is wanted for the module, use DISABLE_ instead +of SUPPORT_ (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?; -- 2.30.2