diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2017-11-11 16:20:02 +0000 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2017-11-11 16:20:02 +0000 |
commit | 3af849e2045f7c580d4c533d829dc9d567162d36 (patch) | |
tree | 068147ead2853f69f74af6587c2481b540d97f00 /src | |
parent | 3f4f5e7256a81b2e8154611174934aa5b6c57ad7 (diff) | |
parent | 72934ba73e5ac5fbd64b56dc684e3371a9651909 (diff) |
Merge branch 'master' into 4.next
Diffstat (limited to 'src')
-rw-r--r-- | src/OS/Makefile-Base | 79 | ||||
-rw-r--r-- | src/exim_monitor/em_version.c | 13 | ||||
-rw-r--r-- | src/src/acl.c | 51 | ||||
-rw-r--r-- | src/src/configure.default | 3 | ||||
-rwxr-xr-x | src/src/convert4r3.src | 12 | ||||
-rwxr-xr-x | src/src/convert4r4.src | 12 | ||||
-rw-r--r-- | src/src/dkim.c | 282 | ||||
-rw-r--r-- | src/src/dkim.h | 3 | ||||
-rw-r--r-- | src/src/exicyclog.src | 5 | ||||
-rw-r--r-- | src/src/exigrep.src | 10 | ||||
-rwxr-xr-x | src/src/exim_checkaccess.src | 8 | ||||
-rw-r--r-- | src/src/eximon.src | 7 | ||||
-rw-r--r-- | src/src/eximstats.src | 8 | ||||
-rw-r--r-- | src/src/exinext.src | 7 | ||||
-rw-r--r-- | src/src/exipick.src | 9 | ||||
-rw-r--r-- | src/src/exiqgrep.src | 9 | ||||
-rw-r--r-- | src/src/exiqsumm.src | 8 | ||||
-rw-r--r-- | src/src/exiwhat.src | 7 | ||||
-rw-r--r-- | src/src/expand.c | 4 | ||||
-rw-r--r-- | src/src/globals.c | 2 | ||||
-rw-r--r-- | src/src/globals.h | 2 | ||||
-rw-r--r-- | src/src/match.c | 238 | ||||
-rw-r--r-- | src/src/pdkim/pdkim.c | 54 | ||||
-rw-r--r-- | src/src/receive.c | 155 | ||||
-rw-r--r-- | src/src/tls-gnu.c | 56 | ||||
-rw-r--r-- | src/src/tls-openssl.c | 69 | ||||
-rw-r--r-- | src/src/transport-filter.src | 8 |
27 files changed, 727 insertions, 394 deletions
diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index d64ed549f..dcd87c29c 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -36,9 +36,9 @@ FE = $(FULLECHO) # are set up, and finally it goes to the main Exim target. all: utils exim -config: $(EDITME) checklocalmake Makefile os.c config.h version.h macro.c +config: $(EDITME) checklocalmake Makefile os.c config.h version.h version.sh macro.c -checklocalmake: +checklocalmake: @if $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(OSTYPE) $(EDITME) || \ $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(ARCHTYPE) $(EDITME) || \ $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(OSTYPE)-$(ARCHTYPE) $(EDITME); \ @@ -260,7 +260,7 @@ buildconfig: buildconfig.c # Target for the exicyclog utility script exicyclog: config ../src/exicyclog.src @rm -f exicyclog - @sed \ + @. ./version.sh && sed \ -e "s?PROCESSED_FLAG?This file has been so processed.?"\ -e "/^# /p" \ -e "/^# /d" \ @@ -277,6 +277,8 @@ exicyclog: config ../src/exicyclog.src -e "s?MV_COMMAND?$(MV_COMMAND)?" \ -e "s?RM_COMMAND?$(RM_COMMAND)?" \ -e "s?TOUCH_COMMAND?$(TOUCH_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exicyclog.src > exicyclog-t @mv exicyclog-t exicyclog @chmod a+x exicyclog @@ -285,13 +287,15 @@ exicyclog: config ../src/exicyclog.src # Target for the exinext utility script exinext: config ../src/exinext.src @rm -f exinext - @sed \ + @. ./version.sh && sed \ -e "s?PROCESSED_FLAG?This file has been so processed.?"\ -e "/^# /p" \ -e "/^# /d" \ -e "s?CONFIGURE_FILE_USE_NODE?$(CONFIGURE_FILE_USE_NODE)?" \ -e "s?CONFIGURE_FILE?$(CONFIGURE_FILE)?" \ -e "s?BIN_DIRECTORY?$(BIN_DIRECTORY)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exinext.src > exinext-t @mv exinext-t exinext @chmod a+x exinext @@ -300,7 +304,7 @@ exinext: config ../src/exinext.src # Target for the exiwhat utility script exiwhat: config ../src/exiwhat.src @rm -f exiwhat - @sed \ + @. ./version.sh && sed \ -e "s?PROCESSED_FLAG?This file has been so processed.?"\ -e "/^# /p" \ -e "/^# /d" \ @@ -313,6 +317,8 @@ exiwhat: config ../src/exiwhat.src -e "s?EXIWHAT_EGREP_ARG?$(EXIWHAT_EGREP_ARG)?" \ -e "s?EXIWHAT_MULTIKILL_CMD?$(EXIWHAT_MULTIKILL_CMD)?" \ -e "s?EXIWHAT_MULTIKILL_ARG?$(EXIWHAT_MULTIKILL_ARG)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exiwhat.src > exiwhat-t @mv exiwhat-t exiwhat @chmod a+x exiwhat @@ -321,7 +327,7 @@ exiwhat: config ../src/exiwhat.src # Target for the exim_checkaccess utility script exim_checkaccess: config ../src/exim_checkaccess.src @rm -f exim_checkaccess - @sed \ + @. ./version.sh && sed \ -e "s?PROCESSED_FLAG?This file has been so processed.?"\ -e "/^# /p" \ -e "/^# /d" \ @@ -329,6 +335,8 @@ exim_checkaccess: config ../src/exim_checkaccess.src -e "s?CONFIGURE_FILE?$(CONFIGURE_FILE)?" \ -e "s?BIN_DIRECTORY?$(BIN_DIRECTORY)?" \ -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exim_checkaccess.src > exim_checkaccess-t @mv exim_checkaccess-t exim_checkaccess @chmod a+x exim_checkaccess @@ -339,7 +347,7 @@ eximon: config ../src/eximon.src ../OS/eximon.conf-Default \ ../Local/eximon.conf @rm -f eximon $(SHELL) $(SCRIPTS)/Configure-eximon - @sed \ + @. ./version.sh && sed \ -e "s?PROCESSED_FLAG?This file has been so processed.?"\ -e "/^# /p" \ -e "/^# /d" \ @@ -349,85 +357,108 @@ eximon: config ../src/eximon.src ../OS/eximon.conf-Default \ -e "s?BASENAME_COMMAND?$(BASENAME_COMMAND)?" \ -e "s?HOSTNAME_COMMAND?$(HOSTNAME_COMMAND)?" \ -e "s?X11_LD_LIBRARY?$(X11_LD_LIB)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/eximon.src >> eximon @echo ">>> eximon script built"; echo "" # Targets for utilities; these are all Perl scripts that have to get the # location of Perl put in them. A few need other things as well. -exigrep: Makefile ../src/exigrep.src +exigrep: config ../src/exigrep.src @rm -f exigrep - @sed \ + @. ./version.sh && sed \ -e "s?PROCESSED_FLAG?This file has been so processed.?"\ -e "/^# /p" \ -e "/^# /d" \ -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ -e "s?ZCAT_COMMAND?$(ZCAT_COMMAND)?" \ -e "s?COMPRESS_SUFFIX?$(COMPRESS_SUFFIX)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exigrep.src > exigrep-t @mv exigrep-t exigrep @chmod a+x exigrep @echo ">>> exigrep script built" -eximstats: Makefile ../src/eximstats.src +eximstats: config ../src/eximstats.src @rm -f eximstats - @sed \ + @. ./version.sh && sed \ -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/eximstats.src > eximstats-t @mv eximstats-t eximstats @chmod a+x eximstats @echo ">>> eximstats script built" -exiqgrep: Makefile ../src/exiqgrep.src +exiqgrep: config ../src/exiqgrep.src @rm -f exiqgrep - @sed \ + @. ./version.sh && sed \ -e "s?PROCESSED_FLAG?This file has been so processed.?"\ -e "/^# /p" \ -e "/^# /d" \ -e "s?BIN_DIRECTORY?$(BIN_DIRECTORY)?" \ -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exiqgrep.src > exiqgrep-t @mv exiqgrep-t exiqgrep @chmod a+x exiqgrep @echo ">>> exiqgrep script built" -exiqsumm: Makefile ../src/exiqsumm.src +exiqsumm: config ../src/exiqsumm.src @rm -f exiqsumm - @sed -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + @. ./version.sh && sed \ + -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exiqsumm.src > exiqsumm-t @mv exiqsumm-t exiqsumm @chmod a+x exiqsumm @echo ">>> exiqsumm script built" -exipick: Makefile ../src/exipick.src +exipick: config ../src/exipick.src @rm -f exipick - @sed -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + @. ./version.sh && sed \ + -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ -e "s?SPOOL_DIRECTORY?$(SPOOL_DIRECTORY)?" \ -e "s?BIN_DIRECTORY?$(BIN_DIRECTORY)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/exipick.src > exipick-t @mv exipick-t exipick @chmod a+x exipick @echo ">>> exipick script built" -transport-filter.pl: Makefile ../src/transport-filter.src +transport-filter.pl: config ../src/transport-filter.src @rm -f transport-filter.pl - @sed -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + @. ./version.sh && sed \ + -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/transport-filter.src > transport-filter.pl-t @mv transport-filter.pl-t transport-filter.pl @chmod a+x transport-filter.pl @echo ">>> transport-filter.pl script built" -convert4r3: Makefile ../src/convert4r3.src +convert4r3: config ../src/convert4r3.src @rm -f convert4r3 - @sed -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + @. ./version.sh && sed \ + -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/convert4r3.src > convert4r3-t @mv convert4r3-t convert4r3 @chmod a+x convert4r3 @echo ">>> convert4r3 script built" -convert4r4: Makefile ../src/convert4r4.src +convert4r4: config ../src/convert4r4.src @rm -f convert4r4 - @sed -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + @. ./version.sh && sed \ + -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/convert4r4.src > convert4r4-t @mv convert4r4-t convert4r4 @chmod a+x convert4r4 @@ -635,7 +666,7 @@ PHDRS = ../config.h \ # Update Exim's version information and build the version object. -version.h:: +version.h version.sh:: @../scripts/reversion cnumber.h: version.h diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c index a2edbfe8a..8228f9467 100644 --- a/src/exim_monitor/em_version.c +++ b/src/exim_monitor/em_version.c @@ -10,6 +10,8 @@ #include <string.h> #include <stdlib.h> +#include "version.h" + extern uschar *version_string; extern uschar *version_date; @@ -21,6 +23,16 @@ uschar today[20]; version_string = US"2.06"; +#ifdef EXIM_BUILD_DATE_OVERRIDE +/* Reproducible build support; build tooling should have given us something looking like + * "25-Feb-2017 20:15:40" in EXIM_BUILD_DATE_OVERRIDE based on $SOURCE_DATE_EPOCH in environ + * per <https://reproducible-builds.org/specs/source-date-epoch/> + */ +version_date = US malloc(32); +version_date[0] = 0; +Ustrncat(version_date, EXIM_BUILD_DATE_OVERRIDE, 31); + +#else Ustrcpy(today, __DATE__); if (today[4] == ' ') i = 1; today[3] = today[6] = '-'; @@ -32,6 +44,7 @@ Ustrncat(version_date, today, 4); Ustrncat(version_date, today+7, 4); Ustrcat(version_date, " "); Ustrcat(version_date, __TIME__); +#endif } /* End of em_version.c */ diff --git a/src/src/acl.c b/src/src/acl.c index 640989997..739cd91ae 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -852,6 +852,26 @@ while ((s = (*func)()) != NULL) compatibility. */ if (c == ACLC_SET) +#ifndef DISABLE_DKIM + if ( Ustrncmp(s, "dkim_verify_status", 18) == 0 + || Ustrncmp(s, "dkim_verify_reason", 18) == 0) + { + uschar * endptr = s+18; + + if (isalnum(*endptr)) + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" " + "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)", + s); + return NULL; + } + cond->u.varname = string_copyn(s, 18); + s = endptr; + while (isspace(*s)) s++; + } + else +#endif { uschar *endptr; @@ -2899,8 +2919,19 @@ for (; cb != NULL; cb = cb->next) if (cb->type == ACLC_SET) { - debug_printf("acl_%s ", cb->u.varname); - lhswidth += 5 + Ustrlen(cb->u.varname); +#ifndef DISABLE_DKIM + if ( Ustrcmp(cb->u.varname, "dkim_verify_status") == 0 + || Ustrcmp(cb->u.varname, "dkim_verify_reason") == 0) + { + debug_printf("%s ", cb->u.varname); + lhswidth += 19; + } + else +#endif + { + debug_printf("acl_%s ", cb->u.varname); + lhswidth += 5 + Ustrlen(cb->u.varname); + } } debug_printf("= %s\n", cb->arg); @@ -3402,7 +3433,7 @@ for (; cb != NULL; cb = cb->next) #ifndef DISABLE_DKIM case ACLC_DKIM_SIGNER: - if (dkim_cur_signer != NULL) + if (dkim_cur_signer) rc = match_isinlist(dkim_cur_signer, &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); else @@ -3410,7 +3441,7 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_DKIM_STATUS: - rc = match_isinlist(dkim_exim_expand_query(DKIM_VERIFY_STATUS), + rc = match_isinlist(dkim_verify_status, &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); break; #endif @@ -3609,12 +3640,22 @@ for (; cb != NULL; cb = cb->next) { int old_pool = store_pool; if ( cb->u.varname[0] == 'c' +#ifndef DISABLE_DKIM + || cb->u.varname[0] == 'd' +#endif #ifndef DISABLE_EVENT || event_name /* An event is being delivered */ #endif ) store_pool = POOL_PERM; - acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); +#ifndef DISABLE_DKIM /* Overwriteable dkim result variables */ + if (Ustrcmp(cb->u.varname, "dkim_verify_status") == 0) + dkim_verify_status = string_copy(arg); + else if (Ustrcmp(cb->u.varname, "dkim_verify_reason") == 0) + dkim_verify_reason = string_copy(arg); + else +#endif + acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); store_pool = old_pool; } break; diff --git a/src/src/configure.default b/src/src/configure.default index a294dc3e6..b828ca20a 100644 --- a/src/src/configure.default +++ b/src/src/configure.default @@ -153,6 +153,9 @@ acl_smtp_data = acl_check_data # tls_certificate = /etc/ssl/exim.crt # tls_privatekey = /etc/ssl/exim.pem +# For OpenSSL, prefer EC- over RSA-authenticated ciphers +# tls_require_ciphers = ECDSA:RSA:!COMPLEMENTOFDEFAILT + # In order to support roaming users who wish to send email from anywhere, # you may want to make Exim listen on other ports as well as port 25, in # case these users need to send email from a network that blocks port 25. diff --git a/src/src/convert4r3.src b/src/src/convert4r3.src index 632eb70d7..d0b94d15e 100755 --- a/src/src/convert4r3.src +++ b/src/src/convert4r3.src @@ -10,6 +10,18 @@ use warnings; BEGIN { pop @INC if $INC[-1] eq '.' }; +use Getopt::Long; +use File::Basename; + +GetOptions( + 'version' => sub { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $^V\n"; + exit 0; + }, +); + ################################################## # Analyse one line # ################################################## diff --git a/src/src/convert4r4.src b/src/src/convert4r4.src index fff4e478b..47987fc8f 100755 --- a/src/src/convert4r4.src +++ b/src/src/convert4r4.src @@ -9,6 +9,18 @@ use warnings; BEGIN { pop @INC if $INC[-1] eq '.' }; +use Getopt::Long; +use File::Basename; + +GetOptions( + 'version' => sub { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $^V\n"; + exit 0; + }, +); + # These are lists of main options which are abolished in Exim 4. # The first contains options that are used to construct new options. diff --git a/src/src/dkim.c b/src/src/dkim.c index d31cae9c7..6bc711ac1 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -134,140 +134,159 @@ store_pool = dkim_verify_oldpool; } -void -dkim_exim_verify_finish(void) +/* Log the result for the given signature */ +static void +dkim_exim_verify_log_sig(pdkim_signature * sig) { -pdkim_signature * sig = NULL; -int rc; -gstring * g = NULL; -const uschar * errstr; - -store_pool = POOL_PERM; - -/* Delete eventual previous signature chain */ - -dkim_signers = NULL; -dkim_signatures = NULL; - -if (dkim_collect_error) - { - log_write(0, LOG_MAIN, - "DKIM: Error during validation, disabling signature verification: %.100s", - dkim_collect_error); - dkim_disable_verify = TRUE; - goto out; - } - -dkim_collect_input = FALSE; - -/* Finish DKIM operation and fetch link to signatures chain */ - -rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr); -if (rc != PDKIM_OK) - { - log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc), - errstr ? ": " : "", errstr ? errstr : US""); - goto out; - } - -for (sig = dkim_signatures; sig; sig = sig->next) - { - uschar * s; - gstring * logmsg; - - /* Log a line for each signature */ - - if (!(s = sig->domain)) s = US"<UNSET>"; - logmsg = string_append(NULL, 2, "d=", s); - if (!(s = sig->selector)) s = US"<UNSET>"; - logmsg = string_append(logmsg, 2, " s=", s); - logmsg = string_append(logmsg, 7, - " c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", - "/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", - " a=", dkim_sig_to_a_tag(sig), - string_sprintf(" b=" SIZE_T_FMT, - (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0)); - if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s); - if (sig->created > 0) logmsg = string_cat(logmsg, - string_sprintf(" t=%lu", sig->created)); - if (sig->expires > 0) logmsg = string_cat(logmsg, - string_sprintf(" x=%lu", sig->expires)); - if (sig->bodylength > -1) logmsg = string_cat(logmsg, - string_sprintf(" l=%lu", sig->bodylength)); - +gstring * logmsg; +uschar * s; + +if (!sig) return; + +logmsg = string_catn(NULL, US"DKIM: ", 6); +if (!(s = sig->domain)) s = US"<UNSET>"; +logmsg = string_append(logmsg, 2, "d=", s); +if (!(s = sig->selector)) s = US"<UNSET>"; +logmsg = string_append(logmsg, 2, " s=", s); +logmsg = string_append(logmsg, 7, +" c=", sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", +"/", sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", +" a=", dkim_sig_to_a_tag(sig), +string_sprintf(" b=" SIZE_T_FMT, + (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0)); +if ((s= sig->identity)) logmsg = string_append(logmsg, 2, " i=", s); +if (sig->created > 0) logmsg = string_cat(logmsg, + string_sprintf(" t=%lu", sig->created)); +if (sig->expires > 0) logmsg = string_cat(logmsg, + string_sprintf(" x=%lu", sig->expires)); +if (sig->bodylength > -1) logmsg = string_cat(logmsg, + string_sprintf(" l=%lu", sig->bodylength)); + +if ( !dkim_verify_status + || ( dkim_verify_status == dkim_exim_expand_query(DKIM_VERIFY_STATUS) + && dkim_verify_reason == dkim_exim_expand_query(DKIM_VERIFY_REASON) + ) ) switch (sig->verify_status) { case PDKIM_VERIFY_NONE: - logmsg = string_cat(logmsg, " [not verified]"); + logmsg = string_cat(logmsg, US" [not verified]"); break; case PDKIM_VERIFY_INVALID: - logmsg = string_cat(logmsg, " [invalid - "); + logmsg = string_cat(logmsg, US" [invalid - "); switch (sig->verify_ext_status) { case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: logmsg = string_cat(logmsg, - "public key record (currently?) unavailable]"); + US"public key record (currently?) unavailable]"); break; case PDKIM_VERIFY_INVALID_BUFFER_SIZE: - logmsg = string_cat(logmsg, "overlong public key record]"); + logmsg = string_cat(logmsg, US"overlong public key record]"); break; case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD: case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: - logmsg = string_cat(logmsg, "syntax error in public key record]"); + logmsg = string_cat(logmsg, US"syntax error in public key record]"); break; - case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR: - logmsg = string_cat(logmsg, "signature tag missing or invalid]"); - break; + case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR: + logmsg = string_cat(logmsg, US"signature tag missing or invalid]"); + break; - case PDKIM_VERIFY_INVALID_DKIM_VERSION: - logmsg = string_cat(logmsg, "unsupported DKIM version]"); - break; + case PDKIM_VERIFY_INVALID_DKIM_VERSION: + logmsg = string_cat(logmsg, US"unsupported DKIM version]"); + break; default: - logmsg = string_cat(logmsg, "unspecified problem]"); + logmsg = string_cat(logmsg, US"unspecified problem]"); } break; case PDKIM_VERIFY_FAIL: - logmsg = - string_cat(logmsg, " [verification failed - "); + logmsg = string_cat(logmsg, US" [verification failed - "); switch (sig->verify_ext_status) { case PDKIM_VERIFY_FAIL_BODY: logmsg = string_cat(logmsg, - "body hash mismatch (body probably modified in transit)]"); + US"body hash mismatch (body probably modified in transit)]"); break; case PDKIM_VERIFY_FAIL_MESSAGE: logmsg = string_cat(logmsg, - "signature did not verify (headers probably modified in transit)]"); + US"signature did not verify " + "(headers probably modified in transit)]"); break; default: - logmsg = string_cat(logmsg, "unspecified reason]"); + logmsg = string_cat(logmsg, US"unspecified reason]"); } break; case PDKIM_VERIFY_PASS: - logmsg = string_cat(logmsg, " [verification succeeded]"); + logmsg = string_cat(logmsg, US" [verification succeeded]"); break; } +else + logmsg = string_append(logmsg, 5, + US" [", dkim_verify_status, US" - ", dkim_verify_reason, US"]"); - log_write(0, LOG_MAIN, "DKIM: %s", string_from_gstring(logmsg)); +log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg)); +return; +} - /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */ - if (sig->domain) - g = string_append_listele(g, ':', sig->domain); +/* Log a line for each signature */ +void +dkim_exim_verify_log_all(void) +{ +pdkim_signature * sig; +for (sig = dkim_signatures; sig; sig = sig->next) dkim_exim_verify_log_sig(sig); +} + + +void +dkim_exim_verify_finish(void) +{ +pdkim_signature * sig; +int rc; +gstring * g = NULL; +const uschar * errstr; - if (sig->identity) - g = string_append_listele(g, ':', sig->identity); +store_pool = POOL_PERM; - /* Process next signature */ +/* Delete eventual previous signature chain */ + +dkim_signers = NULL; +dkim_signatures = NULL; + +if (dkim_collect_error) + { + log_write(0, LOG_MAIN, + "DKIM: Error during validation, disabling signature verification: %.100s", + dkim_collect_error); + dkim_disable_verify = TRUE; + goto out; + } + +dkim_collect_input = FALSE; + +/* Finish DKIM operation and fetch link to signatures chain */ + +rc = pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures, &errstr); +if (rc != PDKIM_OK) + { + log_write(0, LOG_MAIN, "DKIM: validation error: %.100s%s%s", pdkim_errstr(rc), + errstr ? ": " : "", errstr ? errstr : US""); + goto out; + } + +/* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */ + +for (sig = dkim_signatures; sig; sig = sig->next) + { + if (sig->domain) g = string_append_listele(g, ':', sig->domain); + if (sig->identity) g = string_append_listele(g, ':', sig->identity); } if (g) dkim_signers = g->s; @@ -277,40 +296,93 @@ store_pool = dkim_verify_oldpool; } -void -dkim_exim_acl_setup(uschar * id) + +/* Args as per dkim_exim_acl_run() below */ +static int +dkim_acl_call(uschar * id, gstring ** res_ptr, + uschar ** user_msgptr, uschar ** log_msgptr) +{ +int rc; +DEBUG(D_receive) + debug_printf("calling acl_smtp_dkim for dkim_cur_signer='%s'\n", id); + +rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, user_msgptr, log_msgptr); +dkim_exim_verify_log_sig(dkim_cur_sig); +*res_ptr = string_append_listele(*res_ptr, ':', dkim_verify_status); +return rc; +} + + + +/* For the given identity, run the DKIM ACL once for each matching signature. + +Arguments + id Identity to look for in dkim signatures + res_ptr ptr to growable string-list of status results, + appended to per ACL run + user_msgptr where to put a user error (for SMTP response) + log_msgptr where to put a logging message (not for SMTP response) + +Returns: OK access is granted by an ACCEPT verb + DISCARD access is granted by a DISCARD verb + FAIL access is denied + FAIL_DROP access is denied; drop the connection + DEFER can't tell at the moment + ERROR disaster +*/ + +int +dkim_exim_acl_run(uschar * id, gstring ** res_ptr, + uschar ** user_msgptr, uschar ** log_msgptr) { pdkim_signature * sig; uschar * cmp_val; +int rc = -1; -dkim_cur_sig = NULL; +dkim_verify_status = US"none"; +dkim_verify_reason = US""; dkim_cur_signer = id; if (dkim_disable_verify || !id || !dkim_verify_ctx) - return; + return OK; -/* Find signature to run ACL on */ +/* Find signatures to run ACL on */ for (sig = dkim_signatures; sig; sig = sig->next) if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain) && strcmpic(cmp_val, id) == 0 ) { - dkim_cur_sig = sig; - /* The "dkim_domain" and "dkim_selector" expansion variables have - related globals, since they are used in the signing code too. - Instead of inventing separate names for verification, we set - them here. This is easy since a domain and selector is guaranteed - to be in a signature. The other dkim_* expansion items are - dynamically fetched from dkim_cur_sig at expansion time (see - function below). */ + related globals, since they are used in the signing code too. + Instead of inventing separate names for verification, we set + them here. This is easy since a domain and selector is guaranteed + to be in a signature. The other dkim_* expansion items are + dynamically fetched from dkim_cur_sig at expansion time (see + function below). */ + dkim_cur_sig = sig; dkim_signing_domain = US sig->domain; dkim_signing_selector = US sig->selector; dkim_key_length = sig->sighash.len * 8; - return; + + /* These two return static strings, so we can compare the addr + later to see if the ACL overwrote them. Check that when logging */ + + dkim_verify_status = dkim_exim_expand_query(DKIM_VERIFY_STATUS); + dkim_verify_reason = dkim_exim_expand_query(DKIM_VERIFY_REASON); + + if ((rc = dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr)) != OK) + return rc; } + +if (rc != -1) + return rc; + +/* No matching sig found. Call ACL once anyway. */ + +dkim_cur_sig = NULL; +return dkim_acl_call(id, res_ptr, user_msgptr, log_msgptr); } @@ -365,12 +437,12 @@ switch (what) } case DKIM_CANON_HEADERS: - switch (dkim_cur_sig->canon_headers) - { - case PDKIM_CANON_RELAXED: return US"relaxed"; - case PDKIM_CANON_SIMPLE: - default: return US"simple"; - } + switch (dkim_cur_sig->canon_headers) + { + case PDKIM_CANON_RELAXED: return US"relaxed"; + case PDKIM_CANON_SIMPLE: + default: return US"simple"; + } case DKIM_COPIEDHEADERS: return dkim_cur_sig->copiedheaders diff --git a/src/src/dkim.h b/src/src/dkim.h index 735a1162a..c48241ce2 100644 --- a/src/src/dkim.h +++ b/src/src/dkim.h @@ -10,7 +10,8 @@ gstring * dkim_exim_sign(int, off_t, uschar *, struct ob_dkim *, const uschar ** void dkim_exim_verify_init(BOOL); void dkim_exim_verify_feed(uschar *, int); void dkim_exim_verify_finish(void); -void dkim_exim_acl_setup(uschar *); +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 diff --git a/src/src/exicyclog.src b/src/src/exicyclog.src index 4fb160ac0..20bf9fcd4 100644 --- a/src/src/exicyclog.src +++ b/src/src/exicyclog.src @@ -72,6 +72,11 @@ while [ $# -gt 0 ] ; do -k) keep=$2 shift ;; + --version) + echo "`basename $0`: $0" + echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" + exit 0 + ;; *) echo "** exicyclog: unknown option $1" exit 1 ;; diff --git a/src/src/exigrep.src b/src/src/exigrep.src index bdeffae82..5db01fe08 100644 --- a/src/src/exigrep.src +++ b/src/src/exigrep.src @@ -6,6 +6,7 @@ BEGIN { pop @INC if $INC[-1] eq '.' }; use Pod::Usage; use Getopt::Long; +use File::Basename; # Copyright (c) 2007-2017 University of Cambridge. # See the file NOTICE for conditions of use and distribution. @@ -229,6 +230,12 @@ GetOptions( -noperldoc => system('perldoc -V 2>/dev/null >&2') ); }, + 'version' => sub { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; + }, ) and @ARGV or pod2usage; $pattern = shift @ARGV; @@ -237,7 +244,8 @@ $pattern = quotemeta $pattern if $literal; # Start a pager if output goes to a terminal if (-t 1 and $use_pager) { - foreach ($ENV{PAGER}//(), 'less', 'more') + # for perl >= v5.10.x: foreach ($ENV{PAGER}//(), 'less', 'more') + foreach (defined $ENV{PAGER} ? $ENV{PAGER} : (), 'less', 'more') { local $ENV{LESS} .= ' --no-init --quit-if-one-screen'; open(my $pager, '|-', $_) or next; diff --git a/src/src/exim_checkaccess.src b/src/src/exim_checkaccess.src index a780a298a..360f307ba 100755 --- a/src/src/exim_checkaccess.src +++ b/src/src/exim_checkaccess.src @@ -65,8 +65,16 @@ PERL_COMMAND - $exim_path $args <<'End' BEGIN { pop @INC if $INC[-1] eq '.' }; use FileHandle; +use File::Basename; use IPC::Open2; +if ($ARGV[0] eq '--version') { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; +} + if (scalar(@ARGV) < 3) { print "Usage: exim_checkaccess <IP address> <email address> [exim options]\n"; diff --git a/src/src/eximon.src b/src/src/eximon.src index d461ccffa..6293a7cc2 100644 --- a/src/src/eximon.src +++ b/src/src/eximon.src @@ -18,6 +18,13 @@ # X11_LD_LIBRARY # PROCESSED_FLAG +# +if test "x$1" = x--version +then + echo "`basename $0`: $0" + echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" + exit 0 +fi # See if caller wants to invoke gdb diff --git a/src/src/eximstats.src b/src/src/eximstats.src index 80ac93372..76cfe7e97 100644 --- a/src/src/eximstats.src +++ b/src/src/eximstats.src @@ -552,10 +552,18 @@ use integer; BEGIN { pop @INC if $INC[-1] eq '.' }; use strict; use IO::File; +use File::Basename; # use Time::Local; # PH/FANF use POSIX; +if ($ARGV[0] eq '--version') { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; +} + use vars qw($HAVE_GD_Graph_pie $HAVE_GD_Graph_linespoints $HAVE_Spreadsheet_WriteExcel); eval { require GD::Graph::pie; }; $HAVE_GD_Graph_pie = $@ ? 0 : 1; diff --git a/src/src/exinext.src b/src/src/exinext.src index 9c427350b..913801867 100644 --- a/src/src/exinext.src +++ b/src/src/exinext.src @@ -25,6 +25,13 @@ config= eximmacdef= exim_path= +if test "x$1" = x--version +then + echo "`basename $0`: $0" + echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" + exit 0 +fi + if expr -- $1 : '\-' >/dev/null ; then while expr -- $1 : '\-' >/dev/null ; do if [ "$1" = "-C" ]; then diff --git a/src/src/exipick.src b/src/src/exipick.src index a1aa79dc0..b6c28ef23 100644 --- a/src/src/exipick.src +++ b/src/src/exipick.src @@ -17,6 +17,7 @@ my $charset = 'ISO-8859-1'; use strict; BEGIN { pop @INC if $INC[-1] eq '.' }; use Getopt::Long; +use File::Basename; my($p_name) = $0 =~ m|/?([^/]+)$|; my $p_version = "20100323.0"; @@ -83,7 +84,13 @@ GetOptions( 'show-vars=s' => \$G::show_vars, # display the contents of these vars 'just-vars' => \$G::just_vars, # only display vars, no other info 'show-rules' => \$G::show_rules, # display compiled match rules - 'show-tests' => \$G::show_tests # display tests as applied to each message + 'show-tests' => \$G::show_tests, # display tests as applied to each message + 'version' => sub { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; + }, ) || exit(1); # if both freeze and thaw specified, only thaw as it is less destructive diff --git a/src/src/exiqgrep.src b/src/src/exiqgrep.src index d900e9933..c4f7c4b58 100644 --- a/src/src/exiqgrep.src +++ b/src/src/exiqgrep.src @@ -19,7 +19,9 @@ use strict; BEGIN { pop @INC if $INC[-1] eq '.' }; + use Getopt::Std; +use File::Basename; # Have this variable point to your exim binary. my $exim = 'BIN_DIRECTORY/exim'; @@ -44,6 +46,13 @@ if ($^O eq 'darwin') { # aka MacOS X $base = 62; }; +if ($ARGV[0] eq '--version') { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; +} + getopts('hf:r:y:o:s:C:zxlibRca',\%opt); if ($ARGV[0]) { &help; exit;} if ($opt{h}) { &help; exit;} diff --git a/src/src/exiqsumm.src b/src/src/exiqsumm.src index 99a304fef..551ca97ca 100644 --- a/src/src/exiqsumm.src +++ b/src/src/exiqsumm.src @@ -43,6 +43,14 @@ use warnings; BEGIN { pop @INC if $INC[-1] eq '.' }; +use File::Basename; + +if ($ARGV[0] eq '--version') { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; +} sub print_volume_rounded { my($x) = pop @_; diff --git a/src/src/exiwhat.src b/src/src/exiwhat.src index 2542b0198..4fdc09f78 100644 --- a/src/src/exiwhat.src +++ b/src/src/exiwhat.src @@ -52,6 +52,13 @@ signal=EXIWHAT_KILL_SIGNAL # See if this installation is using the esoteric "USE_NODE" feature of Exim, # in which it uses the host's name as a suffix for the configuration file name. +if test "x$1" = x--version +then + echo "`basename $0`: $0" + echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" + exit 0 +fi + if [ "CONFIGURE_FILE_USE_NODE" = "yes" ]; then hostsuffix=.`uname -n` fi diff --git a/src/src/expand.c b/src/src/expand.c index 782467ff7..f44ddf8b8 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -508,8 +508,8 @@ static var_entry var_table[] = { { "dkim_key_testing", vtype_dkim, (void *)DKIM_KEY_TESTING }, { "dkim_selector", vtype_stringptr, &dkim_signing_selector }, { "dkim_signers", vtype_stringptr, &dkim_signers }, - { "dkim_verify_reason", vtype_dkim, (void *)DKIM_VERIFY_REASON }, - { "dkim_verify_status", vtype_dkim, (void *)DKIM_VERIFY_STATUS}, + { "dkim_verify_reason", vtype_stringptr, &dkim_verify_reason }, + { "dkim_verify_status", vtype_stringptr, &dkim_verify_status }, #endif #ifdef EXPERIMENTAL_DMARC { "dmarc_ar_header", vtype_stringptr, &dmarc_ar_header }, diff --git a/src/src/globals.c b/src/src/globals.c index 2a53835e9..5df84bd10 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -668,6 +668,8 @@ uschar *dkim_signers = NULL; uschar *dkim_signing_domain = NULL; uschar *dkim_signing_selector = NULL; uschar *dkim_verify_signers = US"$dkim_signers"; +uschar *dkim_verify_status = NULL; +uschar *dkim_verify_reason = NULL; #endif #ifdef EXPERIMENTAL_DMARC BOOL dmarc_has_been_checked = FALSE; diff --git a/src/src/globals.h b/src/src/globals.h index 62336c275..37d4cada3 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -393,6 +393,8 @@ extern uschar *dkim_signers; /* Expansion variable, holds colon-separa extern uschar *dkim_signing_domain; /* Expansion variable, domain used for signing a message. */ extern uschar *dkim_signing_selector; /* Expansion variable, selector used for signing a message. */ extern uschar *dkim_verify_signers; /* Colon-separated list of domains for each of which we call the DKIM ACL */ +extern uschar *dkim_verify_status; /* result for this signature */ +extern uschar *dkim_verify_reason; /* result for this signature */ #endif #ifdef EXPERIMENTAL_DMARC extern BOOL dmarc_has_been_checked; /* Global variable to check if test has been called yet */ diff --git a/src/src/match.c b/src/src/match.c index c21711410..08ec0eeab 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -461,12 +461,9 @@ HDEBUG(D_any) /* If the list is empty, the answer is no. Skip the debugging output for an unnamed list. */ -if (*listptr == NULL) +if (!*listptr) { - HDEBUG(D_lists) - { - if (ot != NULL) debug_printf("%s no (option unset)\n", ot); - } + HDEBUG(D_lists) if (ot) debug_printf("%s no (option unset)\n", ot); return FAIL; } @@ -485,17 +482,17 @@ else /* If we are searching a domain list, and $domain is not set, set it to the subject that is being sought for the duration of the expansion. */ - if (type == MCL_DOMAIN && deliver_domain == NULL) + if (type == MCL_DOMAIN && !deliver_domain) { check_string_block *cb = (check_string_block *)arg; deliver_domain = string_copy(cb->subject); list = expand_cstring(*listptr); deliver_domain = NULL; } + else + list = expand_cstring(*listptr); - else list = expand_cstring(*listptr); - - if (list == NULL) + if (!list) { if (expand_string_forcedfail) { @@ -511,17 +508,14 @@ else /* For an unnamed list, use the expanded version in comments */ -HDEBUG(D_any) - { - if (ot == NULL) ot = string_sprintf("%s in \"%s\"?", name, list); - } +HDEBUG(D_any) if (ot == NULL) ot = string_sprintf("%s in \"%s\"?", name, list); /* Now scan the list and process each item in turn, until one of them matches, or we hit an error. */ -while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) +while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) { - uschar *ss = sss; + uschar * ss = sss; /* Address lists may contain +caseful, to restore caseful matching of the local part. We have to know the layout of the control block, unfortunately. @@ -534,7 +528,8 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) { check_address_block *cb = (check_address_block *)arg; uschar *at = Ustrrchr(cb->origaddress, '@'); - if (at != NULL) + + if (at) Ustrncpy(cb->address, cb->origaddress, at - cb->origaddress); cb->caseless = FALSE; continue; @@ -594,7 +589,8 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) yield = FAIL; while (isspace((*(++ss)))); } - else yield = OK; + else + yield = OK; /* If the item does not begin with '/', it might be a + item for a named list. Otherwise, it is just a single list entry that has to be matched. @@ -602,7 +598,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) if (*ss != '/') { - if (*ss == '+' && anchorptr != NULL) + if (*ss == '+' && anchorptr) { int bits = 0; int offset = 0; @@ -610,15 +606,18 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) unsigned int *use_cache_bits = original_cache_bits; uschar *cached = US""; namedlist_block *nb; - tree_node *t = tree_search(*anchorptr, ss+1); - - if (t == NULL) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unknown named%s list \"%s\"", - (type == MCL_DOMAIN)? " domain" : - (type == MCL_HOST)? " host" : - (type == MCL_ADDRESS)? " address" : - (type == MCL_LOCALPART)? " local part" : "", + tree_node * t; + + if (!(t = tree_search(*anchorptr, ss+1))) + { + log_write(0, LOG_MAIN|LOG_PANIC, "unknown named%s list \"%s\"", + type == MCL_DOMAIN ? " domain" : + type == MCL_HOST ? " host" : + type == MCL_ADDRESS ? " address" : + type == MCL_LOCALPART ? " local part" : "", ss); + return DEFER; + } nb = t->data.ptr; /* If the list number is negative, it means that this list is not @@ -630,7 +629,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) because the pointer may be NULL from the start if caching is not required. */ - if (use_cache_bits != NULL) + if (use_cache_bits) { offset = (nb->number)/16; shift = ((nb->number)%16)*2; @@ -654,15 +653,13 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) wasn't before. Ensure that this is passed up to the next level. Otherwise, remember the result of the search in the cache. */ - if (use_cache_bits == NULL) - { + if (!use_cache_bits) *cache_ptr = NULL; - } else { use_cache_bits[offset] |= bits << shift; - if (valueptr != NULL) + if (valueptr) { int old_pool = store_pool; namedlist_cacheblock *p; @@ -675,16 +672,14 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) p->key = string_copy(get_check_key(arg, type)); - p->data = (*valueptr == NULL)? NULL : string_copy(*valueptr); + p->data = *valueptr ? string_copy(*valueptr) : NULL; store_pool = old_pool; p->next = nb->cache_data; nb->cache_data = p; - if (*valueptr != NULL) - { + if (*valueptr) DEBUG(D_lists) debug_printf("data from lookup saved for " "cache for %s: %s\n", ss, *valueptr); - } } } } @@ -697,19 +692,18 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) { DEBUG(D_lists) debug_printf("cached %s match for %s\n", ((bits & (-bits)) == bits)? "yes" : "no", ss); + cached = US" - cached"; - if (valueptr != NULL) + if (valueptr) { const uschar *key = get_check_key(arg, type); namedlist_cacheblock *p; - for (p = nb->cache_data; p != NULL; p = p->next) - { + for (p = nb->cache_data; p; p = p->next) if (Ustrcmp(key, p->key) == 0) { *valueptr = p->data; break; } - } DEBUG(D_lists) debug_printf("cached lookup data = %s\n", *valueptr); } } @@ -729,30 +723,30 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) else { - uschar *error = NULL; + uschar * error = NULL; switch ((func)(arg, ss, valueptr, &error)) { case OK: - HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\")\n", ot, - (yield == OK)? "yes" : "no", sss); - return yield; + HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\")\n", ot, + (yield == OK)? "yes" : "no", sss); + return yield; case DEFER: - if (error == NULL) - error = string_sprintf("DNS lookup of \"%s\" deferred", ss); - if (ignore_defer) - { - HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", - error); - break; - } - if (include_defer) - { - log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); - return OK; - } - if (!search_error_message) search_error_message = error; - goto DEFER_RETURN; + if (!error) + error = string_sprintf("DNS lookup of \"%s\" deferred", ss); + if (ignore_defer) + { + HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", + error); + break; + } + if (include_defer) + { + log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); + return OK; + } + if (!search_error_message) search_error_message = error; + goto DEFER_RETURN; /* The ERROR return occurs when checking hosts, when either a forward or reverse lookup has failed. It can also occur in a match_ip list if a @@ -760,24 +754,24 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) which it was. */ case ERROR: - if (ignore_unknown) - { - HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n", - error); - } - else - { - HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot, - include_unknown? "yes":"no", error); - if (!include_unknown) - { - if (LOGGING(unknown_in_list)) - log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); - return FAIL; - } - log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error); - return OK; - } + if (ignore_unknown) + { + HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n", + error); + } + else + { + HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot, + include_unknown? "yes":"no", error); + if (!include_unknown) + { + if (LOGGING(unknown_in_list)) + log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); + return FAIL; + } + log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error); + return OK; + } } } } @@ -788,16 +782,16 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) else { int file_yield = yield; /* In case empty file */ - uschar *filename = ss; - FILE *f = Ufopen(filename, "rb"); + uschar * filename = ss; + FILE * f = Ufopen(filename, "rb"); uschar filebuffer[1024]; /* ot will be null in non-debugging cases, and anyway, we get better wording by reworking it. */ - if (f == NULL) + if (!f) { - uschar *listname = readconf_find_option(listptr); + uschar * listname = readconf_find_option(listptr); if (listname[0] == 0) listname = string_sprintf("\"%s\"", *listptr); log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", @@ -845,48 +839,48 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) switch ((func)(arg, ss, valueptr, &error)) { case OK: - (void)fclose(f); - HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\" in %s)\n", ot, - (yield == OK)? "yes" : "no", sss, filename); - return file_yield; + (void)fclose(f); + HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\" in %s)\n", ot, + yield == OK ? "yes" : "no", sss, filename); + return file_yield; case DEFER: - if (error == NULL) - error = string_sprintf("DNS lookup of %s deferred", ss); - if (ignore_defer) - { - HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", - error); - break; - } - (void)fclose(f); - if (include_defer) - { - log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); - return OK; - } - goto DEFER_RETURN; - - case ERROR: /* host name lookup failed - this can only */ - if (ignore_unknown) /* be for an incoming host (not outgoing) */ - { - HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n", - error); - } - else - { - HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot, - include_unknown? "yes":"no", error); - (void)fclose(f); - if (!include_unknown) - { - if (LOGGING(unknown_in_list)) - log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); - return FAIL; - } - log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error); - return OK; - } + if (!error) + error = string_sprintf("DNS lookup of %s deferred", ss); + if (ignore_defer) + { + HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", + error); + break; + } + (void)fclose(f); + if (include_defer) + { + log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); + return OK; + } + goto DEFER_RETURN; + + case ERROR: /* host name lookup failed - this can only */ + if (ignore_unknown) /* be for an incoming host (not outgoing) */ + { + HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n", + error); + } + else + { + HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot, + include_unknown? "yes":"no", error); + (void)fclose(f); + if (!include_unknown) + { + if (LOGGING(unknown_in_list)) + log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); + return FAIL; + } + log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error); + return OK; + } } } @@ -901,8 +895,8 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) /* End of list reached: if the last item was negated yield OK, else FAIL. */ HDEBUG(D_lists) - debug_printf("%s %s (end of list)\n", ot, (yield == OK)? "no":"yes"); -return (yield == OK)? FAIL : OK; + debug_printf("%s %s (end of list)\n", ot, yield == OK ? "no":"yes"); +return yield == OK ? FAIL : OK; /* Something deferred */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 1420b1a79..138861815 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -250,16 +250,19 @@ pdkim_free_ctx(pdkim_ctx *ctx) /* -------------------------------------------------------------------------- */ /* Matches the name of the passed raw "header" against the passed colon-separated "tick", and invalidates - the entry in tick. Returns OK or fail-code */ -/*XXX might be safer done using a pdkim_stringlist for "tick" */ + the entry in tick. Entries can be prefixed for multi- or over-signing, + in which case do not invalidate. + + Returns OK for a match, or fail-code +*/ static int header_name_match(const uschar * header, uschar * tick) { -uschar * hname; -uschar * lcopy; -uschar * p; -uschar * q; +const uschar * ticklist = tick; +int sep = ':'; +BOOL multisign; +uschar * hname, * p, * ele; uschar * hcolon = Ustrchr(header, ':'); /* Get header name */ if (!hcolon) @@ -268,27 +271,22 @@ if (!hcolon) /* if we had strncmpic() we wouldn't need this copy */ hname = string_copyn(header, hcolon-header); -/* Copy tick-off list locally, so we can punch zeroes into it */ -p = lcopy = string_copy(tick); - -for (q = Ustrchr(p, ':'); q; q = Ustrchr(p, ':')) +while (p = US ticklist, ele = string_nextinlist(&ticklist, &sep, NULL, 0)) { - *q = '\0'; - if (strcmpic(p, hname) == 0) - goto found; - - p = q+1; + switch (*ele) + { + case '=': case '+': multisign = TRUE; ele++; break; + default: multisign = FALSE; break; } -if (strcmpic(p, hname) == 0) - goto found; - + if (strcmpic(ele, hname) == 0) + { + if (!multisign) + *p = '_'; /* Invalidate this header name instance in tick-off list */ + return PDKIM_OK; + } + } return PDKIM_FAIL; - -found: - /* Invalidate header name instance in tick-off list */ - tick[p-lcopy] = '_'; - return PDKIM_OK; } @@ -1445,11 +1443,17 @@ for (sig = ctx->sig; sig; sig = sig->next) } } - /* Any headers we wanted to sign but were not present must also be listed */ + /* Any headers we wanted to sign but were not present must also be listed. + Ignore elements that have been ticked-off or are marked as never-oversign. */ + l = sig->sign_headers; while((s = string_nextinlist(&l, &sep, NULL, 0))) - if (*s != '_') + { + if (*s == '+') /* skip oversigning marker */ + s++; + if (*s != '_' && *s != '=') g = string_append_listele(g, ':', s); + } sig->headernames = string_from_gstring(g); /* Create signature header with b= omitted */ diff --git a/src/src/receive.c b/src/src/receive.c index 31402925d..e7e518a92 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3388,99 +3388,96 @@ else #ifndef DISABLE_DKIM if (!dkim_disable_verify) { - /* Finish verification, this will log individual signature results to - the mainlog */ + /* Finish verification */ dkim_exim_verify_finish(); /* Check if we must run the DKIM ACL */ if (acl_smtp_dkim && dkim_verify_signers && *dkim_verify_signers) { - uschar *dkim_verify_signers_expanded = + uschar * dkim_verify_signers_expanded = expand_string(dkim_verify_signers); - if (!dkim_verify_signers_expanded) + gstring * results = NULL; + int signer_sep = 0; + const uschar * ptr; + uschar * item; + gstring * seen_items = NULL; + int old_pool = store_pool; + + 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); - else - { - int sep = 0; - const uschar *ptr = dkim_verify_signers_expanded; - uschar *item = NULL; - gstring * seen_items = NULL; - - /* Default to OK when no items are present */ - rc = OK; - while ((item = string_nextinlist(&ptr, &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); - BOOL seen_this_item = FALSE; - - while ((seen_item = string_nextinlist(&seen_items_list, &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_cat(seen_items, ":"); - } - - seen_items = string_cat(seen_items, item); - - DEBUG(D_receive) - debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", - item); - - dkim_exim_acl_setup(item); - rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, - &user_msg, &log_msg); - - if (rc != OK) + /* Default to OK when no items are present */ + rc = OK; + while ((item = string_nextinlist(&ptr, &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: acl_check returned %d on %s, " - "skipping remaining items\n", rc, item); - cancel_cutthrough_connection(TRUE, US"dkim acl not ok"); - break; + debug_printf("acl_smtp_dkim: skipping signer %s, " + "already seen\n", item); + continue; } - } - add_acl_headers(ACL_WHERE_DKIM, US"DKIM"); - if (rc == DISCARD) - { - recipients_count = 0; - blackholed_by = US"DKIM ACL"; - if (log_msg != NULL) - 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 */ - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ - } - } + + seen_items = string_catn(seen_items, ":", 1); + } + seen_items = string_cat(seen_items, item); + + 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; + } + } + 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 */ + message_id[0] = 0; /* Indicate no message accepted */ + goto TIDYUP; /* Skip to end of function */ + } } + else + dkim_exim_verify_log_all(); } #endif /* DISABLE_DKIM */ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 43094f30d..898e37cd6 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -804,6 +804,18 @@ err: +static int +tls_add_certfile(exim_gnutls_state_st * state, const host_item * host, + uschar * certfile, uschar * keyfile, uschar ** errstr) +{ +int rc = gnutls_certificate_set_x509_key_file(state->x509_cred, + CS certfile, CS keyfile, GNUTLS_X509_FMT_PEM); +exim_gnutls_err_check( + string_sprintf("cert/key setup: cert=%s key=%s", certfile, keyfile)); +return OK; +} + + /************************************************* * Variables re-expanded post-SNI * *************************************************/ @@ -824,7 +836,7 @@ Returns: OK/DEFER/FAIL */ static int -tls_expand_session_files(exim_gnutls_state_st *state, uschar ** errstr) +tls_expand_session_files(exim_gnutls_state_st * state, uschar ** errstr) { struct stat statbuf; int rc; @@ -839,11 +851,11 @@ int cert_count; if (!host) /* server */ if (!state->received_sni) { - if (state->tls_certificate && - (Ustrstr(state->tls_certificate, US"tls_sni") || - Ustrstr(state->tls_certificate, US"tls_in_sni") || - Ustrstr(state->tls_certificate, US"tls_out_sni") - )) + if ( state->tls_certificate + && ( Ustrstr(state->tls_certificate, US"tls_sni") + || Ustrstr(state->tls_certificate, US"tls_in_sni") + || Ustrstr(state->tls_certificate, US"tls_out_sni") + ) ) { DEBUG(D_tls) debug_printf("We will re-expand TLS session files if we receive SNI.\n"); state->trigger_sni_changes = TRUE; @@ -910,13 +922,29 @@ if (state->exp_tls_certificate && *state->exp_tls_certificate) DEBUG(D_tls) debug_printf("TLS SNI: have a changed cert/key pair.\n"); } - rc = gnutls_certificate_set_x509_key_file(state->x509_cred, - CS state->exp_tls_certificate, CS state->exp_tls_privatekey, - GNUTLS_X509_FMT_PEM); - exim_gnutls_err_check( - string_sprintf("cert/key setup: cert=%s key=%s", - state->exp_tls_certificate, state->exp_tls_privatekey)); - DEBUG(D_tls) debug_printf("TLS: cert/key registered\n"); + if (!host) /* server */ + { + const uschar * clist = state->exp_tls_certificate; + const uschar * klist = state->exp_tls_privatekey; + int csep = 0, ksep = 0; + uschar * cfile, * kfile; + + while (cfile = string_nextinlist(&clist, &csep, NULL, 0)) + if (!(kfile = string_nextinlist(&klist, &ksep, NULL, 0))) + return tls_error(US"cert/key setup: out of keys", NULL, host, errstr); + else if ((rc = tls_add_certfile(state, host, cfile, kfile, errstr))) + return rc; + else + DEBUG(D_tls) debug_printf("TLS: cert/key %s registered\n", cfile); + } + else + { + if ((rc = tls_add_certfile(state, host, + state->exp_tls_certificate, state->exp_tls_privatekey, errstr))) + return rc; + DEBUG(D_tls) debug_printf("TLS: cert/key registered\n"); + } + } /* tls_certificate */ @@ -1276,7 +1304,7 @@ if (host) } else if (state->tls_sni) DEBUG(D_tls) debug_printf("*** PROBABLY A BUG *** " \ - "have an SNI set for a client [%s]\n", state->tls_sni); + "have an SNI set for a server [%s]\n", state->tls_sni); /* This is the priority string support, http://www.gnutls.org/manual/html_node/Priority-Strings.html diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 58401e932..f1176a63e 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1024,6 +1024,30 @@ err: +static int +tls_add_certfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file, + uschar ** errstr) +{ +DEBUG(D_tls) debug_printf("tls_certificate file %s\n", file); +if (!SSL_CTX_use_certificate_chain_file(sctx, CS file)) + return tls_error(string_sprintf( + "SSL_CTX_use_certificate_chain_file file=%s", file), + cbinfo->host, NULL, errstr); +return 0; +} + +static int +tls_add_pkeyfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file, + uschar ** errstr) +{ +DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", file); +if (!SSL_CTX_use_PrivateKey_file(sctx, CS file, SSL_FILETYPE_PEM)) + return tls_error(string_sprintf( + "SSL_CTX_use_PrivateKey_file file=%s", file), cbinfo->host, NULL, errstr); +return 0; +} + + /************************************************* * Expand key and cert file specs * *************************************************/ @@ -1048,7 +1072,7 @@ uschar *expanded; if (!cbinfo->certificate) { - if (cbinfo->host) /* client */ + if (!cbinfo->is_server) /* client */ return OK; /* server */ if (tls_install_selfsign(sctx, errstr) != OK) @@ -1056,6 +1080,8 @@ if (!cbinfo->certificate) } else { + int err; + if (Ustrstr(cbinfo->certificate, US"tls_sni") || Ustrstr(cbinfo->certificate, US"tls_in_sni") || Ustrstr(cbinfo->certificate, US"tls_out_sni") @@ -1065,14 +1091,20 @@ else if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr)) return DEFER; - if (expanded != NULL) - { - DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded); - if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded)) - return tls_error(string_sprintf( - "SSL_CTX_use_certificate_chain_file file=%s", expanded), - cbinfo->host, NULL, errstr); - } + if (expanded) + if (cbinfo->is_server) + { + const uschar * file_list = expanded; + int sep = 0; + uschar * file; + + while (file = string_nextinlist(&file_list, &sep, NULL, 0)) + if ((err = tls_add_certfile(sctx, cbinfo, file, errstr))) + return err; + } + else /* would there ever be a need for multiple client certs? */ + if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr))) + return err; if (cbinfo->privatekey != NULL && !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr)) @@ -1083,12 +1115,19 @@ else key is in the same file as the certificate. */ if (expanded && *expanded) - { - DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); - if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM)) - return tls_error(string_sprintf( - "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL, errstr); - } + if (cbinfo->is_server) + { + const uschar * file_list = expanded; + int sep = 0; + uschar * file; + + while (file = string_nextinlist(&file_list, &sep, NULL, 0)) + if ((err = tls_add_pkeyfile(sctx, cbinfo, file, errstr))) + return err; + } + else /* would there ever be a need for multiple client certs? */ + if ((err = tls_add_pkeyfile(sctx, cbinfo, expanded, errstr))) + return err; } #ifndef DISABLE_OCSP diff --git a/src/src/transport-filter.src b/src/src/transport-filter.src index 3f250e657..db00d877f 100644 --- a/src/src/transport-filter.src +++ b/src/src/transport-filter.src @@ -13,6 +13,14 @@ use warnings; BEGIN { pop @INC if $INC[-1] eq '.' }; +use File::Basename; + +if ($ARGV[0] eq '--version') { + print basename($0) . ": $0\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; +} # If the filter is called with any arguments, insert them into the message # as X-Arg headers, just to verify what they are. |