diff options
Diffstat (limited to 'debian/patches')
47 files changed, 4213 insertions, 0 deletions
diff --git a/debian/patches/31_eximmanpage.dpatch b/debian/patches/31_eximmanpage.dpatch new file mode 100755 index 0000000..cfbfc26 --- /dev/null +++ b/debian/patches/31_eximmanpage.dpatch @@ -0,0 +1,250 @@ +Description: We ship the binary as exim4 instead of exim, fix manpage + accordingly. +Author: Marc Haber <mh+debian-packages@zugschlus.de>, + Andreas Metzler <ametzler@bebt.de> +Last-Update: 2019-10-17 +Forwarded: not-needed (upstream uses the "exim" name) + +--- a/doc/exim.8 ++++ b/doc/exim.8 +@@ -1,9 +1,9 @@ +-.TH EXIM 8 ++.TH EXIM4 8 + .SH NAME +-exim \- a Mail Transfer Agent ++exim4 \- a Mail Transfer Agent + .SH SYNOPSIS + .nf +-.B exim [options] arguments ... ++.B exim4 [options] arguments ... + .B mailq [options] arguments ... + .B rsmtp [options] arguments ... + .B rmail [options] arguments ... +@@ -40,7 +40,7 @@ local message on the standard input, wit + recipients) is assumed. Thus, for example, if Exim is installed in + \fI/usr/sbin\fP, you can send a message from the command line like this: + .sp +- /usr/sbin/exim -i <recipient-address(es)> ++ /usr/sbin/exim4 -i <recipient-address(es)> + <message content, including all the header lines> + CTRL-D + .sp +@@ -125,8 +125,8 @@ ports, on multiple ports, and only on sp + .sp + When a listening daemon + is started without the use of \fB\-oX\fP (that is, without overriding the normal +-configuration), it writes its process id to a file called exim\-daemon.pid +-in Exim's spool directory. This location can be overridden by setting ++configuration), it writes its process id to a file called ++/run/exim4/exim.pid. This location can be overridden by setting + PID_FILE_PATH in Local/Makefile. The file is written while Exim is still + running as root. + .sp +@@ -180,7 +180,7 @@ available to admin users. + This option operates like \fB\-be\fP except that it must be followed by the name + of a file. For example: + .sp +- exim \-bem /tmp/testmessage ++ exim4 \-bem /tmp/testmessage + .sp + The file is read as a message (as if receiving a locally\-submitted non\-SMTP + message) before any of the test expansions are done. Thus, message\-specific +@@ -206,7 +206,7 @@ If you want to test a system filter file + can use both \fB\-bF\fP and \fB\-bf\fP on the same command, in order to test a system + filter and a user filter in the same run. For example: + .sp +- exim \-bF /system/filter \-bf /user/filter </test/message ++ exim4 \-bF /system/filter \-bf /user/filter </test/message + .sp + This is helpful when the system filter adds header lines or sets filter + variables that are used by the user filter. +@@ -258,8 +258,8 @@ This option runs a fake SMTP session as + standard input and output. The IP address may include a port number at the end, + after a full stop. For example: + .sp +- exim \-bh 10.9.8.7.1234 +- exim \-bh fe80::a00:20ff:fe86:a061.5678 ++ exim4 \-bh 10.9.8.7.1234 ++ exim4 \-bh fe80::a00:20ff:fe86:a061.5678 + .sp + When an IPv6 address is given, it is converted into canonical form. In the case + of the second example above, the value of \fI$sender_host_address\fP after +@@ -417,7 +417,7 @@ main configuration options to be written + of one or more specific options can be requested by giving their names as + arguments, for example: + .sp +- exim \-bP qualify_domain hold_domains ++ exim4 \-bP qualify_domain hold_domains + .sp + However, any option setting that is preceded by the word "hide" in the + configuration file is not shown in full, except to an admin user. For other +@@ -445,7 +445,7 @@ written directly into the spool director + .sp + If \fB\-bP\fP is followed by a name preceded by +, for example, + .sp +- exim \-bP +local_domains ++ exim4 \-bP +local_domains + .sp + it searches for a matching named list of any type (domain, host, address, or + local part) and outputs what it finds. +@@ -454,7 +454,7 @@ If one of the words \fBrouter\fP, \fBtra + followed by the name of an appropriate driver instance, the option settings for + that driver are output. For example: + .sp +- exim \-bP transport local_delivery ++ exim4 \-bP transport local_delivery + .sp + The generic driver options are output first, followed by the driver's private + options. A list of the names of drivers of a particular type can be obtained by +@@ -539,7 +539,7 @@ This option is for testing retry rules, + arguments. It causes Exim to look for a retry rule that matches the values + and to write it to the standard output. For example: + .sp +- exim \-brt bach.comp.mus.example ++ exim4 \-brt bach.comp.mus.example + Retry rule: *.comp.mus.example F,2h,15m; F,4d,30m; + .sp + The first +@@ -552,7 +552,7 @@ rule is found that matches the host, one + sought. Finally, an argument that is the name of a specific delivery error, as + used in setting up retry rules, can be given. For example: + .sp +- exim \-brt haydn.comp.mus.example quota_3d ++ exim4 \-brt haydn.comp.mus.example quota_3d + Retry rule: *@haydn.comp.mus.example quota_3d F,1h,15m + .TP 10 + \fB\-brw\fP +@@ -655,7 +655,7 @@ doing such tests. + .TP 10 + \fB\-bV\fP + This option causes Exim to write the current version number, compilation +-number, and compilation date of the \fIexim\fP binary to the standard output. ++number, and compilation date of the \fIexim4\fP binary to the standard output. + It also lists the DBM library that is being used, the optional modules (such as + specific lookup types), the drivers that are included in the binary, and the + name of the runtime configuration file that is in use. +@@ -683,7 +683,7 @@ If no arguments are given, Exim runs in + right angle bracket for addresses to be verified. + .sp + Unlike the \fB\-be\fP test option, you cannot arrange for Exim to use the +-readline() function, because it is running as \fIexim\fP and there are ++readline() function, because it is running as \fIexim4\fP and there are + security issues. + .sp + Verification differs from address testing (the \fB\-bt\fP option) in that routers +@@ -796,14 +796,14 @@ command line item. \fB\-D\fP can be used + string, in which case the equals sign is optional. These two commands are + synonymous: + .sp +- exim \-DABC ... +- exim \-DABC= ... ++ exim4 \-DABC ... ++ exim4 \-DABC= ... + .sp + To include spaces in a macro definition item, quotes must be used. If you use + quotes, spaces are permitted around the macro name and the equals sign. For + example: + .sp +- exim '\-D ABC = something' ... ++ exim4 '\-D ABC = something' ... + .sp + \fB\-D\fP may be repeated up to 10 times on a command line. + Only macro names up to 22 letters long can be set. +@@ -938,8 +938,8 @@ never provoke a bounce. An empty sender + string, or as a pair of angle brackets with nothing between them, as in these + examples of shell commands: + .sp +- exim \-f '<>' user@domain +- exim \-f "" user@domain ++ exim4 \-f '<>' user@domain ++ exim4 \-f "" user@domain + .sp + In addition, the use of \fB\-f\fP is not restricted when testing a filter file + with \fB\-bf\fP or when testing or verifying addresses using the \fB\-bt\fP or +@@ -1315,12 +1315,12 @@ other circumstances, they are ignored un + The \fB\-oMa\fP option sets the sender host address. This may include a port + number at the end, after a full stop (period). For example: + .sp +- exim \-bs \-oMa 10.9.8.7.1234 ++ exim4 \-bs \-oMa 10.9.8.7.1234 + .sp + An alternative syntax is to enclose the IP address in square brackets, + followed by a colon and the port number: + .sp +- exim \-bs \-oMa [10.9.8.7]:1234 ++ exim4 \-bs \-oMa [10.9.8.7]:1234 + .sp + The IP address is placed in the \fI$sender_host_address\fP variable, and the + port, if present, in \fI$sender_host_port\fP. If both \fB\-oMa\fP and \fB\-bh\fP +@@ -1526,22 +1526,22 @@ If other commandline options specify an + will specify a queue to operate on. + For example: + .sp +- exim \-bp \-qGquarantine ++ exim4 \-bp \-qGquarantine + mailq \-qGquarantine +- exim \-qGoffpeak \-Rf @special.domain.example ++ exim4 \-qGoffpeak \-Rf @special.domain.example + .TP 10 + \fB\-q\fP<\fIqflags\fP> <\fIstart id\fP> <\fIend id\fP> + When scanning the queue, Exim can be made to skip over messages whose ids are + lexically less than a given value by following the \fB\-q\fP option with a + starting message id. For example: + .sp +- exim \-q 0t5C6f\-0000c8\-00 ++ exim4 \-q 0t5C6f\-0000c8\-00 + .sp + Messages that arrived earlier than 0t5C6f\-0000c8\-00 are not inspected. If a + second message id is given, messages whose ids are lexically greater than it + are also skipped. If the same id is given twice, for example, + .sp +- exim \-q 0t5C6f\-0000c8\-00 0t5C6f\-0000c8\-00 ++ exim4 \-q 0t5C6f\-0000c8\-00 0t5C6f\-0000c8\-00 + .sp + just one delivery process is started, for that message. This differs from + \fB\-M\fP in that retry data is respected, and it also differs from \fB\-Mc\fP in +@@ -1557,7 +1557,7 @@ starting a queue runner process at inter + single daemon process handles both functions. A common way of starting up a + combined daemon at system boot time is to use a command such as + .sp +- /usr/exim/bin/exim \-bd \-q30m ++ /usr/sbin/exim4 \-bd \-q30m + .sp + Such a daemon listens for incoming SMTP calls, and also starts a queue runner + process every 30 minutes. +@@ -1588,7 +1588,7 @@ regular expression; otherwise it is a li + If you want to do periodic queue runs for messages with specific recipients, + you can combine \fB\-R\fP with \fB\-q\fP and a time value. For example: + .sp +- exim \-q25m \-R @special.domain.example ++ exim4 \-q25m \-R @special.domain.example + .sp + This example does a queue run for messages with recipients in the given domain + every 25 minutes. Any additional flags that are specified with \fB\-q\fP are +@@ -1704,6 +1704,26 @@ under most shells. + .sp + . + .SH "SEE ALSO" ++.BR exicyclog (8), ++.BR exigrep (8), ++.BR exim_checkaccess (8), ++.BR exim_convert4r4 (8), ++.BR exim_db (8), ++.BR exim_dbmbuild (8), ++.BR exim_lock (8), ++.BR eximon (8), ++.BR exinext (8), ++.BR exiqgrep (8), ++.BR exiqsumm (8), ++.BR exiwhat (8), ++.BR update\-exim4.conf (8), ++.BR update\-exim4defaults (8), ++/usr/share/doc/exim4\-base/, ++/usr/share/doc/exim4\-base/README.Debian.[gz|html]. + .rs + .sp + The full Exim specification, the Exim book, and the Exim wiki. ++ ++.SH AUTHOR ++This manual page was provided with the upstream Exim source package. ++It was enhanced for the Debian GNU/Linux system. diff --git a/debian/patches/32_exim4.dpatch b/debian/patches/32_exim4.dpatch new file mode 100644 index 0000000..967869d --- /dev/null +++ b/debian/patches/32_exim4.dpatch @@ -0,0 +1,106 @@ +Description: Accommodate source for installing exim as exim4. +Author: Andreas Metzler <ametzler@debian.org> +Origin: vendor +Forwarded: not-needed +Last-Update: 2018-12-12 + +--- a/OS/Makefile-Linux ++++ b/OS/Makefile-Linux +@@ -28,9 +28,9 @@ XLFLAGS=-L$(X11)/lib + X11_LD_LIB=$(X11)/lib + + EXIWHAT_PS_ARG=ax +-EXIWHAT_EGREP_ARG='/exim( |$$)' ++EXIWHAT_EGREP_ARG='/exim4( |$$)' + EXIWHAT_MULTIKILL_CMD=killall +-EXIWHAT_MULTIKILL_ARG=exim ++EXIWHAT_MULTIKILL_ARG=exim4 + EXIWHAT_KILL_SIGNAL=-USR1 + + # End +--- a/src/exicyclog.src ++++ b/src/exicyclog.src +@@ -149,7 +149,7 @@ done + + st=' ' + exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` +-if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim; fi ++if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi + + spool_directory=`$exim_path -C $config -bP spool_directory | sed 's/.*=[ ]*//'` + +--- a/src/exim_checkaccess.src ++++ b/src/exim_checkaccess.src +@@ -52,7 +52,7 @@ done + # a tab to keep the tab in one place. + + exim_path=`perl -ne 'chop;if (/^\s*exim_path\s*=\s*(.*)/){print "$1\n";last;}' $config` +-if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim; fi ++if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi + + + ######################################################################### +--- a/src/eximon.src ++++ b/src/eximon.src +@@ -79,7 +79,7 @@ config=${EXIMON_EXIM_CONFIG-$config} + + st=' ' + EXIM_PATH=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` +-if test "$EXIM_PATH" = ""; then EXIM_PATH=BIN_DIRECTORY/exim; fi ++if test "$EXIM_PATH" = ""; then EXIM_PATH=BIN_DIRECTORY/exim4; fi + + SPOOL_DIRECTORY=`$EXIM_PATH -C $config -bP spool_directory | sed 's/.*=[ ]*//'` + LOG_FILE_PATH=`$EXIM_PATH -C $config -bP log_file_path | sed 's/.*=[ ]*//'` +--- a/src/exinext.src ++++ b/src/exinext.src +@@ -97,7 +97,7 @@ if [ "$exim_path" = "" ]; then + exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` + fi + +-if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim; fi ++if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi + spool_directory=`$exim_path $eximmacdef -C $config -bP spool_directory | sed 's/.*=[ ]*//'` + qualify_domain=`$exim_path $eximmacdef -C $config -bP qualify_domain | sed 's/.*=[ ]*//'` + +@@ -181,7 +181,7 @@ perl - $exim_path "$eximmacdef" $argone + + # Run exim_dumpdb to get out the retry data and pick off what we want + +- open(DATA, "${exim}_dumpdb $spool retry |") || ++ open(DATA, "/usr/sbin/exim_dumpdb $spool retry |") || + die "can't run exim_dumpdb"; + + while (<DATA>) +--- a/src/exiqgrep.src ++++ b/src/exiqgrep.src +@@ -24,7 +24,7 @@ use Getopt::Std; + use File::Basename; + + # Have this variable point to your exim binary. +-my $exim = 'BIN_DIRECTORY/exim'; ++my $exim = 'BIN_DIRECTORY/exim4'; + my $eargs = '-bpu'; + my %id; + my %opt; +--- a/src/exiwhat.src ++++ b/src/exiwhat.src +@@ -98,7 +98,7 @@ fi + + st=' ' + exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` +-if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim; fi ++if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi + spool_directory=`$exim_path -C $config -bP spool_directory | sed "s/.*=[ ]*//"` + process_log_path=`$exim_path -C $config -bP process_log_path | sed "s/.*=[ ]*//"` + +--- a/src/globals.c ++++ b/src/globals.c +@@ -906,7 +906,7 @@ const uschar *event_name = NULL; + + + gid_t exim_gid = EXIM_GID; +-uschar *exim_path = US BIN_DIRECTORY "/exim" ++uschar *exim_path = US BIN_DIRECTORY "/exim4" + "\0<---------------Space to patch exim_path->"; + uid_t exim_uid = EXIM_UID; + int expand_level = 0; /* Nesting depth, indent for debug */ diff --git a/debian/patches/33_eximon.binary.dpatch b/debian/patches/33_eximon.binary.dpatch new file mode 100755 index 0000000..e0f5593 --- /dev/null +++ b/debian/patches/33_eximon.binary.dpatch @@ -0,0 +1,17 @@ +Description: We move eximon.bin out of $PATH to /usr/libexec/. Let it be + found there. +Author: Andreas Piesk +Last-Update: 2020-03-21 +Forwarded: not-needed + +--- a/OS/eximon.conf-Default ++++ b/OS/eximon.conf-Default +@@ -5,7 +5,7 @@ + # The name of the eximon binary, usually the same as the eximon script, + # with .bin stuck on the end. + +-EXIMON_BINARY="${EXIMON_BINARY-$0.bin}" ++EXIMON_BINARY="/usr/libexec/exim4/${EXIMON_BINARY-${0##*/}.bin}" + + # The remaining parameters are values likely to be changed to suit the + # user's taste. They are documented in the EDITME file. diff --git a/debian/patches/34_eximstatsmanpage.dpatch b/debian/patches/34_eximstatsmanpage.dpatch new file mode 100755 index 0000000..3245965 --- /dev/null +++ b/debian/patches/34_eximstatsmanpage.dpatch @@ -0,0 +1,20 @@ +Description: Add note about installing perl-modules on Debian to + generated manpage +Author: Andreas Metzler <ametzler@debian.org> +Origin: vendor +Forwarded: not-needed +Last-Update: 2013-09-28 + +--- exim4-4.82~rc1.orig/src/eximstats.src ++++ exim4-4.82~rc1/src/eximstats.src +@@ -501,6 +501,10 @@ To install these, download and unpack th + make test + make install + ++On B<Debian GNU/Linux> you can use ++C<apt-get install libgd-perl libgd-text-perl libgd-graph-perl> ++instead. ++ + =item B<-chartdir>I <dir> + + Create the charts in the directory <dir> diff --git a/debian/patches/35_install.dpatch b/debian/patches/35_install.dpatch new file mode 100755 index 0000000..d0ab602 --- /dev/null +++ b/debian/patches/35_install.dpatch @@ -0,0 +1,49 @@ +Description: Exim's installation scripts install the binary as + exim-<version> - disable this feature. +Author: Andreas Metzler <ametzler@debian.org> +Origin: vendor +Forwarded: not-needed +Last-Update: 2016-09-25 + +--- a/scripts/exim_install ++++ b/scripts/exim_install +@@ -221,6 +221,8 @@ while [ $# -gt 0 ]; do + version=exim-`$exim 2>/dev/null | \ + awk '/Exim version/ { OFS=""; print $3,"-",substr($4,2,length($4)-1) }'`${EXE} + ++ version=exim ++ + if [ "${version}" = "exim-${EXE}" ]; then + echo $com "" + echo $com "*** Could not run $exim to find version number ***" +@@ -370,10 +372,8 @@ done + + + +-# If there is no configuration file, install the default, modifying it to refer +-# to the configured system aliases file. If there is no setting for +-# SYSTEM_ALIASES_FILE, use the traditional /etc/aliases. If the file does not +-# exist, install a default (dummy) for that too. ++# Install default configuration file ++# This is a local Debian modification. + + # However, if CONFIGURE_FILE specifies a list of files, skip this code. + +@@ -396,7 +396,7 @@ elif [ ! -f ${CONFIGURE_FILE} ]; then + ${real} ${MKDIR} -p `${DIRNAME} ${CONFIGURE_FILE}` + + echo sed -e '\\' +- echo " \"/SYSTEM_ALIASES_FILE/ s'SYSTEM_ALIASES_FILE'${ACTUAL_SYSTEM_ALIASES_FILE}'\"" '\\' ++ echo " \"/SYSTEM_ALIASES_FILE/ s'SYSTEM_ALIASES_FILE'/etc/aliases'\"" '\\' + echo " ../src/configure.default > \${CONFIGURE_FILE}" + + # I can't find a way of writing this using the ${real} feature because +@@ -405,7 +405,7 @@ elif [ ! -f ${CONFIGURE_FILE} ]; then + + if [ "$real" = "" ] ; then + sed -e \ +- "/SYSTEM_ALIASES_FILE/ s'SYSTEM_ALIASES_FILE'${ACTUAL_SYSTEM_ALIASES_FILE}'" \ ++ "/SYSTEM_ALIASES_FILE/ s'SYSTEM_ALIASES_FILE'/etc/aliases'" \ + ../src/configure.default > ${CONFIGURE_FILE} + else + true diff --git a/debian/patches/60_convert4r4.dpatch b/debian/patches/60_convert4r4.dpatch new file mode 100755 index 0000000..23fb0ae --- /dev/null +++ b/debian/patches/60_convert4r4.dpatch @@ -0,0 +1,41 @@ +Description: Add a warning message to convert4r4 +Author: Marc Haber <mh+debian-packages@zugschlus.de> +Origin: vendor +Forwarded: not-needed +Last-Update: 2013-09-28 + +--- a/src/convert4r4.src ++++ b/src/convert4r4.src +@@ -666,6 +666,32 @@ return defined $main{$_[0]} && $main{$_[ + + print STDERR "Runtime configuration file converter for Exim release 4.\n"; + ++if( !defined $ENV{"CONVERT4R4"} || $ENV{"CONVERT4R4"} ne "I understand this is an unsupported tool" ) { ++ ++ print STDERR <<EOF; ++convert4r4 on Debian GNU/Linux deprecated ++ ++This tool is unsupported by upstream and discouraged by the Debian Exim 4 ++maintainers. It has multiple known bugs, and you need to manually ++review its output after using it anyway. Please seriously consider complete ++manual regeneration of the Exim 4 configuration, preferably by using the new ++Debconf interface to Exim 4. ++ ++If you decide to ignore this advice and to use this script anyway, ++setting the environment variable CONVERT4R4 to the value ++\"I understand this is an unsupported tool\" ++will allow you to run the script. If you find bugs, you get to keep ++the pieces. Please do not file bugs against this script in the Debian ++BTS without providing a patch fixing the bugs, and please do not ++expect the upstream exim-users mailing list to answer questions. ++ ++Kind regards ++the Debian Exim4 Maintainers ++EOF ++ ++ exit 1; ++} ++ + $transport_start = $director_start = $router_start = $retry_start + = $rewrite_start = $auth_start = 999999; + diff --git a/debian/patches/67_unnecessaryCopt.diff b/debian/patches/67_unnecessaryCopt.diff new file mode 100644 index 0000000..daae1e4 --- /dev/null +++ b/debian/patches/67_unnecessaryCopt.diff @@ -0,0 +1,69 @@ +Description: Stop using exim's -C option in utility scripts (exiwhat + et al.) since this breaks with ALT_CONFIG_PREFIX. +Author: Andreas Metzler <ametzler@bebt.de> +Forwarded: http://bugs.exim.org/show_bug.cgi?id=1045 +Last-Update: 2018-12-31 + +--- a/src/exicyclog.src ++++ b/src/exicyclog.src +@@ -151,10 +151,10 @@ st=' ' + exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` + if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi + +-spool_directory=`$exim_path -C $config -bP spool_directory | sed 's/.*=[ ]*//'` ++spool_directory=`$exim_path -bP spool_directory | sed 's/.*=[ ]*//'` + + if [ "$log_file_path" = "" ] ; then +- log_file_path=`$exim_path -C $config -bP log_file_path | sed 's/.*=[ ]*//'` ++ log_file_path=`$exim_path -bP log_file_path | sed 's/.*=[ ]*//'` + fi + + # If log_file_path contains only "syslog" then no Exim log files are in use. +--- a/src/eximon.src ++++ b/src/eximon.src +@@ -81,8 +81,8 @@ st=' ' + EXIM_PATH=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` + if test "$EXIM_PATH" = ""; then EXIM_PATH=BIN_DIRECTORY/exim4; fi + +-SPOOL_DIRECTORY=`$EXIM_PATH -C $config -bP spool_directory | sed 's/.*=[ ]*//'` +-LOG_FILE_PATH=`$EXIM_PATH -C $config -bP log_file_path | sed 's/.*=[ ]*//'` ++SPOOL_DIRECTORY=`$EXIM_PATH -bP spool_directory | sed 's/.*=[ ]*//'` ++LOG_FILE_PATH=`$EXIM_PATH -bP log_file_path | sed 's/.*=[ ]*//'` + + # If log_file_path is "syslog" then logging is only to syslog, and the monitor + # is unable to display a log tail unless EXIMON_LOG_FILE_PATH is set to tell +--- a/src/exinext.src ++++ b/src/exinext.src +@@ -98,8 +98,8 @@ if [ "$exim_path" = "" ]; then + fi + + if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi +-spool_directory=`$exim_path $eximmacdef -C $config -bP spool_directory | sed 's/.*=[ ]*//'` +-qualify_domain=`$exim_path $eximmacdef -C $config -bP qualify_domain | sed 's/.*=[ ]*//'` ++spool_directory=`$exim_path $eximmacdef -bP spool_directory | sed 's/.*=[ ]*//'` ++qualify_domain=`$exim_path $eximmacdef -bP qualify_domain | sed 's/.*=[ ]*//'` + + # Now do the job. Perl uses $ so frequently that we don't want to have to + # escape them all from the shell, so pass in shell variable values as +@@ -144,7 +144,7 @@ perl - $exim_path "$eximmacdef" $argone + # Run Exim to get a list of hosts for the given domain; for + # each one construct the appropriate retry key. + +- open(LIST, "$exim -C $config -v -bt $address |") || ++ open(LIST, "$exim -v -bt $address |") || + die "can't run exim to route $address"; + + while (<LIST>) +--- a/src/exiwhat.src ++++ b/src/exiwhat.src +@@ -99,8 +99,8 @@ fi + st=' ' + exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` + if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi +-spool_directory=`$exim_path -C $config -bP spool_directory | sed "s/.*=[ ]*//"` +-process_log_path=`$exim_path -C $config -bP process_log_path | sed "s/.*=[ ]*//"` ++spool_directory=`$exim_path -bP spool_directory | sed "s/.*=[ ]*//"` ++process_log_path=`$exim_path -bP process_log_path | sed "s/.*=[ ]*//"` + + # The file that Exim writes when sent the SIGUSR1 signal is specified by + # the process_log_path option. If that is not defined, Exim uses the file diff --git a/debian/patches/70_remove_exim-users_references.dpatch b/debian/patches/70_remove_exim-users_references.dpatch new file mode 100755 index 0000000..f3dda95 --- /dev/null +++ b/debian/patches/70_remove_exim-users_references.dpatch @@ -0,0 +1,38 @@ +Description: Point Debian users to Debian specific ML. +Author: Marc Haber <mh+debian-packages@zugschlus.de> +Last-Update: 2018-12-31 +Forwarded: not-needed + +--- a/README ++++ b/README +@@ -14,8 +14,16 @@ from Exim 3, though the basic structure + older book may be helpful for the background, but a lot of the detail has + changed, so it is likely to be confusing to newcomers. + +-There is a website at https://www.exim.org; this contains details of the +-mailing list exim-users@exim.org. ++Information about the way Debian has built the binary packages is ++obtainable in /usr/share/doc/exim4-base/README.Debian.gz, and there ++is a Debian-centered mailing list, ++pkg-exim4-users@lists.alioth.debian.org. Please ask Debian-specific ++questions there, and only write to the upstream exim-users mailing ++list if you are sure that your question is not Debian-specific. You ++can find the subscription web page on ++http://lists.alioth.debian.org/mailman/listinfo/pkg-exim4-users ++ ++There is a website at https://www.exim.org/. + + A copy of the Exim FAQ should be available from the same source that you used + to obtain the Exim distribution. Additional formats for the documentation +--- a/src/eximstats.src ++++ b/src/eximstats.src +@@ -537,8 +537,7 @@ about how to create charts from the tabl + + =head1 AUTHOR + +-There is a website at https://www.exim.org - this contains details of the +-mailing list exim-users@exim.org. ++There is a website at https://www.exim.org/. + + =head1 TO DO + diff --git a/debian/patches/73_01-Fix-DANE-SNI-handling-Bug-2265.patch b/debian/patches/73_01-Fix-DANE-SNI-handling-Bug-2265.patch new file mode 100644 index 0000000..b5a6418 --- /dev/null +++ b/debian/patches/73_01-Fix-DANE-SNI-handling-Bug-2265.patch @@ -0,0 +1,36 @@ +From e8ac8be0a3d56ba0a189fb970c339ac6e84769be Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Mon, 3 May 2021 15:53:28 +0200 +Subject: [PATCH] Fix DANE + SNI handling (Bug 2265) + +Broken in d8e99d6047e709b35eabb1395c2046100d1a1dda +Thanks to JGH and Wolfgang Breyha for contributions. +--- + src/transports/smtp.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/transports/smtp.c b/src/transports/smtp.c +index f26e2337a..9ee6a578a 100644 +--- a/src/transports/smtp.c ++++ b/src/transports/smtp.c +@@ -2015,7 +2015,7 @@ if (continue_hostname && continue_proxy_cipher) + { + case OK: sx->conn_args.dane = TRUE; + ob->tls_tempfail_tryclear = FALSE; /* force TLS */ +- ob->tls_sni = sx->first_addr->domain; /* force SNI */ ++ ob->tls_sni = sx->conn_args.host->name; /* force SNI */ + break; + case FAIL_FORCED: break; + default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, +@@ -2097,7 +2097,7 @@ if (!continue_hostname) + { + case OK: sx->conn_args.dane = TRUE; + ob->tls_tempfail_tryclear = FALSE; /* force TLS */ +- ob->tls_sni = sx->first_addr->domain; /* force SNI */ ++ ob->tls_sni = sx->conn_args.host->name; /* force SNI */ + break; + case FAIL_FORCED: break; + default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, +-- +2.30.2 + diff --git a/debian/patches/73_02-Fix-ipv6norm.patch b/debian/patches/73_02-Fix-ipv6norm.patch new file mode 100644 index 0000000..fb98bc8 --- /dev/null +++ b/debian/patches/73_02-Fix-ipv6norm.patch @@ -0,0 +1,47 @@ +From ed64b5c2f0f44db27ae48128fc97d5ad8406a28e Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Tue, 4 May 2021 13:06:31 +0100 +Subject: [PATCH 2/2] Fix ${ipv6norm:} + +(cherry picked from commit 8b4b6ac90766b11fa74fa3001778b49456adbe42) +--- + doc/ChangeLog | 3 +++ + src/host.c | 4 ++-- + test/scripts/0000-Basic/0002 | 2 +- + test/stdout/0002 | 2 +- + 4 files changed, 7 insertions(+), 4 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -218,10 +218,13 @@ + executed child processes (if any). + + QS/04 Always die if requested from internal logging, even is logging is + disabled. + ++JH/52 Fix ${ip6norm:} operator. Previously, any trailing line text was dropped, ++ making it unusable in complex expressions. ++ + + Exim version 4.94 + ----------------- + + JH/01 Avoid costly startup code when not strictly needed. This reduces time +--- a/src/host.c ++++ b/src/host.c +@@ -1195,13 +1195,13 @@ + } + while (*++c != ':') ; + c++; + } + +-c[-1] = '\0'; /* drop trailing colon */ ++*--c = '\0'; /* drop trailing colon */ + +-/* debug_printf("%s: D k %d <%s> <%s>\n", __FUNCTION__, k, d, d + 2*(k+1)); */ ++/* debug_printf("%s: D k %d <%s> <%s>\n", __FUNCTION__, k, buffer, buffer + 2*(k+1)); */ + if (k >= 0) + { /* collapse */ + c = d + 2*(k+1); + if (d == buffer) c--; /* need extra colon */ + *d++ = ':'; /* 1st 0 */ diff --git a/debian/patches/73_03-Named-Queues-fix-immediate-delivery.-Bug-2743.patch b/debian/patches/73_03-Named-Queues-fix-immediate-delivery.-Bug-2743.patch new file mode 100644 index 0000000..dc4d86a --- /dev/null +++ b/debian/patches/73_03-Named-Queues-fix-immediate-delivery.-Bug-2743.patch @@ -0,0 +1,50 @@ +From c1faf04b865465894c7ca41ab4585fb69d4a5936 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Wed, 12 May 2021 15:01:12 +0100 +Subject: [PATCH 3/3] Named Queues: fix immediate-delivery. Bug 2743 + +(cherry picked from commit 159cf206c97f876b07829d92db2217689745c1e8) +--- + doc/ChangeLog | 4 ++++ + src/exim.c | 6 ++++-- + test/confs/0576 | 6 ++++++ + test/log/0576 | 3 +++ + test/scripts/0000-Basic/0576 | 4 ++++ + 5 files changed, 21 insertions(+), 2 deletions(-) + +diff --git a/doc/ChangeLog b/doc/ChangeLog +index 9f104b4e6..e60c1cad5 100644 +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -223,6 +223,10 @@ QS/04 Always die if requested from internal logging, even is logging is + JH/52 Fix ${ip6norm:} operator. Previously, any trailing line text was dropped, + making it unusable in complex expressions. + ++JH/53 Bug 2743: fix immediate-delivery via named queue. Previously this would ++ fail with a taint-check on the spoolfile name, and leave the message ++ queued. ++ + + Exim version 4.94 + ----------------- +diff --git a/src/exim.c b/src/exim.c +index ee75739ec..7411f0467 100644 +--- a/src/exim.c ++++ b/src/exim.c +@@ -2789,9 +2789,11 @@ on the second character (the one after '-'), to save some effort. */ + else badarg = TRUE; + break; + +- /* -MCG: set the queue name, to a non-default value */ ++ /* -MCG: set the queue name, to a non-default value. Arguably, anything ++ from the commandline should be tainted - but we will need an untainted ++ value for the spoolfile when doing a -odi delivery process. */ + +- case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), TRUE); ++ case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), FALSE); + else badarg = TRUE; + break; + +-- +2.30.2 + diff --git a/debian/patches/73_04-Fix-host_name_lookup-Close-2747.patch b/debian/patches/73_04-Fix-host_name_lookup-Close-2747.patch new file mode 100644 index 0000000..dd5c982 --- /dev/null +++ b/debian/patches/73_04-Fix-host_name_lookup-Close-2747.patch @@ -0,0 +1,80 @@ +From 20812729e3e47a193a21d326ecd036d67a8b2724 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 16 May 2021 19:11:19 +0200 +Subject: [PATCH 4/4] Fix host_name_lookup (Close 2747) + +Thanks to Nico R for providing a reproducing configuration. + + host_lookup = * + message_size_limit = ${if def:sender_host_name {32M}{32M}} + acl_smtp_connect = acl_smtp_connect + acl_smtp_rcpt = acl_smtp_rcpt + + begin acl + acl_smtp_connect: + warn ratelimit = 256 / 1m / per_conn + accept + + acl_smtp_rcpt: + accept hosts = 127.0.0.* + + begin routers + null: + driver = accept + transport = null + + begin transports + null: + driver = appendfile + file = /dev/null + +Tested with + + swaks -f mailbox@example.org -t mailbox@example.org --pipe 'exim -bh 127.0.0.1 -C /opt/exim/etc/exim-bug.conf' + +The IP must have a PTR to "localhost." to reproduce it. +--- + src/host.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/host.c b/src/host.c +index ee9d323a7..2047b9798 100644 +--- a/src/host.c ++++ b/src/host.c +@@ -1577,15 +1577,15 @@ Put it in permanent memory. */ + + sender_host_name = string_copylc(US hosts->h_name); + + /* If the host has aliases, build a copy of the alias list */ + + if (hosts->h_aliases) + { +- int count = 1; ++ int count = 1; /* need 1 more for terminating NULL */ + uschar **ptr; + + for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++; + store_pool = POOL_PERM; + ptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE); + store_pool = POOL_TAINT_PERM; + +@@ -1686,15 +1686,15 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) + if failure. (PTR records that yield empty names have been encountered in + the DNS.) */ + + if (rc == DNS_SUCCEED) + { + uschar **aptr = NULL; + int ssize = 264; +- int count = 0; ++ int count = 1; /* need 1 more for terminating NULL */ + int old_pool = store_pool; + + sender_host_dnssec = dns_is_secure(dnsa); + DEBUG(D_dns) + debug_printf("Reverse DNS security status: %s\n", + sender_host_dnssec ? "DNSSEC verified (AD)" : "unverified"); + +-- +2.30.2 + diff --git a/debian/patches/73_05-Fix-tainted-message-for-fakereject.patch b/debian/patches/73_05-Fix-tainted-message-for-fakereject.patch new file mode 100644 index 0000000..a152417 --- /dev/null +++ b/debian/patches/73_05-Fix-tainted-message-for-fakereject.patch @@ -0,0 +1,44 @@ +From c819f3bcad02bcb06004ae2ad135b68fab0ae888 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Wed, 7 Jul 2021 22:19:07 +0100 +Subject: [PATCH 5/5] Fix tainted message for fakereject + +(cherry picked from commit a9ac2d7fc219e41a353abf1f599258b9b9d21b7e) +--- + doc/ChangeLog | 4 ++++ + src/acl.c | 4 +++- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/doc/ChangeLog b/doc/ChangeLog +index e60c1cad5..3e93f653f 100644 +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -227,6 +227,10 @@ JH/53 Bug 2743: fix immediate-delivery via named queue. Previously this would + fail with a taint-check on the spoolfile name, and leave the message + queued. + ++JH/57 Fix control=fakreject for a custom message containing tainted data. ++ Previously this resulted in a log complaint, due to a re-expansion present ++ since fakereject was originally introduced. ++ + + Exim version 4.94 + ----------------- +diff --git a/src/acl.c b/src/acl.c +index 7061230b4..65324405c 100644 +--- a/src/acl.c ++++ b/src/acl.c +@@ -3137,7 +3137,9 @@ for (; cb; cb = cb->next) + { + const uschar *pp = p + 1; + while (*pp) pp++; +- fake_response_text = expand_string(string_copyn(p+1, pp-p-1)); ++ /* The entire control= line was expanded at top so no need to expand ++ the part after the / */ ++ fake_response_text = string_copyn(p+1, pp-p-1); + p = pp; + } + else /* Explicitly reset to default string */ +-- +2.30.2 + diff --git a/debian/patches/75_01-Introduce-main-config-option-allow_insecure_tainted_.patch b/debian/patches/75_01-Introduce-main-config-option-allow_insecure_tainted_.patch new file mode 100644 index 0000000..0295ec1 --- /dev/null +++ b/debian/patches/75_01-Introduce-main-config-option-allow_insecure_tainted_.patch @@ -0,0 +1,230 @@ +From ec06d64532e4952fc36429f73e0222d26997ef7c Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Thu, 1 Apr 2021 22:44:31 +0200 +Subject: [PATCH 01/23] Introduce main config option + allow_insecure_tainted_data + +This option is deprecated already now. +--- + src/EDITME | 7 +++++ + src/config.h.defaults | 2 ++ + src/functions.h | 54 ++++++++++++++++++++++++++++++--------- + src/globals.c | 10 ++++++++ + src/globals.h | 4 +++ + src/macros.h | 3 +++ + src/readconf.c | 3 +++ + 7 files changed, 71 insertions(+), 12 deletions(-) + +diff --git a/src/EDITME b/src/EDITME +index 8da36a353..cebb8e2ec 100644 +--- a/src/EDITME ++++ b/src/EDITME +@@ -749,6 +749,13 @@ FIXED_NEVER_USERS=root + + # WHITELIST_D_MACROS=TLS:SPOOL + ++# The next setting enables a main config option ++# "allow_insecure_tainted_data" to turn taint failures into warnings. ++# Though this option is new, it is deprecated already now, and will be ++# ignored in future releases of Exim. It is meant as mitigation for ++# upgrading old (possibly insecure) configurations to more secure ones. ++ALLOW_INSECURE_TAINTED_DATA=yes ++ + #------------------------------------------------------------------------------ + # Exim has support for the AUTH (authentication) extension of the SMTP + # protocol, as defined by RFC 2554. If you don't know what SMTP authentication +diff --git a/src/config.h.defaults b/src/config.h.defaults +index e17f015f9..4e8b18904 100644 +--- a/src/config.h.defaults ++++ b/src/config.h.defaults +@@ -17,6 +17,8 @@ Do not put spaces between # and the 'define'. + #define ALT_CONFIG_PREFIX + #define TRUSTED_CONFIG_LIST + ++#define ALLOW_INSECURE_TAINTED_DATA ++ + #define APPENDFILE_MODE 0600 + #define APPENDFILE_DIRECTORY_MODE 0700 + #define APPENDFILE_LOCKFILE_MODE 0600 +diff --git a/src/functions.h b/src/functions.h +index 51bb17a09..1e8083673 100644 +--- a/src/functions.h ++++ b/src/functions.h +@@ -1083,36 +1083,66 @@ if (f.running_in_test_harness && f.testsuite_delays) millisleep(millisec); + + /******************************************************************************/ + /* Taint-checked file opens */ ++static inline uschar * ++is_tainted2(const void *p, int lflags, const uschar* fmt, ...) ++{ ++va_list ap; ++uschar *msg; ++rmark mark; ++ ++if (!is_tainted(p)) ++ return NULL; ++ ++mark = store_mark(); ++va_start(ap, fmt); ++msg = string_from_gstring(string_vformat(NULL, SVFMT_TAINT_NOCHK|SVFMT_EXTEND, fmt, ap)); ++va_end(ap); ++ ++#ifdef ALLOW_INSECURE_TAINTED_DATA ++if (allow_insecure_tainted_data) ++ { ++ if LOGGING(tainted) log_write(0, LOG_MAIN, "Warning: %s", msg); ++ store_reset(mark); ++ return NULL; ++ } ++#endif ++ ++if (lflags) log_write(0, lflags, "%s", msg); ++return msg; /* no store_reset(), as the message might be used afterwards and Exim ++ is expected to exit anyway, so we do not care about the leaked ++ storage */ ++} + + static inline int + exim_open2(const char *pathname, int flags) + { +-if (!is_tainted(pathname)) return open(pathname, flags); +-log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); ++if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) ++ return open(pathname, flags); + errno = EACCES; + return -1; + } ++ + static inline int + exim_open(const char *pathname, int flags, mode_t mode) + { +-if (!is_tainted(pathname)) return open(pathname, flags, mode); +-log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); ++if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) ++ return open(pathname, flags, mode); + errno = EACCES; + return -1; + } + static inline int + exim_openat(int dirfd, const char *pathname, int flags) + { +-if (!is_tainted(pathname)) return openat(dirfd, pathname, flags); +-log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); ++if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) ++ return openat(dirfd, pathname, flags); + errno = EACCES; + return -1; + } + static inline int + exim_openat4(int dirfd, const char *pathname, int flags, mode_t mode) + { +-if (!is_tainted(pathname)) return openat(dirfd, pathname, flags, mode); +-log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); ++if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) ++ return openat(dirfd, pathname, flags, mode); + errno = EACCES; + return -1; + } +@@ -1120,8 +1150,8 @@ return -1; + static inline FILE * + exim_fopen(const char *pathname, const char *mode) + { +-if (!is_tainted(pathname)) return fopen(pathname, mode); +-log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); ++if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) ++ return fopen(pathname, mode); + errno = EACCES; + return NULL; + } +@@ -1129,8 +1159,8 @@ return NULL; + static inline DIR * + exim_opendir(const uschar * name) + { +-if (!is_tainted(name)) return opendir(CCS name); +-log_write(0, LOG_MAIN|LOG_PANIC, "Tainted dirname '%s'", name); ++if (!is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted dirname '%s'", name)) ++ return opendir(CCS name); + errno = EACCES; + return NULL; + } +diff --git a/src/globals.c b/src/globals.c +index c34ac9ddd..ff660c352 100644 +--- a/src/globals.c ++++ b/src/globals.c +@@ -98,6 +98,10 @@ int sqlite_lock_timeout = 5; + BOOL move_frozen_messages = FALSE; + #endif + ++#ifdef ALLOW_INSECURE_TAINTED_DATA ++BOOL allow_insecure_tainted_data = FALSE; ++#endif ++ + /* These variables are outside the #ifdef because it keeps the code less + cluttered in several places (e.g. during logging) if we can always refer to + them. Also, the tls_ variables are now always visible. Note that these are +@@ -1033,6 +1037,9 @@ int log_default[] = { /* for initializing log_selector */ + Li_size_reject, + Li_skip_delivery, + Li_smtp_confirmation, ++#ifdef ALLOW_INSECURE_TAINTED_DATA ++ Li_tainted, ++#endif + Li_tls_certificate_verified, + Li_tls_cipher, + -1 +@@ -1100,6 +1107,9 @@ bit_table log_options[] = { /* must be in alphabetical order, + BIT_TABLE(L, smtp_protocol_error), + BIT_TABLE(L, smtp_syntax_error), + BIT_TABLE(L, subject), ++#ifdef ALLOW_INSECURE_TAINTED_DATA ++ BIT_TABLE(L, tainted), ++#endif + BIT_TABLE(L, tls_certificate_verified), + BIT_TABLE(L, tls_cipher), + BIT_TABLE(L, tls_peerdn), +diff --git a/src/globals.h b/src/globals.h +index a4c1143b7..8d72577e0 100644 +--- a/src/globals.h ++++ b/src/globals.h +@@ -77,6 +77,10 @@ extern int sqlite_lock_timeout; /* Internal lock waiting timeout */ + extern BOOL move_frozen_messages; /* Get them out of the normal directory */ + #endif + ++#ifdef ALLOW_INSECURE_TAINTED_DATA ++extern BOOL allow_insecure_tainted_data; ++#endif ++ + /* These variables are outside the #ifdef because it keeps the code less + cluttered in several places (e.g. during logging) if we can always refer to + them. Also, the tls_ variables are now always visible. */ +diff --git a/src/macros.h b/src/macros.h +index f78ae2e3d..322ddbf56 100644 +--- a/src/macros.h ++++ b/src/macros.h +@@ -498,6 +498,9 @@ enum logbit { + Li_smtp_mailauth, + Li_smtp_no_mail, + Li_subject, ++#ifdef ALLOW_INSECURE_TAINTED_DATA ++ Li_tainted, ++#endif + Li_tls_certificate_verified, + Li_tls_cipher, + Li_tls_peerdn, +diff --git a/src/readconf.c b/src/readconf.c +index 948fa2403..133135f8f 100644 +--- a/src/readconf.c ++++ b/src/readconf.c +@@ -68,6 +68,9 @@ static optionlist optionlist_config[] = { + { "add_environment", opt_stringptr, {&add_environment} }, + { "admin_groups", opt_gidlist, {&admin_groups} }, + { "allow_domain_literals", opt_bool, {&allow_domain_literals} }, ++#ifdef ALLOW_INSECURE_TAINTED_DATA ++ { "allow_insecure_tainted_data", opt_bool, {&allow_insecure_tainted_data} }, ++#endif + { "allow_mx_to_ip", opt_bool, {&allow_mx_to_ip} }, + { "allow_utf8_domains", opt_bool, {&allow_utf8_domains} }, + { "auth_advertise_hosts", opt_stringptr, {&auth_advertise_hosts} }, +-- +2.30.2 + diff --git a/debian/patches/75_02-search.patch b/debian/patches/75_02-search.patch new file mode 100644 index 0000000..226a350 --- /dev/null +++ b/debian/patches/75_02-search.patch @@ -0,0 +1,39 @@ +From b71d675f695c2cf17357b190476129535d5f446c Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Thu, 1 Apr 2021 22:45:03 +0200 +Subject: [PATCH 02/23] search + +--- + src/search.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/src/search.c b/src/search.c +index f8aaacb04..f6e4d1f5b 100644 +--- a/src/search.c ++++ b/src/search.c +@@ -343,12 +343,8 @@ lookup_info *lk = lookup_list[search_type]; + uschar keybuffer[256]; + int old_pool = store_pool; + +-if (filename && is_tainted(filename)) +- { +- log_write(0, LOG_MAIN|LOG_PANIC, +- "Tainted filename for search: '%s'", filename); ++if (filename && is_tainted2(filename, LOG_MAIN|LOG_PANIC, "Tainted filename for search '%s'", filename)) + return NULL; +- } + + /* Change to the search store pool and remember our reset point */ + +@@ -639,7 +635,7 @@ DEBUG(D_lookup) + /* Arrange to put this database at the top of the LRU chain if it is a type + that opens real files. */ + +-if ( open_top != (tree_node *)handle ++if ( open_top != (tree_node *)handle + && lookup_list[t->name[0]-'0']->type == lookup_absfile) + { + search_cache *c = (search_cache *)(t->data.ptr); +-- +2.30.2 + diff --git a/debian/patches/75_03-dbstuff.patch b/debian/patches/75_03-dbstuff.patch new file mode 100644 index 0000000..dc9da8e --- /dev/null +++ b/debian/patches/75_03-dbstuff.patch @@ -0,0 +1,30 @@ +From 35b11dd0e52b5ac176849f807cca8898bcaf0c3d Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 28 Mar 2021 10:49:49 +0200 +Subject: [PATCH 03/23] dbstuff + +--- + src/dbstuff.h | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/dbstuff.h b/src/dbstuff.h +index c1fb54346..dcee78696 100644 +--- a/src/dbstuff.h ++++ b/src/dbstuff.h +@@ -643,11 +643,9 @@ after reading data. */ + : (flags) == O_RDWR ? "O_RDWR" \ + : (flags) == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" \ + : "??"); \ +- if (is_tainted(name) || is_tainted(dirname)) \ +- { \ +- log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted"); \ ++ if (is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted name '%s' for DB file not permitted", name) \ ++ || is_tainted2(dirname, LOG_MAIN|LOG_PANIC, "Tainted name '%s' for DB directory not permitted", dirname)) \ + *dbpp = NULL; \ +- } \ + else \ + { EXIM_DBOPEN__(name, dirname, flags, mode, dbpp); } \ + DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", *dbpp); \ +-- +2.30.2 + diff --git a/debian/patches/75_04-acl.patch b/debian/patches/75_04-acl.patch new file mode 100644 index 0000000..810b2e5 --- /dev/null +++ b/debian/patches/75_04-acl.patch @@ -0,0 +1,67 @@ +From 44fd80ad8abcd885fc1c8dbb294fc2140e4ef481 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 28 Mar 2021 10:50:14 +0200 +Subject: [PATCH 04/23] acl +Last-Update: 2021-05-01 + +--- + src/acl.c | 32 ++++++++++++++++---------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +--- a/src/acl.c ++++ b/src/acl.c +@@ -3596,24 +3596,26 @@ + rc = mime_regex(&arg); + break; + #endif + + case ACLC_QUEUE: +- if (is_tainted(arg)) + { +- *log_msgptr = string_sprintf("Tainted name '%s' for queue not permitted", +- arg); +- return ERROR; ++ uschar *m; ++ if (m = is_tainted2(arg, 0, "Tainted name '%s' for queue not permitted", arg)) ++ { ++ *log_msgptr = m; ++ return ERROR; ++ } ++ if (Ustrchr(arg, '/')) ++ { ++ *log_msgptr = string_sprintf( ++ "Directory separator not permitted in queue name: '%s'", arg); ++ return ERROR; ++ } ++ queue_name = string_copy_perm(arg, FALSE); ++ break; + } +- if (Ustrchr(arg, '/')) +- { +- *log_msgptr = string_sprintf( +- "Directory separator not permitted in queue name: '%s'", arg); +- return ERROR; +- } +- queue_name = string_copy_perm(arg, FALSE); +- break; + + case ACLC_RATELIMIT: + rc = acl_ratelimit(arg, where, log_msgptr); + break; + +@@ -4005,14 +4007,12 @@ + } + + else if (*ss == '/') + { + struct stat statbuf; +- if (is_tainted(ss)) ++ if (is_tainted2(ss, LOG_MAIN|LOG_PANIC, "Tainted ACL file name '%s'", ss)) + { +- log_write(0, LOG_MAIN|LOG_PANIC, +- "attempt to open tainted ACL file name \"%s\"", ss); + /* Avoid leaking info to an attacker */ + *log_msgptr = US"internal configuration error"; + return ERROR; + } + if ((fd = Uopen(ss, O_RDONLY, 0)) < 0) diff --git a/debian/patches/75_05-parse.patch b/debian/patches/75_05-parse.patch new file mode 100644 index 0000000..f9dab90 --- /dev/null +++ b/debian/patches/75_05-parse.patch @@ -0,0 +1,30 @@ +From 7eeeb6f26af05322814ecc77c87f09c72ab2216a Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 28 Mar 2021 10:58:46 +0200 +Subject: [PATCH 05/23] parse + +--- + src/parse.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/src/parse.c b/src/parse.c +index 3ea758ac9..d1bc79039 100644 +--- a/src/parse.c ++++ b/src/parse.c +@@ -1402,12 +1402,8 @@ for (;;) + return FF_ERROR; + } + +- if (is_tainted(filename)) +- { +- *error = string_sprintf("Tainted name '%s' for included file not permitted\n", +- filename); ++ if (*error = is_tainted2(filename, 0, "Tainted name '%s' for included file not permitted\n", filename)) + return FF_ERROR; +- } + + /* Check file name if required */ + +-- +2.30.2 + diff --git a/debian/patches/75_06-rda.patch b/debian/patches/75_06-rda.patch new file mode 100644 index 0000000..f4ca2af --- /dev/null +++ b/debian/patches/75_06-rda.patch @@ -0,0 +1,28 @@ +From a6da9c67acaee699616516be141d600cc178a633 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 28 Mar 2021 10:59:46 +0200 +Subject: [PATCH 06/23] rda + +--- + src/rda.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/rda.c b/src/rda.c +index aed8abc24..6ad7dd8bd 100644 +--- a/src/rda.c ++++ b/src/rda.c +@@ -179,10 +179,8 @@ struct stat statbuf; + /* Reading a file is a form of expansion; we wish to deny attackers the + capability to specify the file name. */ + +-if (is_tainted(filename)) ++if (*error = is_tainted2(filename, 0, "Tainted name '%s' for file read not permitted\n", filename)) + { +- *error = string_sprintf("Tainted name '%s' for file read not permitted\n", +- filename); + *yield = FF_ERROR; + return NULL; + } +-- +2.30.2 + diff --git a/debian/patches/75_07-appendfile.patch b/debian/patches/75_07-appendfile.patch new file mode 100644 index 0000000..5a9e378 --- /dev/null +++ b/debian/patches/75_07-appendfile.patch @@ -0,0 +1,34 @@ +From c29b50d2fe17cc108d751175ed4f4113c25c1768 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 28 Mar 2021 11:00:06 +0200 +Subject: [PATCH 07/23] appendfile + +--- + src/transports/appendfile.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/src/transports/appendfile.c b/src/transports/appendfile.c +index 8ab8b6016..7dbbaa2f9 100644 +--- a/src/transports/appendfile.c ++++ b/src/transports/appendfile.c +@@ -1286,12 +1286,14 @@ if (!(path = expand_string(fdname))) + expand_string_message); + goto ret_panic; + } +-if (is_tainted(path)) ++{ uschar *m; ++if (m = is_tainted2(path, 0, "Tainted '%s' (file or directory " ++ "name for %s transport) not permitted", path, tblock->name)) + { +- addr->message = string_sprintf("Tainted '%s' (file or directory " +- "name for %s transport) not permitted", path, tblock->name); ++ addr->message = m; + goto ret_panic; + } ++} + + if (path[0] != '/') + { +-- +2.30.2 + diff --git a/debian/patches/75_08-autoreply.patch b/debian/patches/75_08-autoreply.patch new file mode 100644 index 0000000..de5eb1d --- /dev/null +++ b/debian/patches/75_08-autoreply.patch @@ -0,0 +1,70 @@ +From 26de37d8960da80473866fb59b9dfd10a5761538 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 28 Mar 2021 11:06:27 +0200 +Subject: [PATCH 08/23] autoreply + +--- + src/transports/autoreply.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/src/transports/autoreply.c b/src/transports/autoreply.c +index 865abbf4f..ed99de4c6 100644 +--- a/src/transports/autoreply.c ++++ b/src/transports/autoreply.c +@@ -404,14 +404,15 @@ recipient cache. */ + + if (oncelog && *oncelog && to) + { ++ uschar *m; + time_t then = 0; + +- if (is_tainted(oncelog)) ++ if (m = is_tainted2(oncelog, 0, "Tainted '%s' (once file for %s transport)" ++ " not permitted", oncelog, tblock->name)) + { + addr->transport_return = DEFER; + addr->basic_errno = EACCES; +- addr->message = string_sprintf("Tainted '%s' (once file for %s transport)" +- " not permitted", oncelog, tblock->name); ++ addr->message = m; + goto END_OFF; + } + +@@ -515,13 +516,14 @@ if (oncelog && *oncelog && to) + + if (then != 0 && (once_repeat_sec <= 0 || now - then < once_repeat_sec)) + { ++ uschar *m; + int log_fd; +- if (is_tainted(logfile)) ++ if (m = is_tainted2(logfile, 0, "Tainted '%s' (logfile for %s transport)" ++ " not permitted", logfile, tblock->name)) + { + addr->transport_return = DEFER; + addr->basic_errno = EACCES; +- addr->message = string_sprintf("Tainted '%s' (logfile for %s transport)" +- " not permitted", logfile, tblock->name); ++ addr->message = m; + goto END_OFF; + } + +@@ -548,12 +550,13 @@ if (oncelog && *oncelog && to) + /* We are going to send a message. Ensure any requested file is available. */ + if (file) + { +- if (is_tainted(file)) ++ uschar *m; ++ if (m = is_tainted2(file, 0, "Tainted '%s' (file for %s transport)" ++ " not permitted", file, tblock->name)) + { + addr->transport_return = DEFER; + addr->basic_errno = EACCES; +- addr->message = string_sprintf("Tainted '%s' (file for %s transport)" +- " not permitted", file, tblock->name); ++ addr->message = m; + return FALSE; + } + if (!(ff = Ufopen(file, "rb")) && !ob->file_optional) +-- +2.30.2 + diff --git a/debian/patches/75_09-pipe.patch b/debian/patches/75_09-pipe.patch new file mode 100644 index 0000000..0ec9bcf --- /dev/null +++ b/debian/patches/75_09-pipe.patch @@ -0,0 +1,36 @@ +From f9628406706112be459adb3f121db8e6cf282c2d Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Fri, 2 Apr 2021 17:30:27 +0200 +Subject: [PATCH 09/23] pipe + +--- + src/transports/pipe.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/transports/pipe.c b/src/transports/pipe.c +index 27422bd42..4c9e68beb 100644 +--- a/src/transports/pipe.c ++++ b/src/transports/pipe.c +@@ -599,13 +599,16 @@ if (!cmd || !*cmd) + tblock->name); + return FALSE; + } +-if (is_tainted(cmd)) ++ ++{ uschar *m; ++if (m = is_tainted2(cmd, 0, "Tainted '%s' (command " ++ "for %s transport) not permitted", cmd, tblock->name)) + { +- addr->message = string_sprintf("Tainted '%s' (command " +- "for %s transport) not permitted", cmd, tblock->name); + addr->transport_return = PANIC; ++ addr->message = m; + return FALSE; + } ++} + + /* When a pipe is set up by a filter file, there may be values for $thisaddress + and numerical the variables in existence. These are passed in +-- +2.30.2 + diff --git a/debian/patches/75_10-deliver.patch b/debian/patches/75_10-deliver.patch new file mode 100644 index 0000000..ea4a542 --- /dev/null +++ b/debian/patches/75_10-deliver.patch @@ -0,0 +1,49 @@ +From 2fee91ae42e974c21202e0b5e17185f6a87bf8af Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Wed, 31 Mar 2021 23:12:44 +0200 +Subject: [PATCH 10/23] deliver + +--- + src/deliver.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/src/deliver.c b/src/deliver.c +index d85edd70e..8b7998f37 100644 +--- a/src/deliver.c ++++ b/src/deliver.c +@@ -5538,10 +5538,11 @@ FILE * fp = NULL; + if (!s || !*s) + log_write(0, LOG_MAIN|LOG_PANIC, + "Failed to expand %s: '%s'\n", varname, filename); +-else if (*s != '/' || is_tainted(s)) +- log_write(0, LOG_MAIN|LOG_PANIC, +- "%s is not %s after expansion: '%s'\n", +- varname, *s == '/' ? "untainted" : "absolute", s); ++else if (*s != '/') ++ log_write(0, LOG_MAIN|LOG_PANIC, "%s is not absolute after expansion: '%s'\n", ++ varname, s); ++else if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted %s after expansion: '%s'\n", varname, s)) ++ ; + else if (!(fp = Ufopen(s, "rb"))) + log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for %s " + "message texts: %s", s, reason, strerror(errno)); +@@ -6148,12 +6149,13 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) + { + uschar *tmp = expand_string(tpname); + address_file = address_pipe = NULL; ++ uschar *m; + if (!tmp) + p->message = string_sprintf("failed to expand \"%s\" as a " + "system filter transport name", tpname); +- if (is_tainted(tmp)) +- p->message = string_sprintf("attempt to used tainted value '%s' for" +- "transport '%s' as a system filter", tmp, tpname); ++ if (is_tainted2(tmp, 0, m = string_sprintf("Tainted values '%s' " ++ "for transport '%s' as a system filter", tmp, tpname))) ++ p->message = m; + tpname = tmp; + } + else +-- +2.30.2 + diff --git a/debian/patches/75_11-directory.patch b/debian/patches/75_11-directory.patch new file mode 100644 index 0000000..4c3a684 --- /dev/null +++ b/debian/patches/75_11-directory.patch @@ -0,0 +1,26 @@ +From 5f41e800ce9cc7ad154047298914df955e905bf4 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Thu, 1 Apr 2021 21:28:59 +0200 +Subject: [PATCH 11/23] directory + +--- + src/directory.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/directory.c b/src/directory.c +index 2d4d565f4..9f88f4141 100644 +--- a/src/directory.c ++++ b/src/directory.c +@@ -44,6 +44,9 @@ uschar c = 1; + struct stat statbuf; + uschar * path; + ++if (is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted path '%s' for new directory", name)) ++ { p = US"create"; path = US name; errno = EACCES; goto bad; } ++ + if (parent) + { + path = string_sprintf("%s%s%s", parent, US"/", name); +-- +2.30.2 + diff --git a/debian/patches/75_12-expand.patch b/debian/patches/75_12-expand.patch new file mode 100644 index 0000000..ebb099d --- /dev/null +++ b/debian/patches/75_12-expand.patch @@ -0,0 +1,34 @@ +From c02ea85f525ff256d78e084d6f76fe3032fd52e1 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Thu, 1 Apr 2021 21:33:50 +0200 +Subject: [PATCH 12/23] expand + +--- + src/expand.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/expand.c b/src/expand.c +index 05de94c49..21b86ebf5 100644 +--- a/src/expand.c ++++ b/src/expand.c +@@ -4383,13 +4383,13 @@ DEBUG(D_expand) + f.expand_string_forcedfail = FALSE; + expand_string_message = US""; + +-if (is_tainted(string)) ++{ uschar *m; ++if (m = is_tainted2(string, LOG_MAIN|LOG_PANIC, "Tainted string '%s' in expansion", s)) + { +- expand_string_message = +- string_sprintf("attempt to expand tainted string '%s'", s); +- log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); ++ expand_string_message = m; + goto EXPAND_FAILED; + } ++} + + while (*s != 0) + { +-- +2.30.2 + diff --git a/debian/patches/75_13-lf_sqlperform.patch b/debian/patches/75_13-lf_sqlperform.patch new file mode 100644 index 0000000..67283a0 --- /dev/null +++ b/debian/patches/75_13-lf_sqlperform.patch @@ -0,0 +1,49 @@ +From 9810dfc25d8b9687b46e57963a3ac30bf5c9b2c9 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Thu, 1 Apr 2021 21:36:12 +0200 +Subject: [PATCH 13/23] lf_sqlperform + +--- + src/lookups/lf_sqlperform.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/src/lookups/lf_sqlperform.c b/src/lookups/lf_sqlperform.c +index ad1df29d1..eda3089e2 100644 +--- a/src/lookups/lf_sqlperform.c ++++ b/src/lookups/lf_sqlperform.c +@@ -102,11 +102,13 @@ if (Ustrncmp(query, "servers", 7) == 0) + } + } + +- if (is_tainted(server)) +- { +- *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); ++ { uschar *m; ++ if (m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server)) ++ { ++ *errmsg = m; + return DEFER; + } ++ } + + rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts); + if (rc != DEFER || defer_break) return rc; +@@ -158,11 +160,13 @@ else + server = ele; + } + +- if (is_tainted(server)) ++ { uschar *m; ++ if (is_tainted2(server, 0, "Tainted %s server '%s'", name, server)) + { +- *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); ++ *errmsg = m; + return DEFER; + } ++ } + + rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts); + if (rc != DEFER || defer_break) return rc; +-- +2.30.2 + diff --git a/debian/patches/75_14-rf_get_transport.patch b/debian/patches/75_14-rf_get_transport.patch new file mode 100644 index 0000000..9e8b69d --- /dev/null +++ b/debian/patches/75_14-rf_get_transport.patch @@ -0,0 +1,28 @@ +From 015fff57c854184f8bce61476c46a2830a97daf8 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Fri, 2 Apr 2021 08:36:24 +0200 +Subject: [PATCH 14/23] rf_get_transport + +--- + src/routers/rf_get_transport.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/routers/rf_get_transport.c b/src/routers/rf_get_transport.c +index 4a43818ff..32bde9ec3 100644 +--- a/src/routers/rf_get_transport.c ++++ b/src/routers/rf_get_transport.c +@@ -66,10 +66,8 @@ if (expandable) + "\"%s\" in %s router: %s", tpname, router_name, expand_string_message); + return FALSE; + } +- if (is_tainted(ss)) ++ if (is_tainted2(ss, LOG_MAIN|LOG_PANIC, "Tainted tainted value '%s' from '%s' for transport", ss, tpname)) + { +- log_write(0, LOG_MAIN|LOG_PANIC, +- "attempt to use tainted value '%s' from '%s' for transport", ss, tpname); + addr->basic_errno = ERRNO_BADTRANSPORT; + /* Avoid leaking info to an attacker */ + addr->message = US"internal configuration error"; +-- +2.30.2 + diff --git a/debian/patches/75_15-deliver.patch b/debian/patches/75_15-deliver.patch new file mode 100644 index 0000000..0c2ca27 --- /dev/null +++ b/debian/patches/75_15-deliver.patch @@ -0,0 +1,31 @@ +From 2bafe3fc82cf62f0c21f939f5891b8d067f3abc7 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sat, 3 Apr 2021 10:54:22 +0200 +Subject: [PATCH 15/23] deliver + +--- + src/deliver.c | 5 +++-- + test/paniclog/0622 | 2 +- + test/stderr/0622 | 2 +- + 3 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/src/deliver.c b/src/deliver.c +index 8b7998f37..87e944b03 100644 +--- a/src/deliver.c ++++ b/src/deliver.c +@@ -6153,9 +6153,10 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) + if (!tmp) + p->message = string_sprintf("failed to expand \"%s\" as a " + "system filter transport name", tpname); +- if (is_tainted2(tmp, 0, m = string_sprintf("Tainted values '%s' " +- "for transport '%s' as a system filter", tmp, tpname))) ++ { uschar *m; ++ if (m = is_tainted2(tmp, 0, "Tainted values '%s' " "for transport '%s' as a system filter", tmp, tpname)) + p->message = m; ++ } + tpname = tmp; + } + else +-- +2.30.2 + diff --git a/debian/patches/75_16-smtp_out.patch b/debian/patches/75_16-smtp_out.patch new file mode 100644 index 0000000..a0280af --- /dev/null +++ b/debian/patches/75_16-smtp_out.patch @@ -0,0 +1,38 @@ +From b9b967cca71a4da51506f8ba596b9ae40cfcef57 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Thu, 1 Apr 2021 21:42:38 +0200 +Subject: [PATCH 16/23] smtp_out + +--- + src/smtp_out.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/src/smtp_out.c b/src/smtp_out.c +index c4c409677..9c160e697 100644 +--- a/src/smtp_out.c ++++ b/src/smtp_out.c +@@ -53,11 +53,8 @@ if (!(expint = expand_string(istring))) + return FALSE; + } + +-if (is_tainted(expint)) ++if (is_tainted2(expint, LOG_MAIN|LOG_PANIC, "Tainted value '%s' from '%s' for interface", expint, istring)) + { +- log_write(0, LOG_MAIN|LOG_PANIC, +- "attempt to use tainted value '%s' from '%s' for interface", +- expint, istring); + addr->transport_return = PANIC; + addr->message = string_sprintf("failed to expand \"interface\" " + "option for %s: configuration error", msg); +@@ -425,7 +422,7 @@ if (ob->socks_proxy) + { + int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface, + sc->tblock, ob->connect_timeout); +- ++ + if (sock >= 0) + { + if (early_data && early_data->data && early_data->len) +-- +2.30.2 + diff --git a/debian/patches/75_17-smtp.patch b/debian/patches/75_17-smtp.patch new file mode 100644 index 0000000..c77a284 --- /dev/null +++ b/debian/patches/75_17-smtp.patch @@ -0,0 +1,29 @@ +From 8b7d4ba8903ace7e3e3db70343798a5a0b7cea23 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Thu, 1 Apr 2021 22:02:27 +0200 +Subject: [PATCH 17/23] smtp + +--- + src/transports/smtp.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/src/transports/smtp.c b/src/transports/smtp.c +index 6540e4d2b..8fecf7eef 100644 +--- a/src/transports/smtp.c ++++ b/src/transports/smtp.c +@@ -4715,11 +4715,8 @@ if (!hostlist || (ob->hosts_override && ob->hosts)) + else + if (ob->hosts_randomize) s = expanded_hosts = string_copy(s); + +- if (is_tainted(s)) ++ if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted host list '%s' from '%s' in transport %s", s, ob->hosts, tblock->name)) + { +- log_write(0, LOG_MAIN|LOG_PANIC, +- "attempt to use tainted host list '%s' from '%s' in transport %s", +- s, ob->hosts, tblock->name); + /* Avoid leaking info to an attacker */ + addrlist->message = US"internal configuration error"; + addrlist->transport_return = PANIC; +-- +2.30.2 + diff --git a/debian/patches/75_18-update-doc.patch b/debian/patches/75_18-update-doc.patch new file mode 100644 index 0000000..2edba69 --- /dev/null +++ b/debian/patches/75_18-update-doc.patch @@ -0,0 +1,154 @@ +From 77cc1ad3058e4ef7ae82adb914ccff0be9fe2c8b Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sat, 3 Apr 2021 09:29:13 +0200 +Subject: [PATCH 18/23] update doc + +--- + doc/doc-docbook/spec.xfpt | 45 ++++++++++++++++++++++++++++++++++++++- + doc/NewStuff | 45 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 89 insertions(+), 1 deletion(-) + +--- a/doc/NewStuff ++++ b/doc/NewStuff +@@ -4,10 +4,55 @@ + This file contains descriptions of new features that have been added to Exim. + Before a formal release, there may be quite a lot of detail so that people can + test from the snapshots or the Git before the documentation is updated. Once + the documentation is updated, this file is reduced to a short list. + ++Version 4.95 ++------------ ++ ++ 1. The fast-ramp two phase queue run support, previously experimental, is ++ now supported by default. ++ ++ 2. The native SRS support, previously experimental, is now supported. It is ++ not built unless specified in the Local/Makefile. ++ ++ 3. TLS resumption support, previously experimental, is now supported and ++ included in default builds. ++ ++ 4. Single-key LMDB lookups, previously experimental, are now supported. ++ The support is not built unless specified in the Local/Makefile. ++ ++ 5. Option "message_linelength_limit" on the smtp transport to enforce (by ++ default) the RFC 998 character limit. ++ ++ 6. An option to ignore the cache on a lookup. ++ ++ 7. Quota checking during reception (i.e. at SMTP time) for appendfile- ++ transport-managed quotas. ++ ++ 8. Sqlite lookups accept a "file=<path>" option to specify a per-operation ++ db file, replacing the previous prefix to the SQL string (which had ++ issues when the SQL used tainted values). ++ ++ 9. Lsearch lookups accept a "ret=full" option, to return both the portion ++ of the line matching the key, and the remainder. ++ ++10. A command-line option to have a daemon not create a notifier socket. ++ ++11. Faster TLS startup. When various configuration options contain no ++ expandable elements, the information can be preloaded and cached rather ++ than the provious behaviour of always loading at startup time for every ++ connection. This helps particularly for the CA bundle. ++ ++12. Proxy Protocol Timeout is configurable via "proxy_protocol_timeout" ++ main config option. ++ ++13. Option "smtp_accept_msx_per_connection" is now expanded. ++ ++13. A main config option "allow_insecure_tainted_data" allows to turn ++ taint errors into warnings. ++ + Version 4.94 + ------------ + + 1. EXPERIMENTAL_SRS_NATIVE optional build feature. See the experimental.spec + file. +--- a/doc/spec.txt ++++ b/doc/spec.txt +@@ -8650,12 +8650,20 @@ + Whether a string is expanded depends upon the context. Usually this is solely + dependent upon the option for which a value is sought; in this documentation, + options for which string expansion is performed are marked with * after the + data type. ACL rules always expand strings. A couple of expansion conditions do + not expand some of the brace-delimited branches, for security reasons, and +-expansion of data deriving from the sender ("tainted data") is not permitted. +- ++expansion of data deriving from the sender ("tainted data") is not permitted ++(including acessing a file using a tainted name). The main config ++option allow_insecure_tainted_data can be used as mitigation during ++uprades to more secure configurations. ++ ++Common ways of obtaining untainted equivalents of variables with tainted ++values come down to using the tainted value as a lookup key in a trusted ++database. This database could be the filesystem structure, or the ++password file, or accessed via a DBMS. Specific methods are indexed ++under "de-tainting". + + 11.1 Literal text in expanded strings + ------------------------------------- + + An uninterpreted dollar can be included in an expanded string by putting a +@@ -12946,10 +12954,12 @@ + + + 14.1 Miscellaneous + ------------------ + ++add_environment environment variables ++allow_insecure_tainted_data turn taint errors into warnings + bi_command to run for -bi command line option + debug_store do extra internal checks + disable_ipv6 do no IPv6 processing + keep_malformed for broken files - should not happen + localhost_number for unique message ids in clusters +@@ -13553,10 +13563,20 @@ + true, and also to add "@[]" to the list of local domains (defined in the named + domain list local_domains in the default configuration). This "magic string" + matches the domain literal form of all the local host's IP addresses. + + +-----------------------------------------------------+ ++|allow_insecure_tainted_data main boolean false | +++-----------------------------------------------------+ ++ ++The handling of tainted data may break older (pre 4.94) configurations. ++Setting this option to "true" turns taint errors (which result in a temporary ++message rejection) into warnings. This option is meant as mitigation only ++and deprecated already today. Future releases of Exim may ignore it. ++The taint log selector can be used to suppress even the warnings. ++ +++-----------------------------------------------------+ + |allow_mx_to_ip|Use: main|Type: boolean|Default: false| + +-----------------------------------------------------+ + + It appears that more and more DNS zone administrators are breaking the rules + and putting domain names that look like IP addresses on the right hand side of +@@ -35316,10 +35336,11 @@ + smtp_mailauth AUTH argument to MAIL commands + smtp_no_mail session with no MAIL commands + smtp_protocol_error SMTP protocol errors + smtp_syntax_error SMTP syntax errors + subject contents of Subject: on <= lines ++*taint taint errors or warnings + *tls_certificate_verified certificate verification status + *tls_cipher TLS cipher suite on <= and => lines + tls_peerdn TLS peer DN on <= and => lines + tls_sni TLS SNI on <= lines + unknown_in_list DNS lookup failed in list match +@@ -35604,11 +35625,13 @@ + + * tls_certificate_verified: An extra item is added to <= and => log lines + when TLS is in use. The item is "CV=yes" if the peer's certificate was + verified using a CA trust anchor, "CA=dane" if using a DNS trust anchor, + and "CV=no" if not. +- ++ * taint: Log warnings about tainted data. This selector can't be ++ turned of if allow_insecure_tainted_data is false (which is the ++ default). + * tls_cipher: When a message is sent or received over an encrypted + connection, the cipher suite used is added to the log line, preceded by X=. + + * tls_peerdn: When a message is sent or received over an encrypted + connection, and a certificate is supplied by the remote host, the peer DN diff --git a/debian/patches/75_20-Set-mainlog_name-and-rejectlog_name-unconditionally.patch b/debian/patches/75_20-Set-mainlog_name-and-rejectlog_name-unconditionally.patch new file mode 100644 index 0000000..a660aeb --- /dev/null +++ b/debian/patches/75_20-Set-mainlog_name-and-rejectlog_name-unconditionally.patch @@ -0,0 +1,42 @@ +From 41c494e2465efadc2e82002a07430e8aec85bc9b Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Mon, 12 Apr 2021 08:41:44 +0200 +Subject: [PATCH 20/23] Set mainlog_name and rejectlog_name unconditionally. + +(cherry picked from commit 3f06b9b4c7244b169d50bce216c1f54b4dfe7efb) +--- + src/log.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/src/log.c b/src/log.c +index 99eba5f90..011c4debc 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -402,18 +402,20 @@ it gets statted to see if it has been cycled. With a datestamp, the datestamp + will be compared. The static slot for saving it is the same size as buffer, + and the text has been checked above to fit, so this use of strcpy() is OK. */ + +-if (type == lt_main && string_datestamp_offset >= 0) ++if (type == lt_main) + { + Ustrcpy(mainlog_name, buffer); +- mainlog_datestamp = mainlog_name + string_datestamp_offset; ++ if (string_datestamp_offset > 0) ++ mainlog_datestamp = mainlog_name + string_datestamp_offset; + } + + /* Ditto for the reject log */ + +-else if (type == lt_reject && string_datestamp_offset >= 0) ++else if (type == lt_reject) + { + Ustrcpy(rejectlog_name, buffer); +- rejectlog_datestamp = rejectlog_name + string_datestamp_offset; ++ if (string_datestamp_offset > 0) ++ rejectlog_datestamp = rejectlog_name + string_datestamp_offset; + } + + /* and deal with the debug log (which keeps the datestamp, but does not +-- +2.30.2 + diff --git a/debian/patches/75_21-tidy-log.c.patch b/debian/patches/75_21-tidy-log.c.patch new file mode 100644 index 0000000..b99f0c6 --- /dev/null +++ b/debian/patches/75_21-tidy-log.c.patch @@ -0,0 +1,124 @@ +From 8021b95c2e266861aba29c97b4bb90dc6f7637a2 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Mon, 12 Apr 2021 09:19:21 +0200 +Subject: [PATCH 21/23] tidy log.c + +(cherry picked from commit 0327b6460eec64da6b0c1543c7e9b3d0f8cb9294) +--- + src/log.c | 97 +++++++++++++++++++++++---------------------------- + 1 file changed, 44 insertions(+), 53 deletions(-) + +diff --git a/src/log.c b/src/log.c +index 011c4debc..7ef7074ec 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -397,62 +397,53 @@ people want, I hope. */ + + ok = string_format(buffer, sizeof(buffer), CS file_path, log_names[type]); + +-/* Save the name of the mainlog for rollover processing. Without a datestamp, +-it gets statted to see if it has been cycled. With a datestamp, the datestamp +-will be compared. The static slot for saving it is the same size as buffer, +-and the text has been checked above to fit, so this use of strcpy() is OK. */ +- +-if (type == lt_main) ++switch (type) + { +- Ustrcpy(mainlog_name, buffer); +- if (string_datestamp_offset > 0) +- mainlog_datestamp = mainlog_name + string_datestamp_offset; +- } +- +-/* Ditto for the reject log */ +- +-else if (type == lt_reject) +- { +- Ustrcpy(rejectlog_name, buffer); +- if (string_datestamp_offset > 0) +- rejectlog_datestamp = rejectlog_name + string_datestamp_offset; +- } +- +-/* and deal with the debug log (which keeps the datestamp, but does not +-update it) */ +- +-else if (type == lt_debug) +- { +- Ustrcpy(debuglog_name, buffer); +- if (tag) +- { +- /* this won't change the offset of the datestamp */ +- ok2 = string_format(buffer, sizeof(buffer), "%s%s", +- debuglog_name, tag); +- if (ok2) +- Ustrcpy(debuglog_name, buffer); +- } +- } +- +-/* Remove any datestamp if this is the panic log. This is rare, so there's no +-need to optimize getting the datestamp length. We remove one non-alphanumeric +-char afterwards if at the start, otherwise one before. */ +- +-else if (string_datestamp_offset >= 0) +- { +- uschar * from = buffer + string_datestamp_offset; +- uschar * to = from + string_datestamp_length; ++ case lt_main: ++ /* Save the name of the mainlog for rollover processing. Without a datestamp, ++ it gets statted to see if it has been cycled. With a datestamp, the datestamp ++ will be compared. The static slot for saving it is the same size as buffer, ++ and the text has been checked above to fit, so this use of strcpy() is OK. */ ++ Ustrcpy(mainlog_name, buffer); ++ if (string_datestamp_offset > 0) ++ mainlog_datestamp = mainlog_name + string_datestamp_offset; ++ case lt_reject: ++ /* Ditto for the reject log */ ++ Ustrcpy(rejectlog_name, buffer); ++ if (string_datestamp_offset > 0) ++ rejectlog_datestamp = rejectlog_name + string_datestamp_offset; ++ case lt_debug: ++ /* and deal with the debug log (which keeps the datestamp, but does not ++ update it) */ ++ Ustrcpy(debuglog_name, buffer); ++ if (tag) ++ { ++ /* this won't change the offset of the datestamp */ ++ ok2 = string_format(buffer, sizeof(buffer), "%s%s", ++ debuglog_name, tag); ++ if (ok2) ++ Ustrcpy(debuglog_name, buffer); ++ } ++ default: ++ /* Remove any datestamp if this is the panic log. This is rare, so there's no ++ need to optimize getting the datestamp length. We remove one non-alphanumeric ++ char afterwards if at the start, otherwise one before. */ ++ if (string_datestamp_offset >= 0) ++ { ++ uschar * from = buffer + string_datestamp_offset; ++ uschar * to = from + string_datestamp_length; + +- if (from == buffer || from[-1] == '/') +- { +- if (!isalnum(*to)) to++; +- } +- else +- if (!isalnum(from[-1])) from--; ++ if (from == buffer || from[-1] == '/') ++ { ++ if (!isalnum(*to)) to++; ++ } ++ else ++ if (!isalnum(from[-1])) from--; + +- /* This copy is ok, because we know that to is a substring of from. But +- due to overlap we must use memmove() not Ustrcpy(). */ +- memmove(from, to, Ustrlen(to)+1); ++ /* This copy is ok, because we know that to is a substring of from. But ++ due to overlap we must use memmove() not Ustrcpy(). */ ++ memmove(from, to, Ustrlen(to)+1); ++ } + } + + /* If the file name is too long, it is an unrecoverable disaster */ +-- +2.30.2 + diff --git a/debian/patches/75_22-Silence-compiler.patch b/debian/patches/75_22-Silence-compiler.patch new file mode 100644 index 0000000..ce6e898 --- /dev/null +++ b/debian/patches/75_22-Silence-compiler.patch @@ -0,0 +1,222 @@ +From 2c9869d0622cc690b424cc74166d4a8393017ece Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Fri, 23 Apr 2021 17:40:40 +0200 +Subject: [PATCH 22/23] Silence compiler + +--- + src/acl.c | 2 +- + src/deliver.c | 3 +-- + src/expand.c | 6 +++++- + src/functions.h | 2 +- + src/lookups/lf_sqlperform.c | 4 ++-- + src/parse.c | 2 +- + src/rda.c | 2 +- + src/transports/appendfile.c | 4 ++-- + src/transports/autoreply.c | 12 ++++++------ + src/transports/pipe.c | 4 ++-- + 10 files changed, 22 insertions(+), 19 deletions(-) + +diff --git a/src/acl.c b/src/acl.c +index 81beab5f3..b62af5c65 100644 +--- a/src/acl.c ++++ b/src/acl.c +@@ -3600,7 +3600,7 @@ for (; cb; cb = cb->next) + case ACLC_QUEUE: + { + uschar *m; +- if (m = is_tainted2(arg, 0, "Tainted name '%s' for queue not permitted", arg)) ++ if ((m = is_tainted2(arg, 0, "Tainted name '%s' for queue not permitted", arg))) + { + *log_msgptr = m; + return ERROR; +diff --git a/src/deliver.c b/src/deliver.c +index 87e944b03..b40eed4f9 100644 +--- a/src/deliver.c ++++ b/src/deliver.c +@@ -6149,12 +6149,11 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) + { + uschar *tmp = expand_string(tpname); + address_file = address_pipe = NULL; +- uschar *m; + if (!tmp) + p->message = string_sprintf("failed to expand \"%s\" as a " + "system filter transport name", tpname); + { uschar *m; +- if (m = is_tainted2(tmp, 0, "Tainted values '%s' " "for transport '%s' as a system filter", tmp, tpname)) ++ if ((m = is_tainted2(tmp, 0, "Tainted values '%s' " "for transport '%s' as a system filter", tmp, tpname))) + p->message = m; + } + tpname = tmp; +diff --git a/src/expand.c b/src/expand.c +index 21b86ebf5..dc4b4e102 100644 +--- a/src/expand.c ++++ b/src/expand.c +@@ -4384,7 +4384,7 @@ f.expand_string_forcedfail = FALSE; + expand_string_message = US""; + + { uschar *m; +-if (m = is_tainted2(string, LOG_MAIN|LOG_PANIC, "Tainted string '%s' in expansion", s)) ++if ((m = is_tainted2(string, LOG_MAIN|LOG_PANIC, "Tainted string '%s' in expansion", s))) + { + expand_string_message = m; + goto EXPAND_FAILED; +@@ -7629,10 +7629,12 @@ while (*s != 0) + /* Manually track tainting, as we deal in individual chars below */ + + if (is_tainted(sub)) ++ { + if (yield->s && yield->ptr) + gstring_rebuffer(yield); + else + yield->s = store_get(yield->size = Ustrlen(sub), TRUE); ++ } + + /* Check the UTF-8, byte-by-byte */ + +@@ -8193,6 +8195,7 @@ that is a bad idea, because expand_string_message is in dynamic store. */ + EXPAND_FAILED: + if (left) *left = s; + DEBUG(D_expand) ++ { + DEBUG(D_noutf8) + { + debug_printf_indent("|failed to expand: %s\n", string); +@@ -8212,6 +8215,7 @@ DEBUG(D_expand) + if (f.expand_string_forcedfail) + debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n"); + } ++ } + if (resetok_p && !resetok) *resetok_p = FALSE; + expand_level--; + return NULL; +diff --git a/src/functions.h b/src/functions.h +index 1e8083673..b4d23c4bc 100644 +--- a/src/functions.h ++++ b/src/functions.h +@@ -1084,7 +1084,7 @@ if (f.running_in_test_harness && f.testsuite_delays) millisleep(millisec); + /******************************************************************************/ + /* Taint-checked file opens */ + static inline uschar * +-is_tainted2(const void *p, int lflags, const uschar* fmt, ...) ++is_tainted2(const void *p, int lflags, const char* fmt, ...) + { + va_list ap; + uschar *msg; +diff --git a/src/lookups/lf_sqlperform.c b/src/lookups/lf_sqlperform.c +index eda3089e2..38b7c2ad3 100644 +--- a/src/lookups/lf_sqlperform.c ++++ b/src/lookups/lf_sqlperform.c +@@ -103,7 +103,7 @@ if (Ustrncmp(query, "servers", 7) == 0) + } + + { uschar *m; +- if (m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server)) ++ if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server))) + { + *errmsg = m; + return DEFER; +@@ -161,7 +161,7 @@ else + } + + { uschar *m; +- if (is_tainted2(server, 0, "Tainted %s server '%s'", name, server)) ++ if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server))) + { + *errmsg = m; + return DEFER; +diff --git a/src/parse.c b/src/parse.c +index d1bc79039..0622b3127 100644 +--- a/src/parse.c ++++ b/src/parse.c +@@ -1402,7 +1402,7 @@ for (;;) + return FF_ERROR; + } + +- if (*error = is_tainted2(filename, 0, "Tainted name '%s' for included file not permitted\n", filename)) ++ if ((*error = is_tainted2(filename, 0, "Tainted name '%s' for included file not permitted\n", filename))) + return FF_ERROR; + + /* Check file name if required */ +diff --git a/src/rda.c b/src/rda.c +index 6ad7dd8bd..bba0b719b 100644 +--- a/src/rda.c ++++ b/src/rda.c +@@ -179,7 +179,7 @@ struct stat statbuf; + /* Reading a file is a form of expansion; we wish to deny attackers the + capability to specify the file name. */ + +-if (*error = is_tainted2(filename, 0, "Tainted name '%s' for file read not permitted\n", filename)) ++if ((*error = is_tainted2(filename, 0, "Tainted name '%s' for file read not permitted\n", filename))) + { + *yield = FF_ERROR; + return NULL; +diff --git a/src/transports/appendfile.c b/src/transports/appendfile.c +index 7dbbaa2f9..6772b338b 100644 +--- a/src/transports/appendfile.c ++++ b/src/transports/appendfile.c +@@ -1287,8 +1287,8 @@ if (!(path = expand_string(fdname))) + goto ret_panic; + } + { uschar *m; +-if (m = is_tainted2(path, 0, "Tainted '%s' (file or directory " +- "name for %s transport) not permitted", path, tblock->name)) ++if ((m = is_tainted2(path, 0, "Tainted '%s' (file or directory " ++ "name for %s transport) not permitted", path, tblock->name))) + { + addr->message = m; + goto ret_panic; +diff --git a/src/transports/autoreply.c b/src/transports/autoreply.c +index ed99de4c6..80c7c0db0 100644 +--- a/src/transports/autoreply.c ++++ b/src/transports/autoreply.c +@@ -407,8 +407,8 @@ if (oncelog && *oncelog && to) + uschar *m; + time_t then = 0; + +- if (m = is_tainted2(oncelog, 0, "Tainted '%s' (once file for %s transport)" +- " not permitted", oncelog, tblock->name)) ++ if ((m = is_tainted2(oncelog, 0, "Tainted '%s' (once file for %s transport)" ++ " not permitted", oncelog, tblock->name))) + { + addr->transport_return = DEFER; + addr->basic_errno = EACCES; +@@ -518,8 +518,8 @@ if (oncelog && *oncelog && to) + { + uschar *m; + int log_fd; +- if (m = is_tainted2(logfile, 0, "Tainted '%s' (logfile for %s transport)" +- " not permitted", logfile, tblock->name)) ++ if ((m = is_tainted2(logfile, 0, "Tainted '%s' (logfile for %s transport)" ++ " not permitted", logfile, tblock->name))) + { + addr->transport_return = DEFER; + addr->basic_errno = EACCES; +@@ -551,8 +551,8 @@ if (oncelog && *oncelog && to) + if (file) + { + uschar *m; +- if (m = is_tainted2(file, 0, "Tainted '%s' (file for %s transport)" +- " not permitted", file, tblock->name)) ++ if ((m = is_tainted2(file, 0, "Tainted '%s' (file for %s transport)" ++ " not permitted", file, tblock->name))) + { + addr->transport_return = DEFER; + addr->basic_errno = EACCES; +diff --git a/src/transports/pipe.c b/src/transports/pipe.c +index 4c9e68beb..fc44fa585 100644 +--- a/src/transports/pipe.c ++++ b/src/transports/pipe.c +@@ -601,8 +601,8 @@ if (!cmd || !*cmd) + } + + { uschar *m; +-if (m = is_tainted2(cmd, 0, "Tainted '%s' (command " +- "for %s transport) not permitted", cmd, tblock->name)) ++if ((m = is_tainted2(cmd, 0, "Tainted '%s' (command " ++ "for %s transport) not permitted", cmd, tblock->name))) + { + addr->transport_return = PANIC; + addr->message = m; +-- +2.30.2 + diff --git a/debian/patches/75_23-Do-not-close-the-main-_log-if-we-do-not-see-a-chance.patch b/debian/patches/75_23-Do-not-close-the-main-_log-if-we-do-not-see-a-chance.patch new file mode 100644 index 0000000..12d9163 --- /dev/null +++ b/debian/patches/75_23-Do-not-close-the-main-_log-if-we-do-not-see-a-chance.patch @@ -0,0 +1,166 @@ +From 235c7030ee9ee1c1aad507786506a470b580bfe2 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Fri, 23 Apr 2021 22:41:57 +0200 +Subject: [PATCH 23/23] Do not close the (main)_log, if we do not see a chance + to open it again. + +The process doing local deliveries runs as an unprivileged user. If this +process needs to log failures or warnings (as caused by the +is_tainting2() function), it can't re-open the main_log and just exits. +--- + src/log.c | 84 ++++++++++++++++----------------- + src/transports/appendfile.c | 6 +++ + 2 files changed, 47 insertions(+), 43 deletions(-) + +diff --git a/src/log.c b/src/log.c +index 7ef7074ec..c2ef698e7 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -646,18 +646,36 @@ return total_written; + } + + +- +-static void +-set_file_path(void) ++void ++set_file_path(BOOL *multiple) + { ++uschar *s; + int sep = ':'; /* Fixed separator - outside use */ +-uschar *t; +-const uschar *tt = US LOG_FILE_PATH; +-while ((t = string_nextinlist(&tt, &sep, log_buffer, LOG_BUFFER_SIZE))) ++uschar *ss = *log_file_path ? log_file_path : LOG_FILE_PATH; ++ ++logging_mode = 0; ++while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) + { +- if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; +- file_path = string_copy(t); +- break; ++ if (Ustrcmp(s, "syslog") == 0) ++ logging_mode |= LOG_MODE_SYSLOG; ++ else if (logging_mode & LOG_MODE_FILE) /* we know a file already */ ++ { ++ if (multiple) *multiple = TRUE; ++ } ++ else ++ { ++ logging_mode |= LOG_MODE_FILE; ++ ++ /* If a non-empty path is given, use it */ ++ ++ if (*s) ++ file_path = string_copy(s); ++ ++ /* If the path is empty, we want to use the first non-empty, non- ++ syslog item in LOG_FILE_PATH, if there is one, since the value of ++ log_file_path may have been set at runtime. If there is no such item, ++ use the ultimate default in the spool directory. */ ++ } + } + } + +@@ -665,7 +683,11 @@ while ((t = string_nextinlist(&tt, &sep, log_buffer, LOG_BUFFER_SIZE))) + void + mainlog_close(void) + { +-if (mainlogfd < 0) return; ++/* avoid closing it if it is closed already or if we do not see a chance ++to open the file mainlog later again */ ++if (mainlogfd < 0 /* already closed */ ++ || !(geteuid() == 0 || geteuid() == exim_uid)) ++ return; + (void)close(mainlogfd); + mainlogfd = -1; + mainlog_inode = 0; +@@ -780,38 +802,7 @@ if (!path_inspected) + /* If nothing has been set, don't waste effort... the default values for the + statics are file_path="" and logging_mode = LOG_MODE_FILE. */ + +- if (*log_file_path) +- { +- int sep = ':'; /* Fixed separator - outside use */ +- uschar *s; +- const uschar *ss = log_file_path; +- +- logging_mode = 0; +- while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) +- { +- if (Ustrcmp(s, "syslog") == 0) +- logging_mode |= LOG_MODE_SYSLOG; +- else if (logging_mode & LOG_MODE_FILE) +- multiple = TRUE; +- else +- { +- logging_mode |= LOG_MODE_FILE; +- +- /* If a non-empty path is given, use it */ +- +- if (*s) +- file_path = string_copy(s); +- +- /* If the path is empty, we want to use the first non-empty, non- +- syslog item in LOG_FILE_PATH, if there is one, since the value of +- log_file_path may have been set at runtime. If there is no such item, +- use the ultimate default in the spool directory. */ +- +- else +- set_file_path(); /* Empty item in log_file_path */ +- } /* First non-syslog item in log_file_path */ +- } /* Scan of log_file_path */ +- } ++ if (*log_file_path) set_file_path(&multiple); + + /* If no modes have been selected, it is a major disaster */ + +@@ -1431,7 +1422,7 @@ if (opts) + resulting in certain setup not having been done. Hack this for now so we + do not segfault; note that nondefault log locations will not work */ + +-if (!*file_path) set_file_path(); ++if (!*file_path) set_file_path(NULL); + + open_log(&fd, lt_debug, tag_name); + +@@ -1453,5 +1444,12 @@ debug_file = NULL; + unlink_log(lt_debug); + } + ++void ++open_logs(const char *m) ++{ ++set_file_path(NULL); ++open_log(&mainlogfd, lt_main, 0); ++open_log(&rejectlogfd, lt_reject, 0); ++} + + /* End of log.c */ +diff --git a/src/transports/appendfile.c b/src/transports/appendfile.c +index 6772b338b..706af6dde 100644 +--- a/src/transports/appendfile.c ++++ b/src/transports/appendfile.c +@@ -217,6 +217,9 @@ Arguments: + Returns: OK, FAIL, or DEFER + */ + ++void ++openlogs(); ++ + static int + appendfile_transport_setup(transport_instance *tblock, address_item *addrlist, + transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg) +@@ -231,6 +234,9 @@ dummy = dummy; + uid = uid; + gid = gid; + ++/* we can't wait until we're not privileged anymore */ ++open_logs("appendfile"); ++ + if (ob->expand_maildir_use_size_file) + ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file, + US"`maildir_use_size_file` in transport", tblock->name); +-- +2.30.2 + diff --git a/debian/patches/75_24-Silence-the-compiler.patch b/debian/patches/75_24-Silence-the-compiler.patch new file mode 100644 index 0000000..14c3d5b --- /dev/null +++ b/debian/patches/75_24-Silence-the-compiler.patch @@ -0,0 +1,57 @@ +From 33d5b8e8e4c2f23b4e834e3a095e3c9dd9f0686b Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 25 Apr 2021 18:58:35 +0200 +Subject: [PATCH 1/4] Silence the compiler + +--- + src/log.c | 4 ++-- + src/transports/appendfile.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/log.c b/src/log.c +index c2ef698e7..11d259197 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -651,7 +651,7 @@ set_file_path(BOOL *multiple) + { + uschar *s; + int sep = ':'; /* Fixed separator - outside use */ +-uschar *ss = *log_file_path ? log_file_path : LOG_FILE_PATH; ++const uschar *ss = *log_file_path ? log_file_path : US LOG_FILE_PATH; + + logging_mode = 0; + while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) +@@ -1445,7 +1445,7 @@ unlink_log(lt_debug); + } + + void +-open_logs(const char *m) ++open_logs(void) + { + set_file_path(NULL); + open_log(&mainlogfd, lt_main, 0); +diff --git a/src/transports/appendfile.c b/src/transports/appendfile.c +index 706af6dde..c0f4de4c8 100644 +--- a/src/transports/appendfile.c ++++ b/src/transports/appendfile.c +@@ -218,7 +218,7 @@ Returns: OK, FAIL, or DEFER + */ + + void +-openlogs(); ++open_logs(void); + + static int + appendfile_transport_setup(transport_instance *tblock, address_item *addrlist, +@@ -235,7 +235,7 @@ uid = uid; + gid = gid; + + /* we can't wait until we're not privileged anymore */ +-open_logs("appendfile"); ++open_logs(); + + if (ob->expand_maildir_use_size_file) + ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file, +-- +2.30.2 + diff --git a/debian/patches/75_26-Disable-taintchecks-for-mkdir-this-isn-t-part-of-4.9.patch b/debian/patches/75_26-Disable-taintchecks-for-mkdir-this-isn-t-part-of-4.9.patch new file mode 100644 index 0000000..cee6066 --- /dev/null +++ b/debian/patches/75_26-Disable-taintchecks-for-mkdir-this-isn-t-part-of-4.9.patch @@ -0,0 +1,27 @@ +From 1416743e923cacf42955392e92995f5fe7e1c680 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sun, 25 Apr 2021 10:19:32 +0200 +Subject: [PATCH 3/4] Disable taintchecks for mkdir, this isn't part of 4.94 + +--- + src/directory.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/directory.c b/src/directory.c +index 9f88f4141..ece1ee8f3 100644 +--- a/src/directory.c ++++ b/src/directory.c +@@ -44,8 +44,10 @@ uschar c = 1; + struct stat statbuf; + uschar * path; + ++/* does not work with 4.94 + if (is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted path '%s' for new directory", name)) + { p = US"create"; path = US name; errno = EACCES; goto bad; } ++*/ + + if (parent) + { +-- +2.30.2 + diff --git a/debian/patches/75_27_Fix-logging-with-empty-element-in-log_file_path-Bug-.patch b/debian/patches/75_27_Fix-logging-with-empty-element-in-log_file_path-Bug-.patch new file mode 100644 index 0000000..58da37c --- /dev/null +++ b/debian/patches/75_27_Fix-logging-with-empty-element-in-log_file_path-Bug-.patch @@ -0,0 +1,275 @@ +From e19790f7707cc901435849e78d20f249056c16b5 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Sat, 15 May 2021 13:37:04 +0200 +Subject: [PATCH 3/4] Fix logging with empty element in log_file_path (Bug + 2733) + +--- + src/log.c | 84 +++++++++++++++++++++++++++++++++------------------ + 1 file changed, 55 insertions(+), 29 deletions(-) + +diff --git a/src/log.c b/src/log.c +index 2108ed46f..716bec553 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -283,16 +283,19 @@ problem. */ + + if (fd < 0 && errno == ENOENT) + { + BOOL created; + uschar *lastslash = Ustrrchr(name, '/'); + *lastslash = 0; + created = directory_make(NULL, name, LOG_DIRECTORY_MODE, FALSE); +- DEBUG(D_any) debug_printf("%s log directory %s\n", +- created ? "created" : "failed to create", name); ++ DEBUG(D_any) ++ if (created) ++ debug_printf("created log directory %s\n", name); ++ else ++ debug_printf("failed to create log directory %s: %s\n", name, strerror(errno)); + *lastslash = '/'; + if (created) fd = Uopen(name, flags, LOG_MODE); + } + + return fd; + } + +@@ -390,17 +393,15 @@ Returns: a file descriptor, or < 0 on failure (errno set) + int + log_open_as_exim(uschar * const name) + { + int fd = -1; + const uid_t euid = geteuid(); + + if (euid == exim_uid) +- { + fd = log_open_already_exim(name); +- } + else if (euid == root_uid) + { + int sock[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == 0) + { + const pid_t pid = exim_fork(US"logfile-open"); + if (pid == 0) +@@ -495,38 +496,48 @@ ok = string_format(buffer, sizeof(buffer), CS file_path, log_names[type]); + switch (type) + { + case lt_main: + /* Save the name of the mainlog for rollover processing. Without a datestamp, + it gets statted to see if it has been cycled. With a datestamp, the datestamp + will be compared. The static slot for saving it is the same size as buffer, + and the text has been checked above to fit, so this use of strcpy() is OK. */ ++ + Ustrcpy(mainlog_name, buffer); + if (string_datestamp_offset > 0) + mainlog_datestamp = mainlog_name + string_datestamp_offset; ++ break; ++ + case lt_reject: + /* Ditto for the reject log */ ++ + Ustrcpy(rejectlog_name, buffer); + if (string_datestamp_offset > 0) + rejectlog_datestamp = rejectlog_name + string_datestamp_offset; ++ break; ++ + case lt_debug: + /* and deal with the debug log (which keeps the datestamp, but does not + update it) */ ++ + Ustrcpy(debuglog_name, buffer); + if (tag) + { + /* this won't change the offset of the datestamp */ + ok2 = string_format(buffer, sizeof(buffer), "%s%s", + debuglog_name, tag); + if (ok2) + Ustrcpy(debuglog_name, buffer); + } ++ break; ++ + default: + /* Remove any datestamp if this is the panic log. This is rare, so there's no + need to optimize getting the datestamp length. We remove one non-alphanumeric + char afterwards if at the start, otherwise one before. */ ++ + if (string_datestamp_offset >= 0) + { + uschar * from = buffer + string_datestamp_offset; + uschar * to = from + string_datestamp_length; + + if (from == buffer || from[-1] == '/') + { +@@ -535,30 +546,29 @@ switch (type) + else + if (!isalnum(from[-1])) from--; + + /* This copy is ok, because we know that to is a substring of from. But + due to overlap we must use memmove() not Ustrcpy(). */ + memmove(from, to, Ustrlen(to)+1); + } ++ break; + } + + /* If the file name is too long, it is an unrecoverable disaster */ + + if (!ok) + die(US"exim: log file path too long: aborting", + US"Logging failure; please try later"); + + /* We now have the file name. After a successful open, return. */ + + *fd = log_open_as_exim(buffer); + + if (*fd >= 0) +- { + return; +- } + + euid = geteuid(); + + /* Creation failed. There are some circumstances in which we get here when + the effective uid is not root or exim, which is the problem. (For example, a + non-setuid binary with log_arguments set, called in certain ways.) Rather than + just bombing out, force the log to stderr and carry on if stderr is available. +@@ -702,45 +712,61 @@ while (1) + left -= wrote; + } + } + return total_written; + } + + ++/* Pull the file out of the configured or the compiled-in list. ++Called for an empty log_file_path element, for debug logging activation ++when file_path has not previously been set, and from the appenfile transport setup. */ ++ + void + set_file_path(BOOL *multiple) + { + uschar *s; + int sep = ':'; /* Fixed separator - outside use */ + const uschar *ss = *log_file_path ? log_file_path : US LOG_FILE_PATH; + +-logging_mode = 0; +-while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) +- { +- if (Ustrcmp(s, "syslog") == 0) +- logging_mode |= LOG_MODE_SYSLOG; +- else if (logging_mode & LOG_MODE_FILE) /* we know a file already */ ++if (*ss) ++ for (logging_mode = 0; ++ s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE); ) + { +- if (multiple) *multiple = TRUE; +- } +- else +- { +- logging_mode |= LOG_MODE_FILE; ++ if (Ustrcmp(s, "syslog") == 0) ++ logging_mode |= LOG_MODE_SYSLOG; ++ else if (!(logging_mode & LOG_MODE_FILE)) /* no file yet */ ++ { ++ /* If a non-empty path is given, use it */ + +- /* If a non-empty path is given, use it */ ++ if (*s) ++ file_path = string_copy(s); + +- if (*s) +- file_path = string_copy(s); ++ /* If handling the config option, and the element is empty, we want to use ++ the first non-empty, non-syslog item in LOG_FILE_PATH, if there is one, ++ since the value of log_file_path may have been set at runtime. If there is ++ no such item, use the ultimate default in the spool directory. */ + +- /* If the path is empty, we want to use the first non-empty, non- +- syslog item in LOG_FILE_PATH, if there is one, since the value of +- log_file_path may have been set at runtime. If there is no such item, +- use the ultimate default in the spool directory. */ ++ else if (*log_file_path && LOG_FILE_PATH[0]) ++ { ++ ss = US LOG_FILE_PATH; ++ continue; ++ } ++ ++ logging_mode |= LOG_MODE_FILE; ++ } ++ else ++ if (multiple) *multiple = TRUE; + } +- } ++ else ++ logging_mode = LOG_MODE_FILE; ++ ++/* Set up the ultimate default if necessary. */ ++ ++if (logging_mode & LOG_MODE_FILE && !*file_path) ++ file_path = string_sprintf("%s/log/%%slog", spool_directory); + } + + + void + mainlog_close(void) + { + /* avoid closing it if it is closed already or if we do not see a chance +@@ -866,19 +892,16 @@ if (!path_inspected) + + /* If no modes have been selected, it is a major disaster */ + + if (logging_mode == 0) + die(US"Neither syslog nor file logging set in log_file_path", + US"Unexpected logging failure"); + +- /* Set up the ultimate default if necessary. Then revert to the old store +- pool, and record that we've sorted out the path. */ ++ /* Revert to the old store pool, and record that we've sorted out the path. */ + +- if (logging_mode & LOG_MODE_FILE && !file_path[0]) +- file_path = string_sprintf("%s/log/%%slog", spool_directory); + store_pool = old_pool; + path_inspected = TRUE; + + /* If more than one file path was given, log a complaint. This recursive call + should work since we have now set up the routing. */ + + if (multiple) +@@ -1224,14 +1247,15 @@ if (flags & LOG_PANIC) + write_syslog(LOG_ALERT, log_buffer); + + /* If this panic logging was caused by a failure to open the main log, + the original log line is in panic_save_buffer. Make an attempt to write it. */ + + if (logging_mode & LOG_MODE_FILE) + { ++ if (!*file_path) set_file_path(NULL); + panic_recurseflag = TRUE; + open_log(&paniclogfd, lt_panic, NULL); /* Won't return on failure */ + panic_recurseflag = FALSE; + + if (panic_save_buffer) + { + int i = write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer)); +@@ -1501,16 +1525,18 @@ if (!debug_file || !debuglog_name[0]) return; + + debug_selector = 0; + fclose(debug_file); + debug_file = NULL; + unlink_log(lt_debug); + } + ++/* Called from the appendfile transport setup. */ + void + open_logs(void) + { + set_file_path(NULL); ++if (!(logging_mode & LOG_MODE_FILE)) return; + open_log(&mainlogfd, lt_main, 0); + open_log(&rejectlogfd, lt_reject, 0); + } + + /* End of log.c */ +-- +2.30.2 + diff --git a/debian/patches/75_28_Fix-logging-with-build-time-config-and-empty-element.patch b/debian/patches/75_28_Fix-logging-with-build-time-config-and-empty-element.patch new file mode 100644 index 0000000..b6d092f --- /dev/null +++ b/debian/patches/75_28_Fix-logging-with-build-time-config-and-empty-element.patch @@ -0,0 +1,118 @@ +From 66392b270e3a6c8202e4626d43bbc9b77545ae23 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de> +Date: Sat, 15 May 2021 13:40:46 +0200 +Subject: [PATCH 4/4] Fix logging with build-time config and empty elements + (Closes 2733) + +--- + src/log.c | 49 +++++++++++++++++++++++-------------------------- + 1 file changed, 23 insertions(+), 26 deletions(-) + +diff --git a/src/log.c b/src/log.c +index 716bec553..1d308d008 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -454,15 +454,15 @@ return fd; + * Open a log file * + *************************************************/ + + /* This function opens one of a number of logs, creating the log directory if + it does not exist. This may be called recursively on failure, in order to open + the panic log. + +-The directory is in the static variable file_path. This is static so that it ++The directory is in the static variable file_path. This is static so that + the work of sorting out the path is done just once per Exim process. + + Exim is normally configured to avoid running as root wherever possible, the log + files must be owned by the non-privileged exim user. To ensure this, first try + an open without O_CREAT - most of the time this will succeed. If it fails, try + to create the file; if running as root, this must be done in a subprocess to + avoid races. +@@ -731,42 +731,40 @@ if (*ss) + for (logging_mode = 0; + s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE); ) + { + if (Ustrcmp(s, "syslog") == 0) + logging_mode |= LOG_MODE_SYSLOG; + else if (!(logging_mode & LOG_MODE_FILE)) /* no file yet */ + { +- /* If a non-empty path is given, use it */ +- +- if (*s) +- file_path = string_copy(s); +- +- /* If handling the config option, and the element is empty, we want to use +- the first non-empty, non-syslog item in LOG_FILE_PATH, if there is one, +- since the value of log_file_path may have been set at runtime. If there is +- no such item, use the ultimate default in the spool directory. */ +- +- else if (*log_file_path && LOG_FILE_PATH[0]) +- { +- ss = US LOG_FILE_PATH; +- continue; +- } +- + logging_mode |= LOG_MODE_FILE; ++ if (*s) file_path = string_copy(s); /* If a non-empty path is given, use it */ + } +- else +- if (multiple) *multiple = TRUE; ++ else if (multiple) *multiple = TRUE; + } +- else +- logging_mode = LOG_MODE_FILE; ++else ++ logging_mode = LOG_MODE_FILE; + + /* Set up the ultimate default if necessary. */ + + if (logging_mode & LOG_MODE_FILE && !*file_path) +- file_path = string_sprintf("%s/log/%%slog", spool_directory); ++ if (LOG_FILE_PATH[0]) ++ { ++ /* If we still do not have a file_path, we take ++ the first non-empty, non-syslog item in LOG_FILE_PATH, if there is ++ one. If there is no such item, use the ultimate default in the ++ spool directory. */ ++ ++ for (ss = US LOG_FILE_PATH; ++ s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE);) ++ { ++ if (*s != '/') continue; ++ file_path = string_copy(s); ++ } ++ } ++ else file_path = string_sprintf("%s/log/%%slog", spool_directory); + } + + + void + mainlog_close(void) + { + /* avoid closing it if it is closed already or if we do not see a chance +@@ -881,18 +879,17 @@ the process. */ + if (!path_inspected) + { + BOOL multiple = FALSE; + int old_pool = store_pool; + + store_pool = POOL_PERM; + +- /* If nothing has been set, don't waste effort... the default values for the +- statics are file_path="" and logging_mode = LOG_MODE_FILE. */ +- +- if (*log_file_path) set_file_path(&multiple); ++ /* make sure that we have a valid log file path in "file_path", ++ the open_log() later relies on it */ ++ set_file_path(&multiple); + + /* If no modes have been selected, it is a major disaster */ + + if (logging_mode == 0) + die(US"Neither syslog nor file logging set in log_file_path", + US"Unexpected logging failure"); + +-- +2.30.2 + diff --git a/debian/patches/75_29-Auths-fix-possible-OOB-write-in-external-authenticat.patch b/debian/patches/75_29-Auths-fix-possible-OOB-write-in-external-authenticat.patch new file mode 100644 index 0000000..f6936a9 --- /dev/null +++ b/debian/patches/75_29-Auths-fix-possible-OOB-write-in-external-authenticat.patch @@ -0,0 +1,22 @@ +From 7bb5bc2c6592e062bf0b514cc71afd2d93e2e0dd Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Thu, 11 May 2023 18:02:43 +0100 +Subject: [PATCH 1/4] Auths: fix possible OOB write in external authenticator. + Bug 2999 + +--- + doc/doc-txt/ChangeLog | 3 +++ + src/src/auths/external.c | 2 +- + 2 files changed, 4 insertions(+), 1 deletion(-) + +--- a/src/auths/external.c ++++ b/src/auths/external.c +@@ -103,7 +103,7 @@ if (expand_nmax == 0) /* skip if rxd da + if (ob->server_param2) + { + uschar * s = expand_string(ob->server_param2); +- auth_vars[expand_nmax] = s; ++ auth_vars[expand_nmax = 1] = s; + expand_nstring[++expand_nmax] = s; + expand_nlength[expand_nmax] = Ustrlen(s); + if (ob->server_param3) diff --git a/debian/patches/75_30-Auths-use-uschar-more-in-spa-authenticator.patch b/debian/patches/75_30-Auths-use-uschar-more-in-spa-authenticator.patch new file mode 100644 index 0000000..03958fc --- /dev/null +++ b/debian/patches/75_30-Auths-use-uschar-more-in-spa-authenticator.patch @@ -0,0 +1,226 @@ +From 0519dcfb5f149154a416b54865fd8026abb57791 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Thu, 11 May 2023 18:53:25 +0100 +Subject: [PATCH 2/4] Auths: use uschar more in spa authenticator + +--- + src/src/auths/auth-spa.c | 72 +++++++++++++++++++++------------------- + src/src/auths/auth-spa.h | 8 ++--- + src/src/auths/spa.c | 13 ++++---- + 3 files changed, 47 insertions(+), 46 deletions(-) + +--- a/src/auths/auth-spa.c ++++ b/src/auths/auth-spa.c +@@ -155,6 +155,9 @@ int main (int argc, char ** argv) + up with a different answer to the one above) + */ + ++#ifndef MACRO_PREDEF ++ ++ + #define DEBUG_X(a,b) ; + + extern int DEBUGLEVEL; +@@ -1229,21 +1232,21 @@ else \ + + #define spa_string_add(ptr, header, string) \ + { \ +-char *p = string; \ ++uschar * p = string; \ + int len = 0; \ +-if (p) len = strlen(p); \ +-spa_bytes_add(ptr, header, (US p), len); \ ++if (p) len = Ustrlen(p); \ ++spa_bytes_add(ptr, header, p, len); \ + } + + #define spa_unicode_add_string(ptr, header, string) \ + { \ +-char *p = string; \ +-uschar *b = NULL; \ ++uschar * p = string; \ ++uschar * b = NULL; \ + int len = 0; \ + if (p) \ + { \ +- len = strlen(p); \ +- b = strToUnicode(p); \ ++ len = Ustrlen(p); \ ++ b = US strToUnicode(CS p); \ + } \ + spa_bytes_add(ptr, header, b, len*2); \ + } +@@ -1366,15 +1369,15 @@ fprintf (fp, " Flags = %08x\n", IVA + #endif + + void +-spa_build_auth_request (SPAAuthRequest * request, char *user, char *domain) ++spa_build_auth_request (SPAAuthRequest * request, uschar * user, uschar * domain) + { +-char *u = strdup (user); +-char *p = strchr (u, '@'); ++uschar * u = string_copy(user); ++uschar * p = Ustrchr(u, '@'); + + if (p) + { + if (!domain) +- domain = p + 1; ++ domain = p + 1; + *p = '\0'; + } + +@@ -1384,7 +1387,6 @@ SIVAL (&request->msgType, 0, 1); + SIVAL (&request->flags, 0, 0x0000b207); /* have to figure out what these mean */ + spa_string_add (request, user, u); + spa_string_add (request, domain, domain); +-free (u); + } + + +@@ -1475,16 +1477,16 @@ free (u); + + void + spa_build_auth_response (SPAAuthChallenge * challenge, +- SPAAuthResponse * response, char *user, +- char *password) ++ SPAAuthResponse * response, uschar * user, ++ uschar * password) + { + uint8x lmRespData[24]; + uint8x ntRespData[24]; + uint32x cf = IVAL(&challenge->flags, 0); +-char *u = strdup (user); +-char *p = strchr (u, '@'); +-char *d = NULL; +-char *domain; ++uschar * u = string_copy(user); ++uschar * p = Ustrchr(u, '@'); ++uschar * d = NULL; ++uschar * domain; + + if (p) + { +@@ -1492,33 +1494,33 @@ if (p) + *p = '\0'; + } + +-else domain = d = strdup((cf & 0x1)? +- CCS GetUnicodeString(challenge, uDomain) : +- CCS GetString(challenge, uDomain)); ++else domain = d = string_copy(cf & 0x1 ++ ? CUS GetUnicodeString(challenge, uDomain) ++ : CUS GetString(challenge, uDomain)); + +-spa_smb_encrypt (US password, challenge->challengeData, lmRespData); +-spa_smb_nt_encrypt (US password, challenge->challengeData, ntRespData); ++spa_smb_encrypt(password, challenge->challengeData, lmRespData); ++spa_smb_nt_encrypt(password, challenge->challengeData, ntRespData); + + response->bufIndex = 0; + memcpy (response->ident, "NTLMSSP\0\0\0", 8); + SIVAL (&response->msgType, 0, 3); + +-spa_bytes_add (response, lmResponse, lmRespData, (cf & 0x200) ? 24 : 0); +-spa_bytes_add (response, ntResponse, ntRespData, (cf & 0x8000) ? 24 : 0); ++spa_bytes_add(response, lmResponse, lmRespData, cf & 0x200 ? 24 : 0); ++spa_bytes_add(response, ntResponse, ntRespData, cf & 0x8000 ? 24 : 0); + + if (cf & 0x1) { /* Unicode Text */ +- spa_unicode_add_string (response, uDomain, domain); +- spa_unicode_add_string (response, uUser, u); +- spa_unicode_add_string (response, uWks, u); ++ spa_unicode_add_string(response, uDomain, domain); ++ spa_unicode_add_string(response, uUser, u); ++ spa_unicode_add_string(response, uWks, u); + } else { /* OEM Text */ +- spa_string_add (response, uDomain, domain); +- spa_string_add (response, uUser, u); +- spa_string_add (response, uWks, u); ++ spa_string_add(response, uDomain, domain); ++ spa_string_add(response, uUser, u); ++ spa_string_add(response, uWks, u); + } + +-spa_string_add (response, sessionKey, NULL); ++spa_string_add(response, sessionKey, NULL); + response->flags = challenge->flags; +- +-if (d != NULL) free (d); +-free (u); + } ++ ++ ++#endif /*!MACRO_PREDEF*/ +--- a/src/auths/auth-spa.h ++++ b/src/auths/auth-spa.h +@@ -79,10 +79,10 @@ typedef struct + + void spa_bits_to_base64 (unsigned char *, const unsigned char *, int); + int spa_base64_to_bits(char *, int, const char *); +-void spa_build_auth_response (SPAAuthChallenge *challenge, +- SPAAuthResponse *response, char *user, char *password); +-void spa_build_auth_request (SPAAuthRequest *request, char *user, +- char *domain); ++void spa_build_auth_response (SPAAuthChallenge * challenge, ++ SPAAuthResponse * response, uschar * user, uschar * password); ++void spa_build_auth_request (SPAAuthRequest * request, uschar * user, ++ uschar * domain); + extern void spa_smb_encrypt (unsigned char * passwd, unsigned char * c8, + unsigned char * p24); + extern void spa_smb_nt_encrypt (unsigned char * passwd, unsigned char * c8, +--- a/src/auths/spa.c ++++ b/src/auths/spa.c +@@ -284,14 +284,13 @@ SPAAuthRequest request; + SPAAuthChallenge challenge; + SPAAuthResponse response; + char msgbuf[2048]; +-char *domain = NULL; +-char *username, *password; ++uschar * domain = NULL, * username, * password; + + /* Code added by PH to expand the options */ + + *buffer = 0; /* Default no message when cancelled */ + +-if (!(username = CS expand_string(ob->spa_username))) ++if (!(username = expand_string(ob->spa_username))) + { + if (f.expand_string_forcedfail) return CANCELLED; + string_format(buffer, buffsize, "expansion of \"%s\" failed in %s " +@@ -300,7 +299,7 @@ if (!(username = CS expand_string(ob->sp + return ERROR; + } + +-if (!(password = CS expand_string(ob->spa_password))) ++if (!(password = expand_string(ob->spa_password))) + { + if (f.expand_string_forcedfail) return CANCELLED; + string_format(buffer, buffsize, "expansion of \"%s\" failed in %s " +@@ -310,7 +309,7 @@ if (!(password = CS expand_string(ob->sp + } + + if (ob->spa_domain) +- if (!(domain = CS expand_string(ob->spa_domain))) ++ if (!(domain = expand_string(ob->spa_domain))) + { + if (f.expand_string_forcedfail) return CANCELLED; + string_format(buffer, buffsize, "expansion of \"%s\" failed in %s " +@@ -330,7 +329,7 @@ if (!smtp_read_response(sx, US buffer, b + + DSPA("\n\n%s authenticator: using domain %s\n\n", ablock->name, domain); + +-spa_build_auth_request(&request, CS username, domain); ++spa_build_auth_request(&request, username, domain); + spa_bits_to_base64(US msgbuf, US &request, spa_request_length(&request)); + + DSPA("\n\n%s authenticator: sending request (%s)\n\n", ablock->name, msgbuf); +@@ -347,7 +346,7 @@ if (!smtp_read_response(sx, US buffer, b + DSPA("\n\n%s authenticator: challenge (%s)\n\n", ablock->name, buffer + 4); + spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4)); + +-spa_build_auth_response(&challenge, &response, CS username, CS password); ++spa_build_auth_response(&challenge, &response, username, password); + spa_bits_to_base64(US msgbuf, US &response, spa_request_length(&response)); + DSPA("\n\n%s authenticator: challenge response (%s)\n\n", ablock->name, msgbuf); + diff --git a/debian/patches/75_31-Auths-fix-possible-OOB-write-in-SPA-authenticator.-B.patch b/debian/patches/75_31-Auths-fix-possible-OOB-write-in-SPA-authenticator.-B.patch new file mode 100644 index 0000000..8c763fa --- /dev/null +++ b/debian/patches/75_31-Auths-fix-possible-OOB-write-in-SPA-authenticator.-B.patch @@ -0,0 +1,24 @@ +From e17b8b0f19b25a223b0cc41933b881c3a1073e61 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Thu, 11 May 2023 19:31:54 +0100 +Subject: [PATCH 3/4] Auths: fix possible OOB write in SPA authenticator. Bug + 3000 + +--- + doc/doc-txt/ChangeLog | 3 +++ + src/src/auths/auth-spa.c | 4 +++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/src/auths/auth-spa.c ++++ b/src/auths/auth-spa.c +@@ -1214,7 +1214,9 @@ char versionString[] = "libntlm version + + #define spa_bytes_add(ptr, header, buf, count) \ + { \ +-if (buf && (count) != 0) /* we hate -Wint-in-bool-contex */ \ ++if ( buf && (count) != 0 /* we hate -Wint-in-bool-contex */ \ ++ && ptr->bufIndex + count < sizeof(ptr->buffer) \ ++ ) \ + { \ + SSVAL(&ptr->header.len,0,count); \ + SSVAL(&ptr->header.maxlen,0,count); \ diff --git a/debian/patches/75_32-Auths-fix-possible-OOB-read-in-SPA-authenticator.-Bu.patch b/debian/patches/75_32-Auths-fix-possible-OOB-read-in-SPA-authenticator.-Bu.patch new file mode 100644 index 0000000..d4e0eb6 --- /dev/null +++ b/debian/patches/75_32-Auths-fix-possible-OOB-read-in-SPA-authenticator.-Bu.patch @@ -0,0 +1,75 @@ +From 04107e98d58efb69f7e2d7b81176e5374c7098a3 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Thu, 11 May 2023 21:08:08 +0100 +Subject: [PATCH 4/4] Auths: fix possible OOB read in SPA authenticator. Bug + 3001 + +--- + doc/doc-txt/ChangeLog | 3 +++ + src/src/auths/auth-spa.c | 36 ++++++++++++++++++++++++++++-------- + 2 files changed, 31 insertions(+), 8 deletions(-) + +--- a/src/auths/auth-spa.c ++++ b/src/auths/auth-spa.c +@@ -1254,15 +1254,10 @@ spa_bytes_add(ptr, header, b, len*2); \ + } + + +-#define GetUnicodeString(structPtr, header) \ +-unicodeToString(((char*)structPtr) + IVAL(&structPtr->header.offset,0) , SVAL(&structPtr->header.len,0)/2) +-#define GetString(structPtr, header) \ +-toString(((CS structPtr) + IVAL(&structPtr->header.offset,0)), SVAL(&structPtr->header.len,0)) +- + #ifdef notdef + + #define DumpBuffer(fp, structPtr, header) \ +-dumpRaw(fp,(US structPtr)+IVAL(&structPtr->header.offset,0),SVAL(&structPtr->header.len,0)) ++ dumpRaw(fp,(US structPtr)+IVAL(&structPtr->header.offset,0),SVAL(&structPtr->header.len,0)) + + + static void +@@ -1326,8 +1321,33 @@ buf[len] = 0; + return buf; + } + ++static inline uschar * ++get_challenge_unistr(SPAAuthChallenge * challenge, SPAStrHeader * hdr) ++{ ++int off = IVAL(&hdr->offset, 0); ++int len = SVAL(&hdr->len, 0); ++return off + len < sizeof(SPAAuthChallenge) ++ ? US unicodeToString(CS challenge + off, len/2) : US""; ++} ++ ++static inline uschar * ++get_challenge_str(SPAAuthChallenge * challenge, SPAStrHeader * hdr) ++{ ++int off = IVAL(&hdr->offset, 0); ++int len = SVAL(&hdr->len, 0); ++return off + len < sizeof(SPAAuthChallenge) ++ ? US toString(CS challenge + off, len) : US""; ++} ++ + #ifdef notdef + ++#define GetUnicodeString(structPtr, header) \ ++ unicodeToString(((char*)structPtr) + IVAL(&structPtr->header.offset,0) , SVAL(&structPtr->header.len,0)/2) ++ ++#define GetString(structPtr, header) \ ++ toString(((CS structPtr) + IVAL(&structPtr->header.offset,0)), SVAL(&structPtr->header.len,0)) ++ ++ + void + dumpSmbNtlmAuthRequest (FILE * fp, SPAAuthRequest * request) + { +@@ -1497,8 +1517,8 @@ if (p) + } + + else domain = d = string_copy(cf & 0x1 +- ? CUS GetUnicodeString(challenge, uDomain) +- : CUS GetString(challenge, uDomain)); ++ ? CUS get_challenge_unistr(challenge, &challenge->uDomain) ++ : CUS get_challenge_str(challenge, &challenge->uDomain)); + + spa_smb_encrypt(password, challenge->challengeData, lmRespData); + spa_smb_nt_encrypt(password, challenge->challengeData, ntRespData); diff --git a/debian/patches/78_01-Command-line-option-for-no-notifier-socket.-Bug-2616.patch b/debian/patches/78_01-Command-line-option-for-no-notifier-socket.-Bug-2616.patch new file mode 100644 index 0000000..9ab1d42 --- /dev/null +++ b/debian/patches/78_01-Command-line-option-for-no-notifier-socket.-Bug-2616.patch @@ -0,0 +1,198 @@ +From 99ea5f6faeaf714e34bbcd75fdc50cc94dc7a1c8 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Fri, 10 Jul 2020 13:55:25 +0100 +Subject: [PATCH] Command-line option for no notifier socket. Bug 2616 + +--- + doc/doc-docbook/spec.xfpt | 33 +++++++++++++++++++++++++-- + doc/NewStuff | 2 ++ + src/daemon.c | 5 ++++ + src/exim.c | 9 +++++++- + test/scripts/0999-EXP-Queue-Ramp/0999 | 2 +- + 5 files changed, 47 insertions(+), 4 deletions(-) + +--- a/doc/NewStuff ++++ b/doc/NewStuff +@@ -2,14 +2,20 @@ New Features in Exim + -------------------- + + This file contains descriptions of new features that have been added to Exim. + Before a formal release, there may be quite a lot of detail so that people can + test from the snapshots or the Git before the documentation is updated. Once + the documentation is updated, this file is reduced to a short list. + ++Cherrypicked from GIT master: ++------------ ++ ++10. A command-line option to have a daemon not create a notifier socket. ++ ++ + Version 4.95 + ------------ + + 1. The fast-ramp two phase queue run support, previously experimental, is + now supported by default. + + 2. The native SRS support, previously experimental, is now supported. It is +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -1140,14 +1140,19 @@ static void + daemon_notifier_socket(void) + { + int fd; + const uschar * where; + struct sockaddr_un sa_un = {.sun_family = AF_UNIX}; + int len; + ++if (!notifier_socket || !*notifier_socket) ++ { ++ DEBUG(D_any) debug_printf("-oY used so not creating notifier socket\n"); ++ return; ++ } + if (override_local_interfaces && !override_pid_file_path) + { + DEBUG(D_any) + debug_printf("-oX used without -oP so not creating notifier socket\n"); + return; + } + +--- a/src/exim.c ++++ b/src/exim.c +@@ -3231,14 +3231,21 @@ on the second character (the one after ' + /* Limits: Is there a real limit we want here? 1024 is very arbitrary. */ + + case 'X': + if (*argrest) badarg = TRUE; + else override_local_interfaces = string_copy_taint(exim_str_fail_toolong(argv[++i], 1024, "-oX"), TRUE); + break; + ++ /* -oY: Override creation of daemon notifier socket */ ++ ++ case 'Y': ++ if (*argrest) badarg = TRUE; ++ else notifier_socket = NULL; ++ break; ++ + /* Unknown -o argument */ + + default: + badarg = TRUE; + } + break; + +@@ -4818,15 +4825,15 @@ if (originator_login == NULL || f.runnin + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed to get user name for uid %d", + (int)real_uid); + } + + /* Ensure that the user name is in a suitable form for use as a "phrase" in an + RFC822 address.*/ + +-originator_name = parse_fix_phrase(originator_name, Ustrlen(originator_name)); ++originator_name = US parse_fix_phrase(originator_name, Ustrlen(originator_name)); + + /* If a message is created by this call of Exim, the uid/gid of its originator + are those of the caller. These values are overridden if an existing message is + read in from the spool. */ + + originator_uid = real_uid; + originator_gid = real_gid; +--- a/doc/spec.txt ++++ b/doc/spec.txt +@@ -4189,14 +4189,27 @@ brief message about itself and exits. + + This option is relevant only when the -bd (start listening daemon) option + is also given. It controls which ports and interfaces the daemon uses. + Details of the syntax, and how it interacts with configuration file + options, are given in chapter 13. When -oX is used to start a daemon, no + pid file is written unless -oP is also present to specify a pid filename. + ++-oY ++ ++ This option controls the creation of an inter-process communications ++ endpoint by the Exim daemon. It is only relevant when the -bd ++ (start listening daemon) option is also given. Normally the daemon ++ creates this socket, unless a oX and no -oP option is also ++ present. If this option is given then the socket will not be created. ++ This could be required if the system is running multiple daemons. ++ ++ The socket is currently used for ++ * fast ramp-up of queue runner processes ++ * obtaining a current queue size ++ + -pd + + This option applies when an embedded Perl interpreter is linked with Exim + (see chapter 12). It overrides the setting of the perl_at_start option, + forcing the starting of the interpreter to be delayed until it is needed. + + -ps +@@ -11733,14 +11746,16 @@ $queue_name + + The name of the spool queue in use; empty for the default queue. + + $queue_size + + This variable contains the number of messages queued. It is evaluated on + demand, but no more often than once every minute. ++ If there is no daemon notifier socket open, the value will be ++ an empty string. + + $r_... + + Values can be placed in these variables by the set option of a router. They + can be given any name that starts with $r_. The values persist for the + address being handled through subsequent routers and the eventual + transport. +@@ -15227,18 +15242,20 @@ driver. + +-----------------------------------------------------------------------------+ + + This option gives the name for a unix-domain socket on which the daemon listens + for work and information-requests. Only installations running multiple daemons + sharing a spool directory should need to modify the default. + + The option is expanded before use. If the platform supports Linux-style +-abstract socket names, the result is used with a nul byte prefixed. Otherwise, +-it should be a full path name and use a directory accessible to Exim. ++abstract socket names, the result is used with a nul byte prefixed. ++Otherwise, if nonempty, it should be a full path name and use a directory ++accessible to Exim. + +-If the Exim command line uses a -oX option and does not use -oP then a notifier ++If this option is set as empty, or the command line -oY option is used, or ++the command line uses a -oX option and does not use -oP, then a notifier + socket is not created. + + +-----------------------------------------------------------------------------+ + | |Use: | Type: |Default: +no_sslv2 +no_sslv3 +single_dh_use| + |openssl_options| main | string | +no_ticket +no_renegotiation| + | | | list | | + +-----------------------------------------------------------------------------+ +--- a/doc/exim.8 ++++ b/doc/exim.8 +@@ -1445,14 +1445,25 @@ the \fBsmtp_receive_timeout\fP option; i + This option has exactly the same effect as \fB\-v\fP. + .TP 10 + \fB\-oX\fP <\fInumber or string\fP> + This option is relevant only when the \fB\-bd\fP (start listening daemon) option + is also given. It controls which ports and interfaces the daemon uses. When \fB\-oX\fP is used to start a daemon, no pid + file is written unless \fB\-oP\fP is also present to specify a pid filename. + .TP 10 ++\fB\-oY\fP ++This option controls the creation of an inter-process communications endpoint ++by the Exim daemon. It is only relevant when the \fB\-bd\fP (start listening ++daemon) option is also given. ++Normally the daemon creates this socket, unless a \fB\-oX\fP and no \fB\-oP\fP ++option is also present. ++If this option is given then the socket will not be created. This could be ++required if the system is running multiple daemons. ++The socket is currently used for fast ramp-up of queue runner processes and ++obtaining a current queue size. ++.TP 10 + \fB\-pd\fP + This option applies when an embedded Perl interpreter is linked with Exim. It overrides the setting of the \fBperl_at_start\fP + option, forcing the starting of the interpreter to be delayed until it is + needed. + .TP 10 + \fB\-ps\fP + This option applies when an embedded Perl interpreter is linked with Exim. It overrides the setting of the \fBperl_at_start\fP diff --git a/debian/patches/79_CVE-2023-51766_4.97.1-release.diff b/debian/patches/79_CVE-2023-51766_4.97.1-release.diff new file mode 100644 index 0000000..6199af9 --- /dev/null +++ b/debian/patches/79_CVE-2023-51766_4.97.1-release.diff @@ -0,0 +1,435 @@ +Description: Fix smtp-smuggling (CVE-2023-51766) + Pull upstream changes from 4.97.1 security release. +Author: Jeremy Harris <jgh146exb@wizmail.org> +Bug-Debian: https://bugs.debian.org/1059387 +Origin: upstream +Last-Update: 2023-12-31 + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -229,10 +229,15 @@ JH/53 Bug 2743: fix immediate-delivery v + + JH/57 Fix control=fakreject for a custom message containing tainted data. + Previously this resulted in a log complaint, due to a re-expansion present + since fakereject was originally introduced. + ++JH/s1 Refuse to accept a line "dot, LF" as end-of-DATA unless operating in ++ LF-only mode (as detected from the first header line). Previously we did ++ accept that in (normal) CRLF mode; this has been raised as a possible ++ attack scenario (under the name "smtp smuggling", CVE-2023-51766). ++ + + Exim version 4.94 + ----------------- + + JH/01 Avoid costly startup code when not strictly needed. This reduces time +--- /dev/null ++++ b/doc/doc-txt/cve-2023-51766 +@@ -0,0 +1,69 @@ ++CVE ID: CVE-2023-51766 ++Date: 2016-12-15 ++Credits: https://sec-consult.com/blog/detail/smtp-smuggling-spoofing-e-mails-worldwide/ ++Version(s): all up to 4.97 inclusive ++Issue: Given a buggy relay, Exim can be induced to accept a second message embedded ++ as part of the body of a first message ++ ++Conditions ++========== ++ ++If *all* the following conditions are met ++ ++ Runtime options ++ --------------- ++ ++ * Exim offers PIPELINING on incoming connections ++ ++ * Exim offers CHUNKING on incoming connections ++ ++ Operation ++ --------- ++ ++ * DATA (as opposed to BDAT) is used for a message reception ++ ++ * The relay host sends to the Exim MTA message data including ++ one of "LF . LF" or "CR LF . LF" or "LF . CR LF". ++ ++ * Exim interprets the sequence as signalling the end of data for ++ the SMTP DATA command, and hence a first message. ++ ++ * Exim interprets further input which the relay had as message body ++ data, as SMTP commands and data. This could include a MAIL, RCPT, ++ BDAT (etc) sequence, resulting in a further message acceptance. ++ ++Impact ++====== ++ ++One or more messages can be accepted by Exim that have not been ++properly validated by the buggy relay. ++ ++Fix ++=== ++ ++Install a fixed Exim version: ++ ++ 4.98 (once available) ++ 4.97.1 ++ ++If you can't install one of the above versions, ask your package ++maintainer for a version containing the backported fix. On request and ++depending on our resources we will support you in backporting the fix. ++(Please note, that Exim project officially doesn't support versions ++prior the current stable version.) ++ ++ ++Workaround ++========== ++ ++ Disable CHUNKING advertisement for incoming connections. ++ ++ An attempt to "smuggle" a DATA command will trip a syncronisation ++ check. ++ ++*or* ++ ++ Disable PIPELINING advertisement for incoming connections. ++ ++ The "smuggled" MAIL FROM command will then trip a syncronisation ++ check. +--- a/src/receive.c ++++ b/src/receive.c +@@ -805,104 +805,118 @@ we make the CRs optional in all cases. + + July 2003: Bare CRs cause trouble. We now treat them as line terminators as + well, so that there are no CRs in spooled messages. However, the message + terminating dot is not recognized between two bare CRs. + ++Dec 2023: getting a site to send a body including an "LF . LF" sequence ++followed by SMTP commands is a possible "smtp smuggling" attack. If ++the first (header) line for the message has a proper CRLF then enforce ++that for the body: convert bare LF to a space. ++ + Arguments: +- fout a FILE to which to write the message; NULL if skipping ++ fout a FILE to which to write the message; NULL if skipping ++ strict_crlf require full CRLF sequence as a line ending + + Returns: One of the END_xxx values indicating why it stopped reading + */ + + static int +-read_message_data_smtp(FILE *fout) ++read_message_data_smtp(FILE * fout, BOOL strict_crlf) + { +-int ch_state = 0; +-int ch; +-int linelength = 0; ++enum { s_linestart, s_normal, s_had_cr, s_had_nl_dot, s_had_dot_cr } ch_state = ++ s_linestart; ++int linelength = 0, ch; + + while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) + { + if (ch == 0) body_zerocount++; + switch (ch_state) + { +- case 0: /* After LF or CRLF */ +- if (ch == '.') +- { +- ch_state = 3; +- continue; /* Don't ever write . after LF */ +- } +- ch_state = 1; ++ case s_linestart: /* After LF or CRLF */ ++ if (ch == '.') ++ { ++ ch_state = s_had_nl_dot; ++ continue; /* Don't ever write . after LF */ ++ } ++ ch_state = s_normal; + +- /* Else fall through to handle as normal uschar. */ ++ /* Else fall through to handle as normal uschar. */ + +- case 1: /* Normal state */ +- if (ch == '\n') +- { +- ch_state = 0; +- body_linecount++; ++ case s_normal: /* Normal state */ ++ if (ch == '\r') ++ { ++ ch_state = s_had_cr; ++ continue; /* Don't write the CR */ ++ } ++ if (ch == '\n') /* Bare LF at end of line */ ++ if (strict_crlf) ++ ch = ' '; /* replace LF with space */ ++ else ++ { /* treat as line ending */ ++ ch_state = s_linestart; ++ body_linecount++; ++ if (linelength > max_received_linelength) ++ max_received_linelength = linelength; ++ linelength = -1; ++ } ++ break; ++ ++ case s_had_cr: /* After (unwritten) CR */ ++ body_linecount++; /* Any char ends line */ + if (linelength > max_received_linelength) +- max_received_linelength = linelength; ++ max_received_linelength = linelength; + linelength = -1; +- } +- else if (ch == '\r') +- { +- ch_state = 2; +- continue; +- } +- break; ++ if (ch == '\n') /* proper CRLF */ ++ ch_state = s_linestart; ++ else ++ { ++ message_size++; /* convert the dropped CR to a stored NL */ ++ if (fout && fputc('\n', fout) == EOF) return END_WERROR; ++ cutthrough_data_put_nl(); ++ if (ch == '\r') /* CR; do not write */ ++ continue; ++ ch_state = s_normal; /* not LF or CR; process as standard */ ++ } ++ break; + +- case 2: /* After (unwritten) CR */ +- body_linecount++; +- if (linelength > max_received_linelength) +- max_received_linelength = linelength; +- linelength = -1; +- if (ch == '\n') +- { +- ch_state = 0; +- } +- else +- { +- message_size++; +- if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; +- cutthrough_data_put_nl(); +- if (ch != '\r') ch_state = 1; else continue; +- } +- break; ++ case s_had_nl_dot: /* After [CR] LF . */ ++ if (ch == '\n') /* [CR] LF . LF */ ++ if (strict_crlf) ++ ch = ' '; /* replace LF with space */ ++ else ++ return END_DOT; ++ else if (ch == '\r') /* [CR] LF . CR */ ++ { ++ ch_state = s_had_dot_cr; ++ continue; /* Don't write the CR */ ++ } ++ /* The dot was removed on reaching s_had_nl_dot. For a doubled dot, here, ++ reinstate it to cutthrough. The current ch, dot or not, is passed both to ++ cutthrough and to file below. */ ++ else if (ch == '.') ++ { ++ uschar c = ch; ++ cutthrough_data_puts(&c, 1); ++ } ++ ch_state = s_normal; ++ break; + +- case 3: /* After [CR] LF . */ +- if (ch == '\n') +- return END_DOT; +- if (ch == '\r') +- { +- ch_state = 4; +- continue; +- } +- /* The dot was removed at state 3. For a doubled dot, here, reinstate +- it to cutthrough. The current ch, dot or not, is passed both to cutthrough +- and to file below. */ +- if (ch == '.') +- { +- uschar c= ch; +- cutthrough_data_puts(&c, 1); +- } +- ch_state = 1; +- break; ++ case s_had_dot_cr: /* After [CR] LF . CR */ ++ if (ch == '\n') ++ return END_DOT; /* Preferred termination */ + +- case 4: /* After [CR] LF . CR */ +- if (ch == '\n') return END_DOT; +- message_size++; +- body_linecount++; +- if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; +- cutthrough_data_put_nl(); +- if (ch == '\r') +- { +- ch_state = 2; +- continue; +- } +- ch_state = 1; +- break; ++ message_size++; /* convert the dropped CR to a stored NL */ ++ body_linecount++; ++ if (fout && fputc('\n', fout) == EOF) return END_WERROR; ++ cutthrough_data_put_nl(); ++ if (ch == '\r') ++ { ++ ch_state = s_had_cr; ++ continue; /* CR; do not write */ ++ } ++ ch_state = s_normal; ++ break; + } + + /* Add the character to the spool file, unless skipping; then loop for the + next. */ + +@@ -1114,11 +1128,11 @@ Returns: nothing + void + receive_swallow_smtp(void) + { + if (message_ended >= END_NOTENDED) + message_ended = chunking_state <= CHUNKING_OFFERED +- ? read_message_data_smtp(NULL) ++ ? read_message_data_smtp(NULL, FALSE) + : read_message_bdat_smtp_wire(NULL); + } + + + +@@ -1899,12 +1913,14 @@ for (;;) + LF specially by inserting a white space after it to ensure that the header + line is not terminated. */ + + if (ch == '\n') + { +- if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = FALSE; +- else if (first_line_ended_crlf) receive_ungetc(' '); ++ if (first_line_ended_crlf == TRUE_UNSET) ++ first_line_ended_crlf = FALSE; ++ else if (first_line_ended_crlf) ++ receive_ungetc(' '); + goto EOL; + } + + /* This is not the end of the line. If this is SMTP input and this is + the first character in the line and it is a "." character, ignore it. +@@ -1915,12 +1931,17 @@ for (;;) + prevent further reading), and break out of the loop, having freed the + empty header, and set next = NULL to indicate no data line. */ + + if (ptr == 0 && ch == '.' && f.dot_ends) + { ++ /* leading dot while in headers-read mode */ + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); +- if (ch == '\r') ++ if (ch == '\n' && first_line_ended_crlf == TRUE /* and not TRUE_UNSET */ ) ++ /* dot, LF but we are in CRLF mode. Attack? */ ++ ch = ' '; /* replace the LF with a space */ ++ ++ else if (ch == '\r') + { + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); + if (ch != '\n') + { + receive_ungetc(ch); +@@ -1952,11 +1973,12 @@ for (;;) + if (ch == '\r') + { + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); + if (ch == '\n') + { +- if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE; ++ if (first_line_ended_crlf == TRUE_UNSET) ++ first_line_ended_crlf = TRUE; + goto EOL; + } + + /* Otherwise, put back the character after CR, and turn the bare CR + into LF SP. */ +@@ -3084,11 +3106,11 @@ if (cutthrough.cctx.sock >= 0 && cutthro + (void) cutthrough_headers_send(); + } + + + /* Open a new spool file for the data portion of the message. We need +-to access it both via a file descriptor and a stream. Try to make the ++to access it both via a file descriptor and a stdio stream. Try to make the + directory if it isn't there. */ + + spool_name = spool_fname(US"input", message_subdir, message_id, US"-D"); + DEBUG(D_receive) debug_printf("Data file name: %s\n", spool_name); + +@@ -3153,11 +3175,11 @@ message id or "next" line. */ + if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) + { + if (smtp_input) + { + message_ended = chunking_state <= CHUNKING_OFFERED +- ? read_message_data_smtp(spool_data_file) ++ ? read_message_data_smtp(spool_data_file, first_line_ended_crlf) + : spool_wireformat + ? read_message_bdat_smtp_wire(spool_data_file) + : read_message_bdat_smtp(spool_data_file); + receive_linecount++; /* The terminating "." line */ + } +--- a/src/smtp_in.c ++++ b/src/smtp_in.c +@@ -5393,16 +5393,16 @@ while (done <= 0) + } + break; + } + + if (chunking_state > CHUNKING_OFFERED) +- rc = OK; /* No predata ACL or go-ahead output for BDAT */ ++ rc = OK; /* There is no predata ACL or go-ahead output for BDAT */ + else + { +- /* If there is an ACL, re-check the synchronization afterwards, since the +- ACL may have delayed. To handle cutthrough delivery enforce a dummy call +- to get the DATA command sent. */ ++ /* If there is a predata-ACL, re-check the synchronization afterwards, ++ since the ACL may have delayed. To handle cutthrough delivery enforce a ++ dummy call to get the DATA command sent. */ + + if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0) + rc = OK; + else + { +--- a/doc/spec.txt ++++ b/doc/spec.txt +@@ -32960,12 +32960,10 @@ MTA within an operating system would use + has shown that this is not the case; for example, there are Unix applications + that use CRLF in this circumstance. For this reason, and for compatibility with + other MTAs, the way Exim handles line endings for all messages is now as + follows: + +- * LF not preceded by CR is treated as a line ending. +- + * CR is treated as a line ending; if it is immediately followed by LF, the LF + is ignored. + + * The sequence "CR, dot, CR" does not terminate an incoming SMTP message, nor + a local message in the state where a line containing only a dot is a +@@ -32976,11 +32974,14 @@ follows: + behind this is that bare CRs in header lines are most likely either to be + mistakes, or people trying to play silly games. + + * If the first header line received in a message ends with CRLF, a subsequent + bare LF in a header line is treated in the same way as a bare CR in a +- header line. ++ header line and a bare LF in a body line is replaced with a space. ++ ++ * If the first header line received in a message does not end with CRLF, a ++ subsequent LF not preceded by CR is treated as a line ending. + + + 48.3 Unqualified addresses + -------------------------- + diff --git a/debian/patches/90_localscan_dlopen.dpatch b/debian/patches/90_localscan_dlopen.dpatch new file mode 100644 index 0000000..592b7d7 --- /dev/null +++ b/debian/patches/90_localscan_dlopen.dpatch @@ -0,0 +1,307 @@ +Description: Allow one to use and switch between different local_scan functions + without recompiling exim. + http://marc.merlins.org/linux/exim/files/sa-exim-current/ Original patch from + David Woodhouse, modified first by Derrick 'dman' Hudson and then by Marc + MERLIN for SA-Exim and minor/major API version tracking +Author: David Woodhouse, Derrick 'dman' Hudson, Marc MERLIN +Origin: other, http://marc.merlins.org/linux/exim/files/sa-exim-current/ +Forwarded: https://bugs.exim.org/show_bug.cgi?id=2671 +Last-Update: 2019-10-20 + +--- a/src/EDITME ++++ b/src/EDITME +@@ -878,6 +878,21 @@ HEADERS_CHARSET="ISO-8859-1" + + + #------------------------------------------------------------------------------ ++# On systems which support dynamic loading of shared libraries, Exim can ++# load a local_scan function specified in its config file instead of having ++# to be recompiled with the desired local_scan function. For a full ++# description of the API to this function, see the Exim specification. ++ ++DLOPEN_LOCAL_SCAN=yes ++ ++# If you set DLOPEN_LOCAL_SCAN, then you need to include -rdynamic in the ++# linker flags. Without it, the loaded .so won't be able to access any ++# functions from exim. ++ ++LDFLAGS += -rdynamic ++CFLAGS += -fvisibility=hidden ++ ++#------------------------------------------------------------------------------ + # The default distribution of Exim contains only the plain text form of the + # documentation. Other forms are available separately. If you want to install + # the documentation in "info" format, first fetch the Texinfo documentation +--- a/src/config.h.defaults ++++ b/src/config.h.defaults +@@ -33,6 +33,8 @@ Do not put spaces between # and the 'def + + #define AUTH_VARS 3 + ++#define DLOPEN_LOCAL_SCAN ++ + #define BIN_DIRECTORY + + #define CONFIGURE_FILE +--- a/src/globals.c ++++ b/src/globals.c +@@ -117,6 +117,10 @@ int dsn_ret = 0; + const pcre *regex_DSN = NULL; + uschar *dsn_advertise_hosts = NULL; + ++#ifdef DLOPEN_LOCAL_SCAN ++uschar *local_scan_path = NULL; ++#endif ++ + #ifndef DISABLE_TLS + BOOL gnutls_compat_mode = FALSE; + BOOL gnutls_allow_auto_pkcs11 = FALSE; +--- a/src/globals.h ++++ b/src/globals.h +@@ -148,6 +148,9 @@ extern int dsn_ret; / + extern const pcre *regex_DSN; /* For recognizing DSN settings */ + extern uschar *dsn_advertise_hosts; /* host for which TLS is advertised */ + ++#ifdef DLOPEN_LOCAL_SCAN ++extern uschar *local_scan_path; /* Path to local_scan() library */ ++#endif + /* Input-reading functions for messages, so we can use special ones for + incoming TCP/IP. */ + +--- a/src/local_scan.c ++++ b/src/local_scan.c +@@ -6,22 +6,6 @@ + /* See the file NOTICE for conditions of use and distribution. */ + + +-/****************************************************************************** +-This file contains a template local_scan() function that just returns ACCEPT. +-If you want to implement your own version, you should copy this file to, say +-Local/local_scan.c, and edit the copy. To use your version instead of the +-default, you must set +- +-HAVE_LOCAL_SCAN=yes +-LOCAL_SCAN_SOURCE=Local/local_scan.c +- +-in your Local/Makefile. This makes it easy to copy your version for use with +-subsequent Exim releases. +- +-For a full description of the API to this function, see the Exim specification. +-******************************************************************************/ +- +- + /* This is the only Exim header that you should include. The effect of + including any other Exim header is not defined, and may change from release to + release. Use only the documented interface! */ +@@ -29,37 +13,129 @@ release. Use only the documented interfa + #include "local_scan.h" + + +-/* This is a "do-nothing" version of a local_scan() function. The arguments +-are: +- +- fd The file descriptor of the open -D file, which contains the +- body of the message. The file is open for reading and +- writing, but modifying it is dangerous and not recommended. +- +- return_text A pointer to an unsigned char* variable which you can set in +- order to return a text string. It is initialized to NULL. +- +-The return values of this function are: +- +- LOCAL_SCAN_ACCEPT +- The message is to be accepted. The return_text argument is +- saved in $local_scan_data. +- +- LOCAL_SCAN_REJECT +- The message is to be rejected. The returned text is used +- in the rejection message. +- +- LOCAL_SCAN_TEMPREJECT +- This specifies a temporary rejection. The returned text +- is used in the rejection message. +-*/ ++#ifdef DLOPEN_LOCAL_SCAN ++#include <dlfcn.h> ++static int (*local_scan_fn)(int fd, uschar **return_text) = NULL; ++static int load_local_scan_library(void); ++#endif + + int + local_scan(int fd, uschar **return_text) + { + fd = fd; /* Keep picky compilers happy */ + return_text = return_text; +-return LOCAL_SCAN_ACCEPT; ++#ifdef DLOPEN_LOCAL_SCAN ++/* local_scan_path is defined AND not the empty string */ ++if (local_scan_path && *local_scan_path) ++ { ++ if (!local_scan_fn) ++ { ++ if (!load_local_scan_library()) ++ { ++ char *base_msg , *error_msg , *final_msg ; ++ int final_length = -1 ; ++ ++ base_msg=US"Local configuration error - local_scan() library failure\n"; ++ error_msg = dlerror() ; ++ ++ final_length = strlen(base_msg) + strlen(error_msg) + 1 ; ++ final_msg = (char*)malloc( final_length*sizeof(char) ) ; ++ *final_msg = '\0' ; ++ ++ strcat( final_msg , base_msg ) ; ++ strcat( final_msg , error_msg ) ; ++ ++ *return_text = final_msg ; ++ return LOCAL_SCAN_TEMPREJECT; ++ } ++ } ++ return local_scan_fn(fd, return_text); ++ } ++else ++#endif ++ return LOCAL_SCAN_ACCEPT; + } + ++#ifdef DLOPEN_LOCAL_SCAN ++ ++static int load_local_scan_library(void) ++{ ++/* No point in keeping local_scan_lib since we'll never dlclose() anyway */ ++void *local_scan_lib = NULL; ++int (*local_scan_version_fn)(void); ++int vers_maj; ++int vers_min; ++ ++local_scan_lib = dlopen(local_scan_path, RTLD_NOW); ++if (!local_scan_lib) ++ { ++ log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library open failed - " ++ "message temporarily rejected"); ++ return FALSE; ++ } ++ ++local_scan_version_fn = dlsym(local_scan_lib, "local_scan_version_major"); ++if (!local_scan_version_fn) ++ { ++ dlclose(local_scan_lib); ++ log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain " ++ "local_scan_version_major() function - message temporarily rejected"); ++ return FALSE; ++ } ++ ++/* The major number is increased when the ABI is changed in a non ++ backward compatible way. */ ++vers_maj = local_scan_version_fn(); ++ ++local_scan_version_fn = dlsym(local_scan_lib, "local_scan_version_minor"); ++if (!local_scan_version_fn) ++ { ++ dlclose(local_scan_lib); ++ log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain " ++ "local_scan_version_minor() function - message temporarily rejected"); ++ return FALSE; ++ } ++ ++/* The minor number is increased each time a new feature is added (in a ++ way that doesn't break backward compatibility) -- Marc */ ++vers_min = local_scan_version_fn(); ++ ++ ++if (vers_maj != LOCAL_SCAN_ABI_VERSION_MAJOR) ++ { ++ dlclose(local_scan_lib); ++ local_scan_lib = NULL; ++ log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible major" ++ "version number, you need to recompile your module for this version" ++ "of exim (The module was compiled for version %d.%d and this exim provides" ++ "ABI version %d.%d)", vers_maj, vers_min, LOCAL_SCAN_ABI_VERSION_MAJOR, ++ LOCAL_SCAN_ABI_VERSION_MINOR); ++ return FALSE; ++ } ++else if (vers_min > LOCAL_SCAN_ABI_VERSION_MINOR) ++ { ++ dlclose(local_scan_lib); ++ local_scan_lib = NULL; ++ log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible minor" ++ "version number, you need to recompile your module for this version" ++ "of exim (The module was compiled for version %d.%d and this exim provides" ++ "ABI version %d.%d)", vers_maj, vers_min, LOCAL_SCAN_ABI_VERSION_MAJOR, ++ LOCAL_SCAN_ABI_VERSION_MINOR); ++ return FALSE; ++ } ++ ++local_scan_fn = dlsym(local_scan_lib, "local_scan"); ++if (!local_scan_fn) ++ { ++ dlclose(local_scan_lib); ++ log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain " ++ "local_scan() function - message temporarily rejected"); ++ return FALSE; ++ } ++return TRUE; ++} ++ ++#endif /* DLOPEN_LOCAL_SCAN */ ++ ++ + /* End of local_scan.c */ +--- a/src/local_scan.h ++++ b/src/local_scan.h +@@ -27,6 +27,7 @@ settings, and the store functions. */ + + #include <stdarg.h> + #include <sys/types.h> ++#pragma GCC visibility push(default) + #include "config.h" + #include "mytypes.h" + #include "store.h" +@@ -166,6 +167,9 @@ extern header_line *header_list; / + extern BOOL host_checking; /* Set when checking a host */ + extern uschar *interface_address; /* Interface for incoming call */ + extern int interface_port; /* Port number for incoming call */ ++#ifdef DLOPEN_LOCAL_SCAN ++extern uschar *local_scan_path; ++#endif + extern uschar *message_id; /* Internal id of message being handled */ + extern uschar *received_protocol; /* Name of incoming protocol */ + extern int recipients_count; /* Number of recipients */ +@@ -235,4 +239,6 @@ extern pid_t child_open_exim2_functio + extern pid_t child_open_function(uschar **, uschar **, int, int *, int *, BOOL, const uschar *); + #endif + ++#pragma GCC visibility pop ++ + /* End of local_scan.h */ +--- a/src/readconf.c ++++ b/src/readconf.c +@@ -205,6 +205,9 @@ static optionlist optionlist_config[] = + { "local_from_prefix", opt_stringptr, {&local_from_prefix} }, + { "local_from_suffix", opt_stringptr, {&local_from_suffix} }, + { "local_interfaces", opt_stringptr, {&local_interfaces} }, ++#ifdef DLOPEN_LOCAL_SCAN ++ { "local_scan_path", opt_stringptr, &local_scan_path }, ++#endif + #ifdef HAVE_LOCAL_SCAN + { "local_scan_timeout", opt_time, {&local_scan_timeout} }, + #endif +--- a/src/string.c ++++ b/src/string.c +@@ -413,6 +413,7 @@ return ss; + + #if (defined(HAVE_LOCAL_SCAN) || defined(EXPAND_DLFUNC)) \ + && !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) ++#pragma GCC visibility push(default) + /************************************************* + * Copy and save string * + *************************************************/ +@@ -465,6 +466,7 @@ Ustrncpy(ss, s, n); + ss[n] = 0; + return ss; + } ++#pragma GCC visibility pop + #endif + + diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..3d95d1d --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,46 @@ +31_eximmanpage.dpatch +32_exim4.dpatch +33_eximon.binary.dpatch +34_eximstatsmanpage.dpatch +35_install.dpatch +60_convert4r4.dpatch +67_unnecessaryCopt.diff +70_remove_exim-users_references.dpatch +73_01-Fix-DANE-SNI-handling-Bug-2265.patch +73_02-Fix-ipv6norm.patch +73_03-Named-Queues-fix-immediate-delivery.-Bug-2743.patch +73_04-Fix-host_name_lookup-Close-2747.patch +73_05-Fix-tainted-message-for-fakereject.patch +75_01-Introduce-main-config-option-allow_insecure_tainted_.patch +75_02-search.patch +75_03-dbstuff.patch +75_04-acl.patch +75_05-parse.patch +75_06-rda.patch +75_07-appendfile.patch +75_08-autoreply.patch +75_09-pipe.patch +75_10-deliver.patch +75_11-directory.patch +75_12-expand.patch +75_13-lf_sqlperform.patch +75_14-rf_get_transport.patch +75_15-deliver.patch +75_16-smtp_out.patch +75_17-smtp.patch +75_18-update-doc.patch +75_20-Set-mainlog_name-and-rejectlog_name-unconditionally.patch +75_21-tidy-log.c.patch +75_22-Silence-compiler.patch +75_23-Do-not-close-the-main-_log-if-we-do-not-see-a-chance.patch +75_24-Silence-the-compiler.patch +75_26-Disable-taintchecks-for-mkdir-this-isn-t-part-of-4.9.patch +75_27_Fix-logging-with-empty-element-in-log_file_path-Bug-.patch +75_28_Fix-logging-with-build-time-config-and-empty-element.patch +75_29-Auths-fix-possible-OOB-write-in-external-authenticat.patch +75_30-Auths-use-uschar-more-in-spa-authenticator.patch +75_31-Auths-fix-possible-OOB-write-in-SPA-authenticator.-B.patch +75_32-Auths-fix-possible-OOB-read-in-SPA-authenticator.-Bu.patch +78_01-Command-line-option-for-no-notifier-socket.-Bug-2616.patch +79_CVE-2023-51766_4.97.1-release.diff +90_localscan_dlopen.dpatch |