diff options
Diffstat (limited to 'src')
200 files changed, 16199 insertions, 10471 deletions
diff --git a/src/.gitignore b/src/.gitignore index 7839e9785..8965c1113 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,4 +1,4 @@ Local build-* tags -cscope.out +cscope.* diff --git a/src/ACKNOWLEDGMENTS b/src/ACKNOWLEDGMENTS index 1c4a93445..2e1ede016 100644 --- a/src/ACKNOWLEDGMENTS +++ b/src/ACKNOWLEDGMENTS @@ -350,7 +350,7 @@ John Jetmore Tom Kistner DKIM. Content scanning. SPA. Todd Lyons Nigel Metheringham Transitioning out of Default Victim status. -Phil Pennock Release Coordinator. Breaks lots of things. +Phil Pennock Mostly idle; some security bits still. David Woodhouse Dynamic modules. Security. @@ -449,6 +449,7 @@ Jan Srzednicki Patch improving Dovecot authenticator Samuel Thibault Patch fixing IPv6 interface address detection on Hurd Martin Tscholak Reported issue with TLS anonymous ciphersuites Stephen Usher Patch fixing use of Oracle's LDAP libraries on Solaris +Jasper Wallace Patch for LibreSSL compatibility Holger Weiß Patch leting ${run} return more data than OS pipe buffer size Moritz Wilhelmy Pointed out PCRE_PRERELEASE glitch diff --git a/src/Makefile b/src/Makefile index 96c0d58ee..adda7ceb3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ # appropriate links, and then creating and running the main makefile in that # directory. -# Copyright (c) University of Cambridge, 1995 - 2014 +# Copyright (c) University of Cambridge, 1995 - 2015 # See the file NOTICE for conditions of use and distribution. # IRIX make uses the shell that is in the SHELL variable, which often defaults @@ -28,6 +28,14 @@ buildname=$${build:-`$(SHELL) scripts/os-type`-`$(SHELL) scripts/arch-type`} all: Local/Makefile configure @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) + +# This pair for the convinience of of the Debian maintainers +exim: Local/Makefile configure + @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) exim +utils: Local/Makefile configure + @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) utils + + Local/Makefile: @echo "" @echo "*** Please create Local/Makefile by copying src/EDITME and making" @@ -93,6 +101,7 @@ cscope.files: FRC find src Local OS exim_monitor -name "*.[cshyl]" -print \ -o -name "os.h*" -print \ -o -name "*akefile*" -print \ + -o -name config.h.defaults -print \ -o -name EDITME -print >> $@ ls OS/* >> $@ diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index f82549ded..960c9afd6 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -7,6 +7,7 @@ SHELL = $(MAKE_SHELL) SCRIPTS = ../scripts +O = ../OS EDITME = ../Local/Makefile EXIMON_EDITME = ../Local/eximon.conf @@ -32,10 +33,10 @@ FE = $(FULLECHO) # up-to-date. Then the os-specific source files and the C configuration file # are set up, and finally it goes to the main Exim target. -all: allexim -config: $(EDITME) checklocalmake Makefile os.h os.c config.h version.h +all: utils exim +config: $(EDITME) checklocalmake Makefile os.c config.h version.h -checklocalmake: +checklocalmake: @if $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(OSTYPE) $(EDITME) || \ $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(ARCHTYPE) $(EDITME) || \ $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(OSTYPE)-$(ARCHTYPE) $(EDITME); \ @@ -76,12 +77,29 @@ Makefile: ../OS/Makefile-Base ../OS/Makefile-Default \ # Build (link) the os.h file -os.h: +os.h: $(SCRIPTS)/Configure-os.h \ + $(O)/os.h-AIX $(O)/os.h-BSDI $(O)/os.h-cygwin \ + $(O)/os.h-Darwin $(O)/os.h-DGUX $(O)/os.h-DragonFly \ + $(O)/os.h-FreeBSD $(O)/os.h-GNU $(O)/os.h-GNUkFreeBSD \ + $(O)/os.h-GNUkNetBSD $(O)/os.h-HI-OSF \ + $(O)/os.h-HI-UX $(O)/os.h-HP-UX $(O)/os.h-HP-UX-9 \ + $(O)/os.h-IRIX $(O)/os.h-IRIX6 $(O)/os.h-IRIX632 \ + $(O)/os.h-IRIX65 $(O)/os.h-Linux $(O)/os.h-mips \ + $(O)/os.h-NetBSD $(O)/os.h-NetBSD-a.out \ + $(O)/os.h-OpenBSD $(O)/os.h-OpenUNIX $(O)/os.h-OSF1 \ + $(O)/os.h-QNX $(O)/os.h-SCO $(O)/os.h-SCO_SV \ + $(O)/os.h-SunOS4 $(O)/os.h-SunOS5 $(O)/os.h-SunOS5-hal \ + $(O)/os.h-ULTRIX $(O)/os.h-UNIX_SV \ + $(O)/os.h-Unixware7 $(O)/os.h-USG $(SHELL) $(SCRIPTS)/Configure-os.h # Build the os.c file -os.c: ../src/os.c +os.c: ../src/os.c \ + $(SCRIPTS)/Configure-os.c \ + $(O)/os.c-cygwin $(O)/os.c-GNU $(O)/os.c-HI-OSF \ + $(O)/os.c-IRIX $(O)/os.c-IRIX6 $(O)/os.c-IRIX632 \ + $(O)/os.c-IRIX65 $(O)/os.c-Linux $(O)/os.c-OSF1 $(SHELL) $(SCRIPTS)/Configure-os.c # Build the config.h file. @@ -95,19 +113,16 @@ config.h: Makefile buildconfig ../src/config.h.defaults $(EDITME) # therefore always be run, even if the files exist. This shouldn't in fact be a # problem, but it does no harm. Other make programs will just ignore this. -.PHONY: all config allexim buildauths buildlookups buildpdkim buildrouters \ +.PHONY: all config utils \ + buildauths buildlookups buildpdkim buildrouters \ buildtransports checklocalmake clean -# This is the real default target for all the various exim binaries and -# scripts, once the configuring stuff is done. - -allexim: $(EXIM_MONITOR) exicyclog exinext exiwhat \ +utils: $(EXIM_MONITOR) exicyclog exinext exiwhat \ exigrep eximstats exipick exiqgrep exiqsumm \ transport-filter.pl convert4r3 convert4r4 \ exim_checkaccess \ - exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock \ - exim + exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock # Targets for special-purpose configuration header builders @@ -298,7 +313,14 @@ convert4r4: Makefile ../src/convert4r4.src OBJ_WITH_CONTENT_SCAN = malware.o mime.o regex.o spam.o spool_mbox.o OBJ_WITH_OLD_DEMIME = demime.o -OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dcc.o dmarc.o dane.o +OBJ_EXPERIMENTAL = bmi_spam.o \ + dane.o \ + dcc.o \ + dmarc.o \ + imap_utf7.o \ + spf.o \ + srs.o \ + utf8.o # Targets for final binaries; the main one has a build number which is # updated each time. We don't bother with that for the auxiliaries. @@ -396,7 +418,7 @@ exim_dbmbuild: exim_dbmbuild.o # The utility for locking a mailbox while messing around with it -exim_lock: exim_lock.c +exim_lock: exim_lock.c os.h @echo "$(CC) exim_lock.c" $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) exim_lock.c @echo "$(LNCC) -o exim_lock" @@ -448,8 +470,28 @@ eximon.bin: $(EXIMON_EDITME) eximon $(OBJ_MONBIN) \ # in one. This list is overkill, but it doesn't really take much time to # rebuild Exim on a modern computer. -HDRS = config.h dbfunctions.h dbstuff.h exim.h functions.h globals.h local_scan.h macros.h mytypes.h structs.h -PHDRS = ../config.h ../dbfunctions.h ../dbstuff.h ../exim.h ../functions.h ../globals.h ../local_scan.h ../macros.h ../mytypes.h ../structs.h +HDRS = config.h \ + dbfunctions.h \ + dbstuff.h \ + exim.h \ + functions.h \ + globals.h \ + local_scan.h \ + macros.h \ + mytypes.h \ + structs.h \ + os.h +PHDRS = ../config.h \ + ../dbfunctions.h \ + ../dbstuff.h \ + ../exim.h \ + ../functions.h \ + ../globals.h \ + ../local_scan.h \ + ../macros.h \ + ../mytypes.h \ + ../structs.h \ + ../os.h .SUFFIXES: .o .c .c.o:; @echo "$(CC) $*.c" @@ -578,12 +620,14 @@ spool_out.o: $(HDRS) spool_out.c std-crypto.o: $(HDRS) std-crypto.c store.o: $(HDRS) store.c string.o: $(HDRS) string.c -tls.o: $(HDRS) tls.c tls-gnu.c tlscert-gnu.c tls-openssl.c tlscert-openssl.c +tls.o: $(HDRS) tls.c setenv.c \ + tls-gnu.c tlscert-gnu.c \ + tls-openssl.c tlscert-openssl.c tod.o: $(HDRS) tod.c transport.o: $(HDRS) transport.c tree.o: $(HDRS) tree.c verify.o: $(HDRS) verify.c -dkim.o: $(HDRS) dkim.c +dkim.o: $(HDRS) pdkim/pdkim.h dkim.c # Dependencies for WITH_CONTENT_SCAN modules @@ -596,17 +640,19 @@ spool_mbox.o: $(HDRS) spool_mbox.c # Dependencies for WITH_OLD_DEMIME modules -demime.o: $(HDRS) demime.c +demime.o: $(HDRS) demime.c # Dependencies for EXPERIMENTAL_* modules -bmi_spam.o: $(HDRS) bmi_spam.c -dane.o: $(HDRS) dane.c dane-gnu.c dane-openssl.c -dcc.o: $(HDRS) dcc.h dcc.c -dmarc.o: $(HDRS) dmarc.h dmarc.c -spf.o: $(HDRS) spf.h spf.c -srs.o: $(HDRS) srs.h srs.c +bmi_spam.o: $(HDRS) bmi_spam.c +dane.o: $(HDRS) dane.c dane-gnu.c dane-openssl.c +dcc.o: $(HDRS) dcc.h dcc.c +dmarc.o: $(HDRS) pdkim/pdkim.h dmarc.h dmarc.c +imap_utf7.o: $(HDRS) imap_utf7.c +spf.o: $(HDRS) spf.h spf.c +srs.o: $(HDRS) srs.h srs.c +utf8.o: $(HDRS) utf8.c # The module containing tables of available lookups, routers, auths, and # transports must be rebuilt if any of them are. However, because the makefiles @@ -717,11 +763,11 @@ sa-os.o: $(HDRS) os.c # These are the test targets themselves test_dbfn: config.h dbfn.c dummies.o sa-globals.o sa-os.o store.o \ - string.o tod.o version.o + string.o tod.o version.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE dbfn.c $(LNCC) -o test_dbfn $(LFLAGS) dbfn.o \ dummies.o sa-globals.o sa-os.o store.o string.o \ - tod.o version.o $(LIBS) $(DBMLIB) + tod.o version.o utf8.o $(LIBS) $(DBMLIB) $(LDFLAGS) rm -f dbfn.o test_host: config.h child.c host.c dns.c dummies.c sa-globals.o os.o \ @@ -735,23 +781,24 @@ test_host: config.h child.c host.c dns.c dummies.c sa-globals.o os.o \ tod.o tree.o $(LIBS) $(LIBRESOLV) rm -f child.o dummies.o host.o dns.o -test_os: os.h os.c dummies.o sa-globals.o store.o string.o tod.o +test_os: os.h os.c dummies.o sa-globals.o store.o string.o tod.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE os.c $(LNCC) -o test_os $(LFLAGS) os.o dummies.o \ - sa-globals.o store.o string.o tod.o $(LIBS) + sa-globals.o store.o string.o tod.o utf8.o $(LIBS) $(LDFLAGS) rm -f os.o test_parse: config.h parse.c dummies.o sa-globals.o \ - store.o string.o tod.o version.o + store.o string.o tod.o version.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE parse.c $(LNCC) -o test_parse $(LFLAGS) parse.o \ - dummies.o sa-globals.o store.o string.o tod.o version.o + dummies.o sa-globals.o store.o string.o tod.o version.o \ + utf8.o $(LDFLAGS) rm -f parse.o -test_string: config.h string.c dummies.o sa-globals.o store.o tod.o +test_string: config.h string.c dummies.o sa-globals.o store.o tod.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE string.c $(LNCC) -o test_string $(LFLAGS) -DSTAND_ALONE string.o \ - dummies.o sa-globals.o store.o tod.o $(LIBS) + dummies.o sa-globals.o store.o tod.o utf8.o $(LIBS) $(LDFLAGS) rm -f string.o # End diff --git a/src/OS/Makefile-CYGWIN b/src/OS/Makefile-CYGWIN index 774fa4f33..23981d479 100644 --- a/src/OS/Makefile-CYGWIN +++ b/src/OS/Makefile-CYGWIN @@ -2,8 +2,10 @@ # This file provided by Pierre A. Humblet <Pierre.Humblet@ieee.org> +HAVE_IPV6 = yes HAVE_ICONV = yes -CFLAGS= -g -Wall -O2 +# Use c99 to have %z +CFLAGS= -g -Wall -std=c99 -U __STRICT_ANSI__ LIBS= -lcrypt -lresolv LIBS_EXIM= -liconv EXIWHAT_PS_ARG=-as @@ -98,17 +100,15 @@ ZCAT_COMMAND=/usr/bin/zcat SUPPORT_PAM=yes CFLAGS += -DINCLUDE_PAM -I ../pam -I ../../pam -APPENDFILE_MODE = 0644 # default if no ntsec -APPENDFILE_DIRECTORY_MODE = 0777 -APPENDFILE_LOCKFILE_MODE = 0666 -EXIMDB_DIRECTORY_MODE = 0777 +# All modes are in octal and must start with 0 +EXIMDB_DIRECTORY_MODE = 01777 EXIMDB_MODE = 0666 EXIMDB_LOCKFILE_MODE = 0666 -INPUT_DIRECTORY_MODE = 0777 -LOG_DIRECTORY_MODE = 0777 +INPUT_DIRECTORY_MODE = 01777 +LOG_DIRECTORY_MODE = 01777 LOG_MODE = 0666 -MSGLOG_DIRECTORY_MODE = 0777 -SPOOL_DIRECTORY_MODE = 0777 -SPOOL_MODE = 0666 +MSGLOG_DIRECTORY_MODE = 01777 +SPOOL_DIRECTORY_MODE = 01777 +SPOOL_MODE = 0600 # End diff --git a/src/OS/Makefile-Default b/src/OS/Makefile-Default index 60d5ea83b..a0d9afa95 100644 --- a/src/OS/Makefile-Default +++ b/src/OS/Makefile-Default @@ -186,14 +186,6 @@ EXIWHAT_KILL_SIGNAL=-USR1 # IPV6_USE_INET_PTON=yes -# Setting the next option brings in support for A6 DNS records for IPV6. These -# were at one time expected to supplant AAAA records, but were eventually -# rejected. The code remains in Exim, but has not been compiled or tested for -# quite some time. Do not set this unless you know what you are doing. - -# SUPPORT_A6=yes - - # HOSTNAME_COMMAND contains the path to the "hostname" command, which varies # from OS to OS. This is used when building the Exim monitor script only. (See # also BASENAME_COMMAND.) If HOSTNAME_COMMAND is set to "look_for_it" then the diff --git a/src/OS/os.c-cygwin b/src/OS/os.c-cygwin index ea17a436e..d7645fdbe 100644 --- a/src/OS/os.c-cygwin +++ b/src/OS/os.c-cygwin @@ -2,8 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Cygwin-specific code. December 2002 - This is concatenated onto the generic src/os.c file. +/* Cygwin-specific code. December 2002. Updated Jan 2015. + This is prefixed to the src/os.c file. This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org> */ @@ -18,23 +18,12 @@ int cygwin_mkdir( const char *path, mode_t mode ) return mkdir(p, mode); } -/* We have strsignal but cannot use #define - because types don't match */ -#define OS_STRSIGNAL /* src/os.c need not provide it */ -char * os_strsignal(int sig) -{ - return (char *) strsignal(sig); -} - #ifndef COMPILE_UTILITY /* Utilities don't need special code */ -#ifdef INCLUDE_MINIRES -#include "../minires/minires.c" -#include "../minires/os-interface.c" -#endif #ifdef INCLUDE_PAM #include "../pam/pam.c" #endif +#include <alloca.h> unsigned int cygwin_WinVersion; @@ -47,23 +36,25 @@ unsigned int cygwin_WinVersion; #endif #include <windows.h> +#include <ntstatus.h> +#include <lmcons.h> + #define EqualLuid(Luid1, Luid2) \ ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart)) #include <sys/cygwin.h> /* Special static variables */ static BOOL cygwin_debug = FALSE; -static int privileged = 1; /* when not privileged, setuid = noop */ +static int fakesetugid = 1; /* when not privileged, setugid = noop */ #undef setuid int cygwin_setuid(uid_t uid ) { - int res; - if (privileged <= 0) return 0; - else { + int res = 0; + if (fakesetugid == 0) { res = setuid(uid); if (cygwin_debug) - fprintf(stderr, "setuid %lu %lu %d pid: %d\n", + fprintf(stderr, "setuid %u %u %d pid: %d\n", uid, getuid(),res, getpid()); } return res; @@ -72,12 +63,11 @@ int cygwin_setuid(uid_t uid ) #undef setgid int cygwin_setgid(gid_t gid ) { - int res; - if (privileged <= 0) return 0; - else { + int res = 0; + if (fakesetugid == 0) { res = setgid(gid); if (cygwin_debug) - fprintf(stderr, "setgid %lu %lu %d pid: %d\n", + fprintf(stderr, "setgid %u %u %d pid: %d\n", gid, getgid(), res, getpid()); } return res; @@ -97,8 +87,8 @@ static void cygwin_setpriority() Next byte: 0 Next byte: minor version of OS Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */ -#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */ -#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */ +//#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */ +//#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */ /* Routine to find if process or thread is privileged @@ -106,7 +96,6 @@ static void cygwin_setpriority() enum { CREATE_BIT = 1, - RESTORE_BIT = 2 }; static DWORD get_privileges () @@ -132,15 +121,12 @@ static DWORD get_privileges () for (i = 0; i < privs->PrivilegeCount; i++) { if (EqualLuid(privs->Privileges[i].Luid, cluid)) ret |= CREATE_BIT; - else if (EqualLuid(privs->Privileges[i].Luid, rluid)) - ret |= RESTORE_BIT; - else continue; - if (ret == (CREATE_BIT | RESTORE_BIT)) + if (ret == (CREATE_BIT)) break; } } else - fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError()); + fprintf(stderr, "has_create_token_privilege %u\n", GetLastError()); if (hToken) CloseHandle(hToken); @@ -148,17 +134,18 @@ static DWORD get_privileges () return ret; } -/* We use a special routine to initialize - cygwin_init is called from the OS_INIT macro in main(). */ - -void cygwin_init(int argc, char ** argv, void * rup, - void * eup, void * egp, void * cup, void * cgp) +/* + We use cygwin_premain to fake a few things + and to provide some debug info +*/ +void cygwin_premain2(int argc, char ** argv, struct per_process * ptr) { - int i; + int i, res, is_daemon = 0, is_spoolwritable, is_privileged, is_eximuser; uid_t myuid, systemuid; gid_t mygid, adminsgid; - struct passwd * pwp; - char *cygenv, win32_path[MAX_PATH]; + struct passwd * pwp = NULL; + struct stat buf; + char *cygenv; SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID); SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); DWORD priv_flags; @@ -174,77 +161,103 @@ void cygwin_init(int argc, char ** argv, void * rup, for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (argv[i][1] == 'c') { + ssize_t size; + wchar_t *win32_path; argv[i][1] = 'n'; /* Replace -c by -n */ cygwin_debug = TRUE; - fprintf(stderr, "CYGWIN = \"%s\".", cygenv); - cygwin_conv_to_win32_path("/", win32_path); - fprintf(stderr, " Root / mapped to %s.\n", win32_path); + fprintf(stderr, "CYGWIN = \"%s\".\n", cygenv); + if (((size = cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, 0)) > 0) + && ((win32_path = malloc(size)) != NULL) + && (cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, size) == 0)) { + fprintf(stderr, " Root / mapped to %ls.\n", win32_path); + free(win32_path); + } } - else if (argv[i][1] == 'b' && argv[i][2] == 'd') + else if (argv[i][1] == 'b' && argv[i][2] == 'd') { + is_daemon = 1; cygwin_setpriority(); } } - if (VERSION_IS_58M(cygwin_WinVersion)) { - * (uid_t *) rup = myuid; /* Pretend we are root */ - * (uid_t *) eup = myuid; /* ... and exim */ - * (gid_t *) egp = mygid; - return; } + /* Nt/2000/XP - We initially set the exim uid & gid to those of the "real exim", + We initially set the exim uid & gid to those of the "exim user", or to the root uid (SYSTEM) and exim gid (ADMINS), If privileged, we setuid to those. We always set the configure uid to the system uid. We always set the root uid to the real uid - to avoid useless execs following forks. + to allow exim imposed restrictions (bypassable by recompiling) + and to avoid exec that cause loss of privilege If not privileged and unable to chown, we set the exim uid to our uid. - If unprivileged, we fake all subsequent setuid. */ + If unprivileged and /var/spool/exim is writable and not runing as listening daemon, + we fake all subsequent setuid. */ + + /* Get the system and admins uid from their sids */ + if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1) { + fprintf(stderr, "Cannot map System sid. Aborting\n"); + exit(1); + } + if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1) { + fprintf(stderr, "Cannot map Admins sid. Aborting\n"); + exit(1); + } priv_flags = get_privileges (); - privileged = !!(priv_flags & CREATE_BIT); - - /* Get the system and admins uid from their sids, - or use the default values from the Makefile. */ - if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1) - systemuid = * (uid_t *) eup; - if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1) - adminsgid = * (gid_t *) egp; - - if ((pwp = getpwnam("exim")) != NULL) { - * (uid_t *) eup = pwp->pw_uid; /* Set it according to passwd */ - * (gid_t *) egp = pwp->pw_gid; + is_privileged = !!(priv_flags & CREATE_BIT); + + /* Call getpwnam for account exim after getting the local exim name */ + char exim_username[DNLEN + UNLEN + 2]; + if (cygwin_internal(CW_CYGNAME_FROM_WINNAME, "exim", exim_username, sizeof exim_username) != 0) + pwp = getpwnam (exim_username); + + /* If cannot setuid to exim or and is not the daemon (which is assumed to be + able to chown or to be the exim user) set the exim ugid to our ugid to avoid + chown failures after creating files and to be able to setuid to exim in + exim.c ( "privilege not needed" ). */ + if ((is_privileged == 0) && (!is_daemon)) { + exim_uid = myuid; + exim_gid = mygid; + } + else if (pwp != NULL) { + exim_uid = pwp->pw_uid; /* Set it according to passwd */ + exim_gid = pwp->pw_gid; + is_eximuser = 1; } else { - * (uid_t *) eup = systemuid; - * (gid_t *) egp = adminsgid; + exim_uid = systemuid; + exim_gid = adminsgid; + is_eximuser = 0; } - /* Set the configuration uid and gid to the system uid and admins gid. - Note that exim uid is also accepted as owner of exim.conf. */ - * (uid_t *) cup = systemuid; - * (gid_t *) cgp = adminsgid; + res = stat("/var/spool/exim", &buf); + /* Check if writable (and can be stat) */ + is_spoolwritable = ((res == 0) && ((buf.st_mode & S_IWOTH) != 0)); + + fakesetugid = (is_privileged == 0) && (is_daemon == 0) && (is_spoolwritable == 1); - if (privileged) { /* Can setuid */ - if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */ - || cygwin_setuid(* (uid_t *) eup)) - privileged = -1; /* Problem... Perhaps not in 544 */ + if (is_privileged) { /* Can setuid */ + if (cygwin_setgid(exim_gid) /* Setuid to exim */ + || cygwin_setuid(exim_uid)) { + fprintf(stderr, "Unable to setuid/gid to exim. priv_flags: %x\n", priv_flags); + exit(0); /* Problem... Perhaps not in 544 */ + } } - /* Pretend we are root to avoid useless execs. - We are limited by file access rights */ - * (uid_t *) rup = getuid (); + /* Set the configuration file uid and gid to the system uid and admins gid. */ + config_uid = systemuid; + config_gid = adminsgid; - /* If we have not setuid to exim and cannot chown, - set the exim uid to our uid to avoid chown failures */ - if (privileged <= 0 && !(priv_flags & RESTORE_BIT)) - * (uid_t *) eup = * (uid_t *) rup; + /* Pretend we are root to avoid useless exec + and avoid exim set limitations. + We are limited by file access rights */ + root_uid = getuid (); if (cygwin_debug) { - fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n", - myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged); - fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld, config_gid %ld.\n", - * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup, * (gid_t *) cgp); + fprintf(stderr, "Starting uid %u, gid %u, priv_flags %x, is_privileged %d, is_daemon %d, is_spoolwritable %d.\n", + myuid, mygid, priv_flags, is_privileged, is_daemon, is_spoolwritable); + fprintf(stderr, "root_uid %u, exim_uid %u, exim_gid %u, config_uid %u, config_gid %u, is_eximuser %d.\n", + root_uid, exim_uid, exim_gid, config_uid, config_gid, is_eximuser); } return; } @@ -253,24 +266,15 @@ void cygwin_init(int argc, char ** argv, void * rup, #define OS_LOAD_AVERAGE /* src/os.c need not provide it */ /***************************************************************** - * Functions for average load measurements - There are two methods, which work only on NT. + Uses NtQuerySystemInformation. + This requires definitions that are not part of + standard include files. - The first one uses the HKEY_PERFORMANCE_DATA registry to - get performance data. It is complex but well documented - and works on all NT versions. - - The second one uses NtQuerySystemInformation. - Its use is discouraged starting with WinXP. - - Until 4.43, the Cygwin port of exim was using the first - method. - -*****************************************************************/ -#define PERF_METHOD2 + This is discouraged starting with WinXP. +*************************************************************/ /* Structure to compute the load average efficiently */ typedef struct { DWORD Lock; @@ -279,11 +283,6 @@ typedef struct { unsigned long long LastCounter; /* Last measurement counter */ unsigned long long PerfFreq; /* Perf counter frequency */ int LastLoad; /* Last reported load, or -1 */ -#ifdef PERF_METHOD1 - PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */ - DWORD BufferSize; /* Size of PerfData */ - LPSTR * NamesArray; /* Temporary (malloc) buffer for index */ -#endif } cygwin_perf_t; static struct { @@ -292,317 +291,6 @@ static struct { cygwin_perf_t *perf; } cygwin_load = {NULL, 0, NULL}; -#ifdef PERF_METHOD1 -/************************************************************* - METHOD 1 - - Obtaining statistics in Windows is done at a low level by - calling registry functions, in particular the key - HKEY_PERFORMANCE_DATA on NT and successors. - Something equivalent exists on Win95, see Microsoft article - HOWTO: Access the Performance Registry Under Windows 95 (KB 174631) - but it is not implemented here. - - The list of objects to be polled is specified in the string - passed to RegQueryValueEx in ReadStat() below. - On NT, all objects are polled even if info about only one is - required. This is fixed in Windows 2000. See articles - INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127) - INFO: Performance Data Changes Between Windows NT 4.0 and Windows - 2000 (KB 296523) - - It is unclear to me how the counters are primarily identified. - Whether it's by name strings or by the offset of their strings - as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as - reported by the registry functions in GetNameStrings( ) below]. - Microsoft documentation seems to say that both methods should - work. - - In the interest of speed and language independence, the main - code below relies on offsets. However if debug is enabled, the - code verifies that the names of the corresponding strings are - as expected. - -*****************************************************************/ - -/* Object and counter indices and names */ -#define PROCESSOR_OBJECT_INDEX 238 -#define PROCESSOR_OBJECT_STRING "238" -#define PROCESSOR_OBJECT_NAME "Processor" -#define PROCESSOR_TIME_COUNTER 6 -#define PROCESSOR_TIME_NAME "% Processor Time" - -#define BYTEINCREMENT 800 /* Block to add to PerfData */ - -/***************************************************************** - * - Macros to navigate through the performance data. - - *****************************************************************/ -#define FirstObject(PerfData)\ - ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength)) -#define NextObject(PerfObj)\ - ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength)) -#define ObjectCounterBlock(PerfObj)\ - ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength ) -#define FirstInstance(PerfObj )\ - ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength)) -#define InstanceCounterBlock(PerfInst)\ - ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength )) -#define NextInstance(PerfInst )\ - ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \ - InstanceCounterBlock(PerfInst)->ByteLength) ) -#define FirstCounter(PerfObj)\ - ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength)) -#define NextCounter(PerfCntr)\ - ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength)) - -/***************************************************************** - * - Load the counter and object names from the registry - to cygwin_load.perf->NameStrings - and index them in cygwin_load.perf->NamesArray - - NameStrings seems to be taken from the file - X:\Winnt\system32\perfc009.dat - - This is used only for name verification during initialization, - if DEBUG(D_load) is TRUE. - -*****************************************************************/ -static BOOL GetNameStrings( ) -{ - HKEY hKeyPerflib; // handle to registry key - DWORD dwArraySize; // size for array - DWORD dwNamesSize; // size for strings - LPSTR lpCurrentString; // pointer for enumerating data strings - DWORD dwCounter; // current counter index - LONG res; - - /* Get the number of Counter items into dwArraySize. */ - if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE, - "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib", - 0, - KEY_QUERY_VALUE, /* KEY_READ, */ - &hKeyPerflib)) - != ERROR_SUCCESS) { - DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res); - return FALSE; - } - dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */ - if ((res = RegQueryValueEx( hKeyPerflib, - "Last Counter", - NULL, - NULL, - (LPBYTE) &dwArraySize, - &dwNamesSize )) - != ERROR_SUCCESS) { - DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res); - return FALSE; - } - RegCloseKey( hKeyPerflib ); - /* Open the key containing the counter and object names. */ - if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE, - "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009", - 0, - KEY_READ, - &hKeyPerflib)) - != ERROR_SUCCESS) { - DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res); - return FALSE; - } - /* Get the size of the Counter value in the key - and then read the value in the tail of NamesArray */ - dwNamesSize = 0; - lpCurrentString = NULL; - while (1) { - res = RegQueryValueEx( hKeyPerflib, - "Counter", - NULL, - NULL, - (unsigned char *) lpCurrentString, - &dwNamesSize); - if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */ - (cygwin_load.perf->NamesArray != NULL)) break; - if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */ - (res == ERROR_MORE_DATA)) { - /* Allocate memory BOTH for the names array and for the counter and object names */ - if ((cygwin_load.perf->NamesArray = - (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR))) - != NULL) { - /* Point to area for the counter and object names */ - lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1]; - continue; - } - DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno)); - } - else { /* Serious error */ - DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res); - } - return FALSE; - } - RegCloseKey( hKeyPerflib ); - /* Index the names into an array. */ - while (*lpCurrentString) { - dwCounter = atol( lpCurrentString ); - lpCurrentString += (lstrlen(lpCurrentString)+1); - cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString; - lpCurrentString += (strlen(lpCurrentString)+1); - } - return TRUE; -} - -/***************************************************************** - * - Find the value of the Processor Time counter - -*****************************************************************/ -static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj, - PPERF_COUNTER_DEFINITION CurCntr, - PPERF_COUNTER_BLOCK PtrToCntr, - unsigned long long * TimePtr){ - int j; - /* Scan all counters. */ - for( j = 0; j < PerfObj->NumCounters; j++ ) { - if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) { - /* Verify it is really the proc time counter */ - if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */ - ((cygwin_load.perf->NamesArray != NULL) && /* Verify name */ - (strcmp(cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex], - PROCESSOR_TIME_NAME)))) { - log_write(0, LOG_MAIN|LOG_PANIC, - "Incorrect Perf counter type or name %x %s", - (unsigned) CurCntr->CounterType, - cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]); - return FALSE; - } - *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset); - return TRUE; /* return TRUE as soon as we found the counter */ - } - /* Get the next counter. */ - CurCntr = NextCounter( CurCntr ); - } - return FALSE; -} - -/***************************************************************** - * - ReadStat() - Measures current Time100ns and IdleCount - Return TRUE if success. - - *****************************************************************/ -static BOOL ReadStat(unsigned long long int *Time100nsPtr, - unsigned long long int * IdleCountPtr) -{ - PPERF_OBJECT_TYPE PerfObj; - PPERF_INSTANCE_DEFINITION PerfInst; - PPERF_COUNTER_DEFINITION PerfCntr; - PPERF_COUNTER_BLOCK PtrToCntr; - DWORD i, k, res; - - /* Get the performance data for the Processor object - There is no need to open a key. - We may need to blindly increase the buffer size. - BufferSize does not return info but may be changed */ - while (1) { - DWORD BufferSize = cygwin_load.perf->BufferSize; - res = RegQueryValueEx( HKEY_PERFORMANCE_DATA, - PROCESSOR_OBJECT_STRING, - NULL, - NULL, - (LPBYTE) cygwin_load.perf->PerfData, - &BufferSize ); - if (res == ERROR_SUCCESS) break; - if (res == ERROR_MORE_DATA ) { - /* Increment if necessary to get a buffer that is big enough. */ - cygwin_load.perf->BufferSize += BYTEINCREMENT; - if ((cygwin_load.perf->PerfData = - (PPERF_DATA_BLOCK) realloc( cygwin_load.perf->PerfData, cygwin_load.perf->BufferSize )) - != NULL) continue; - DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno)); - } - else { /* Serious error */ - DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res); - } - return FALSE; - } - /* Initialize the counters */ - *Time100nsPtr = 0; - *IdleCountPtr = 0; - /* We should only have one object, but write general code just in case. */ - PerfObj = FirstObject( cygwin_load.perf->PerfData ); - for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) { - /* We are only interested in the processor object */ - if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) { - /* Possibly verify it is really the Processor object. */ - if ((cygwin_load.perf->NamesArray != NULL) && - (strcmp(cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex], - PROCESSOR_OBJECT_NAME))) { - log_write(0, LOG_MAIN|LOG_PANIC, - "Incorrect Perf object name %s", - cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex]); - return FALSE; - } - /* Get the first counter */ - PerfCntr = FirstCounter( PerfObj ); - /* See if the object has instances. - It should, but write general code. */ - if( PerfObj->NumInstances != PERF_NO_INSTANCES ) { - PerfInst = FirstInstance( PerfObj ); - for( k = 0; k < PerfObj->NumInstances; k++ ) { - /* There can be several processors. - Accumulate both the Time100ns and the idle counter. - Starting with Win2000 there is an instance named "_Total". - Do not use it. We only use instances with a single - character in the name. - If we examine the object names, we also look at the instance - names and their lengths and issue reports */ - if ( cygwin_load.perf->NamesArray != NULL) { - CHAR ascii[30]; /* The name is in unicode */ - wsprintf(ascii,"%.29lS", - (char *)((PBYTE)PerfInst + PerfInst->NameOffset)); - log_write(0, LOG_MAIN, - "Perf: Found processor instance \"%s\", length %d", - ascii, PerfInst->NameLength); - if ((PerfInst->NameLength != 4) && - (strcmp(ascii, "_Total") != 0)) { - log_write(0, LOG_MAIN|LOG_PANIC, - "Perf: WARNING: Unexpected processor instance name"); - return FALSE; - } - } - if (PerfInst->NameLength == 4) { - *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart; - PtrToCntr = InstanceCounterBlock(PerfInst); - if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) { - return FALSE; - } - } - PerfInst = NextInstance( PerfInst ); - } - return (*Time100nsPtr != 0); /* Something was read */ - } - else { /* No instance, just the counter data */ - *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart; - PtrToCntr = ObjectCounterBlock(PerfObj); - return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr); - } - } - PerfObj = NextObject( PerfObj ); - } - return FALSE; /* Did not find the Processor object */ -} - -#elif defined(PERF_METHOD2) - -/************************************************************* - METHOD 2 - - Uses NtQuerySystemInformation. - This requires definitions that are not part of - standard include files. -*************************************************************/ #include <ntdef.h> typedef enum _SYSTEM_INFORMATION_CLASS @@ -669,10 +357,9 @@ static BOOL LoadNtdll() return TRUE; DEBUG(D_load) - debug_printf("perf: load: %ld (Windows)\n", GetLastError()); + debug_printf("perf: load: %u (Windows)\n", GetLastError()); return FALSE; } - /***************************************************************** * ReadStat() @@ -694,7 +381,7 @@ static BOOL ReadStat(unsigned long long int *Time100nsPtr, (PVOID) &sbi, sizeof sbi, NULL)) != STATUS_SUCCESS) { DEBUG(D_load) - debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n", + debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n", RtlNtStatusToDosError(ret)); } else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) { @@ -705,7 +392,7 @@ static BOOL ReadStat(unsigned long long int *Time100nsPtr, sizeof spt[0] * sbi.NumberProcessors, NULL)) != STATUS_SUCCESS) { DEBUG(D_load) - debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n", + debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n", RtlNtStatusToDosError(ret)); } else { @@ -719,7 +406,6 @@ static BOOL ReadStat(unsigned long long int *Time100nsPtr, } return FALSE; } -#endif /* PERF_METHODX */ /***************************************************************** * @@ -736,14 +422,6 @@ static void InitLoadAvg(cygwin_perf_t *this) QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq); QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter); -#ifdef PERF_METHOD1 - DEBUG(D_load) { - /* Get the name strings through the registry - to verify that the object and counter numbers - have the names we expect */ - success = GetNameStrings(); - } -#endif /* Get initial values for Time100ns and IdleCount */ success = success && ReadStat( & this->Time100ns, @@ -754,13 +432,6 @@ static void InitLoadAvg(cygwin_perf_t *this) log_write(0, LOG_MAIN, "Cannot obtain Load Average"); this->LastLoad = -1; } -#ifdef PERF_METHOD1 - /* Free the buffer created for debug name verification */ - if (this->NamesArray != NULL) { - free(this->NamesArray); - this->NamesArray = NULL; - } -#endif } @@ -791,24 +462,22 @@ int os_getloadavg() BOOL new; cygwin_load.pid = newpid; -#ifdef PERF_METHOD2 if (!LoadNtdll()) { log_write(0, LOG_MAIN, "Cannot obtain Load Average"); cygwin_load.perf = NULL; return -1; } -#endif if ((new = !cygwin_load.handle)) { cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, sizeof(cygwin_perf_t), NULL); DEBUG(D_load) - debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle); + debug_printf("Perf: CreateFileMapping: handle %p\n", (void *) cygwin_load.handle); } cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); DEBUG(D_load) - debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf); + debug_printf("Perf: MapViewOfFile: addr %p\n", (void *) cygwin_load.perf); if (new && cygwin_load.perf) InitLoadAvg(cygwin_load.perf); } diff --git a/src/OS/os.h-AIX b/src/OS/os.h-AIX index f3a84f2e3..5cd4501a4 100644 --- a/src/OS/os.h-AIX +++ b/src/OS/os.h-AIX @@ -20,4 +20,8 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + + /* End */ diff --git a/src/OS/os.h-BSDI b/src/OS/os.h-BSDI index cd91936de..6e16722fd 100644 --- a/src/OS/os.h-BSDI +++ b/src/OS/os.h-BSDI @@ -8,4 +8,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-DGUX b/src/OS/os.h-DGUX index 838ddd991..9040f0e72 100644 --- a/src/OS/os.h-DGUX +++ b/src/OS/os.h-DGUX @@ -22,4 +22,7 @@ forego the detection of some source-routing based IP attacks. */ #define NO_IP_OPTIONS +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-Darwin b/src/OS/os.h-Darwin index 559003f53..f4087404e 100644 --- a/src/OS/os.h-Darwin +++ b/src/OS/os.h-Darwin @@ -42,4 +42,7 @@ updating Exim to use the newer interface. */ #define OFF_T_FMT "%lld" #define LONGLONG_T long int +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-DragonFly b/src/OS/os.h-DragonFly index 669bb2327..4c2f1d508 100644 --- a/src/OS/os.h-DragonFly +++ b/src/OS/os.h-DragonFly @@ -7,4 +7,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-FreeBSD b/src/OS/os.h-FreeBSD index c5ed04275..ba4889fec 100644 --- a/src/OS/os.h-FreeBSD +++ b/src/OS/os.h-FreeBSD @@ -10,4 +10,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-GNU b/src/OS/os.h-GNU index cc4da0e3b..44993163d 100644 --- a/src/OS/os.h-GNU +++ b/src/OS/os.h-GNU @@ -17,4 +17,7 @@ typedef struct flock flock_t; /* Hurd-specific bits below */ +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-GNUkFreeBSD b/src/OS/os.h-GNUkFreeBSD index e60690f71..ab3503131 100644 --- a/src/OS/os.h-GNUkFreeBSD +++ b/src/OS/os.h-GNUkFreeBSD @@ -19,4 +19,7 @@ typedef struct flock flock_t; #define HAVE_SYS_MOUNT_H #define SIOCGIFCONF_GIVES_ADDR +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-GNUkNetBSD b/src/OS/os.h-GNUkNetBSD index 121f2d3b3..bc3bc259d 100644 --- a/src/OS/os.h-GNUkNetBSD +++ b/src/OS/os.h-GNUkNetBSD @@ -19,4 +19,7 @@ typedef struct flock flock_t; #define HAVE_SYS_MOUNT_H #define SIOCGIFCONF_GIVES_ADDR +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-HI-OSF b/src/OS/os.h-HI-OSF index 76bd4295d..0f50fb660 100644 --- a/src/OS/os.h-HI-OSF +++ b/src/OS/os.h-HI-OSF @@ -6,4 +6,7 @@ typedef struct flock flock_t; #define F_FREESP O_TRUNC #define DN_EXPAND_ARG4_TYPE u_char * +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-HI-UX b/src/OS/os.h-HI-UX index 97b83edf1..f3df9638c 100644 --- a/src/OS/os.h-HI-UX +++ b/src/OS/os.h-HI-UX @@ -15,4 +15,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-HP-UX b/src/OS/os.h-HP-UX index 87e4dfc97..1b599231d 100644 --- a/src/OS/os.h-HP-UX +++ b/src/OS/os.h-HP-UX @@ -1,6 +1,5 @@ /* Exim: OS-specific C header file for HP-UX versions greater than 9 */ -#define ICONV_ARG2_TYPE char ** #define EXIM_SOCKLEN_T size_t #define LOAD_AVG_NEEDS_ROOT @@ -11,6 +10,7 @@ #define FSCALE 1.0 #define HAVE_SYS_STATVFS_H +#define MISSING_UNSETENV_3 #define F_FREESP O_TRUNC #define NEED_H_ERRNO 1 @@ -24,4 +24,12 @@ typedef struct __res_state *res_state; #define strtoll(a,b,c) strtoimax(a,b,c) +/* Determined by sockaddr_un */ + +struct sockaddr_storage +{ + short ss_family; + char __ss_padding[92]; +}; + /* End */ diff --git a/src/OS/os.h-HP-UX-9 b/src/OS/os.h-HP-UX-9 index dab965e3b..5a260d607 100644 --- a/src/OS/os.h-HP-UX-9 +++ b/src/OS/os.h-HP-UX-9 @@ -17,4 +17,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-IRIX b/src/OS/os.h-IRIX index ac5a6b326..1d4bf46ba 100644 --- a/src/OS/os.h-IRIX +++ b/src/OS/os.h-IRIX @@ -14,7 +14,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff --git a/src/OS/os.h-IRIX6 b/src/OS/os.h-IRIX6 index c41a23424..bf3076713 100644 --- a/src/OS/os.h-IRIX6 +++ b/src/OS/os.h-IRIX6 @@ -13,7 +13,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff --git a/src/OS/os.h-IRIX632 b/src/OS/os.h-IRIX632 index 0196931d2..90f1c582c 100644 --- a/src/OS/os.h-IRIX632 +++ b/src/OS/os.h-IRIX632 @@ -15,7 +15,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff --git a/src/OS/os.h-IRIX65 b/src/OS/os.h-IRIX65 index 683c66a9f..4b248fe36 100644 --- a/src/OS/os.h-IRIX65 +++ b/src/OS/os.h-IRIX65 @@ -13,7 +13,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff --git a/src/OS/os.h-Linux b/src/OS/os.h-Linux index 3fead17d7..05c153e2c 100644 --- a/src/OS/os.h-Linux +++ b/src/OS/os.h-Linux @@ -44,9 +44,6 @@ storage" as quickly as Exim thinks they are. */ #define NEED_SYNC_DIRECTORY -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - #define os_find_running_interfaces os_find_running_interfaces_linux /* Need a prototype for the Linux-specific function. The structure hasn't diff --git a/src/OS/os.h-NetBSD b/src/OS/os.h-NetBSD index 19a8ac0c7..d2d3e0d82 100644 --- a/src/OS/os.h-NetBSD +++ b/src/OS/os.h-NetBSD @@ -22,4 +22,7 @@ typedef struct flock flock_t; #define HAVE_SYS_STATVFS_H #endif +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-OSF1 b/src/OS/os.h-OSF1 index f04a5b700..6b5fa4973 100644 --- a/src/OS/os.h-OSF1 +++ b/src/OS/os.h-OSF1 @@ -13,7 +13,4 @@ changed. */ /* Still not "socklen_t", which is the most common setting */ #define EXIM_SOCKLEN_T int -/* The default for this is "const char **" */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff --git a/src/OS/os.h-OpenBSD b/src/OS/os.h-OpenBSD index 9578047af..35eddd5a5 100644 --- a/src/OS/os.h-OpenBSD +++ b/src/OS/os.h-OpenBSD @@ -20,4 +20,7 @@ typedef struct flock flock_t; typedef struct __res_state *res_state; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-OpenUNIX b/src/OS/os.h-OpenUNIX index 90be8d56d..67d1063b0 100644 --- a/src/OS/os.h-OpenUNIX +++ b/src/OS/os.h-OpenUNIX @@ -13,4 +13,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-QNX b/src/OS/os.h-QNX index 106b0a61b..798f799ed 100644 --- a/src/OS/os.h-QNX +++ b/src/OS/os.h-QNX @@ -18,4 +18,7 @@ doesn't have/need this header file. From Karsten P. Hoffmann. */ extern int h_errno; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-SCO b/src/OS/os.h-SCO index 07d21bd94..e5e915ed0 100644 --- a/src/OS/os.h-SCO +++ b/src/OS/os.h-SCO @@ -15,4 +15,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-SCO_SV b/src/OS/os.h-SCO_SV index 486fcbe94..0ca29f74a 100644 --- a/src/OS/os.h-SCO_SV +++ b/src/OS/os.h-SCO_SV @@ -13,4 +13,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-SunOS4 b/src/OS/os.h-SunOS4 index b0deefc49..65556204c 100644 --- a/src/OS/os.h-SunOS4 +++ b/src/OS/os.h-SunOS4 @@ -33,4 +33,7 @@ flag causes this to get done in exim.h. */ #define FUDGE_GETC_AND_FRIENDS +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-SunOS5 b/src/OS/os.h-SunOS5 index 8bc0799d2..bb1d77fe9 100644 --- a/src/OS/os.h-SunOS5 +++ b/src/OS/os.h-SunOS5 @@ -28,4 +28,13 @@ it seems. */ #define PAM_CONVERSE_ARG2_TYPE struct pam_message + +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + +#if _POSIX_C_SOURCE < 200112L +# define MISSING_UNSETENV_3 +#endof + + /* End */ diff --git a/src/OS/os.h-SunOS5-hal b/src/OS/os.h-SunOS5-hal index 044e09b30..cd9e877a9 100644 --- a/src/OS/os.h-SunOS5-hal +++ b/src/OS/os.h-SunOS5-hal @@ -8,4 +8,7 @@ #define LOAD_AVG_SYMBOL "avenrun_1min" #define LOAD_AVG_FIELD value.ul +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-ULTRIX b/src/OS/os.h-ULTRIX index 9985af263..08db5aec8 100644 --- a/src/OS/os.h-ULTRIX +++ b/src/OS/os.h-ULTRIX @@ -12,4 +12,7 @@ a minority operating system, easiest just to say "no" until someone asks. */ #define NO_OPENLOG typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-UNIX_SV b/src/OS/os.h-UNIX_SV index 9ad824ab7..4943a07de 100644 --- a/src/OS/os.h-UNIX_SV +++ b/src/OS/os.h-UNIX_SV @@ -19,4 +19,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-USG b/src/OS/os.h-USG index 1c780ee64..e76922067 100644 --- a/src/OS/os.h-USG +++ b/src/OS/os.h-USG @@ -13,4 +13,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-Unixware7 b/src/OS/os.h-Unixware7 index 159209497..4d3ed42f2 100644 --- a/src/OS/os.h-Unixware7 +++ b/src/OS/os.h-Unixware7 @@ -2,7 +2,6 @@ #define NO_SYSEXITS -#define ICONV_ARG2_TYPE char ** #define EXIM_SOCKLEN_T size_t #define LOAD_AVG_NEEDS_ROOT diff --git a/src/OS/os.h-cygwin b/src/OS/os.h-cygwin index 740300af6..6ef59e0cc 100644 --- a/src/OS/os.h-cygwin +++ b/src/OS/os.h-cygwin @@ -1,19 +1,7 @@ /* Exim: OS-specific C header file for Cygwin */ -/* This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org> */ - -/* Define the OS_INIT macro that we insert in exim.c:main() - to set the root and exim uid depending on the system */ -/* We use a special routine to initialize */ -void cygwin_init(int, char **, void *, void *, void *, void *, void *); -#define OS_INIT\ - cygwin_init(argc, (char **) argv, &root_uid, &exim_uid, &exim_gid, &config_uid, &config_gid); - -/* We need a special mkdir that - allows names starting with // */ -#include <sys/stat.h> /* Do not redefine mkdir in sys/stat.h */ -int cygwin_mkdir( const char *_path, mode_t __mode ); -#define mkdir cygwin_mkdir /* redefine mkdir elsewhere */ +/* This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org> + December 2002. Updated Jan 2015. */ /* Redefine the set*id calls to run when faking root */ #include <unistd.h> /* Do not redefine in unitsd.h */ @@ -22,8 +10,8 @@ int cygwin_setgid(gid_t gid ); #define setuid cygwin_setuid #define setgid cygwin_setgid -extern unsigned int cygwin_WinVersion; - +#define os_strsignal strsignal +#define OS_STRSIGNAL #define BASE_62 36 /* Windows aliases lower and upper cases in filenames. Consider reducing MAX_LOCALHOST_NUMBER */ #define CRYPT_H @@ -31,7 +19,6 @@ extern unsigned int cygwin_WinVersion; #define HAVE_SYS_VFS_H #define NO_IP_VAR_H #define NO_IP_OPTIONS -#define F_FREESP O_TRUNC /* Defining LOAD_AVG_NEEDS_ROOT causes an initial call to os_getloadavg. In our case this is beneficial because it initializes the counts */ @@ -48,4 +35,7 @@ struct { \ DWORD SubAuthority[n]; \ } name = { SID_REVISION, n, {SECURITY_NT_AUTHORITY}, {sid}} +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/OS/os.h-mips b/src/OS/os.h-mips index 79f3ff253..325e3a134 100644 --- a/src/OS/os.h-mips +++ b/src/OS/os.h-mips @@ -21,4 +21,7 @@ extern char *strerror(int); extern int sys_nerr; extern char *sys_errlist[]; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index 3277012f0..6415e4238 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -2,7 +2,7 @@ * Exim Monitor * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index a50f75b36..e011c3ca1 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -27,254 +27,112 @@ echo ">>> Creating links to source files..." # Firstly the lookups mkdir lookups cd lookups -ln -s ../../src/lookups/README README # Makefile is generated -ln -s ../../src/lookups/cdb.c cdb.c -ln -s ../../src/lookups/dbmdb.c dbmdb.c -ln -s ../../src/lookups/dnsdb.c dnsdb.c -ln -s ../../src/lookups/dsearch.c dsearch.c -ln -s ../../src/lookups/ibase.c ibase.c -ln -s ../../src/lookups/ldap.h ldap.h -ln -s ../../src/lookups/ldap.c ldap.c -ln -s ../../src/lookups/lsearch.c lsearch.c -ln -s ../../src/lookups/mysql.c mysql.c -ln -s ../../src/lookups/redis.c redis.c -ln -s ../../src/lookups/nis.c nis.c -ln -s ../../src/lookups/nisplus.c nisplus.c -ln -s ../../src/lookups/oracle.c oracle.c -ln -s ../../src/lookups/passwd.c passwd.c -ln -s ../../src/lookups/pgsql.c pgsql.c -ln -s ../../src/lookups/spf.c spf.c -ln -s ../../src/lookups/sqlite.c sqlite.c -ln -s ../../src/lookups/testdb.c testdb.c -ln -s ../../src/lookups/whoson.c whoson.c - -ln -s ../../src/lookups/lf_functions.h lf_functions.h -ln -s ../../src/lookups/lf_check_file.c lf_check_file.c -ln -s ../../src/lookups/lf_quote.c lf_quote.c -ln -s ../../src/lookups/lf_sqlperform.c lf_sqlperform.c +for f in README cdb.c dbmdb.c dnsdb.c dsearch.c ibase.c ldap.h ldap.c \ + lsearch.c mysql.c redis.c nis.c nisplus.c oracle.c passwd.c \ + pgsql.c spf.c sqlite.c testdb.c whoson.c \ + lf_functions.h lf_check_file.c lf_quote.c lf_sqlperform.c +do + ln -s ../../src/lookups/$f $f +done cd .. # Likewise for the code for the routers mkdir routers cd routers -ln -s ../../src/routers/README README -ln -s ../../src/routers/Makefile Makefile -ln -s ../../src/routers/accept.h accept.h -ln -s ../../src/routers/accept.c accept.c -ln -s ../../src/routers/dnslookup.h dnslookup.h -ln -s ../../src/routers/dnslookup.c dnslookup.c -ln -s ../../src/routers/ipliteral.h ipliteral.h -ln -s ../../src/routers/ipliteral.c ipliteral.c -ln -s ../../src/routers/iplookup.h iplookup.h -ln -s ../../src/routers/iplookup.c iplookup.c -ln -s ../../src/routers/manualroute.h manualroute.h -ln -s ../../src/routers/manualroute.c manualroute.c -ln -s ../../src/routers/queryprogram.h queryprogram.h -ln -s ../../src/routers/queryprogram.c queryprogram.c -ln -s ../../src/routers/redirect.h redirect.h -ln -s ../../src/routers/redirect.c redirect.c - -ln -s ../../src/routers/rf_functions.h rf_functions.h -ln -s ../../src/routers/rf_change_domain.c rf_change_domain.c -ln -s ../../src/routers/rf_expand_data.c rf_expand_data.c -ln -s ../../src/routers/rf_get_errors_address.c rf_get_errors_address.c -ln -s ../../src/routers/rf_get_munge_headers.c rf_get_munge_headers.c -ln -s ../../src/routers/rf_get_transport.c rf_get_transport.c -ln -s ../../src/routers/rf_get_ugid.c rf_get_ugid.c -ln -s ../../src/routers/rf_queue_add.c rf_queue_add.c -ln -s ../../src/routers/rf_lookup_hostlist.c rf_lookup_hostlist.c -ln -s ../../src/routers/rf_self_action.c rf_self_action.c -ln -s ../../src/routers/rf_set_ugid.c rf_set_ugid.c +for f in README Makefile accept.h accept.c dnslookup.h dnslookup.c \ + ipliteral.h ipliteral.c iplookup.h iplookup.c manualroute.h \ + manualroute.c queryprogram.h queryprogram.c redirect.h redirect.c \ + rf_functions.h rf_change_domain.c rf_expand_data.c rf_get_errors_address.c \ + rf_get_munge_headers.c rf_get_transport.c rf_get_ugid.c rf_queue_add.c \ + rf_lookup_hostlist.c rf_self_action.c rf_set_ugid.c +do + ln -s ../../src/routers/$f $f +done cd .. # Likewise for the code for the transports mkdir transports cd transports -ln -s ../../src/transports/README README -ln -s ../../src/transports/Makefile Makefile -ln -s ../../src/transports/appendfile.h appendfile.h -ln -s ../../src/transports/appendfile.c appendfile.c -ln -s ../../src/transports/autoreply.h autoreply.h -ln -s ../../src/transports/autoreply.c autoreply.c -ln -s ../../src/transports/lmtp.h lmtp.h -ln -s ../../src/transports/lmtp.c lmtp.c -ln -s ../../src/transports/pipe.h pipe.h -ln -s ../../src/transports/pipe.c pipe.c -ln -s ../../src/transports/smtp.h smtp.h -ln -s ../../src/transports/smtp.c smtp.c - -ln -s ../../src/transports/tf_maildir.c tf_maildir.c -ln -s ../../src/transports/tf_maildir.h tf_maildir.h +for f in README Makefile appendfile.h appendfile.c autoreply.h \ + autoreply.c lmtp.h lmtp.c pipe.h pipe.c smtp.h smtp.c smtp_socks.c \ + tf_maildir.c tf_maildir.h +do + ln -s ../../src/transports/$f $f +done cd .. # Likewise for the code for the authorization functions mkdir auths cd auths -ln -s ../../src/auths/README README -ln -s ../../src/auths/Makefile Makefile -ln -s ../../src/auths/b64encode.c b64encode.c -ln -s ../../src/auths/b64decode.c b64decode.c -ln -s ../../src/auths/call_pam.c call_pam.c -ln -s ../../src/auths/call_pwcheck.c call_pwcheck.c -ln -s ../../src/auths/call_radius.c call_radius.c -ln -s ../../src/auths/check_serv_cond.c check_serv_cond.c -ln -s ../../src/auths/cyrus_sasl.c cyrus_sasl.c -ln -s ../../src/auths/cyrus_sasl.h cyrus_sasl.h -ln -s ../../src/auths/gsasl_exim.c gsasl_exim.c -ln -s ../../src/auths/gsasl_exim.h gsasl_exim.h -ln -s ../../src/auths/get_data.c get_data.c -ln -s ../../src/auths/get_no64_data.c get_no64_data.c -ln -s ../../src/auths/heimdal_gssapi.c heimdal_gssapi.c -ln -s ../../src/auths/heimdal_gssapi.h heimdal_gssapi.h -ln -s ../../src/auths/md5.c md5.c -ln -s ../../src/auths/xtextencode.c xtextencode.c -ln -s ../../src/auths/xtextdecode.c xtextdecode.c -ln -s ../../src/auths/cram_md5.c cram_md5.c -ln -s ../../src/auths/cram_md5.h cram_md5.h -ln -s ../../src/auths/plaintext.c plaintext.c -ln -s ../../src/auths/plaintext.h plaintext.h -ln -s ../../src/auths/pwcheck.c pwcheck.c -ln -s ../../src/auths/pwcheck.h pwcheck.h -ln -s ../../src/auths/auth-spa.c auth-spa.c -ln -s ../../src/auths/auth-spa.h auth-spa.h -ln -s ../../src/auths/dovecot.c dovecot.c -ln -s ../../src/auths/dovecot.h dovecot.h -ln -s ../../src/auths/sha1.c sha1.c -ln -s ../../src/auths/spa.c spa.c -ln -s ../../src/auths/spa.h spa.h +for f in README Makefile b64encode.c b64decode.c call_pam.c call_pwcheck.c \ + call_radius.c check_serv_cond.c cyrus_sasl.c cyrus_sasl.h gsasl_exim.c \ + gsasl_exim.h get_data.c get_no64_data.c heimdal_gssapi.c heimdal_gssapi.h \ + md5.c xtextencode.c xtextdecode.c cram_md5.c cram_md5.h plaintext.c plaintext.h \ + pwcheck.c pwcheck.h auth-spa.c auth-spa.h dovecot.c dovecot.h sha1.c spa.c \ + spa.h tls.c tls.h +do + ln -s ../../src/auths/$f $f +done cd .. # Likewise for the code for the PDKIM library mkdir pdkim cd pdkim -ln -s ../../src/pdkim/README README -ln -s ../../src/pdkim/Makefile Makefile -ln -s ../../src/pdkim/base64.c base64.c -ln -s ../../src/pdkim/base64.h base64.h -ln -s ../../src/pdkim/bignum.c bignum.c -ln -s ../../src/pdkim/bignum.h bignum.h -ln -s ../../src/pdkim/bn_mul.h bn_mul.h -ln -s ../../src/pdkim/pdkim.c pdkim.c -ln -s ../../src/pdkim/pdkim.h pdkim.h -ln -s ../../src/pdkim/rsa.c rsa.c -ln -s ../../src/pdkim/rsa.h rsa.h -ln -s ../../src/pdkim/sha1.c sha1.c -ln -s ../../src/pdkim/sha1.h sha1.h -ln -s ../../src/pdkim/sha2.c sha2.c -ln -s ../../src/pdkim/sha2.h sha2.h -cd .. +for f in README Makefile base64.c bignum.c part-x509parse.c pdkim.c \ + pdkim.h pdkim-rsa.c pdkim-rsa.h rsa.c sha1.c sha2.c +do + ln -s ../../src/pdkim/$f $f +done +mkdir polarssl +cd polarssl +for i in `ls ../../../src/pdkim/polarssl/` ; do + ln -s ../../../src/pdkim/polarssl/$i $i +done +cd ../.. # The basic source files for Exim and utilities. NB local_scan.h gets linked, # but local_scan.c does not, because its location is taken from the build-time # configuration. Likewise for the os.c file, which gets build dynamically. -ln -s ../src/dbfunctions.h dbfunctions.h -ln -s ../src/dbstuff.h dbstuff.h -ln -s ../src/exim.h exim.h -ln -s ../src/functions.h functions.h -ln -s ../src/globals.h globals.h -ln -s ../src/local_scan.h local_scan.h -ln -s ../src/macros.h macros.h -ln -s ../src/mytypes.h mytypes.h -ln -s ../src/osfunctions.h osfunctions.h -ln -s ../src/store.h store.h -ln -s ../src/structs.h structs.h -ln -s ../src/lookupapi.h lookupapi.h - -ln -s ../src/acl.c acl.c -ln -s ../src/buildconfig.c buildconfig.c -ln -s ../src/child.c child.c -ln -s ../src/crypt16.c crypt16.c -ln -s ../src/daemon.c daemon.c -ln -s ../src/dbfn.c dbfn.c -ln -s ../src/debug.c debug.c -ln -s ../src/deliver.c deliver.c -ln -s ../src/directory.c directory.c -ln -s ../src/dns.c dns.c -ln -s ../src/drtables.c drtables.c -ln -s ../src/dummies.c dummies.c -ln -s ../src/enq.c enq.c -ln -s ../src/exim.c exim.c -ln -s ../src/exim_dbmbuild.c exim_dbmbuild.c -ln -s ../src/exim_dbutil.c exim_dbutil.c -ln -s ../src/exim_lock.c exim_lock.c -ln -s ../src/expand.c expand.c -ln -s ../src/filter.c filter.c -ln -s ../src/filtertest.c filtertest.c -ln -s ../src/globals.c globals.c -ln -s ../src/header.c header.c -ln -s ../src/host.c host.c -ln -s ../src/ip.c ip.c -ln -s ../src/log.c log.c -ln -s ../src/lss.c lss.c -ln -s ../src/match.c match.c -ln -s ../src/moan.c moan.c -ln -s ../src/parse.c parse.c -ln -s ../src/perl.c perl.c -ln -s ../src/queue.c queue.c -ln -s ../src/rda.c rda.c -ln -s ../src/readconf.c readconf.c -ln -s ../src/receive.c receive.c -ln -s ../src/retry.c retry.c -ln -s ../src/rewrite.c rewrite.c -ln -s ../src/rfc2047.c rfc2047.c -ln -s ../src/route.c route.c -ln -s ../src/search.c search.c -ln -s ../src/sieve.c sieve.c -ln -s ../src/smtp_in.c smtp_in.c -ln -s ../src/smtp_out.c smtp_out.c -ln -s ../src/spool_in.c spool_in.c -ln -s ../src/spool_out.c spool_out.c -ln -s ../src/std-crypto.c std-crypto.c -ln -s ../src/store.c store.c -ln -s ../src/string.c string.c -ln -s ../src/tls.c tls.c -ln -s ../src/tlscert-gnu.c tlscert-gnu.c -ln -s ../src/tlscert-openssl.c tlscert-openssl.c -ln -s ../src/tls-gnu.c tls-gnu.c -ln -s ../src/tls-openssl.c tls-openssl.c -ln -s ../src/tod.c tod.c -ln -s ../src/transport.c transport.c -ln -s ../src/tree.c tree.c -ln -s ../src/verify.c verify.c -ln -s ../src/version.c version.c -ln -s ../src/dkim.c dkim.c -ln -s ../src/dkim.h dkim.h -ln -s ../src/dmarc.c dmarc.c -ln -s ../src/dmarc.h dmarc.h -ln -s ../src/valgrind.h valgrind.h -ln -s ../src/memcheck.h memcheck.h +for f in dbfunctions.h dbstuff.h exim.h functions.h globals.h local_scan.h \ + macros.h mytypes.h osfunctions.h store.h structs.h lookupapi.h \ + \ + acl.c buildconfig.c child.c crypt16.c daemon.c dbfn.c debug.c deliver.c \ + directory.c dns.c drtables.c dummies.c enq.c exim.c exim_dbmbuild.c \ + exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c globals.c \ + header.c host.c ip.c log.c lss.c match.c moan.c parse.c perl.c queue.c \ + rda.c readconf.c receive.c retry.c rewrite.c rfc2047.c route.c search.c \ + setenv.c \ + sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \ + string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-gnu.c tls-openssl.c \ + tod.c transport.c tree.c verify.c version.c dkim.c dkim.h dmarc.c dmarc.h \ + valgrind.h memcheck.h +do + ln -s ../src/$f $f +done # WITH_CONTENT_SCAN -ln -s ../src/spam.c spam.c -ln -s ../src/spam.h spam.h -ln -s ../src/spool_mbox.c spool_mbox.c -ln -s ../src/regex.c regex.c -ln -s ../src/mime.c mime.c -ln -s ../src/mime.h mime.h -ln -s ../src/malware.c malware.c +for f in spam.c spam.h spool_mbox.c regex.c mime.c mime.h malware.c +do + ln -s ../src/$f $f +done # WITH_OLD_DEMIME -ln -s ../src/demime.c demime.c -ln -s ../src/demime.h demime.h +for f in demime.c demime.h +do + ln -s ../src/$f $f +done # EXPERIMENTAL_* -ln -s ../src/bmi_spam.c bmi_spam.c -ln -s ../src/bmi_spam.h bmi_spam.h -ln -s ../src/spf.c spf.c -ln -s ../src/spf.h spf.h -ln -s ../src/srs.c srs.c -ln -s ../src/srs.h srs.h -ln -s ../src/dcc.c dcc.c -ln -s ../src/dcc.h dcc.h -ln -s ../src/dane.c dane.c -ln -s ../src/dane-gnu.c dane-gnu.c -ln -s ../src/dane-openssl.c dane-openssl.c -ln -s ../src/danessl.h danessl.h +for f in bmi_spam.c bmi_spam.h dcc.c dcc.h dane.c dane-gnu.c dane-openssl.c \ + danessl.h imap_utf7.c spf.c spf.h srs.c srs.h utf8.c +do + ln -s ../src/$f $f +done # End of MakeLinks diff --git a/src/scripts/lookups-Makefile b/src/scripts/lookups-Makefile index 61493c632..4e69a9a77 100755 --- a/src/scripts/lookups-Makefile +++ b/src/scripts/lookups-Makefile @@ -161,7 +161,7 @@ sed -n "1,/$tag_marker/p" < "$input" for name_mod in \ CDB DBM:dbmdb DNSDB DSEARCH IBASE LSEARCH MYSQL NIS NISPLUS ORACLE \ - PASSWD PGSQL SQLITE TESTDB WHOSON + PASSWD PGSQL REDIS SQLITE TESTDB WHOSON do emit_module_rule $name_mod done @@ -177,14 +177,6 @@ fi OBJ="${OBJ} spf.o" -# Because the variable is EXPERIMENTAL_REDIS and not LOOKUP_REDIS we -# use a different function to check for EXPERIMENTAL_* features -# requested. Don't use the SPF method with dummy functions above. -if want_experimental REDIS -then - OBJ="${OBJ} redis.o" -fi - echo "MODS = $MODS" echo "OBJ = $OBJ" diff --git a/src/src/EDITME b/src/src/EDITME index 353dde3ee..a3b021218 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -288,6 +288,11 @@ TRANSPORT_SMTP=yes # library. # NOTE: LDAP cannot be built as a module! # +# For Redis you need to have hiredis installed on your system +# (https://github.com/redis/hiredis). +# Depending on where it is installed you may have to edit the CFLAGS +# (often += -I/usr/local/include) and LDFLAGS (-lhiredis) lines. + # If your system has pkg-config then the _INCLUDE/_LIBS setting can be # handled for you automatically by also defining the _PC variable to reference # the name of the pkg-config package, if such is available. @@ -306,6 +311,7 @@ LOOKUP_DNSDB=yes # LOOKUP_ORACLE=yes # LOOKUP_PASSWD=yes # LOOKUP_PGSQL=yes +# LOOKUP_REDIS=yes # LOOKUP_SQLITE=yes # LOOKUP_SQLITE_PC=sqlite3 # LOOKUP_WHOSON=yes @@ -336,7 +342,7 @@ LOOKUP_DNSDB=yes #------------------------------------------------------------------------------ -# The PCRE library is required for exim. There is no longer an embedded +# The PCRE library is required for Exim. There is no longer an embedded # version of the PCRE library included with the source code, instead you # must use a system library or build your own copy of PCRE. # In either case you must specify the library link info here. If the @@ -357,7 +363,8 @@ PCRE_CONFIG=yes # the command for linking Exim itself, not on any auxiliary programs. You # don't need to set LOOKUP_INCLUDE if the relevant directories are already # specified in INCLUDE. The settings below are just examples; -lpq is for -# PostgreSQL, -lgds is for Interbase, -lsqlite3 is for SQLite. +# PostgreSQL, -lgds is for Interbase, -lsqlite3 is for SQLite, -lhiredis +# is for Redis. # # You do not need to use this for any lookup information added via pkg-config. @@ -392,6 +399,7 @@ EXIM_MONITOR=eximon.bin # WITH_OLD_DEMIME=yes +#------------------------------------------------------------------------------ # If you're using ClamAV and are backporting fixes to an old version, instead # of staying current (which is the more usual approach) then you may need to # use an older API which uses a STREAM command, now deprecated, instead of @@ -401,6 +409,7 @@ EXIM_MONITOR=eximon.bin # # WITH_OLD_CLAMAV_STREAM=yes + #------------------------------------------------------------------------------ # By default Exim includes code to support DKIM (DomainKeys Identified # Mail, RFC4871) signing and verification. Verification of signatures is @@ -426,9 +435,14 @@ EXIM_MONITOR=eximon.bin # By default, Exim has support for checking the AD bit in a DNS response, to # determine if DNSSEC validation was successful. If your system libraries # do not support that bit, then set DISABLE_DNSSEC to "yes" +# Note: Enabling EXPERIMENTAL_DANE unconditionally overrides this setting. # DISABLE_DNSSEC=yes +# To disable support for Events set DISABLE_EVENT to "yes" + +# DISABLE_EVENT=yes + #------------------------------------------------------------------------------ # Compiling Exim with experimental features. These are documented in @@ -437,6 +451,7 @@ EXIM_MONITOR=eximon.bin # Uncomment the following line to add support for talking to dccifd. This # defaults the socket path to /usr/local/dcc/var/dccifd. +# Doing so will also explicitly turn on the WITH_CONTENT_SCAN option. # EXPERIMENTAL_DCC=yes @@ -472,28 +487,13 @@ EXIM_MONITOR=eximon.bin # CFLAGS += -I/usr/local/include # LDFLAGS += -lopendmarc - -# Uncomment the following line to support Events, -# eg. for logging to a database. -# EXPERIMENTAL_EVENT=yes - -# Uncomment the following line to add Redis lookup support -# You need to have hiredis installed on your system (https://github.com/redis/hiredis). -# Depending on where it is installed you may have to edit the CFLAGS and LDFLAGS lines. -# EXPERIMENTAL_REDIS=yes -# CFLAGS += -I/usr/local/include -# LDFLAGS += -lhiredis - -# Uncomment the following line to enable Experimental Proxy Protocol -# EXPERIMENTAL_PROXY=yes - -# Uncomment the following line to enable support for checking certiticate -# ownership -# EXPERIMENTAL_CERTNAMES=yes - # Uncomment the following line to add DANE support +# Note: Enabling this unconditionally overrides DISABLE_DNSSEC # EXPERIMENTAL_DANE=yes +# Uncomment the following to include extra information in fail DSN message (bounces) +# EXPERIMENTAL_DSN_INFO=yes + ############################################################################### # THESE ARE THINGS YOU MIGHT WANT TO SPECIFY # ############################################################################### @@ -627,6 +627,7 @@ FIXED_NEVER_USERS=root # AUTH_HEIMDAL_GSSAPI_PC=heimdal-gssapi # AUTH_PLAINTEXT=yes # AUTH_SPA=yes +# AUTH_TLS=yes #------------------------------------------------------------------------------ @@ -790,7 +791,7 @@ HEADERS_CHARSET="ISO-8859-1" # with the extension "texinfo" in the doc directory. You may find that the # version number of the texinfo files is different to your Exim version number, # because the main documentation isn't updated as often as the code. For -# example, if you have Exim version 4.43, the source tarball upacks into a +# example, if you have Exim version 4.43, the source tarball unpacks into a # directory called exim-4.43, but the texinfo tarball unpacks into exim-4.40. # In this case, move the contents of exim-4.40/doc into exim-4.43/doc after you # have unpacked them. Then set INFO_DIRECTORY to the location of your info @@ -868,9 +869,15 @@ COMPRESS_SUFFIX=gz # If the exigrep utility is fed compressed log files, it tries to uncompress # them using this command. +# Leave it empty to enforce autodetection at runtime: +# ZCAT_COMMAND= +# +# Omit the path if you want to use your system's PATH: +# ZCAT_COMMAND=zcat +# +# Or specify the full pathname: ZCAT_COMMAND=/usr/bin/zcat - #------------------------------------------------------------------------------ # Compiling in support for embedded Perl: If you want to be able to # use Perl code in Exim's string manipulation language and you have Perl @@ -904,6 +911,32 @@ ZCAT_COMMAND=/usr/bin/zcat #------------------------------------------------------------------------------ +# Proxying. +# +# If you may want to use outbound (client-side) proxying, using Socks5, +# uncomment the line below. + +# SUPPORT_SOCKS=yes + +# If you may want to use inbound (server-side) proxying, using Proxy Protocol, +# uncomment the line below. + +# SUPPORT_PROXY=yes + + +#------------------------------------------------------------------------------ +# Internationalisation. +# +# Uncomment the following to include Internationalisation features. This is the +# SMTPUTF8 ESMTP extension, and associated facilities for handling UTF8 domain +# and localparts, per RFCs 5890, 6530 and 6533. +# You need to have the IDN library installed. + +# SUPPORT_I18N=yes +# LDFLAGS += -lidn + + +#------------------------------------------------------------------------------ # Support for authentication via Radius is also available. The Exim support, # which is intended for use in conjunction with the SMTP AUTH facilities, # is included only when requested by setting the following parameter to the @@ -945,7 +978,7 @@ ZCAT_COMMAND=/usr/bin/zcat # There is no need to install all of SASL on your system. You just need to run # ./configure --with-pwcheck, cd to the pwcheck directory within the sources, # make and make install. You must create the socket directory (default -# /var/pwcheck) and chown it to exim's user and group. Once you have installed +# /var/pwcheck) and chown it to Exim's user and group. Once you have installed # pwcheck, you should arrange for it to be started by root at boot time. # CYRUS_PWCHECK_SOCKET=/var/pwcheck/pwcheck @@ -953,7 +986,7 @@ ZCAT_COMMAND=/usr/bin/zcat #------------------------------------------------------------------------------ # Support for authentication via the Cyrus SASL saslauthd daemon is available. -# The Exim support, which is intented for use in conjunction with the SMTP AUTH +# The Exim support, which is intended for use in conjunction with the SMTP AUTH # facilities, is included only when requested by setting the following # parameter to the location of the saslauthd daemon's socket. # @@ -961,7 +994,7 @@ ZCAT_COMMAND=/usr/bin/zcat # ./configure --with-saslauthd (and any other options you need, for example, to # select or deselect authentication mechanisms), cd to the saslauthd directory # within the sources, make and make install. You must create the socket -# directory (default /var/state/saslauthd) and chown it to exim's user and +# directory (default /var/state/saslauthd) and chown it to Exim's user and # group. Once you have installed saslauthd, you should arrange for it to be # started by root at boot time. @@ -1122,7 +1155,7 @@ TMPDIR="/tmp" # to handle the different cases. If CONFIGURE_FILE_USE_EUID is defined, then # Exim will first look for a configuration file whose name is that defined # by CONFIGURE_FILE, with the effective uid tacked on the end, separated by -# a period (for eximple, /usr/exim/configure.0). If this file does not exist, +# a period (for example, /usr/exim/configure.0). If this file does not exist, # then the bare configuration file name is tried. In the case when both # CONFIGURE_FILE_USE_EUID and CONFIGURE_FILE_USE_NODE are set, four files # are tried: <name>.<euid>.<node>, <name>.<node>, <name>.<euid>, and <name>. @@ -1306,7 +1339,7 @@ TMPDIR="/tmp" #------------------------------------------------------------------------------ -# Expanding match_* second paramters: BE CAREFUL IF ENABLING THIS! +# Expanding match_* second parameters: BE CAREFUL IF ENABLING THIS! # It has proven too easy in practice for administrators to configure security # problems into their Exim install, by treating match_domain{}{} and friends # as a form of string comparison, where the second string comes from untrusted diff --git a/src/src/acl.c b/src/src/acl.c index 4b66fad45..1456cc724 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -181,17 +181,17 @@ that follows! */ enum { CONTROL_AUTH_UNADVERTISED, - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL CONTROL_BMI_RUN, - #endif +#endif CONTROL_DEBUG, - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM CONTROL_DKIM_VERIFY, - #endif - #ifdef EXPERIMENTAL_DMARC +#endif +#ifdef EXPERIMENTAL_DMARC CONTROL_DMARC_VERIFY, CONTROL_DMARC_FORENSIC, - #endif +#endif CONTROL_DSCP, CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, @@ -203,11 +203,14 @@ enum { CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, CONTROL_SUPPRESS_LOCAL_FIXUPS, - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN CONTROL_NO_MBOX_UNSPOOL, - #endif +#endif CONTROL_FAKEDEFER, CONTROL_FAKEREJECT, +#ifdef SUPPORT_I18N + CONTROL_UTF8_DOWNCONVERT, +#endif CONTROL_NO_MULTILINE, CONTROL_NO_PIPELINING, CONTROL_NO_DELAY_FLUSH, @@ -221,17 +224,17 @@ and should be tidied up. */ static uschar *controls[] = { US"allow_auth_unadvertised", - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL US"bmi_run", - #endif +#endif US"debug", - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM US"dkim_disable_verify", - #endif - #ifdef EXPERIMENTAL_DMARC +#endif +#ifdef EXPERIMENTAL_DMARC US"dmarc_disable_verify", US"dmarc_enable_forensic", - #endif +#endif US"dscp", US"error", US"caseful_local_part", @@ -243,11 +246,14 @@ static uschar *controls[] = { US"queue_only", US"submission", US"suppress_local_fixups", - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN US"no_mbox_unspool", - #endif +#endif US"fakedefer", US"fakereject", +#ifdef SUPPORT_I18N + US"utf8_downconvert", +#endif US"no_multiline_responses", US"no_pipelining", US"no_delay_flush", @@ -469,8 +475,10 @@ static unsigned int cond_forbids[] = { ~(1<<ACL_WHERE_DATA), /* dmarc_status */ #endif - (1<<ACL_WHERE_NOTSMTP)| /* dnslists */ - (1<<ACL_WHERE_NOTSMTP_START), + /* Explicit key lookups can be made in non-smtp ACLs so pass + always and check in the verify processing itself. */ + + 0, /* dnslists */ (unsigned int) ~((1<<ACL_WHERE_RCPT) /* domains */ @@ -600,26 +608,26 @@ static unsigned int control_forbids[] = { (unsigned int) ~((1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)), /* allow_auth_unadvertised */ - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL 0, /* bmi_run */ - #endif +#endif 0, /* debug */ - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_disable_verify */ - #ifndef DISABLE_PRDR +# ifndef DISABLE_PRDR (1<<ACL_WHERE_PRDR)| - #endif +# endif (1<<ACL_WHERE_NOTSMTP_START), - #endif +#endif - #ifdef EXPERIMENTAL_DMARC +#ifdef EXPERIMENTAL_DMARC (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_disable_verify */ (1<<ACL_WHERE_NOTSMTP_START), (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_enable_forensic */ (1<<ACL_WHERE_NOTSMTP_START), - #endif +#endif (1<<ACL_WHERE_NOTSMTP)| (1<<ACL_WHERE_NOTSMTP_START)| @@ -663,30 +671,34 @@ static unsigned int control_forbids[] = { (1<<ACL_WHERE_PREDATA)| (1<<ACL_WHERE_NOTSMTP_START)), - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN (unsigned int) ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* no_mbox_unspool */ (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)| // (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */ (1<<ACL_WHERE_MIME)), - #endif +#endif (unsigned int) ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakedefer */ (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)| - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR (1<<ACL_WHERE_PRDR)| - #endif +#endif (1<<ACL_WHERE_MIME)), (unsigned int) ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakereject */ (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)| - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR (1<<ACL_WHERE_PRDR)| - #endif +#endif (1<<ACL_WHERE_MIME)), +#ifdef SUPPORT_I18N + 0, /* utf8_downconvert */ +#endif + (1<<ACL_WHERE_NOTSMTP)| /* no_multiline */ (1<<ACL_WHERE_NOTSMTP_START), @@ -709,37 +721,40 @@ typedef struct control_def { } control_def; static control_def controls_list[] = { - { US"allow_auth_unadvertised", CONTROL_AUTH_UNADVERTISED, FALSE }, + { US"allow_auth_unadvertised", CONTROL_AUTH_UNADVERTISED, FALSE }, #ifdef EXPERIMENTAL_BRIGHTMAIL - { US"bmi_run", CONTROL_BMI_RUN, FALSE }, + { US"bmi_run", CONTROL_BMI_RUN, FALSE }, #endif - { US"debug", CONTROL_DEBUG, TRUE }, + { US"debug", CONTROL_DEBUG, TRUE }, #ifndef DISABLE_DKIM - { US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE }, + { US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE }, #endif #ifdef EXPERIMENTAL_DMARC - { US"dmarc_disable_verify", CONTROL_DMARC_VERIFY, FALSE }, - { US"dmarc_enable_forensic", CONTROL_DMARC_FORENSIC, FALSE }, + { US"dmarc_disable_verify", CONTROL_DMARC_VERIFY, FALSE }, + { US"dmarc_enable_forensic", CONTROL_DMARC_FORENSIC, FALSE }, #endif - { US"dscp", CONTROL_DSCP, TRUE }, - { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE }, - { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE }, - { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE }, - { US"freeze", CONTROL_FREEZE, TRUE }, - { US"no_callout_flush", CONTROL_NO_CALLOUT_FLUSH, FALSE }, - { US"no_delay_flush", CONTROL_NO_DELAY_FLUSH, FALSE }, - { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE }, - { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE }, - { US"no_pipelining", CONTROL_NO_PIPELINING, FALSE }, - { US"queue_only", CONTROL_QUEUE_ONLY, FALSE }, + { US"dscp", CONTROL_DSCP, TRUE }, + { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE }, + { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE }, + { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE }, + { US"freeze", CONTROL_FREEZE, TRUE }, + { US"no_callout_flush", CONTROL_NO_CALLOUT_FLUSH, FALSE }, + { US"no_delay_flush", CONTROL_NO_DELAY_FLUSH, FALSE }, + { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE }, + { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE }, + { US"no_pipelining", CONTROL_NO_PIPELINING, FALSE }, + { US"queue_only", CONTROL_QUEUE_ONLY, FALSE }, #ifdef WITH_CONTENT_SCAN - { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE }, + { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE }, #endif - { US"fakedefer", CONTROL_FAKEDEFER, TRUE }, - { US"fakereject", CONTROL_FAKEREJECT, TRUE }, - { US"submission", CONTROL_SUBMISSION, TRUE }, + { US"fakedefer", CONTROL_FAKEDEFER, TRUE }, + { US"fakereject", CONTROL_FAKEREJECT, TRUE }, + { US"submission", CONTROL_SUBMISSION, TRUE }, { US"suppress_local_fixups", CONTROL_SUPPRESS_LOCAL_FIXUPS, FALSE }, - { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, FALSE } + { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, FALSE }, +#ifdef SUPPORT_I18N + { US"utf8_downconvert", CONTROL_UTF8_DOWNCONVERT, TRUE } +#endif }; /* Support data structures for Client SMTP Authorization. acl_verify_csa() @@ -802,7 +817,7 @@ static uschar *ratelimit_option_string[] = { /* Enable recursion between acl_check_internal() and acl_check_condition() */ -static int acl_check_wargs(int, address_item *, uschar *, int, uschar **, +static int acl_check_wargs(int, address_item *, const uschar *, int, uschar **, uschar **); @@ -1049,9 +1064,9 @@ Returns: nothing */ static void -setup_header(uschar *hstring) +setup_header(const uschar *hstring) { -uschar *p, *q; +const uschar *p, *q; int hlen = Ustrlen(hstring); /* Ignore any leading newlines */ @@ -1059,14 +1074,24 @@ while (*hstring == '\n') hstring++, hlen--; /* An empty string does nothing; ensure exactly one final newline. */ if (hlen <= 0) return; -if (hstring[--hlen] != '\n') hstring = string_sprintf("%s\n", hstring); -else while(hstring[--hlen] == '\n') hstring[hlen+1] = '\0'; +if (hstring[--hlen] != '\n') + q = string_sprintf("%s\n", hstring); +else if (hstring[hlen-1] == '\n') + { + uschar * s = string_copy(hstring); + while(s[--hlen] == '\n') + s[hlen+1] = '\0'; + q = s; + } +else + q = hstring; /* Loop for multiple header lines, taking care about continuations */ -for (p = q = hstring; *p != 0; ) +for (p = q; *p != 0; ) { - uschar *s; + const uschar *s; + uschar * hdr; int newtype = htype_add_bot; header_line **hptr = &acl_added_headers; @@ -1114,14 +1139,14 @@ for (p = q = hstring; *p != 0; ) if (*s == ':' || !isgraph(*s)) break; } - s = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", (int) (q - p), p); - hlen = Ustrlen(s); + hdr = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", (int) (q - p), p); + hlen = Ustrlen(hdr); /* See if this line has already been added */ while (*hptr != NULL) { - if (Ustrncmp((*hptr)->text, s, hlen) == 0) break; + if (Ustrncmp((*hptr)->text, hdr, hlen) == 0) break; hptr = &((*hptr)->next); } @@ -1130,7 +1155,7 @@ for (p = q = hstring; *p != 0; ) if (*hptr == NULL) { header_line *h = store_get(sizeof(header_line)); - h->text = s; + h->text = hdr; h->next = NULL; h->type = newtype; h->slen = hlen; @@ -1197,15 +1222,12 @@ Returns: nothing */ static void -setup_remove_header(uschar *hnames) +setup_remove_header(const uschar *hnames) { if (*hnames != 0) - { - if (acl_removed_headers == NULL) - acl_removed_headers = hnames; - else - acl_removed_headers = string_sprintf("%s : %s", acl_removed_headers, hnames); - } + acl_removed_headers = acl_removed_headers + ? string_sprintf("%s : %s", acl_removed_headers, hnames) + : string_copy(hnames); } @@ -1389,9 +1411,6 @@ for (rr = dns_next_rr(dnsa, dnss, reset); if (rr->type != T_A #if HAVE_IPV6 && rr->type != T_AAAA - #ifdef SUPPORT_A6 - && rr->type != T_A6 - #endif #endif ) continue; @@ -1444,10 +1463,11 @@ Returns: CSA_UNKNOWN no valid CSA record found */ static int -acl_verify_csa(uschar *domain) +acl_verify_csa(const uschar *domain) { tree_node *t; -uschar *found, *p; +const uschar *found; +uschar *p; int priority, weight, port; dns_answer dnsa; dns_scan dnss; @@ -1470,7 +1490,7 @@ containing a keyword and a colon before the actual IP address. */ if (domain[0] == '[') { - uschar *start = Ustrchr(domain, ':'); + const uschar *start = Ustrchr(domain, ':'); if (start == NULL) start = domain; domain = string_copyn(start + 1, Ustrlen(start) - 2); } @@ -1550,7 +1570,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); assertion: legitimate SMTP clients are all explicitly authorized with CSA SRV records of their own. */ - if (found != domain) + if (Ustrcmp(found, domain) != 0) { if (port & 1) return t->data.val = CSA_FAIL_EXPLICIT; @@ -1610,24 +1630,20 @@ else type = T_A; -#if HAVE_IPV6 && defined(SUPPORT_A6) -DNS_LOOKUP_AGAIN: -#endif - lookup_dnssec_authenticated = NULL; switch (dns_lookup(&dnsa, target, type, NULL)) { /* If something bad happened (most commonly DNS_AGAIN), defer. */ default: - return t->data.val = CSA_DEFER_ADDR; + return t->data.val = CSA_DEFER_ADDR; /* If the query succeeded, scan the addresses and return the result. */ case DNS_SUCCEED: - rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ANSWERS, target); - if (rc != CSA_FAIL_NOADDR) return t->data.val = rc; - /* else fall through */ + rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ANSWERS, target); + if (rc != CSA_FAIL_NOADDR) return t->data.val = rc; + /* else fall through */ /* If the target has no IP addresses, the client cannot have an authorized IP address. However, if the target site uses A6 records (not AAAA records) @@ -1635,12 +1651,7 @@ switch (dns_lookup(&dnsa, target, type, NULL)) case DNS_NOMATCH: case DNS_NODATA: - - #if HAVE_IPV6 && defined(SUPPORT_A6) - if (type == T_AAAA) { type = T_A6; goto DNS_LOOKUP_AGAIN; } - #endif - - return t->data.val = CSA_FAIL_NOADDR; + return t->data.val = CSA_FAIL_NOADDR; } } @@ -1662,7 +1673,7 @@ typedef struct { unsigned alt_opt_sep; /* >0 Non-/ option separator (custom parser) */ } verify_type_t; static verify_type_t verify_type_list[] = { - { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, ~0, TRUE, 0 }, + { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, ~0, FALSE, 0 }, { US"certificate", VERIFY_CERT, ~0, TRUE, 0 }, { US"helo", VERIFY_HELO, ~0, TRUE, 0 }, { US"csa", VERIFY_CSA, ~0, FALSE, 0 }, @@ -1726,7 +1737,7 @@ Returns: OK verification condition succeeded */ static int -acl_verify(int where, address_item *addr, uschar *arg, +acl_verify(int where, address_item *addr, const uschar *arg, uschar **user_msgptr, uschar **log_msgptr, int *basic_errno) { int sep = '/'; @@ -1750,7 +1761,7 @@ an error if options are given for items that don't expect them. */ uschar *slash = Ustrchr(arg, '/'); -uschar *list = arg; +const uschar *list = arg; uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); verify_type_t * vp; @@ -1783,34 +1794,38 @@ switch(vp->value) { case VERIFY_REV_HOST_LKUP: if (sender_host_address == NULL) return OK; - return acl_verify_reverse(user_msgptr, log_msgptr); + if ((rc = acl_verify_reverse(user_msgptr, log_msgptr)) == DEFER) + while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) + if (strcmpic(ss, US"defer_ok") == 0) + return OK; + return rc; case VERIFY_CERT: /* TLS certificate verification is done at STARTTLS time; here we just test whether it was successful or not. (This is for optional verification; for mandatory verification, the connection doesn't last this long.) */ - if (tls_in.certificate_verified) return OK; - *user_msgptr = US"no verified certificate"; - return FAIL; + if (tls_in.certificate_verified) return OK; + *user_msgptr = US"no verified certificate"; + return FAIL; case VERIFY_HELO: /* We can test the result of optional HELO verification that might have occurred earlier. If not, we can attempt the verification now. */ - if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); - return helo_verified? OK : FAIL; + if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); + return helo_verified? OK : FAIL; case VERIFY_CSA: /* Do Client SMTP Authorization checks in a separate function, and turn the result code into user-friendly strings. */ - rc = acl_verify_csa(list); - *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", + rc = acl_verify_csa(list); + *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", csa_reason_string[rc]); - csa_status = csa_status_string[rc]; - DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); - return csa_return_code[rc]; + csa_status = csa_status_string[rc]; + DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); + return csa_return_code[rc]; case VERIFY_HDR_SYNTAX: /* Check that all relevant header lines have the correct syntax. If there is @@ -1819,8 +1834,11 @@ switch(vp->value) always). */ rc = verify_check_headers(log_msgptr); - if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) - *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + if (rc != OK && *log_msgptr) + if (smtp_return_error_details) + *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + else + acl_verify_message = *log_msgptr; return rc; case VERIFY_HDR_NAMES_ASCII: @@ -1911,12 +1929,13 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) while (isspace(*ss)) ss++; if (*ss++ == '=') { + const uschar * sublist = ss; int optsep = ','; uschar *opt; uschar buffer[256]; - while (isspace(*ss)) ss++; + while (isspace(*sublist)) sublist++; - while ((opt = string_nextinlist(&ss, &optsep, buffer, sizeof(buffer))) + while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer))) != NULL) { callout_opt_t * op; @@ -2078,6 +2097,13 @@ else if (verify_sender_address != NULL) uschar *save_address_data = deliver_address_data; sender_vaddr = deliver_make_addr(verify_sender_address, TRUE); +#ifdef SUPPORT_I18N + if ((sender_vaddr->prop.utf8_msg = message_smtputf8)) + { + sender_vaddr->prop.utf8_downcvt = message_utf8_downconvert == 1; + sender_vaddr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + } +#endif if (no_details) setflag(sender_vaddr, af_sverify_told); if (verify_sender_address[0] != 0) { @@ -2134,7 +2160,7 @@ else if (verify_sender_address != NULL) /* Put the sender address_data value into $sender_address_data */ - sender_address_data = sender_vaddr->p.address_data; + sender_address_data = sender_vaddr->prop.address_data; } /* A recipient address just gets a straightforward verify; again we must handle @@ -2164,7 +2190,7 @@ else if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE; /* Make $address_data visible */ - deliver_address_data = addr2.p.address_data; + deliver_address_data = addr2.prop.address_data; } /* We have a result from the relevant test. Handle defer overrides first. */ @@ -2183,13 +2209,9 @@ sender_verified_failed to the address item that actually failed. */ if (rc != OK && verify_sender_address != NULL) { if (rc != DEFER) - { *log_msgptr = *user_msgptr = US"Sender verify failed"; - } else if (*basic_errno != ERRNO_CALLOUTDEFER) - { *log_msgptr = *user_msgptr = US"Could not complete sender verify"; - } else { *log_msgptr = US"Could not complete sender verify callout"; @@ -2239,7 +2261,7 @@ Returns: CONTROL_xxx value */ static int -decode_control(uschar *arg, uschar **pptr, int where, uschar **log_msgptr) +decode_control(const uschar *arg, const uschar **pptr, int where, uschar **log_msgptr) { int len; control_def *d; @@ -2324,7 +2346,7 @@ Returns: OK - Sender's rate is above limit */ static int -acl_ratelimit(uschar *arg, int where, uschar **log_msgptr) +acl_ratelimit(const uschar *arg, int where, uschar **log_msgptr) { double limit, period, count; uschar *ss; @@ -2352,7 +2374,10 @@ rate measurement as opposed to rate limiting. */ sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0); if (sender_rate_limit == NULL) + { limit = -1.0; + ss = NULL; /* compiler quietening */ + } else { limit = Ustrtod(sender_rate_limit, &ss); @@ -2850,7 +2875,7 @@ Returns: OK - Completed. */ static int -acl_udpsend(uschar *arg, uschar **log_msgptr) +acl_udpsend(const uschar *arg, uschar **log_msgptr) { int sep = 0; uschar *hostname; @@ -2976,7 +3001,6 @@ uschar *user_message = NULL; uschar *log_message = NULL; uschar *debug_tag = NULL; uschar *debug_opts = NULL; -uschar *p = NULL; int rc = OK; #ifdef WITH_CONTENT_SCAN int sep = -'/'; @@ -2984,7 +3008,7 @@ int sep = -'/'; for (; cb != NULL; cb = cb->next) { - uschar *arg; + const uschar *arg; int control_type; /* The message and log_message items set up messages to be used in @@ -3123,267 +3147,319 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_CONTROL: - control_type = decode_control(arg, &p, where, log_msgptr); - - /* Check if this control makes sense at this time */ - - if ((control_forbids[control_type] & (1 << where)) != 0) { - *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL", - controls[control_type], acl_wherenames[where]); - return ERROR; - } + const uschar *p = NULL; + control_type = decode_control(arg, &p, where, log_msgptr); - switch(control_type) - { - case CONTROL_AUTH_UNADVERTISED: - allow_auth_unadvertised = TRUE; - break; + /* Check if this control makes sense at this time */ - #ifdef EXPERIMENTAL_BRIGHTMAIL - case CONTROL_BMI_RUN: - bmi_run = 1; - break; - #endif - - #ifndef DISABLE_DKIM - case CONTROL_DKIM_VERIFY: - dkim_disable_verify = TRUE; - #ifdef EXPERIMENTAL_DMARC - /* Since DKIM was blocked, skip DMARC too */ - dmarc_disable_verify = TRUE; - dmarc_enable_forensic = FALSE; - #endif - break; - #endif + if ((control_forbids[control_type] & (1 << where)) != 0) + { + *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL", + controls[control_type], acl_wherenames[where]); + return ERROR; + } - #ifdef EXPERIMENTAL_DMARC - case CONTROL_DMARC_VERIFY: - dmarc_disable_verify = TRUE; - break; + switch(control_type) + { + case CONTROL_AUTH_UNADVERTISED: + allow_auth_unadvertised = TRUE; + break; - case CONTROL_DMARC_FORENSIC: - dmarc_enable_forensic = TRUE; - break; - #endif + #ifdef EXPERIMENTAL_BRIGHTMAIL + case CONTROL_BMI_RUN: + bmi_run = 1; + break; + #endif + + #ifndef DISABLE_DKIM + case CONTROL_DKIM_VERIFY: + dkim_disable_verify = TRUE; + #ifdef EXPERIMENTAL_DMARC + /* Since DKIM was blocked, skip DMARC too */ + dmarc_disable_verify = TRUE; + dmarc_enable_forensic = FALSE; + #endif + break; + #endif - case CONTROL_DSCP: - if (*p == '/') - { - int fd, af, level, optname, value; - /* If we are acting on stdin, the setsockopt may fail if stdin is not - a socket; we can accept that, we'll just debug-log failures anyway. */ - fd = fileno(smtp_in); - af = ip_get_address_family(fd); - if (af < 0) - { - HDEBUG(D_acl) - debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n", - strerror(errno)); - break; - } - if (dscp_lookup(p+1, af, &level, &optname, &value)) - { - if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) - { - HDEBUG(D_acl) debug_printf("failed to set input DSCP[%s]: %s\n", - p+1, strerror(errno)); - } - else - { - HDEBUG(D_acl) debug_printf("set input DSCP to \"%s\"\n", p+1); - } - } - else - { - *log_msgptr = string_sprintf("unrecognised DSCP value in \"control=%s\"", arg); - return ERROR; - } - } - else - { - *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); - return ERROR; - } - break; + #ifdef EXPERIMENTAL_DMARC + case CONTROL_DMARC_VERIFY: + dmarc_disable_verify = TRUE; + break; - case CONTROL_ERROR: - return ERROR; + case CONTROL_DMARC_FORENSIC: + dmarc_enable_forensic = TRUE; + break; + #endif - case CONTROL_CASEFUL_LOCAL_PART: - deliver_localpart = addr->cc_local_part; - break; + case CONTROL_DSCP: + if (*p == '/') + { + int fd, af, level, optname, value; + /* If we are acting on stdin, the setsockopt may fail if stdin is not + a socket; we can accept that, we'll just debug-log failures anyway. */ + fd = fileno(smtp_in); + af = ip_get_address_family(fd); + if (af < 0) + { + HDEBUG(D_acl) + debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n", + strerror(errno)); + break; + } + if (dscp_lookup(p+1, af, &level, &optname, &value)) + { + if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) + { + HDEBUG(D_acl) debug_printf("failed to set input DSCP[%s]: %s\n", + p+1, strerror(errno)); + } + else + { + HDEBUG(D_acl) debug_printf("set input DSCP to \"%s\"\n", p+1); + } + } + else + { + *log_msgptr = string_sprintf("unrecognised DSCP value in \"control=%s\"", arg); + return ERROR; + } + } + else + { + *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); + return ERROR; + } + break; - case CONTROL_CASELOWER_LOCAL_PART: - deliver_localpart = addr->lc_local_part; - break; + case CONTROL_ERROR: + return ERROR; - case CONTROL_ENFORCE_SYNC: - smtp_enforce_sync = TRUE; - break; + case CONTROL_CASEFUL_LOCAL_PART: + deliver_localpart = addr->cc_local_part; + break; - case CONTROL_NO_ENFORCE_SYNC: - smtp_enforce_sync = FALSE; - break; + case CONTROL_CASELOWER_LOCAL_PART: + deliver_localpart = addr->lc_local_part; + break; - #ifdef WITH_CONTENT_SCAN - case CONTROL_NO_MBOX_UNSPOOL: - no_mbox_unspool = TRUE; - break; - #endif + case CONTROL_ENFORCE_SYNC: + smtp_enforce_sync = TRUE; + break; - case CONTROL_NO_MULTILINE: - no_multiline_responses = TRUE; - break; + case CONTROL_NO_ENFORCE_SYNC: + smtp_enforce_sync = FALSE; + break; - case CONTROL_NO_PIPELINING: - pipelining_enable = FALSE; - break; + #ifdef WITH_CONTENT_SCAN + case CONTROL_NO_MBOX_UNSPOOL: + no_mbox_unspool = TRUE; + break; + #endif - case CONTROL_NO_DELAY_FLUSH: - disable_delay_flush = TRUE; - break; + case CONTROL_NO_MULTILINE: + no_multiline_responses = TRUE; + break; - case CONTROL_NO_CALLOUT_FLUSH: - disable_callout_flush = TRUE; - break; + case CONTROL_NO_PIPELINING: + pipelining_enable = FALSE; + break; - case CONTROL_FAKEREJECT: - cancel_cutthrough_connection("fakereject"); - case CONTROL_FAKEDEFER: - fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL; - if (*p == '/') - { - uschar *pp = p + 1; - while (*pp != 0) pp++; - fake_response_text = expand_string(string_copyn(p+1, pp-p-1)); - p = pp; - } - else - { - /* Explicitly reset to default string */ - fake_response_text = US"Your message has been rejected but is being kept for evaluation.\nIf it was a legitimate message, it may still be delivered to the target recipient(s)."; - } - break; + case CONTROL_NO_DELAY_FLUSH: + disable_delay_flush = TRUE; + break; - case CONTROL_FREEZE: - deliver_freeze = TRUE; - deliver_frozen_at = time(NULL); - freeze_tell = freeze_tell_config; /* Reset to configured value */ - if (Ustrncmp(p, "/no_tell", 8) == 0) - { - p += 8; - freeze_tell = NULL; - } - if (*p != 0) - { - *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); - return ERROR; - } - cancel_cutthrough_connection("item frozen"); - break; + case CONTROL_NO_CALLOUT_FLUSH: + disable_callout_flush = TRUE; + break; - case CONTROL_QUEUE_ONLY: - queue_only_policy = TRUE; - cancel_cutthrough_connection("queueing forced"); - break; + case CONTROL_FAKEREJECT: + cancel_cutthrough_connection("fakereject"); + case CONTROL_FAKEDEFER: + fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL; + if (*p == '/') + { + const uschar *pp = p + 1; + while (*pp != 0) pp++; + fake_response_text = expand_string(string_copyn(p+1, pp-p-1)); + p = pp; + } + else + { + /* Explicitly reset to default string */ + fake_response_text = US"Your message has been rejected but is being kept for evaluation.\nIf it was a legitimate message, it may still be delivered to the target recipient(s)."; + } + break; - case CONTROL_SUBMISSION: - originator_name = US""; - submission_mode = TRUE; - while (*p == '/') - { - if (Ustrncmp(p, "/sender_retain", 14) == 0) - { - p += 14; - active_local_sender_retain = TRUE; - active_local_from_check = FALSE; - } - else if (Ustrncmp(p, "/domain=", 8) == 0) - { - uschar *pp = p + 8; - while (*pp != 0 && *pp != '/') pp++; - submission_domain = string_copyn(p+8, pp-p-8); - p = pp; - } - /* The name= option must be last, because it swallows the rest of - the string. */ - else if (Ustrncmp(p, "/name=", 6) == 0) - { - uschar *pp = p + 6; - while (*pp != 0) pp++; - submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6, - big_buffer, big_buffer_size)); - p = pp; - } - else break; - } - if (*p != 0) - { - *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); - return ERROR; - } - break; + case CONTROL_FREEZE: + deliver_freeze = TRUE; + deliver_frozen_at = time(NULL); + freeze_tell = freeze_tell_config; /* Reset to configured value */ + if (Ustrncmp(p, "/no_tell", 8) == 0) + { + p += 8; + freeze_tell = NULL; + } + if (*p != 0) + { + *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); + return ERROR; + } + cancel_cutthrough_connection("item frozen"); + break; - case CONTROL_DEBUG: - while (*p == '/') - { - if (Ustrncmp(p, "/tag=", 5) == 0) - { - uschar *pp = p + 5; - while (*pp != '\0' && *pp != '/') pp++; - debug_tag = string_copyn(p+5, pp-p-5); - p = pp; - } - else if (Ustrncmp(p, "/opts=", 6) == 0) - { - uschar *pp = p + 6; - while (*pp != '\0' && *pp != '/') pp++; - debug_opts = string_copyn(p+6, pp-p-6); - p = pp; - } - } - debug_logging_activate(debug_tag, debug_opts); - break; + case CONTROL_QUEUE_ONLY: + queue_only_policy = TRUE; + cancel_cutthrough_connection("queueing forced"); + break; - case CONTROL_SUPPRESS_LOCAL_FIXUPS: - suppress_local_fixups = TRUE; - break; + case CONTROL_SUBMISSION: + originator_name = US""; + submission_mode = TRUE; + while (*p == '/') + { + if (Ustrncmp(p, "/sender_retain", 14) == 0) + { + p += 14; + active_local_sender_retain = TRUE; + active_local_from_check = FALSE; + } + else if (Ustrncmp(p, "/domain=", 8) == 0) + { + const uschar *pp = p + 8; + while (*pp != 0 && *pp != '/') pp++; + submission_domain = string_copyn(p+8, pp-p-8); + p = pp; + } + /* The name= option must be last, because it swallows the rest of + the string. */ + else if (Ustrncmp(p, "/name=", 6) == 0) + { + const uschar *pp = p + 6; + while (*pp != 0) pp++; + submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6, + big_buffer, big_buffer_size)); + p = pp; + } + else break; + } + if (*p != 0) + { + *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); + return ERROR; + } + break; - case CONTROL_CUTTHROUGH_DELIVERY: - if (deliver_freeze) - *log_msgptr = US"frozen"; - else if (queue_only_policy) - *log_msgptr = US"queue-only"; - else if (fake_response == FAIL) - *log_msgptr = US"fakereject"; - else - { - cutthrough_delivery = TRUE; + case CONTROL_DEBUG: + while (*p == '/') + { + if (Ustrncmp(p, "/tag=", 5) == 0) + { + const uschar *pp = p + 5; + while (*pp != '\0' && *pp != '/') pp++; + debug_tag = string_copyn(p+5, pp-p-5); + p = pp; + } + else if (Ustrncmp(p, "/opts=", 6) == 0) + { + const uschar *pp = p + 6; + while (*pp != '\0' && *pp != '/') pp++; + debug_opts = string_copyn(p+6, pp-p-6); + p = pp; + } + } + debug_logging_activate(debug_tag, debug_opts); break; + + case CONTROL_SUPPRESS_LOCAL_FIXUPS: + suppress_local_fixups = TRUE; + break; + + case CONTROL_CUTTHROUGH_DELIVERY: +#ifndef DISABLE_PRDR + if (prdr_requested) +#else + if (0) +#endif + /* Too hard to think about for now. We might in future cutthrough + the case where both sides handle prdr and this-node prdr acl + is "accept" */ + *log_msgptr = string_sprintf("PRDR on %s reception\n", arg); + else + { + if (deliver_freeze) + *log_msgptr = US"frozen"; + else if (queue_only_policy) + *log_msgptr = US"queue-only"; + else if (fake_response == FAIL) + *log_msgptr = US"fakereject"; + else + { + if (rcpt_count == 1) cutthrough.delivery = TRUE; + break; + } + *log_msgptr = string_sprintf("\"control=%s\" on %s item", + arg, *log_msgptr); + } + return ERROR; + +#ifdef SUPPORT_I18N + case CONTROL_UTF8_DOWNCONVERT: + if (*p == '/') + { + if (p[1] == '1') + { + message_utf8_downconvert = 1; + addr->prop.utf8_downcvt = TRUE; + addr->prop.utf8_downcvt_maybe = FALSE; + p += 2; + break; + } + if (p[1] == '0') + { + message_utf8_downconvert = 0; + addr->prop.utf8_downcvt = FALSE; + addr->prop.utf8_downcvt_maybe = FALSE; + p += 2; + break; + } + if (p[1] == '-' && p[2] == '1') + { + message_utf8_downconvert = -1; + addr->prop.utf8_downcvt = FALSE; + addr->prop.utf8_downcvt_maybe = TRUE; + p += 3; + break; + } + *log_msgptr = US"bad option value for control=utf8_downconvert"; + } + else + { + message_utf8_downconvert = 1; + addr->prop.utf8_downcvt = TRUE; + addr->prop.utf8_downcvt_maybe = FALSE; + break; + } + return ERROR; +#endif + } - *log_msgptr = string_sprintf("\"control=%s\" on %s item", - arg, *log_msgptr); - return ERROR; + break; } - break; #ifdef EXPERIMENTAL_DCC case ACLC_DCC: { /* Seperate the regular expression and any optional parameters. */ - uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); + const uschar * list = arg; + uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); /* Run the dcc backend. */ rc = dcc_process(&ss); /* Modify return code based upon the existance of options. */ - while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) - != NULL) { + while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) - { - /* FAIL so that the message is passed to the next ACL */ - rc = FAIL; - } - } + rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } break; #endif @@ -3413,6 +3489,34 @@ for (; cb != NULL; cb = cb->next) debug_printf("delay skipped in -bh checking mode\n"); } + /* NOTE 1: Remember that we may be + dealing with stdin/stdout here, in addition to TCP/IP connections. + Also, delays may be specified for non-SMTP input, where smtp_out and + smtp_in will be NULL. Whatever is done must work in all cases. + + NOTE 2: The added feature of flushing the output before a delay must + apply only to SMTP input. Hence the test for smtp_out being non-NULL. + */ + + else + { + if (smtp_out != NULL && !disable_delay_flush) + mac_smtp_fflush(); + +#if !defined(NO_POLL_H) && defined (POLLRDHUP) + { + struct pollfd p; + nfds_t n = 0; + if (smtp_out) + { + p.fd = fileno(smtp_out); + p.events = POLLRDHUP; + n = 1; + } + if (poll(&p, n, delay*1000) > 0) + HDEBUG(D_acl) debug_printf("delay cancelled by peer close\n"); + } +#else /* It appears to be impossible to detect that a TCP/IP connection has gone away without reading from it. This means that we cannot shorten the delay below if the client goes away, because we cannot discover @@ -3422,20 +3526,10 @@ for (; cb != NULL; cb = cb->next) Exim process is not held up unnecessarily. However, it seems that we can't. The poll() function does not do the right thing, and in any case it is not always available. - - NOTE 1: If ever this state of affairs changes, remember that we may be - dealing with stdin/stdout here, in addition to TCP/IP connections. - Also, delays may be specified for non-SMTP input, where smtp_out and - smtp_in will be NULL. Whatever is done must work in all cases. - - NOTE 2: The added feature of flushing the output before a delay must - apply only to SMTP input. Hence the test for smtp_out being non-NULL. */ - else - { - if (smtp_out != NULL && !disable_delay_flush) mac_smtp_fflush(); while (delay > 0) delay = sleep(delay); +#endif } } } @@ -3475,12 +3569,12 @@ for (; cb != NULL; cb = cb->next) #endif case ACLC_DNSLISTS: - rc = verify_check_dnsbl(&arg); + rc = verify_check_dnsbl(where, &arg, log_msgptr); break; case ACLC_DOMAINS: rc = match_isinlist(addr->domain, &arg, 0, &domainlist_anchor, - addr->domain_cache, MCL_DOMAIN, TRUE, &deliver_domain_data); + addr->domain_cache, MCL_DOMAIN, TRUE, CUSS &deliver_domain_data); break; /* The value in tls_cipher is the full cipher name, for example, @@ -3513,24 +3607,25 @@ for (; cb != NULL; cb = cb->next) case ACLC_HOSTS: rc = verify_check_this_host(&arg, sender_host_cache, NULL, - (sender_host_address == NULL)? US"" : sender_host_address, &host_data); - if (host_data != NULL) host_data = string_copy_malloc(host_data); + (sender_host_address == NULL)? US"" : sender_host_address, + CUSS &host_data); + if (rc == DEFER) *log_msgptr = search_error_message; + if (host_data) host_data = string_copy_malloc(host_data); break; case ACLC_LOCAL_PARTS: rc = match_isinlist(addr->cc_local_part, &arg, 0, &localpartlist_anchor, addr->localpart_cache, MCL_LOCALPART, TRUE, - &deliver_localpart_data); + CUSS &deliver_localpart_data); break; case ACLC_LOG_REJECT_TARGET: { int logbits = 0; int sep = 0; - uschar *s = arg; + const uschar *s = arg; uschar *ss; - while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) - != NULL) + while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))) { if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN; else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC; @@ -3549,7 +3644,7 @@ for (; cb != NULL; cb = cb->next) case ACLC_LOGWRITE: { int logbits = 0; - uschar *s = arg; + const uschar *s = arg; if (*s == ':') { s++; @@ -3583,12 +3678,13 @@ for (; cb != NULL; cb = cb->next) case ACLC_MALWARE: /* Run the malware backend. */ { /* Separate the regular expression and any optional parameters. */ - uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); + const uschar * list = arg; + uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); uschar *opt; BOOL defer_ok = FALSE; int timeout = 0; - while ((opt = string_nextinlist(&arg, &sep, NULL, 0))) + while ((opt = string_nextinlist(&list, &sep, NULL, 0))) if (strcmpic(opt, US"defer_ok") == 0) defer_ok = TRUE; else if ( strncmpic(opt, US"tmo=", 4) == 0 @@ -3615,8 +3711,8 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_RECIPIENTS: - rc = match_address_list(addr->address, TRUE, TRUE, &arg, NULL, -1, 0, - &recipient_data); + rc = match_address_list((const uschar *)addr->address, TRUE, TRUE, &arg, NULL, -1, 0, + CUSS &recipient_data); break; #ifdef WITH_CONTENT_SCAN @@ -3640,8 +3736,8 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_SENDERS: - rc = match_address_list(sender_address, TRUE, TRUE, &arg, - sender_address_cache, -1, 0, &sender_data); + rc = match_address_list((const uschar *)sender_address, TRUE, TRUE, &arg, + sender_address_cache, -1, 0, CUSS &sender_data); break; /* Connection variables must persist forever */ @@ -3649,7 +3745,12 @@ for (; cb != NULL; cb = cb->next) case ACLC_SET: { int old_pool = store_pool; - if (cb->u.varname[0] == 'c') store_pool = POOL_PERM; + if ( cb->u.varname[0] == 'c' +#ifndef DISABLE_EVENT + || event_name /* An event is being delivered */ +#endif + ) + store_pool = POOL_PERM; acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); store_pool = old_pool; } @@ -3659,11 +3760,12 @@ for (; cb != NULL; cb = cb->next) case ACLC_SPAM: { /* Seperate the regular expression and any optional parameters. */ - uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); + const uschar * list = arg; + uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); /* Run the spam backend. */ - rc = spam(&ss); + rc = spam(CUSS &ss); /* Modify return code based upon the existance of options. */ - while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) + while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) != NULL) { if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) { @@ -3696,7 +3798,8 @@ for (; cb != NULL; cb = cb->next) case ACLC_VERIFY: rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno); - acl_verify_message = *user_msgptr; + if (*user_msgptr) + acl_verify_message = *user_msgptr; if (verb == ACL_WARN) *user_msgptr = NULL; break; @@ -4059,19 +4162,12 @@ while (acl != NULL) int cond; int basic_errno = 0; BOOL endpass_seen = FALSE; + BOOL acl_quit_check = level == 0 + && (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT); *log_msgptr = *user_msgptr = NULL; acl_temp_details = FALSE; - if ((where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT) && - acl->verb != ACL_ACCEPT && - acl->verb != ACL_WARN) - { - *log_msgptr = string_sprintf("\"%s\" is not allowed in a QUIT or not-QUIT ACL", - verbs[acl->verb]); - return ERROR; - } - HDEBUG(D_acl) debug_printf("processing \"%s\"\n", verbs[acl->verb]); /* Clear out any search error message from a previous check before testing @@ -4152,6 +4248,7 @@ while (acl != NULL) if (cond == OK) { HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name); + if (acl_quit_check) goto badquit; acl_temp_details = TRUE; return DEFER; } @@ -4161,6 +4258,7 @@ while (acl != NULL) if (cond == OK) { HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name); + if (acl_quit_check) goto badquit; return FAIL; } break; @@ -4169,6 +4267,7 @@ while (acl != NULL) if (cond == OK || cond == DISCARD) { HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name); + if (acl_quit_check) goto badquit; return DISCARD; } if (endpass_seen) @@ -4182,6 +4281,7 @@ while (acl != NULL) if (cond == OK) { HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name); + if (acl_quit_check) goto badquit; return FAIL_DROP; } break; @@ -4190,6 +4290,7 @@ while (acl != NULL) if (cond != OK) { HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name); + if (acl_quit_check) goto badquit; return cond; } break; @@ -4197,7 +4298,7 @@ while (acl != NULL) case ACL_WARN: if (cond == OK) acl_warn(where, *user_msgptr, *log_msgptr); - else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0) + else if (cond == DEFER && LOGGING(acl_warn_skipped)) log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: " "condition test deferred%s%s", host_and_ident(TRUE), (*log_msgptr == NULL)? US"" : US": ", @@ -4220,6 +4321,11 @@ while (acl != NULL) HDEBUG(D_acl) debug_printf("end of %s: implicit DENY\n", acl_name); return FAIL; + +badquit: + *log_msgptr = string_sprintf("QUIT or not-QUIT teplevel ACL may not fail " + "('%s' verb used incorrectly)", verbs[acl->verb]); + return ERROR; } @@ -4229,7 +4335,7 @@ return FAIL; the name of an ACL followed optionally by up to 9 space-separated arguments. The name and args are separately expanded. Args go into $acl_arg globals. */ static int -acl_check_wargs(int where, address_item *addr, uschar *s, int level, +acl_check_wargs(int where, address_item *addr, const uschar *s, int level, uschar **user_msgptr, uschar **log_msgptr) { uschar * tmp; @@ -4348,9 +4454,9 @@ ratelimiters_cmd = NULL; log_reject_target = LOG_MAIN|LOG_REJECT; #ifndef DISABLE_PRDR -if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR ) +if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR) #else -if (where == ACL_WHERE_RCPT ) +if (where == ACL_WHERE_RCPT) #endif { adb = address_defaults; @@ -4361,6 +4467,13 @@ if (where == ACL_WHERE_RCPT ) *log_msgptr = US"defer in percent_hack_domains check"; return DEFER; } +#ifdef SUPPORT_I18N + if ((addr->prop.utf8_msg = message_smtputf8)) + { + addr->prop.utf8_downcvt = message_utf8_downconvert == 1; + addr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + } +#endif deliver_domain = addr->domain; deliver_localpart = addr->local_part; } @@ -4394,9 +4507,7 @@ case ACL_WHERE_RCPT: #ifndef DISABLE_PRDR case ACL_WHERE_PRDR: #endif - if( rcpt_count > 1 ) - cancel_cutthrough_connection("more than one recipient"); - else if (rc == OK && cutthrough_delivery && cutthrough_fd < 0) + if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt) open_cutthrough_connection(addr); break; diff --git a/src/src/auths/Makefile b/src/src/auths/Makefile index c6ef218b2..45d294932 100644 --- a/src/src/auths/Makefile +++ b/src/src/auths/Makefile @@ -9,7 +9,7 @@ OBJ = auth-spa.o b64decode.o b64encode.o call_pam.o call_pwcheck.o \ call_radius.o check_serv_cond.o cram_md5.o cyrus_sasl.o dovecot.o \ get_data.o get_no64_data.o gsasl_exim.o heimdal_gssapi.o \ md5.o plaintext.o pwcheck.o sha1.o \ - spa.o xtextdecode.o xtextencode.o + spa.o tls.o xtextdecode.o xtextencode.o auths.a: $(OBJ) @$(RM_COMMAND) -f auths.a @@ -43,5 +43,6 @@ gsasl_exim.o: $(HDRS) gsasl_exim.c gsasl_exim.h heimdal_gssapi.o: $(HDRS) heimdal_gssapi.c heimdal_gssapi.h plaintext.o: $(HDRS) plaintext.c plaintext.h spa.o: $(HDRS) spa.c spa.h +tls.o: $(HDRS) tls.c tls.h # End diff --git a/src/src/auths/auth-spa.c b/src/src/auths/auth-spa.c index 7ad5a1de8..9abc7b778 100644 --- a/src/src/auths/auth-spa.c +++ b/src/src/auths/auth-spa.c @@ -368,7 +368,7 @@ void mdfour (unsigned char *out, unsigned char *in, int n); static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -#define BAD -1 +#define BAD (char) -1 static const char base64val[] = { BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, diff --git a/src/src/auths/call_pam.c b/src/src/auths/call_pam.c index 710de7ded..b4677ec5a 100644 --- a/src/src/auths/call_pam.c +++ b/src/src/auths/call_pam.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -36,7 +36,7 @@ data pointer passed to the conversation function. However, I was unable to get this to work on Solaris 2.6, so static variables are used instead. */ static int pam_conv_had_error; -static uschar *pam_args; +static const uschar *pam_args; static BOOL pam_arg_ended; @@ -129,7 +129,7 @@ Returns: OK if authentication succeeded */ int -auth_call_pam(uschar *s, uschar **errptr) +auth_call_pam(const uschar *s, uschar **errptr) { pam_handle_t *pamh = NULL; struct pam_conv pamc; diff --git a/src/src/auths/call_pwcheck.c b/src/src/auths/call_pwcheck.c index a4567c1a9..089f501a7 100644 --- a/src/src/auths/call_pwcheck.c +++ b/src/src/auths/call_pwcheck.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module contains interface functions to the two Cyrus authentication @@ -88,8 +88,8 @@ Returns: OK if authentication succeeded */ int -auth_call_saslauthd(uschar *username, uschar *password, uschar *service, - uschar *realm, uschar **errptr) +auth_call_saslauthd(const uschar *username, const uschar *password, + const uschar *service, const uschar *realm, uschar **errptr) { uschar *reply = NULL; diff --git a/src/src/auths/call_radius.c b/src/src/auths/call_radius.c index 2064ed221..78ee466d8 100644 --- a/src/src/auths/call_radius.c +++ b/src/src/auths/call_radius.c @@ -8,6 +8,16 @@ /* This file was originally supplied by Ian Kirk. The libradius support came from Alex Kiernan. */ +/* ugly hack to work around redefinition of ENV by radiusclient.h and + * db.h: define _DB_H_ so the db.h include thinks it's already included, + * we can get away with it like this, since this file doesn't use any db + * functions. */ +#ifndef _DB_H_ +# define _DB_H_ 1 +# define _DB_EXT_PROT_IN_ 1 +# define DB void +#endif + #include "../exim.h" /* This module contains functions that call the Radius authentication @@ -36,9 +46,14 @@ using its original API. At release 0.4.0 the API changed. */ #include <radlib.h> #else #if !defined(RADIUS_LIB_RADIUSCLIENT) && !defined(RADIUS_LIB_RADIUSCLIENTNEW) - #define RADIUS_LIB_RADIUSCLIENT + # define RADIUS_LIB_RADIUSCLIENT + #endif + + #ifdef RADIUS_LIB_RADIUSCLIENTNEW + # include <freeradius-client.h> + #else + # include <radiusclient.h> #endif - #include <radiusclient.h> #endif @@ -60,10 +75,10 @@ Returns: OK if authentication succeeded */ int -auth_call_radius(uschar *s, uschar **errptr) +auth_call_radius(const uschar *s, uschar **errptr) { uschar *user; -uschar *radius_args = s; +const uschar *radius_args = s; int result; int sep = 0; diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index c7fb59348..5088cd3ad 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This code was originally contributed by Matthew Byng-Maddick */ @@ -97,7 +97,7 @@ auth_cyrus_sasl_init(auth_instance *ablock) { auth_cyrus_sasl_options_block *ob = (auth_cyrus_sasl_options_block *)(ablock->options_block); -uschar *list, *listptr, *buffer; +const uschar *list, *listptr, *buffer; int rc, i; unsigned int len; uschar *rs_point, *expanded_hostname; @@ -146,7 +146,7 @@ if( rc != SASL_OK ) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " "couldn't initialise Cyrus SASL server connection.", ablock->name); -rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i); +rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)&list, &len, &i); if( rc != SASL_OK ) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " "couldn't get Cyrus SASL mechanism list.", ablock->name); diff --git a/src/src/auths/plaintext.c b/src/src/auths/plaintext.c index ff449e5e2..38dc4b1dc 100644 --- a/src/src/auths/plaintext.c +++ b/src/src/auths/plaintext.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -66,7 +66,7 @@ auth_plaintext_server(auth_instance *ablock, uschar *data) { auth_plaintext_options_block *ob = (auth_plaintext_options_block *)(ablock->options_block); -uschar *prompts = ob->server_prompts; +const uschar *prompts = ob->server_prompts; uschar *clear, *end, *s; int number = 1; int len, rc; @@ -76,7 +76,7 @@ int sep = 0; if (prompts != NULL) { - prompts = expand_string(prompts); + prompts = expand_cstring(prompts); if (prompts == NULL) { auth_defer_msg = expand_string_message; @@ -163,7 +163,7 @@ auth_plaintext_client( { auth_plaintext_options_block *ob = (auth_plaintext_options_block *)(ablock->options_block); -uschar *text = ob->client_send; +const uschar *text = ob->client_send; uschar *s; BOOL first = TRUE; int sep = 0; diff --git a/src/src/auths/tls.c b/src/src/auths/tls.c new file mode 100644 index 000000000..51c096cd0 --- /dev/null +++ b/src/src/auths/tls.c @@ -0,0 +1,80 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* This file provides an Exim authenticator driver for +a server to verify a client SSL certificate +*/ + + +#include "../exim.h" +#include "tls.h" + +/* Options specific to the tls authentication mechanism. */ + +optionlist auth_tls_options[] = { + { "server_param", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param1)) }, + { "server_param1", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param1)) }, + { "server_param2", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param2)) }, + { "server_param3", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param3)) }, +}; + +/* Size of the options list. An extern variable has to be used so that its +address can appear in the tables drtables.c. */ + +int auth_tls_options_count = nelem(auth_tls_options); + +/* Default private options block for the authentication method. */ + +auth_tls_options_block auth_tls_option_defaults = { + NULL, /* server_param1 */ + NULL, /* server_param2 */ + NULL, /* server_param3 */ +}; + + +/************************************************* +* Initialization entry point * +*************************************************/ + +/* Called for each instance, after its options have been read, to +enable consistency checks to be done, or anything else that needs +to be set up. */ + +void +auth_tls_init(auth_instance *ablock) +{ +ablock->public_name = ablock->name; /* needed for core code */ +} + + + +/************************************************* +* Server entry point * +*************************************************/ + +/* For interface, see auths/README */ + +int +auth_tls_server(auth_instance *ablock, uschar *data) +{ +auth_tls_options_block * ob = (auth_tls_options_block *)ablock->options_block; + +if (ob->server_param1) + auth_vars[expand_nmax++] = expand_string(ob->server_param1); +if (ob->server_param2) + auth_vars[expand_nmax++] = expand_string(ob->server_param2); +if (ob->server_param2) + auth_vars[expand_nmax++] = expand_string(ob->server_param3); +return auth_check_serv_cond(ablock); +} + + +/* End of tls.c */ diff --git a/src/src/auths/tls.h b/src/src/auths/tls.h new file mode 100644 index 000000000..bf2a2a1c6 --- /dev/null +++ b/src/src/auths/tls.h @@ -0,0 +1,30 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* Private structure for the private options. */ + +typedef struct { + uschar * server_param1; + uschar * server_param2; + uschar * server_param3; +} auth_tls_options_block; + +/* Data for reading the private options. */ + +extern optionlist auth_tls_options[]; +extern int auth_tls_options_count; + +/* Block containing default values. */ + +extern auth_tls_options_block auth_tls_option_defaults; + +/* The entry points for the mechanism */ + +extern void auth_tls_init(auth_instance *); +extern int auth_tls_server(auth_instance *, uschar *); + +/* End of sa.h */ diff --git a/src/src/child.c b/src/src/child.c index 20083b43c..36d192e9a 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -141,7 +141,7 @@ if (exec_type == CEE_RETURN_ARGV) failure. We know that there will always be at least one extra option in the call when exec() is done here, so it can be used to add to the panic data. */ -DEBUG(D_exec) debug_print_argv(argv); +DEBUG(D_exec) debug_print_argv(CUSS argv); exim_nullstd(); /* Make sure std{in,out,err} exist */ execv(CS argv[0], (char *const *)argv); @@ -307,8 +307,9 @@ Returns: the pid of the created process or -1 if anything has gone wrong */ pid_t -child_open_uid(uschar **argv, uschar **envp, int newumask, uid_t *newuid, - gid_t *newgid, int *infdptr, int *outfdptr, uschar *wd, BOOL make_leader) +child_open_uid(const uschar **argv, const uschar **envp, int newumask, + uid_t *newuid, gid_t *newgid, int *infdptr, int *outfdptr, uschar *wd, + BOOL make_leader) { int save_errno; int inpfd[2], outpfd[2]; @@ -387,7 +388,7 @@ if (pid == 0) /* Now do the exec */ if (envp == NULL) execv(CS argv[0], (char *const *)argv); - else execve(CS argv[0], (char *const *)argv, (char *const *)envp); + else execve(CS argv[0], (char *const *)argv, (char *const *)envp); /* Failed to execv. Signal this failure using EX_EXECFAILED. We are losing the actual errno we got back, because there is no way to return @@ -450,8 +451,8 @@ pid_t child_open(uschar **argv, uschar **envp, int newumask, int *infdptr, int *outfdptr, BOOL make_leader) { -return child_open_uid(argv, envp, newumask, NULL, NULL, infdptr, outfdptr, - NULL, make_leader); +return child_open_uid(CUSS argv, CUSS envp, newumask, NULL, NULL, + infdptr, outfdptr, NULL, make_leader); } diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index e339bdf9e..14de083fe 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* The default settings for Exim configuration variables. A #define without @@ -24,6 +24,7 @@ it's a default value. */ #define AUTH_HEIMDAL_GSSAPI #define AUTH_PLAINTEXT #define AUTH_SPA +#define AUTH_TLS #define AUTH_VARS 3 @@ -40,10 +41,11 @@ it's a default value. */ #define DEFAULT_CRYPT crypt #define DELIVER_IN_BUFFER_SIZE 8192 #define DELIVER_OUT_BUFFER_SIZE 8192 +#define DISABLE_DNSSEC #define DISABLE_DKIM +#define DISABLE_EVENT #define DISABLE_PRDR #define DISABLE_OCSP -#define DISABLE_DNSSEC #define DISABLE_D_OPTION #define ENABLE_DISABLE_FSYNC @@ -93,6 +95,7 @@ it's a default value. */ #define LOOKUP_ORACLE #define LOOKUP_PASSWD #define LOOKUP_PGSQL +#define LOOKUP_REDIS #define LOOKUP_SQLITE #define LOOKUP_TESTDB #define LOOKUP_WHOSON @@ -115,6 +118,8 @@ it's a default value. */ #define RADIUS_CONFIG_FILE #define RADIUS_LIB_TYPE +#define REGEX_VARS 9 + #define ROUTER_ACCEPT #define ROUTER_DNSLOOKUP #define ROUTER_IPLITERAL @@ -128,13 +133,15 @@ it's a default value. */ #define SPOOL_MODE 0640 #define STRING_SPRINTF_BUFFER_SIZE (8192 * 4) -#define SUPPORT_A6 #define SUPPORT_CRYPTEQ +#define SUPPORT_I18N #define SUPPORT_MAILDIR #define SUPPORT_MAILSTORE #define SUPPORT_MBX #define SUPPORT_MOVE_FROZEN_MESSAGES #define SUPPORT_PAM +#define SUPPORT_PROXY +#define SUPPORT_SOCKS #define SUPPORT_TLS #define SUPPORT_TRANSLATE_IP_ADDRESS @@ -169,12 +176,10 @@ it's a default value. */ #define EXPERIMENTAL_BRIGHTMAIL #define EXPERIMENTAL_DANE #define EXPERIMENTAL_DCC +#define EXPERIMENTAL_DSN_INFO #define EXPERIMENTAL_DMARC -#define EXPERIMENTAL_PROXY -#define EXPERIMENTAL_REDIS #define EXPERIMENTAL_SPF #define EXPERIMENTAL_SRS -#define EXPERIMENTAL_EVENT /* For developers */ #define WANT_DEEPER_PRINTF_CHECKS diff --git a/src/src/configure.default b/src/src/configure.default index c16221fc1..ec60700df 100644 --- a/src/src/configure.default +++ b/src/src/configure.default @@ -236,6 +236,13 @@ host_lookup = * #rfc1413_query_timeout = 5s +# Enable an efficiency feature. We advertise the feature; clients +# may request to use it. For multi-recipient mails we then can +# reject or accept per-user after the message is received. +# +prdr_enable = true + + # By default, Exim expects all envelope addresses to be fully qualified, that # is, they must contain both a local part and a domain. If you want to accept # unqualified addresses (just a local part) from certain hosts, you can specify @@ -249,6 +256,13 @@ host_lookup = * # and/or qualify_recipient (see above). +# Unless you run a high-volume site you probably want more logging +# detail than the default. Adjust to suit. + +log_selector = +smtp_protocol_error +smtp_syntax_error \ + +tls_certificate_verified + + # If you want Exim to support the "percent hack" for certain domains, # uncomment the following line and provide a list of domains. The "percent # hack" is the feature by which mail addressed to x%y@z (where z is one of @@ -478,6 +492,11 @@ acl_check_rcpt: acl_check_data: + # Deny if the message contains an overlong line. Per the standards + # we should never receive one such via SMTP. + # + deny condition = ${if > {$max_received_linelength}{998}} + # Deny if the message contains a virus. Before enabling this check, you # must install a virus scanner and set the av_scanner option above. # @@ -670,9 +689,13 @@ begin transports # This transport is used for delivering messages over SMTP connections. +# Refuse to send any messsage with over-long lines, which could have +# been receved other than via SMTP. The use of message_size_limit to +# enforce this is a red herring. remote_smtp: driver = smtp + message_size_limit = ${if > {$max_received_linelength}{998} {1}{0}} # This transport is used for local delivery to user mailboxes in traditional diff --git a/src/src/daemon.c b/src/src/daemon.c index 256cc9cb7..24874c374 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with running Exim as a daemon */ @@ -145,7 +145,7 @@ int dup_accept_socket = -1; int max_for_this_host = 0; int wfsize = 0; int wfptr = 0; -int use_log_write_selector = log_write_selector; +int save_log_selector = *log_selector; uschar *whofrom = NULL; void *reset_point = store_get(0); @@ -206,11 +206,11 @@ memory is reclaimed. */ whofrom = string_append(whofrom, &wfsize, &wfptr, 3, "[", sender_host_address, "]"); -if ((log_extra_selector & LX_incoming_port) != 0) +if (LOGGING(incoming_port)) whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d", sender_host_port)); -if ((log_extra_selector & LX_incoming_interface) != 0) +if (LOGGING(incoming_interface)) whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[", interface_address, "]:", string_sprintf("%d", interface_port)); @@ -338,11 +338,12 @@ the generalized logging code each time when the selector is false. If the selector is set, check whether the host is on the list for logging. If not, arrange to unset the selector in the subprocess. */ -if ((log_write_selector & L_smtp_connection) != 0) +if (LOGGING(smtp_connection)) { uschar *list = hosts_connection_nolog; + memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (list != NULL && verify_check_host(&list) == OK) - use_log_write_selector &= ~L_smtp_connection; + save_log_selector &= ~L_smtp_connection; else log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s " "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1); @@ -372,7 +373,7 @@ if (pid == 0) /* May have been modified for the subprocess */ - log_write_selector = use_log_write_selector; + *log_selector = save_log_selector; /* Get the local interface address into permanent store */ @@ -735,6 +736,7 @@ else (void)close(dup_accept_socket); /* Release any store used in this process, including the store used for holding the incoming host address and an expanded active_hostname. */ +log_close_all(); store_reset(reset_point); sender_host_address = NULL; } @@ -1048,7 +1050,7 @@ if (daemon_listen && !inetd_wait_mode) int sep; int pct = 0; uschar *s; - uschar *list; + const uschar * list; uschar *local_iface_source = US"local_interfaces"; ip_address_item *ipa; ip_address_item **pipa; @@ -1071,8 +1073,7 @@ if (daemon_listen && !inetd_wait_mode) list = override_local_interfaces; sep = 0; - while ((s = string_nextinlist(&list,&sep,big_buffer,big_buffer_size)) - != NULL) + while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) { uschar joinstr[4]; uschar **ptr; @@ -1127,13 +1128,13 @@ if (daemon_listen && !inetd_wait_mode) list = daemon_smtp_port; sep = 0; - while ((s = string_nextinlist(&list,&sep,big_buffer,big_buffer_size))) + while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) pct++; default_smtp_port = store_get((pct+1) * sizeof(int)); list = daemon_smtp_port; sep = 0; for (pct = 0; - (s = string_nextinlist(&list,&sep,big_buffer,big_buffer_size)); + (s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)); pct++) { if (isdigit(*s)) @@ -1603,7 +1604,7 @@ if (inetd_wait_mode) log_write(0, LOG_MAIN, "exim %s daemon started: pid=%d, launched with listening socket, %s", version_string, getpid(), big_buffer); - set_process_info("daemon: pre-listening socket"); + set_process_info("daemon(%s): pre-listening socket", version_string); /* set up the timeout logic */ sigalrm_seen = 1; @@ -1688,7 +1689,7 @@ else if (daemon_listen) log_write(0, LOG_MAIN, "exim %s daemon started: pid=%d, %s, listening for %s", version_string, getpid(), qinfo, big_buffer); - set_process_info("daemon: %s, listening for %s", qinfo, big_buffer); + set_process_info("daemon(%s): %s, listening for %s", version_string, qinfo, big_buffer); } else @@ -1696,7 +1697,8 @@ else log_write(0, LOG_MAIN, "exim %s daemon started: pid=%d, -q%s, not listening for SMTP", version_string, getpid(), readconf_printtime(queue_interval)); - set_process_info("daemon: -q%s, not listening", + set_process_info("daemon(%s): -q%s, not listening", + version_string, readconf_printtime(queue_interval)); } diff --git a/src/src/dane-openssl.c b/src/src/dane-openssl.c index 6345b39ca..e5f9f9784 100644 --- a/src/src/dane-openssl.c +++ b/src/src/dane-openssl.c @@ -1,3 +1,7 @@ +/* + * Author: Viktor Dukhovni + * License: THIS CODE IS IN THE PUBLIC DOMAIN. + */ #include <stdio.h> #include <string.h> #include <stdint.h> @@ -10,79 +14,82 @@ #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/evp.h> +#include <openssl/bn.h> #if OPENSSL_VERSION_NUMBER < 0x1000000fL # error "OpenSSL 1.0.0 or higher required" #else /* remainder of file */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define X509_up_ref(x) CRYPTO_add(&((x)->references), 1, CRYPTO_LOCK_X509) +#endif + #include "danessl.h" -#define DANE_F_ADD_SKID 100 -#define DANE_F_CHECK_END_ENTITY 101 -#define DANE_F_GROW_CHAIN 102 -#define DANE_F_LIST_ALLOC 103 -#define DANE_F_MATCH 104 -#define DANE_F_PUSH_EXT 105 -#define DANE_F_SET_TRUST_ANCHOR 106 -#define DANE_F_SSL_CTX_DANE_INIT 107 -#define DANE_F_SSL_DANE_ADD_TLSA 108 -#define DANE_F_SSL_DANE_INIT 109 -#define DANE_F_SSL_DANE_LIBRARY_INIT 110 -#define DANE_F_VERIFY_CERT 111 -#define DANE_F_WRAP_CERT 112 - -#define DANE_R_BAD_CERT 100 -#define DANE_R_BAD_CERT_PKEY 101 -#define DANE_R_BAD_DATA_LENGTH 102 -#define DANE_R_BAD_DIGEST 103 -#define DANE_R_BAD_NULL_DATA 104 -#define DANE_R_BAD_PKEY 105 -#define DANE_R_BAD_SELECTOR 106 -#define DANE_R_BAD_USAGE 107 -#define DANE_R_DANE_INIT 108 -#define DANE_R_DANE_SUPPORT 109 -#define DANE_R_LIBRARY_INIT 110 -#define DANE_R_NOSIGN_KEY 111 -#define DANE_R_SCTX_INIT 112 +#define DANESSL_F_ADD_SKID 100 +#define DANESSL_F_ADD_TLSA 101 +#define DANESSL_F_CHECK_END_ENTITY 102 +#define DANESSL_F_CTX_INIT 103 +#define DANESSL_F_GROW_CHAIN 104 +#define DANESSL_F_INIT 105 +#define DANESSL_F_LIBRARY_INIT 106 +#define DANESSL_F_LIST_ALLOC 107 +#define DANESSL_F_MATCH 108 +#define DANESSL_F_PUSH_EXT 109 +#define DANESSL_F_SET_TRUST_ANCHOR 110 +#define DANESSL_F_VERIFY_CERT 111 +#define DANESSL_F_WRAP_CERT 112 + +#define DANESSL_R_BAD_CERT 100 +#define DANESSL_R_BAD_CERT_PKEY 101 +#define DANESSL_R_BAD_DATA_LENGTH 102 +#define DANESSL_R_BAD_DIGEST 103 +#define DANESSL_R_BAD_NULL_DATA 104 +#define DANESSL_R_BAD_PKEY 105 +#define DANESSL_R_BAD_SELECTOR 106 +#define DANESSL_R_BAD_USAGE 107 +#define DANESSL_R_INIT 108 +#define DANESSL_R_LIBRARY_INIT 109 +#define DANESSL_R_NOSIGN_KEY 110 +#define DANESSL_R_SCTX_INIT 111 +#define DANESSL_R_SUPPORT 112 #ifndef OPENSSL_NO_ERR -# define DANE_F_PLACEHOLDER 0 /* FIRST! Value TBD */ -static ERR_STRING_DATA dane_str_functs[] = -{ - {DANE_F_PLACEHOLDER, "DANE library"}, /* FIRST!!! */ - {DANE_F_ADD_SKID, "add_skid"}, - {DANE_F_CHECK_END_ENTITY, "check_end_entity"}, - {DANE_F_GROW_CHAIN, "grow_chain"}, - {DANE_F_LIST_ALLOC, "list_alloc"}, - {DANE_F_MATCH, "match"}, - {DANE_F_PUSH_EXT, "push_ext"}, - {DANE_F_SET_TRUST_ANCHOR, "set_trust_anchor"}, - {DANE_F_SSL_CTX_DANE_INIT, "SSL_CTX_dane_init"}, - {DANE_F_SSL_DANE_ADD_TLSA, "SSL_dane_add_tlsa"}, - {DANE_F_SSL_DANE_INIT, "SSL_dane_init"}, - {DANE_F_SSL_DANE_LIBRARY_INIT, "SSL_dane_library_init"}, - {DANE_F_VERIFY_CERT, "verify_cert"}, - {DANE_F_WRAP_CERT, "wrap_cert"}, - {0, NULL} +#define DANESSL_F_PLACEHOLDER 0 /* FIRST! Value TBD */ +static ERR_STRING_DATA dane_str_functs[] = { + {DANESSL_F_PLACEHOLDER, "DANE library"}, /* FIRST!!! */ + {DANESSL_F_ADD_SKID, "add_skid"}, + {DANESSL_F_ADD_TLSA, "DANESSL_add_tlsa"}, + {DANESSL_F_CHECK_END_ENTITY, "check_end_entity"}, + {DANESSL_F_CTX_INIT, "DANESSL_CTX_init"}, + {DANESSL_F_GROW_CHAIN, "grow_chain"}, + {DANESSL_F_INIT, "DANESSL_init"}, + {DANESSL_F_LIBRARY_INIT, "DANESSL_library_init"}, + {DANESSL_F_LIST_ALLOC, "list_alloc"}, + {DANESSL_F_MATCH, "match"}, + {DANESSL_F_PUSH_EXT, "push_ext"}, + {DANESSL_F_SET_TRUST_ANCHOR, "set_trust_anchor"}, + {DANESSL_F_VERIFY_CERT, "verify_cert"}, + {DANESSL_F_WRAP_CERT, "wrap_cert"}, + {0, NULL} }; -static ERR_STRING_DATA dane_str_reasons[] = -{ - {DANE_R_BAD_CERT, "Bad TLSA record certificate"}, - {DANE_R_BAD_CERT_PKEY, "Bad TLSA record certificate public key"}, - {DANE_R_BAD_DATA_LENGTH, "Bad TLSA record digest length"}, - {DANE_R_BAD_DIGEST, "Bad TLSA record digest"}, - {DANE_R_BAD_NULL_DATA, "Bad TLSA record null data"}, - {DANE_R_BAD_PKEY, "Bad TLSA record public key"}, - {DANE_R_BAD_SELECTOR, "Bad TLSA record selector"}, - {DANE_R_BAD_USAGE, "Bad TLSA record usage"}, - {DANE_R_DANE_INIT, "SSL_dane_init() required"}, - {DANE_R_DANE_SUPPORT, "DANE library features not supported"}, - {DANE_R_LIBRARY_INIT, "SSL_dane_library_init() required"}, - {DANE_R_SCTX_INIT, "SSL_CTX_dane_init() required"}, - {DANE_R_NOSIGN_KEY, "Certificate usage 2 requires EC support"}, - {0, NULL} +static ERR_STRING_DATA dane_str_reasons[] = { + {DANESSL_R_BAD_CERT, "Bad TLSA record certificate"}, + {DANESSL_R_BAD_CERT_PKEY, "Bad TLSA record certificate public key"}, + {DANESSL_R_BAD_DATA_LENGTH, "Bad TLSA record digest length"}, + {DANESSL_R_BAD_DIGEST, "Bad TLSA record digest"}, + {DANESSL_R_BAD_NULL_DATA, "Bad TLSA record null data"}, + {DANESSL_R_BAD_PKEY, "Bad TLSA record public key"}, + {DANESSL_R_BAD_SELECTOR, "Bad TLSA record selector"}, + {DANESSL_R_BAD_USAGE, "Bad TLSA record usage"}, + {DANESSL_R_INIT, "DANESSL_init() required"}, + {DANESSL_R_LIBRARY_INIT, "DANESSL_library_init() required"}, + {DANESSL_R_NOSIGN_KEY, "Certificate usage 2 requires EC support"}, + {DANESSL_R_SCTX_INIT, "DANESSL_CTX_init() required"}, + {DANESSL_R_SUPPORT, "DANE library features not supported"}, + {0, NULL} }; -#endif /*OPENSSL_NO_ERR*/ +#endif #define DANEerr(f, r) ERR_PUT_error(err_lib_dane, (f), (r), __FILE__, __LINE__) @@ -166,21 +173,25 @@ typedef struct ssl_dane int (*verify)(X509_STORE_CTX *); STACK_OF(X509) *roots; STACK_OF(X509) *chain; - const char *thost; /* TLSA base domain */ - char *mhost; /* Matched, peer name */ + X509 *match; /* Matched cert */ + const char *thost; /* TLSA base domain */ + char *mhost; /* Matched peer name */ dane_pkey_list pkeys; dane_cert_list certs; dane_host_list hosts; - dane_selector_list selectors[SSL_DANE_USAGE_LAST + 1]; + dane_selector_list selectors[DANESSL_USAGE_LAST + 1]; int depth; - int multi; /* Multi-label wildcards? */ - int count; /* Number of TLSA records */ + int mdpth; /* Depth of matched cert */ + int multi; /* Multi-label wildcards? */ + int count; /* Number of TLSA records */ } ssl_dane; #ifndef X509_V_ERR_HOSTNAME_MISMATCH # define X509_V_ERR_HOSTNAME_MISMATCH X509_V_ERR_APPLICATION_VERIFICATION #endif + + static int match(dane_selector_list slist, X509 *cert, int depth) { @@ -191,14 +202,14 @@ int matched; * pkey digest or a certificate digest. We return MATCHED_PKEY or * MATCHED_CERT accordingly. */ -#define MATCHED_CERT (SSL_DANE_SELECTOR_CERT + 1) -#define MATCHED_PKEY (SSL_DANE_SELECTOR_SPKI + 1) +#define MATCHED_CERT (DANESSL_SELECTOR_CERT + 1) +#define MATCHED_PKEY (DANESSL_SELECTOR_SPKI + 1) /* * Loop over each selector, mtype, and associated data element looking * for a match. */ -for(matched = 0; !matched && slist; slist = slist->next) +for (matched = 0; !matched && slist; slist = slist->next) { dane_mtype_list m; unsigned char mdbuf[EVP_MAX_MD_SIZE]; @@ -211,21 +222,21 @@ for(matched = 0; !matched && slist; slist = slist->next) */ switch(slist->value->selector) { - case SSL_DANE_SELECTOR_CERT: + case DANESSL_SELECTOR_CERT: len = i2d_X509(cert, NULL); buf2 = buf = (unsigned char *) OPENSSL_malloc(len); if(buf) i2d_X509(cert, &buf2); break; - case SSL_DANE_SELECTOR_SPKI: + case DANESSL_SELECTOR_SPKI: len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); buf2 = buf = (unsigned char *) OPENSSL_malloc(len); if(buf) i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &buf2); break; } - if(!buf) + if (!buf) { - DANEerr(DANE_F_MATCH, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_MATCH, ERR_R_MALLOC_FAILURE); return 0; } OPENSSL_assert(buf2 - buf == len); @@ -233,7 +244,7 @@ for(matched = 0; !matched && slist; slist = slist->next) /* * Loop over each mtype and data element */ - for(m = slist->value->mtype; !matched && m; m = m->next) + for (m = slist->value->mtype; !matched && m; m = m->next) { dane_data_list d; unsigned char *cmpbuf = buf; @@ -243,15 +254,15 @@ for(matched = 0; !matched && slist; slist = slist->next) * If it is a digest, compute the corresponding digest of the * DER data for comparison, otherwise, use the full object. */ - if(m->value->md) + if (m->value->md) { cmpbuf = mdbuf; - if(!EVP_Digest(buf, len, cmpbuf, &cmplen, m->value->md, 0)) + if (!EVP_Digest(buf, len, cmpbuf, &cmplen, m->value->md, 0)) matched = -1; } - for(d = m->value->data; !matched && d; d = d->next) - if( cmplen == d->value->datalen - && memcmp(cmpbuf, d->value->data, cmplen) == 0) + for (d = m->value->data; !matched && d; d = d->next) + if ( cmplen == d->value->datalen + && memcmp(cmpbuf, d->value->data, cmplen) == 0) matched = slist->value->selector + 1; } @@ -264,18 +275,13 @@ return matched; static int push_ext(X509 *cert, X509_EXTENSION *ext) { -X509_EXTENSIONS *exts; - -if(ext) - { - if(!(exts = cert->cert_info->extensions)) - exts = cert->cert_info->extensions = sk_X509_EXTENSION_new_null(); - if (exts && sk_X509_EXTENSION_push(exts, ext)) - return 1; - X509_EXTENSION_free(ext); - } -DANEerr(DANE_F_PUSH_EXT, ERR_R_MALLOC_FAILURE); -return 0; + if (ext) { + if (X509_add_ext(cert, ext, -1)) + return 1; + X509_EXTENSION_free(ext); + } + DANEerr(DANESSL_F_PUSH_EXT, ERR_R_MALLOC_FAILURE); + return 0; } static int @@ -293,19 +299,19 @@ set_serial(X509 *cert, AUTHORITY_KEYID *akid, X509 *subject) int ret = 0; BIGNUM *bn; -if(akid && akid->serial) +if (akid && akid->serial) return (X509_set_serialNumber(cert, akid->serial)); /* * Add one to subject's serial to avoid collisions between TA serial and * serial of signing root. */ -if( (bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(subject), 0)) != 0 - && BN_add_word(bn, 1) - && BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert))) +if ( (bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(subject), 0)) != 0 + && BN_add_word(bn, 1) + && BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert))) ret = 1; -if(bn) +if (bn) BN_free(bn); return ret; } @@ -325,16 +331,16 @@ int ret = 0; * exempt from any potential (off by default for now in OpenSSL) * self-signature checks! */ -id = (ASN1_STRING *) ((akid && akid->keyid) ? akid->keyid : 0); -if(id && M_ASN1_STRING_length(id) == 1 && *M_ASN1_STRING_data(id) == c) +id = (akid && akid->keyid) ? akid->keyid : 0; +if (id && ASN1_STRING_length(id) == 1 && *ASN1_STRING_data(id) == c) c = 1; -if( (akid = AUTHORITY_KEYID_new()) != 0 - && (akid->keyid = ASN1_OCTET_STRING_new()) != 0 - && M_ASN1_OCTET_STRING_set(akid->keyid, (void *) &c, 1) - && X509_add1_ext_i2d(cert, nid, akid, 0, X509V3_ADD_APPEND)) +if ( (akid = AUTHORITY_KEYID_new()) != 0 + && (akid->keyid = ASN1_OCTET_STRING_new()) != 0 + && M_ASN1_OCTET_STRING_set(akid->keyid, (void *) &c, 1) + && X509_add1_ext_i2d(cert, nid, akid, 0, X509V3_ADD_APPEND)) ret = 1; -if(akid) +if (akid) AUTHORITY_KEYID_free(akid); return ret; } @@ -344,7 +350,7 @@ add_skid(X509 *cert, AUTHORITY_KEYID *akid) { int nid = NID_subject_key_identifier; -if(!akid || !akid->keyid) +if (!akid || !akid->keyid) return add_ext(0, cert, nid, "hash"); return X509_add1_ext_i2d(cert, nid, akid->keyid, 0, X509V3_ADD_APPEND) > 0; } @@ -352,16 +358,16 @@ return X509_add1_ext_i2d(cert, nid, akid->keyid, 0, X509V3_ADD_APPEND) > 0; static X509_NAME * akid_issuer_name(AUTHORITY_KEYID *akid) { -if(akid && akid->issuer) +if (akid && akid->issuer) { int i; GENERAL_NAMES *gens = akid->issuer; - for(i = 0; i < sk_GENERAL_NAME_num(gens); ++i) + for (i = 0; i < sk_GENERAL_NAME_num(gens); ++i) { GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); - if(gn->type == GEN_DIRNAME) + if (gn->type == GEN_DIRNAME) return (gn->d.dirn); } } @@ -390,27 +396,27 @@ static ASN1_OBJECT *serverAuth = 0; #define UNTRUSTED 0 #define TRUSTED 1 -if( trusted && !serverAuth - && !(serverAuth = OBJ_nid2obj(NID_server_auth))) +if ( trusted && !serverAuth + && !(serverAuth = OBJ_nid2obj(NID_server_auth))) { - DANEerr(DANE_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); return 0; } -if(!*xs && !(*xs = sk_X509_new_null())) +if (!*xs && !(*xs = sk_X509_new_null())) { - DANEerr(DANE_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); return 0; } -if(cert) +if (cert) { - if(trusted && !X509_add1_trust_object(cert, serverAuth)) + if (trusted && !X509_add1_trust_object(cert, serverAuth)) return 0; CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); if (!sk_X509_push(*xs, cert)) { X509_free(cert); - DANEerr(DANE_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); return 0; } } @@ -429,13 +435,13 @@ EVP_PKEY *newkey = key ? key : X509_get_pubkey(subject); #define WRAP_MID 0 /* Ensure intermediate. */ #define WRAP_TOP 1 /* Ensure self-signed. */ -if(!name || !newkey || !(cert = X509_new())) +if (!name || !newkey || !(cert = X509_new())) return 0; /* * Record the depth of the trust-anchor certificate. */ -if(dane->depth < 0) +if (dane->depth < 0) dane->depth = depth + 1; /* @@ -454,27 +460,27 @@ akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0); * * CA cert valid for +/- 30 days */ -if( !X509_set_version(cert, 2) - || !set_serial(cert, akid, subject) - || !X509_set_subject_name(cert, name) - || !set_issuer_name(cert, akid) - || !X509_gmtime_adj(X509_get_notBefore(cert), -30 * 86400L) - || !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L) - || !X509_set_pubkey(cert, newkey) - || !add_ext(0, cert, NID_basic_constraints, "CA:TRUE") - || (!top && !add_akid(cert, akid)) - || !add_skid(cert, akid) - || ( !top && wrap_to_root - && !wrap_issuer(dane, newkey, cert, depth, WRAP_TOP))) +if ( !X509_set_version(cert, 2) + || !set_serial(cert, akid, subject) + || !X509_set_subject_name(cert, name) + || !set_issuer_name(cert, akid) + || !X509_gmtime_adj(X509_get_notBefore(cert), -30 * 86400L) + || !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L) + || !X509_set_pubkey(cert, newkey) + || !add_ext(0, cert, NID_basic_constraints, "CA:TRUE") + || (!top && !add_akid(cert, akid)) + || !add_skid(cert, akid) + || ( !top && wrap_to_root + && !wrap_issuer(dane, newkey, cert, depth, WRAP_TOP))) ret = 0; -if(akid) +if (akid) AUTHORITY_KEYID_free(akid); -if(!key) +if (!key) EVP_PKEY_free(newkey); -if(ret) +if (ret) ret = grow_chain(dane, !top && wrap_to_root ? UNTRUSTED : TRUSTED, cert); -if(cert) +if (cert) X509_free(cert); return ret; } @@ -482,18 +488,18 @@ return ret; static int wrap_cert(ssl_dane *dane, X509 *tacert, int depth) { -if(dane->depth < 0) +if (dane->depth < 0) dane->depth = depth + 1; /* * If the TA certificate is self-issued, or need not be, use it directly. * Otherwise, synthesize requisuite ancestors. */ -if( !wrap_to_root - || X509_check_issued(tacert, tacert) == X509_V_OK) +if ( !wrap_to_root + || X509_check_issued(tacert, tacert) == X509_V_OK) return grow_chain(dane, TRUSTED, tacert); -if(wrap_issuer(dane, 0, tacert, depth, WRAP_MID)) +if (wrap_issuer(dane, 0, tacert, depth, WRAP_MID)) return grow_chain(dane, UNTRUSTED, tacert); return 0; } @@ -515,9 +521,9 @@ int done = 0; */ for (x = dane->certs; !done && x; x = x->next) { - if(X509_check_issued(x->value, cert) == X509_V_OK) + if (X509_check_issued(x->value, cert) == X509_V_OK) { - if(!(pk = X509_get_pubkey(x->value))) + if (!(pk = X509_get_pubkey(x->value))) { /* * The cert originally contained a valid pkey, which does @@ -527,7 +533,7 @@ for (x = dane->certs; !done && x; x = x->next) break; } /* Check signature, since some other TA may work if not this. */ - if(X509_verify(cert, pk) > 0) + if (X509_verify(cert, pk) > 0) done = wrap_cert(dane, x->value, depth) ? 1 : -1; EVP_PKEY_free(pk); } @@ -553,8 +559,8 @@ for (x = dane->certs; !done && x; x = x->next) * This may push errors onto the stack when the certificate signature is * not of the right type or length, throw these away, */ -for(k = dane->pkeys; !done && k; k = k->next) - if(X509_verify(cert, k->value) > 0) +for (k = dane->pkeys; !done && k; k = k->next) + if (X509_verify(cert, k->value) > 0) done = wrap_issuer(dane, k->value, cert, depth, WRAP_MID) ? 1 : -1; else ERR_clear_error(); @@ -573,25 +579,25 @@ EVP_PKEY *takey; X509 *ca; STACK_OF(X509) *in = ctx->untrusted; /* XXX: Accessor? */ -if(!grow_chain(dane, UNTRUSTED, 0)) +if (!grow_chain(dane, UNTRUSTED, 0)) return -1; /* * Accept a degenerate case: depth 0 self-signed trust-anchor. */ -if(X509_check_issued(cert, cert) == X509_V_OK) +if (X509_check_issued(cert, cert) == X509_V_OK) { dane->depth = 0; - matched = match(dane->selectors[SSL_DANE_USAGE_TRUSTED_CA], cert, 0); - if(matched > 0 && !grow_chain(dane, TRUSTED, cert)) + matched = match(dane->selectors[DANESSL_USAGE_DANE_TA], cert, 0); + if (matched > 0 && !grow_chain(dane, TRUSTED, cert)) matched = -1; return matched; } /* Make a shallow copy of the input untrusted chain. */ -if(!(in = sk_X509_dup(in))) +if (!(in = sk_X509_dup(in))) { - DANEerr(DANE_F_SET_TRUST_ANCHOR, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_SET_TRUST_ANCHOR, ERR_R_MALLOC_FAILURE); return -1; } @@ -603,29 +609,29 @@ if(!(in = sk_X509_dup(in))) * * Caller ensures that the initial certificate is not self-signed. */ -for(n = sk_X509_num(in); n > 0; --n, ++depth) +for (n = sk_X509_num(in); n > 0; --n, ++depth) { - for(i = 0; i < n; ++i) - if(X509_check_issued(sk_X509_value(in, i), cert) == X509_V_OK) + for (i = 0; i < n; ++i) + if (X509_check_issued(sk_X509_value(in, i), cert) == X509_V_OK) break; /* * Final untrusted element with no issuer in the peer's chain, it may * however be signed by a pkey or cert obtained via a TLSA RR. */ - if(i == n) + if (i == n) break; /* Peer's chain contains an issuer ca. */ ca = sk_X509_delete(in, i); /* If not a trust anchor, record untrusted ca and continue. */ - if((matched = match(dane->selectors[SSL_DANE_USAGE_TRUSTED_CA], ca, depth+1)) - == 0) + if ((matched = match(dane->selectors[DANESSL_USAGE_DANE_TA], ca, + depth + 1)) == 0) { - if(grow_chain(dane, UNTRUSTED, ca)) + if (grow_chain(dane, UNTRUSTED, ca)) { - if(!X509_check_issued(ca, ca) == X509_V_OK) + if (!X509_check_issued(ca, ca) == X509_V_OK) { /* Restart with issuer as subject */ cert = ca; @@ -644,13 +650,13 @@ for(n = sk_X509_num(in); n > 0; --n, ++depth) } else if(matched == MATCHED_PKEY) { - if( !(takey = X509_get_pubkey(ca)) - || !wrap_issuer(dane, takey, cert, depth, WRAP_MID)) + if ( !(takey = X509_get_pubkey(ca)) + || !wrap_issuer(dane, takey, cert, depth, WRAP_MID)) { - if(takey) + if (takey) EVP_PKEY_free(takey); else - DANEerr(DANE_F_SET_TRUST_ANCHOR, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_SET_TRUST_ANCHOR, ERR_R_MALLOC_FAILURE); matched = -1; } } @@ -665,7 +671,7 @@ sk_X509_free(in); * no issuer in the chain, we check for a possible signature via a DNS * obtained TA cert or public key. */ -if(matched == 0 && cert) +if (matched == 0 && cert) matched = ta_signed(dane, cert, depth); return matched; @@ -676,19 +682,24 @@ check_end_entity(X509_STORE_CTX *ctx, ssl_dane *dane, X509 *cert) { int matched; -matched = match(dane->selectors[SSL_DANE_USAGE_FIXED_LEAF], cert, 0); -if(matched > 0) +matched = match(dane->selectors[DANESSL_USAGE_DANE_EE], cert, 0); +if (matched > 0) + { + dane->mdpth = 0; + dane->match = cert; + X509_up_ref(cert); if(!ctx->chain) { - if( (ctx->chain = sk_X509_new_null()) - && sk_X509_push(ctx->chain, cert)) - CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + if ( (ctx->chain = sk_X509_new_null()) != 0 + && sk_X509_push(ctx->chain, cert)) + X509_up_ref(cert); else { - DANEerr(DANE_F_CHECK_END_ENTITY, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_CHECK_END_ENTITY, ERR_R_MALLOC_FAILURE); return -1; } } + } return matched; } @@ -698,7 +709,7 @@ match_name(const char *certid, ssl_dane *dane) int multi = dane->multi; dane_host_list hosts; -for(hosts = dane->hosts; hosts; hosts = hosts->next) +for (hosts = dane->hosts; hosts; hosts = hosts->next) { int match_subdomain = 0; const char *domain = hosts->value; @@ -706,7 +717,7 @@ for(hosts = dane->hosts; hosts; hosts = hosts->next) int idlen; int domlen; - if(*domain == '.' && domain[1] != '\0') + if (*domain == '.' && domain[1] != '\0') { ++domain; match_subdomain = 1; @@ -717,9 +728,9 @@ for(hosts = dane->hosts; hosts; hosts = hosts->next) */ if(match_subdomain) { - if( (idlen = strlen(certid)) > (domlen = strlen(domain)) + 1 - && certid[idlen - domlen - 1] == '.' - && !strcasecmp(certid + (idlen - domlen), domain)) + if ( (idlen = strlen(certid)) > (domlen = strlen(domain)) + 1 + && certid[idlen - domlen - 1] == '.' + && !strcasecmp(certid + (idlen - domlen), domain)) return 1; else continue; @@ -730,11 +741,11 @@ for(hosts = dane->hosts; hosts; hosts = hosts->next) * matches one (if multi is false) or more hostname components under * the condition that the certid contains multiple hostname components. */ - if( !strcasecmp(certid, domain) - || ( certid[0] == '*' && certid[1] == '.' && certid[2] != 0 - && (parent = strchr(domain, '.')) != 0 - && (idlen = strlen(certid + 1)) <= (domlen = strlen(parent)) - && strcasecmp(multi ? parent + domlen - idlen : parent, certid+1) == 0)) + if ( !strcasecmp(certid, domain) + || ( certid[0] == '*' && certid[1] == '.' && certid[2] != 0 + && (parent = strchr(domain, '.')) != 0 + && (idlen = strlen(certid + 1)) <= (domlen = strlen(parent)) + && strcasecmp(multi ? parent + domlen - idlen : parent, certid+1) == 0)) return 1; } return 0; @@ -745,11 +756,11 @@ check_name(char *name, int len) { char *cp = name + len; -while(len > 0 && !*--cp) +while (len > 0 && !*--cp) --len; /* Ignore trailing NULs */ -if(len <= 0) +if (len <= 0) return 0; -for(cp = name; *cp; cp++) +for (cp = name; *cp; cp++) { char c = *cp; if (!((c >= 'a' && c <= 'z') || @@ -759,7 +770,7 @@ for(cp = name; *cp; cp++) (c == '*'))) return 0; /* Only LDH, '.' and '*' */ } -if(cp - name != len) /* Guard against internal NULs */ +if (cp - name != len) /* Guard against internal NULs */ return 0; return name; } @@ -767,9 +778,9 @@ return name; static char * parse_dns_name(const GENERAL_NAME *gn) { -if(gn->type != GEN_DNS) +if (gn->type != GEN_DNS) return 0; -if(ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) +if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) return 0; return check_name((char *) ASN1_STRING_data(gn->d.ia5), ASN1_STRING_length(gn->d.ia5)); @@ -786,16 +797,16 @@ int nid = NID_commonName; int len; int i; -if(!name || (i = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) +if (!name || (i = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) return 0; -if(!(entry = X509_NAME_get_entry(name, i))) +if (!(entry = X509_NAME_get_entry(name, i))) return 0; -if(!(entry_str = X509_NAME_ENTRY_get_data(entry))) +if (!(entry_str = X509_NAME_ENTRY_get_data(entry))) return 0; -if((len = ASN1_STRING_to_UTF8(&namebuf, entry_str)) < 0) +if ((len = ASN1_STRING_to_UTF8(&namebuf, entry_str)) < 0) return 0; -if(len <= 0 || check_name((char *) namebuf, len) == 0) +if (len <= 0 || check_name((char *) namebuf, len) == 0) { OPENSSL_free(namebuf); return 0; @@ -811,26 +822,27 @@ BOOL got_altname = FALSE; GENERAL_NAMES *gens; gens = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); -if(gens) +if (gens) { int n = sk_GENERAL_NAME_num(gens); int i; - for(i = 0; i < n; ++i) + for (i = 0; i < n; ++i) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); const char *certid; - if(gn->type != GEN_DNS) + if (gn->type != GEN_DNS) continue; got_altname = TRUE; certid = parse_dns_name(gn); - if(certid && *certid) + if (certid && *certid) { - if((matched = match_name(certid, dane)) == 0) + if ((matched = match_name(certid, dane)) == 0) continue; - if(!(dane->mhost = OPENSSL_strdup(certid))) + if (!(dane->mhost = OPENSSL_strdup(certid))) matched = -1; + DEBUG(D_tls) debug_printf("Dane name_check: matched SAN %s\n", certid); break; } } @@ -841,11 +853,16 @@ if(gens) * XXX: Should the subjectName be skipped when *any* altnames are present, * or only when DNS altnames are present? */ -if(got_altname) +if (!got_altname) { char *certid = parse_subject_name(cert); - if(certid != 0 && *certid && (matched = match_name(certid, dane)) != 0) - dane->mhost = certid; /* Already a copy */ + if (certid != 0 && *certid && (matched = match_name(certid, dane)) != 0) + { + DEBUG(D_tls) debug_printf("Dane name_check: matched SN %s\n", certid); + dane->mhost = OPENSSL_strdup(certid); + } + if (certid) + OPENSSL_free(certid); } return matched; } @@ -863,24 +880,24 @@ X509 *cert = ctx->cert; /* XXX: accessor? */ int matched = 0; int chain_length = sk_X509_num(ctx->chain); -DEBUG(D_tls) debug_printf("Dane verify-chain\n"); +DEBUG(D_tls) debug_printf("Dane verify_chain\n"); -issuer_rrs = dane->selectors[SSL_DANE_USAGE_LIMIT_ISSUER]; -leaf_rrs = dane->selectors[SSL_DANE_USAGE_LIMIT_LEAF]; +issuer_rrs = dane->selectors[DANESSL_USAGE_PKIX_TA]; +leaf_rrs = dane->selectors[DANESSL_USAGE_PKIX_EE]; ctx->verify = dane->verify; -if((matched = name_check(dane, cert)) < 0) +if ((matched = name_check(dane, cert)) < 0) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); return 0; } -if(!matched) +if (!matched) { ctx->error_depth = 0; ctx->current_cert = cert; X509_STORE_CTX_set_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH); - if(!cb(0, ctx)) + if (!cb(0, ctx)) return 0; } matched = 0; @@ -892,16 +909,20 @@ matched = 0; * XXX: internal_verify() doesn't callback with top certs that are not * self-issued. This should be fixed in a future OpenSSL. */ -if(dane->roots && sk_X509_num(dane->roots)) +if (dane->roots && sk_X509_num(dane->roots)) { -#ifndef NO_CALLBACK_WORKAROUND X509 *top = sk_X509_value(ctx->chain, dane->depth); - if(X509_check_issued(top, top) != X509_V_OK) + dane->mdpth = dane->depth; + dane->match = top; + X509_up_ref(top); + +#ifndef NO_CALLBACK_WORKAROUND + if (X509_check_issued(top, top) != X509_V_OK) { ctx->error_depth = dane->depth; ctx->current_cert = top; - if(!cb(1, ctx)) + if (!cb(1, ctx)) return 0; } #endif @@ -909,43 +930,75 @@ if(dane->roots && sk_X509_num(dane->roots)) while (--chain_length > dane->depth) X509_free(sk_X509_pop(ctx->chain)); } -else if(issuer_rrs || leaf_rrs) +else { - int n = chain_length; + int n = 0; + X509 *xn = cert; /* * Check for an EE match, then a CA match at depths > 0, and * finally, if the EE cert is self-issued, for a depth 0 CA match. */ - if(leaf_rrs) - matched = match(leaf_rrs, cert, 0); - while(!matched && issuer_rrs && --n >= 0) - { - X509 *xn = sk_X509_value(ctx->chain, n); - - if(n > 0 || X509_check_issued(xn, xn) == X509_V_OK) - matched = match(issuer_rrs, xn, n); - } + if (leaf_rrs) + matched = match(leaf_rrs, xn, 0); + if (matched) DEBUG(D_tls) debug_printf("Dane verify_chain: matched EE\n"); - if(matched < 0) - { - X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); - return 0; - } + if (!matched && issuer_rrs) + for (n = chain_length-1; !matched && n >= 0; --n) + { + xn = sk_X509_value(ctx->chain, n); + if (n > 0 || X509_check_issued(xn, xn) == X509_V_OK) + matched = match(issuer_rrs, xn, n); + } + if (matched) DEBUG(D_tls) debug_printf("Dane verify_chain: matched %s\n", + n>0 ? "CA" : "selfisssued EE"); - if(!matched) + if (!matched) { ctx->current_cert = cert; ctx->error_depth = 0; X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_UNTRUSTED); - if(!cb(0, ctx)) + if (!cb(0, ctx)) return 0; } + else + { + dane->mdpth = n; + dane->match = xn; + X509_up_ref(xn); + } } return ctx->verify(ctx); } +static void +dane_reset(ssl_dane *dane) +{ +dane->depth = -1; +if (dane->mhost) + { + OPENSSL_free(dane->mhost); + dane->mhost = 0; + } +if (dane->roots) + { + sk_X509_pop_free(dane->roots, X509_free); + dane->roots = 0; + } +if (dane->chain) + { + sk_X509_pop_free(dane->chain, X509_free); + dane->chain = 0; + } +if (dane->match) + { + X509_free(dane->match); + dane->match = 0; + } +dane->mdpth = -1; +} + static int verify_cert(X509_STORE_CTX *ctx, void *unused_ctx) { @@ -956,63 +1009,84 @@ int (*cb)(int, X509_STORE_CTX *) = ctx->verify_cb; int matched; X509 *cert = ctx->cert; /* XXX: accessor? */ -DEBUG(D_tls) debug_printf("Dane verify-cert\n"); +DEBUG(D_tls) debug_printf("Dane verify_cert\n"); -if(ssl_idx < 0) +if (ssl_idx < 0) ssl_idx = SSL_get_ex_data_X509_STORE_CTX_idx(); -if(dane_idx < 0) +if (dane_idx < 0) { - DANEerr(DANE_F_VERIFY_CERT, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_VERIFY_CERT, ERR_R_MALLOC_FAILURE); return -1; } ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_idx); -if(!(dane = SSL_get_ex_data(ssl, dane_idx)) || !cert) +if (!(dane = SSL_get_ex_data(ssl, dane_idx)) || !cert) return X509_verify_cert(ctx); -if(dane->selectors[SSL_DANE_USAGE_FIXED_LEAF]) +/* Reset for verification of a new chain, perhaps a renegotiation. */ +dane_reset(dane); + +if (dane->selectors[DANESSL_USAGE_DANE_EE]) { - if((matched = check_end_entity(ctx, dane, cert)) > 0) + if ((matched = check_end_entity(ctx, dane, cert)) > 0) { ctx->error_depth = 0; ctx->current_cert = cert; return cb(1, ctx); } - if(matched < 0) + if (matched < 0) { X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); return -1; } } -if(dane->selectors[SSL_DANE_USAGE_TRUSTED_CA]) - { - if((matched = set_trust_anchor(ctx, dane, cert)) < 0) - { - X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); - return -1; - } - if(matched) + if (dane->selectors[DANESSL_USAGE_DANE_TA]) { - /* - * Check that setting the untrusted chain updates the expected - * structure member at the expected offset. - */ - X509_STORE_CTX_trusted_stack(ctx, dane->roots); - X509_STORE_CTX_set_chain(ctx, dane->chain); - OPENSSL_assert(ctx->untrusted == dane->chain); + if ((matched = set_trust_anchor(ctx, dane, cert)) < 0) + { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); + return -1; + } + if (matched) + { + /* + * Check that setting the untrusted chain updates the expected + * structure member at the expected offset. + */ + X509_STORE_CTX_trusted_stack(ctx, dane->roots); + X509_STORE_CTX_set_chain(ctx, dane->chain); + OPENSSL_assert(ctx->untrusted == dane->chain); + } } - } -/* - * Name checks and usage 0/1 constraint enforcement are delayed until - * X509_verify_cert() builds the full chain and calls our verify_chain() - * wrapper. - */ -dane->verify = ctx->verify; -ctx->verify = verify_chain; + /* + * Name checks and usage 0/1 constraint enforcement are delayed until + * X509_verify_cert() builds the full chain and calls our verify_chain() + * wrapper. + */ + dane->verify = ctx->verify; + ctx->verify = verify_chain; -return X509_verify_cert(ctx); + if (X509_verify_cert(ctx)) + return 1; + + /* + * If the chain is invalid, clear any matching cert or hostname, to + * protect callers that might erroneously rely on these alone without + * checking the validation status. + */ + if (dane->match) + { + X509_free(dane->match); + dane->match = 0; + } + if (dane->mhost) + { + OPENSSL_free(dane->mhost); + dane->mhost = 0; + } + return 0; } static dane_list @@ -1021,15 +1095,15 @@ list_alloc(size_t vsize) void *value = (void *) OPENSSL_malloc(vsize); dane_list l; -if(!value) +if (!value) { - DANEerr(DANE_F_LIST_ALLOC, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_LIST_ALLOC, ERR_R_MALLOC_FAILURE); return 0; } -if(!(l = (dane_list) OPENSSL_malloc(sizeof(*l)))) +if (!(l = (dane_list) OPENSSL_malloc(sizeof(*l)))) { OPENSSL_free(value); - DANEerr(DANE_F_LIST_ALLOC, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_LIST_ALLOC, ERR_R_MALLOC_FAILURE); return 0; } l->next = 0; @@ -1043,7 +1117,7 @@ list_free(void *list, void (*f)(void *)) dane_list head; dane_list next; -for(head = (dane_list) list; head; head = next) +for (head = (dane_list) list; head; head = next) { next = head->next; if (f && head->value) @@ -1055,7 +1129,7 @@ for(head = (dane_list) list; head; head = next) static void dane_mtype_free(void *p) { -list_free(((dane_mtype) p)->data, OPENSSL_freeFunc); +list_free(((dane_mtype) p)->data, CRYPTO_free); OPENSSL_free(p); } @@ -1090,25 +1164,20 @@ int u; DEBUG(D_tls) debug_printf("Dane lib-cleanup\n"); -if(dane_idx < 0 || !(dane = SSL_get_ex_data(ssl, dane_idx))) +if (dane_idx < 0 || !(dane = SSL_get_ex_data(ssl, dane_idx))) return; (void) SSL_set_ex_data(ssl, dane_idx, 0); -if(dane->hosts) - list_free(dane->hosts, OPENSSL_freeFunc); -if(dane->mhost) - OPENSSL_free(dane->mhost); -for(u = 0; u <= SSL_DANE_USAGE_LAST; ++u) - if(dane->selectors[u]) +dane_reset(dane); +if (dane->hosts) + list_free(dane->hosts, CRYPTO_free); +for (u = 0; u <= DANESSL_USAGE_LAST; ++u) + if (dane->selectors[u]) list_free(dane->selectors[u], dane_selector_free); -if(dane->pkeys) +if (dane->pkeys) list_free(dane->pkeys, pkey_free); -if(dane->certs) +if (dane->certs) list_free(dane->certs, cert_free); -if(dane->roots) - sk_X509_pop_free(dane->roots, X509_free); -if(dane->chain) - sk_X509_pop_free(dane->chain, X509_free); OPENSSL_free(dane); } @@ -1117,12 +1186,12 @@ host_list_init(const char **src) { dane_host_list head = NULL; -while(*src) +while (*src) { dane_host_list elem = (dane_host_list) OPENSSL_malloc(sizeof(*elem)); - if(!elem) + if (elem == 0) { - list_free(head, OPENSSL_freeFunc); + list_free(head, CRYPTO_free); return 0; } elem->value = OPENSSL_strdup(*src++); @@ -1132,6 +1201,65 @@ return head; } +int +DANESSL_get_match_cert(SSL *ssl, X509 **match, const char **mhost, int *depth) +{ +ssl_dane *dane; + +if (dane_idx < 0 || (dane = SSL_get_ex_data(ssl, dane_idx)) == 0) + { + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_INIT); + return -1; + } + +if (dane->match) + { + if (match) + *match = dane->match; + if (mhost) + *mhost = dane->mhost; + if (depth) + *depth = dane->mdpth; + } + + return (dane->match != 0); +} + + +#ifdef never_called +int +DANESSL_verify_chain(SSL *ssl, STACK_OF(X509) *chain) +{ +int ret; +X509 *cert; +X509_STORE_CTX store_ctx; +SSL_CTX *ssl_ctx = SSL_get_SSL_CTX(ssl); +X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); +int store_ctx_idx = SSL_get_ex_data_X509_STORE_CTX_idx(); + +cert = sk_X509_value(chain, 0); +if (!X509_STORE_CTX_init(&store_ctx, store, cert, chain)) + return 0; +X509_STORE_CTX_set_ex_data(&store_ctx, store_ctx_idx, ssl); + +X509_STORE_CTX_set_default(&store_ctx, + SSL_is_server(ssl) ? "ssl_client" : "ssl_server"); +X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(&store_ctx), + SSL_get0_param(ssl)); + +if (SSL_get_verify_callback(ssl)) + X509_STORE_CTX_set_verify_cb(&store_ctx, SSL_get_verify_callback(ssl)); + +ret = verify_cert(&store_ctx, NULL); + +SSL_set_verify_result(ssl, X509_STORE_CTX_get_error(&store_ctx)); +X509_STORE_CTX_cleanup(&store_ctx); + +return (ret); +} +#endif + + /* @@ -1170,121 +1298,139 @@ DEBUG(D_tls) debug_printf("Dane add-tlsa: usage %u sel %u mdname \"%s\"\n", if(dane_idx < 0 || !(dane = SSL_get_ex_data(ssl, dane_idx))) { - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_DANE_INIT); + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_INIT); return -1; } -if(usage > SSL_DANE_USAGE_LAST) - { - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_USAGE); - return 0; - } -if(selector > SSL_DANE_SELECTOR_LAST) - { - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_SELECTOR); - return 0; - } -if(mdname && !(md = EVP_get_digestbyname(mdname))) - { - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_DIGEST); - return 0; - } -if(!data) +if (usage > DANESSL_USAGE_LAST) { - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_NULL_DATA); + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_USAGE); return 0; } -if(mdname && dlen != EVP_MD_size(md)) +if (selector > DANESSL_SELECTOR_LAST) { - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_DATA_LENGTH); + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_SELECTOR); return 0; } -if(!mdname) - { - X509 *x = 0; - EVP_PKEY *k = 0; - const unsigned char *p = data; + /* Support built-in standard one-digit mtypes */ + if (mdname && *mdname && mdname[1] == '\0') + switch (*mdname - '0') + { + case DANESSL_MATCHING_FULL: mdname = 0; break; + case DANESSL_MATCHING_2256: mdname = "sha256"; break; + case DANESSL_MATCHING_2512: mdname = "sha512"; break; + } + if (mdname && *mdname && (md = EVP_get_digestbyname(mdname)) == 0) + { + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_DIGEST); + return 0; + } + if (mdname && *mdname && dlen != EVP_MD_size(md)) + { + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_DATA_LENGTH); + return 0; + } + if (!data) + { + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_NULL_DATA); + return 0; + } + + /* + * Full Certificate or Public Key when NULL or empty digest name + */ + if (!mdname || !*mdname) + { + X509 *x = 0; + EVP_PKEY *k = 0; + const unsigned char *p = data; #define xklistinit(lvar, ltype, var, freeFunc) do { \ - (lvar) = (ltype) OPENSSL_malloc(sizeof(*(lvar))); \ - if (!(lvar)) { \ - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, ERR_R_MALLOC_FAILURE); \ - freeFunc((var)); \ - return 0; \ - } \ - (lvar)->next = 0; \ - lvar->value = var; \ - } while (0) + (lvar) = (ltype) OPENSSL_malloc(sizeof(*(lvar))); \ + if ((lvar) == 0) { \ + DANEerr(DANESSL_F_ADD_TLSA, ERR_R_MALLOC_FAILURE); \ + freeFunc((var)); \ + return 0; \ + } \ + (lvar)->next = 0; \ + lvar->value = var; \ + } while (0) #define xkfreeret(ret) do { \ - if (xlist) list_free(xlist, cert_free); \ - if (klist) list_free(klist, pkey_free); \ - return (ret); \ - } while (0) + if (xlist) list_free(xlist, cert_free); \ + if (klist) list_free(klist, pkey_free); \ + return (ret); \ + } while (0) - switch(selector) + switch (selector) { - case SSL_DANE_SELECTOR_CERT: - if(!d2i_X509(&x, &p, dlen) || dlen != p - data) + case DANESSL_SELECTOR_CERT: + if (!d2i_X509(&x, &p, dlen) || dlen != p - data) { if (x) - X509_free(x); - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_CERT); + X509_free(x); + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_CERT); return 0; } k = X509_get_pubkey(x); EVP_PKEY_free(k); - if(!k) + if (k == 0) { X509_free(x); - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_CERT_PKEY); + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_CERT_PKEY); return 0; } - if(usage == SSL_DANE_USAGE_TRUSTED_CA) + if (usage == DANESSL_USAGE_DANE_TA) xklistinit(xlist, dane_cert_list, x, X509_free); break; - case SSL_DANE_SELECTOR_SPKI: - if(!d2i_PUBKEY(&k, &p, dlen) || dlen != p - data) + case DANESSL_SELECTOR_SPKI: + if (!d2i_PUBKEY(&k, &p, dlen) || dlen != p - data) { - if(k) + if (k) EVP_PKEY_free(k); - DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_PKEY); - return 0; + DANEerr(DANESSL_F_ADD_TLSA, DANESSL_R_BAD_PKEY); + return 0; } - if(usage == SSL_DANE_USAGE_TRUSTED_CA) + if (usage == DANESSL_USAGE_DANE_TA) xklistinit(klist, dane_pkey_list, k, EVP_PKEY_free); break; } } /* Find insertion point and don't add duplicate elements. */ -for(s = dane->selectors[usage]; s; s = s->next) - if(s->value->selector == selector) - for(m = s->value->mtype; m; m = m->next) - if(m->value->md == md) - for(d = m->value->data; d; d = d->next) - if( d->value->datalen == dlen - && memcmp(d->value->data, data, dlen) == 0) +for (s = dane->selectors[usage]; s; s = s->next) + if (s->value->selector == selector) + { + for (m = s->value->mtype; m; m = m->next) + if (m->value->md == md) + { + for (d = m->value->data; d; d = d->next) + if ( d->value->datalen == dlen + && memcmp(d->value->data, data, dlen) == 0) xkfreeret(1); + break; + } + break; + } -if(!(d = (dane_data_list) list_alloc(sizeof(*d->value) + dlen))) +if ((d = (dane_data_list) list_alloc(sizeof(*d->value) + dlen)) == 0) xkfreeret(0); d->value->datalen = dlen; memcpy(d->value->data, data, dlen); -if(!m) +if (!m) { - if(!(m = (dane_mtype_list) list_alloc(sizeof(*m->value)))) + if ((m = (dane_mtype_list) list_alloc(sizeof(*m->value))) == 0) { - list_free(d, OPENSSL_freeFunc); + list_free(d, CRYPTO_free); xkfreeret(0); } m->value->data = 0; - if((m->value->md = md) != 0) + if ((m->value->md = md) != 0) m->value->mdlen = dlen; - if(!s) + if (!s) { - if(!(s = (dane_selector_list) list_alloc(sizeof(*s->value)))) + if ((s = (dane_selector_list) list_alloc(sizeof(*s->value))) == 0) { list_free(m, dane_mtype_free); xkfreeret(0); @@ -1297,9 +1443,9 @@ if(!m) } LINSERT(m->value->data, d); -if(xlist) +if (xlist) LINSERT(dane->certs, xlist); -else if(klist) +else if (klist) LINSERT(dane->pkeys, klist); ++dane->count; return 1; @@ -1334,35 +1480,25 @@ DANESSL_init(SSL *ssl, const char *sni_domain, const char **hostnames) { ssl_dane *dane; int i; -#ifdef OPENSSL_INTERNAL -SSL_CTX *sctx = SSL_get_SSL_CTX(ssl); - -if(sctx->app_verify_callback != verify_cert) +DEBUG(D_tls) debug_printf("Dane ssl_init\n"); +if (dane_idx < 0) { - DANEerr(DANE_F_SSL_DANE_INIT, DANE_R_SCTX_INIT); + DANEerr(DANESSL_F_INIT, DANESSL_R_LIBRARY_INIT); return -1; } -#else -DEBUG(D_tls) debug_printf("Dane ssl-init\n"); -if(dane_idx < 0) - { - DANEerr(DANE_F_SSL_DANE_INIT, DANE_R_LIBRARY_INIT); - return -1; - } -#endif -if(sni_domain && !SSL_set_tlsext_host_name(ssl, sni_domain)) - return 0; +if (sni_domain && !SSL_set_tlsext_host_name(ssl, sni_domain)) + return 0; -if(!(dane = (ssl_dane *) OPENSSL_malloc(sizeof(ssl_dane)))) +if ((dane = (ssl_dane *) OPENSSL_malloc(sizeof(ssl_dane))) == 0) { - DANEerr(DANE_F_SSL_DANE_INIT, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_INIT, ERR_R_MALLOC_FAILURE); return 0; } -if(!SSL_set_ex_data(ssl, dane_idx, dane)) +if (!SSL_set_ex_data(ssl, dane_idx, dane)) { - DANEerr(DANE_F_SSL_DANE_INIT, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_INIT, ERR_R_MALLOC_FAILURE); OPENSSL_free(dane); return 0; } @@ -1373,18 +1509,21 @@ dane->thost = 0; dane->pkeys = 0; dane->certs = 0; dane->chain = 0; +dane->match = 0; dane->roots = 0; dane->depth = -1; -dane->mhost = 0; /* Future SSL control interface */ -dane->multi = 0; /* Future SSL control interface */ +dane->mhost = 0; /* Future SSL control interface */ +dane->mdpth = 0; /* Future SSL control interface */ +dane->multi = 0; /* Future SSL control interface */ dane->count = 0; +dane->hosts = 0; -for(i = 0; i <= SSL_DANE_USAGE_LAST; ++i) - dane->selectors[i] = 0; +for (i = 0; i <= DANESSL_USAGE_LAST; ++i) + dane->selectors[i] = 0; -if(hostnames && !(dane->hosts = host_list_init(hostnames))) +if (hostnames && (dane->hosts = host_list_init(hostnames)) == 0) { - DANEerr(DANE_F_SSL_DANE_INIT, ERR_R_MALLOC_FAILURE); + DANEerr(DANESSL_F_INIT, ERR_R_MALLOC_FAILURE); DANESSL_cleanup(ssl); return 0; } @@ -1415,12 +1554,12 @@ int DANESSL_CTX_init(SSL_CTX *ctx) { DEBUG(D_tls) debug_printf("Dane ctx-init\n"); -if(dane_idx >= 0) +if (dane_idx >= 0) { SSL_CTX_set_cert_verify_callback(ctx, verify_cert, 0); return 1; } -DANEerr(DANE_F_SSL_CTX_DANE_INIT, DANE_R_LIBRARY_INIT); +DANEerr(DANESSL_F_CTX_INIT, DANESSL_R_LIBRARY_INIT); return -1; } @@ -1430,15 +1569,15 @@ init_once(volatile int *value, int (*init)(void), void (*postinit)(void)) int wlock = 0; CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); -if(*value < 0) +if (*value < 0) { CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); wlock = 1; - if(*value < 0) + if (*value < 0) { *value = init(); - if(postinit) + if (postinit) postinit(); } } @@ -1466,12 +1605,12 @@ ERR_load_strings(err_lib_dane, dane_str_reasons); * Register SHA-2 digests, if implemented and not already registered. */ #if defined(LN_sha256) && defined(NID_sha256) && !defined(OPENSSL_NO_SHA256) -if(!EVP_get_digestbyname(LN_sha224)) EVP_add_digest(EVP_sha224()); -if(!EVP_get_digestbyname(LN_sha256)) EVP_add_digest(EVP_sha256()); +if (!EVP_get_digestbyname(LN_sha224)) EVP_add_digest(EVP_sha224()); +if (!EVP_get_digestbyname(LN_sha256)) EVP_add_digest(EVP_sha256()); #endif #if defined(LN_sha512) && defined(NID_sha512) && !defined(OPENSSL_NO_SHA512) -if(!EVP_get_digestbyname(LN_sha384)) EVP_add_digest(EVP_sha384()); -if(!EVP_get_digestbyname(LN_sha512)) EVP_add_digest(EVP_sha512()); +if (!EVP_get_digestbyname(LN_sha384)) EVP_add_digest(EVP_sha384()); +if (!EVP_get_digestbyname(LN_sha512)) EVP_add_digest(EVP_sha512()); #endif /* @@ -1501,16 +1640,15 @@ int DANESSL_library_init(void) { DEBUG(D_tls) debug_printf("Dane lib-init\n"); -if(err_lib_dane < 0) +if (err_lib_dane < 0) init_once(&err_lib_dane, ERR_get_next_error_library, dane_init); #if defined(LN_sha256) /* No DANE without SHA256 support */ -if(dane_idx >= 0 && EVP_get_digestbyname(LN_sha256) != 0) +if (dane_idx >= 0 && EVP_get_digestbyname(LN_sha256) != 0) return 1; #endif - -DANEerr(DANE_F_SSL_DANE_LIBRARY_INIT, DANE_R_DANE_SUPPORT); +DANEerr(DANESSL_F_LIBRARY_INIT, DANESSL_R_SUPPORT); return 0; } diff --git a/src/src/dane.c b/src/src/dane.c index 20dfe5b18..99ca7d02a 100644 --- a/src/src/dane.c +++ b/src/src/dane.c @@ -33,6 +33,11 @@ static void dummy(int x) { dummy(x-1); } # error DANE support requires that TLS support must be enabled. Abort build. # endif +/* DNSSEC support is also required */ +# ifndef RES_USE_DNSSEC +# error DANE support requires that the DNS reolver library supports DNSSEC +# endif + # ifdef USE_GNUTLS # include "dane-gnu.c" # else diff --git a/src/src/danessl.h b/src/src/danessl.h index 5b1584da2..1d6439e2f 100644 --- a/src/src/danessl.h +++ b/src/src/danessl.h @@ -1,5 +1,9 @@ -#ifndef HEADER_SSL_DANE_H -#define HEADER_SSL_DANE_H +/* + * Author: Viktor Dukhovni + * License: THIS CODE IS IN THE PUBLIC DOMAIN. + */ +#ifndef HEADER_DANESSL_H +#define HEADER_DANESSL_H #include <stdint.h> #include <openssl/ssl.h> @@ -8,24 +12,36 @@ * Certificate usages: * https://tools.ietf.org/html/rfc6698#section-2.1.1 */ -#define SSL_DANE_USAGE_LIMIT_ISSUER 0 -#define SSL_DANE_USAGE_LIMIT_LEAF 1 -#define SSL_DANE_USAGE_TRUSTED_CA 2 -#define SSL_DANE_USAGE_FIXED_LEAF 3 -#define SSL_DANE_USAGE_LAST SSL_DANE_USAGE_FIXED_LEAF +#define DANESSL_USAGE_PKIX_TA 0 +#define DANESSL_USAGE_PKIX_EE 1 +#define DANESSL_USAGE_DANE_TA 2 +#define DANESSL_USAGE_DANE_EE 3 +#define DANESSL_USAGE_LAST DANESSL_USAGE_DANE_EE /*- * Selectors: * https://tools.ietf.org/html/rfc6698#section-2.1.2 */ -#define SSL_DANE_SELECTOR_CERT 0 -#define SSL_DANE_SELECTOR_SPKI 1 -#define SSL_DANE_SELECTOR_LAST SSL_DANE_SELECTOR_SPKI +#define DANESSL_SELECTOR_CERT 0 +#define DANESSL_SELECTOR_SPKI 1 +#define DANESSL_SELECTOR_LAST DANESSL_SELECTOR_SPKI + +/*- + * Matching types: + * https://tools.ietf.org/html/rfc6698#section-2.1.3 + */ +#define DANESSL_MATCHING_FULL 0 +#define DANESSL_MATCHING_2256 1 +#define DANESSL_MATCHING_2512 2 +#define DANESSL_MATCHING_LAST DANESSL_MATCHING_2512 extern int DANESSL_library_init(void); extern int DANESSL_CTX_init(SSL_CTX *); extern int DANESSL_init(SSL *, const char *, const char **); extern void DANESSL_cleanup(SSL *); extern int DANESSL_add_tlsa(SSL *, uint8_t, uint8_t, const char *, - unsigned const char *, size_t); + unsigned const char *, size_t); +extern int DANESSL_get_match_cert(SSL *, X509 **, const char **, int *); +extern int DANESSL_verify_chain(SSL *, STACK_OF(X509) *); + #endif diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 4a1c20fe6..f521f2c20 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -294,17 +294,21 @@ Returns: a pointer to the retrieved record, or */ void * -dbfn_read_with_length(open_db *dbblock, uschar *key, int *length) +dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) { void *yield; EXIM_DATUM key_datum, result_datum; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); DEBUG(D_hints_lookup) debug_printf("dbfn_read: key=%s\n", key); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; @@ -334,18 +338,22 @@ Returns: the yield of the underlying dbm or db "write" function. If this */ int -dbfn_write(open_db *dbblock, uschar *key, void *ptr, int length) +dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length) { EXIM_DATUM key_datum, value_datum; dbdata_generic *gptr = (dbdata_generic *)ptr; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); DEBUG(D_hints_lookup) debug_printf("dbfn_write: key=%s\n", key); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; EXIM_DATUM_DATA(value_datum) = CS ptr; EXIM_DATUM_SIZE(value_datum) = length; return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum); @@ -366,12 +374,16 @@ Returns: the yield of the underlying dbm or db "delete" function. */ int -dbfn_delete(open_db *dbblock, uschar *key) +dbfn_delete(open_db *dbblock, const uschar *key) { +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); EXIM_DATUM key_datum; EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; return EXIM_DBDEL(dbblock->dbptr, key_datum); } diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h index 1963fa991..93d12efc3 100644 --- a/src/src/dbfunctions.h +++ b/src/src/dbfunctions.h @@ -2,18 +2,18 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading/writing exim database files */ void dbfn_close(open_db *); -int dbfn_delete(open_db *, uschar *); +int dbfn_delete(open_db *, const uschar *); open_db *dbfn_open(uschar *, int, open_db *, BOOL); -void *dbfn_read_with_length(open_db *, uschar *, int *); +void *dbfn_read_with_length(open_db *, const uschar *, int *); uschar *dbfn_scan(open_db *, BOOL, EXIM_CURSOR **); -int dbfn_write(open_db *, uschar *, void *, int); +int dbfn_write(open_db *, const uschar *, void *, int); /* Macro for the common call to read without wanting to know the length. */ diff --git a/src/src/dcc.c b/src/src/dcc.c index 44c0c009a..b03690ca6 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Wolfgang Breyha 2005-2013 +/* Copyright (c) Wolfgang Breyha 2005 - 2015 * Vienna University Computer Center * wbreyha@gmx.net * See the file NOTICE for conditions of use and distribution. @@ -45,9 +45,11 @@ int flushbuffer (int socket, uschar *buffer) return retval; } -int dcc_process(uschar **listptr) { +int +dcc_process(uschar **listptr) +{ int sep = 0; - uschar *list = *listptr; + const uschar *list = *listptr; FILE *data_file; uschar *dcc_default_ip_option = US"127.0.0.1"; uschar *dcc_helo_option = US"localhost"; @@ -142,18 +144,18 @@ int dcc_process(uschar **listptr) { bzero(opts,sizeof(opts)); Ustrncpy(opts, dccifd_options, sizeof(opts)-1); /* if $acl_m_dcc_override_client_ip is set use it */ - if (((override_client_ip = expand_string(US"$acl_m_dcc_override_client_ip")) != NULL) && + if (((override_client_ip = expand_string(US"$acl_m_dcc_override_client_ip")) != NULL) && (override_client_ip[0] != '\0')) { Ustrncpy(client_ip, override_client_ip, sizeof(client_ip)-1); DEBUG(D_acl) debug_printf("DCC: Client IP (overridden): %s\n", client_ip); - } + } else if(sender_host_address) { /* else if $sender_host_address is available use that? */ Ustrncpy(client_ip, sender_host_address, sizeof(client_ip)-1); DEBUG(D_acl) debug_printf("DCC: Client IP (sender_host_address): %s\n", client_ip); - } + } else { /* sender_host_address is NULL which means it comes from localhost */ Ustrncpy(client_ip, dcc_default_ip_option, sizeof(client_ip)-1); @@ -449,7 +451,7 @@ int dcc_process(uschar **listptr) { /* The third and following lines are the X-DCC header, * so we store it in dcc_header_str. */ /* check if we don't get more than we can handle */ - if(k < sizeof(dcc_header_str)) { + if(k < sizeof(dcc_header_str)) { dcc_header_str[k] = recvbuf[i]; k++; } diff --git a/src/src/debug.c b/src/src/debug.c index ebd932f64..8bf4aedd9 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -75,7 +75,7 @@ Returns: nothing */ void -debug_print_argv(uschar **argv) +debug_print_argv(const uschar ** argv) { debug_printf("exec"); while (*argv != NULL) debug_printf(" %.256s", *argv++); diff --git a/src/src/deliver.c b/src/src/deliver.c index a0c48d652..6eb9a65d5 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2,13 +2,14 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* The main code for delivering a message. */ #include "exim.h" +#include <assert.h> /* Data block for keeping track of subprocesses for parallel remote @@ -125,10 +126,10 @@ Returns: nothing void deliver_set_expansions(address_item *addr) { -if (addr == NULL) +if (!addr) { - uschar ***p = address_expansions; - while (*p != NULL) **p++ = NULL; + const uschar ***p = address_expansions; + while (*p) **p++ = NULL; return; } @@ -136,7 +137,7 @@ if (addr == NULL) what they contain. These first ones are always set, taking their values from the first address. */ -if (addr->host_list == NULL) +if (!addr->host_list) { deliver_host = deliver_host_address = US""; deliver_host_port = 0; @@ -149,9 +150,9 @@ else } deliver_recipients = addr; -deliver_address_data = addr->p.address_data; -deliver_domain_data = addr->p.domain_data; -deliver_localpart_data = addr->p.localpart_data; +deliver_address_data = addr->prop.address_data; +deliver_domain_data = addr->prop.domain_data; +deliver_localpart_data = addr->prop.localpart_data; /* These may be unset for multiple addresses */ @@ -167,7 +168,7 @@ bmi_base64_tracker_verdict = NULL; /* If there's only one address we can set everything. */ -if (addr->next == NULL) +if (!addr->next) { address_item *addr_orig; @@ -175,8 +176,7 @@ if (addr->next == NULL) deliver_localpart_prefix = addr->prefix; deliver_localpart_suffix = addr->suffix; - for (addr_orig = addr; addr_orig->parent != NULL; - addr_orig = addr_orig->parent); + for (addr_orig = addr; addr_orig->parent; addr_orig = addr_orig->parent) ; deliver_domain_orig = addr_orig->domain; /* Re-instate any prefix and suffix in the original local part. In all @@ -185,30 +185,33 @@ if (addr->next == NULL) filter sets up a pipe, file, or autoreply delivery, no router is involved. In this case, though, there won't be any prefix or suffix to worry about. */ - deliver_localpart_orig = (addr_orig->router == NULL)? addr_orig->local_part : - addr_orig->router->caseful_local_part? - addr_orig->cc_local_part : addr_orig->lc_local_part; + deliver_localpart_orig = !addr_orig->router + ? addr_orig->local_part + : addr_orig->router->caseful_local_part + ? addr_orig->cc_local_part + : addr_orig->lc_local_part; /* If there's a parent, make its domain and local part available, and if delivering to a pipe or file, or sending an autoreply, get the local part from the parent. For pipes and files, put the pipe or file string into address_pipe and address_file. */ - if (addr->parent != NULL) + if (addr->parent) { deliver_domain_parent = addr->parent->domain; - deliver_localpart_parent = (addr->parent->router == NULL)? - addr->parent->local_part : - addr->parent->router->caseful_local_part? - addr->parent->cc_local_part : addr->parent->lc_local_part; + deliver_localpart_parent = !addr->parent->router + ? addr->parent->local_part + : addr->parent->router->caseful_local_part + ? addr->parent->cc_local_part + : addr->parent->lc_local_part; /* File deliveries have their own flag because they need to be picked out as special more often. */ if (testflag(addr, af_pfr)) { - if (testflag(addr, af_file)) address_file = addr->local_part; - else if (deliver_localpart[0] == '|') address_pipe = addr->local_part; + if (testflag(addr, af_file)) address_file = addr->local_part; + else if (deliver_localpart[0] == '|') address_pipe = addr->local_part; deliver_localpart = addr->parent->local_part; deliver_localpart_prefix = addr->parent->prefix; deliver_localpart_suffix = addr->parent->suffix; @@ -222,9 +225,8 @@ if (addr->next == NULL) /* get message delivery status (0 - don't deliver | 1 - deliver) */ bmi_deliver = bmi_get_delivery_status(bmi_base64_verdict); /* if message is to be delivered, get eventual alternate location */ - if (bmi_deliver == 1) { + if (bmi_deliver == 1) bmi_alt_location = bmi_get_alt_location(bmi_base64_verdict); - }; #endif } @@ -239,18 +241,19 @@ else address_item *addr2; if (testflag(addr, af_pfr)) { - if (testflag(addr, af_file)) address_file = addr->local_part; - else if (addr->local_part[0] == '|') address_pipe = addr->local_part; + if (testflag(addr, af_file)) address_file = addr->local_part; + else if (addr->local_part[0] == '|') address_pipe = addr->local_part; } - for (addr2 = addr->next; addr2 != NULL; addr2 = addr2->next) + for (addr2 = addr->next; addr2; addr2 = addr2->next) { - if (deliver_domain != NULL && - Ustrcmp(deliver_domain, addr2->domain) != 0) + if (deliver_domain && Ustrcmp(deliver_domain, addr2->domain) != 0) deliver_domain = NULL; - if (self_hostname != NULL && (addr2->self_hostname == NULL || - Ustrcmp(self_hostname, addr2->self_hostname) != 0)) + if ( self_hostname + && ( !addr2->self_hostname + || Ustrcmp(self_hostname, addr2->self_hostname) != 0 + ) ) self_hostname = NULL; - if (deliver_domain == NULL && self_hostname == NULL) break; + if (!deliver_domain && !self_hostname) break; } } } @@ -361,15 +364,15 @@ static void replicate_status(address_item *addr) { address_item *addr2; -for (addr2 = addr->next; addr2 != NULL; addr2 = addr2->next) +for (addr2 = addr->next; addr2; addr2 = addr2->next) { - addr2->transport = addr->transport; + addr2->transport = addr->transport; addr2->transport_return = addr->transport_return; - addr2->basic_errno = addr->basic_errno; - addr2->more_errno = addr->more_errno; - addr2->special_action = addr->special_action; - addr2->message = addr->message; - addr2->user_message = addr->user_message; + addr2->basic_errno = addr->basic_errno; + addr2->more_errno = addr->more_errno; + addr2->special_action = addr->special_action; + addr2->message = addr->message; + addr2->user_message = addr->user_message; } } @@ -402,7 +405,7 @@ Returns: TRUE if the lists refer to the same host set static BOOL same_hosts(host_item *one, host_item *two) { -while (one != NULL && two != NULL) +while (one && two) { if (Ustrcmp(one->name, two->name) != 0) { @@ -416,8 +419,8 @@ while (one != NULL && two != NULL) /* Find the ends of the shortest sequence of identical MX values */ - while (end_one->next != NULL && end_one->next->mx == mx && - end_two->next != NULL && end_two->next->mx == mx) + while ( end_one->next && end_one->next->mx == mx + && end_two->next && end_two->next->mx == mx) { end_one = end_one->next; end_two = end_two->next; @@ -476,13 +479,11 @@ Returns: TRUE if the lists refer to the same header set static BOOL same_headers(header_line *one, header_line *two) { -for (;;) +for (;; one = one->next, two = two->next) { if (one == two) return TRUE; /* Includes the case where both NULL */ - if (one == NULL || two == NULL) return FALSE; + if (!one || !two) return FALSE; if (Ustrcmp(one->text, two->text) != 0) return FALSE; - one = one->next; - two = two->next; } } @@ -506,7 +507,7 @@ static BOOL same_strings(uschar *one, uschar *two) { if (one == two) return TRUE; /* Includes the case where both NULL */ -if (one == NULL || two == NULL) return FALSE; +if (!one || !two) return FALSE; return (Ustrcmp(one, two) == 0); } @@ -531,21 +532,21 @@ Returns: TRUE or FALSE static BOOL same_ugid(transport_instance *tp, address_item *addr1, address_item *addr2) { -if (!tp->uid_set && tp->expand_uid == NULL && !tp->deliver_as_creator) - { - if (testflag(addr1, af_uid_set) != testflag(addr2, af_gid_set) || - (testflag(addr1, af_uid_set) && - (addr1->uid != addr2->uid || - testflag(addr1, af_initgroups) != testflag(addr2, af_initgroups)))) - return FALSE; - } +if ( !tp->uid_set && !tp->expand_uid + && !tp->deliver_as_creator + && ( testflag(addr1, af_uid_set) != testflag(addr2, af_gid_set) + || ( testflag(addr1, af_uid_set) + && ( addr1->uid != addr2->uid + || testflag(addr1, af_initgroups) != testflag(addr2, af_initgroups) + ) ) ) ) + return FALSE; -if (!tp->gid_set && tp->expand_gid == NULL) - { - if (testflag(addr1, af_gid_set) != testflag(addr2, af_gid_set) || - (testflag(addr1, af_gid_set) && addr1->gid != addr2->gid)) - return FALSE; - } +if ( !tp->gid_set && !tp->expand_gid + && ( testflag(addr1, af_gid_set) != testflag(addr2, af_gid_set) + || ( testflag(addr1, af_gid_set) + && addr1->gid != addr2->gid + ) ) ) + return FALSE; return TRUE; } @@ -598,7 +599,7 @@ update_spool = TRUE; /* Ensure spool gets updated */ /* Top-level address */ -if (addr->parent == NULL) +if (!addr->parent) { tree_add_nonrecipient(addr->unique); tree_add_nonrecipient(addr->address); @@ -608,11 +609,9 @@ if (addr->parent == NULL) else if (testflag(addr, af_homonym)) { - if (addr->transport != NULL) - { + if (addr->transport) tree_add_nonrecipient( string_sprintf("%s/%s", addr->unique + 3, addr->transport->name)); - } } /* Non-homonymous child address */ @@ -622,14 +621,12 @@ else tree_add_nonrecipient(addr->unique); /* Check the list of duplicate addresses and ensure they are now marked done as well. */ -for (dup = addr_duplicate; dup != NULL; dup = dup->next) - { +for (dup = addr_duplicate; dup; dup = dup->next) if (Ustrcmp(addr->unique, dup->unique) == 0) { tree_add_nonrecipient(dup->unique); child_done(dup, now); } - } } @@ -656,7 +653,7 @@ static void child_done(address_item *addr, uschar *now) { address_item *aa; -while (addr->parent != NULL) +while (addr->parent) { addr = addr->parent; if ((addr->child_count -= 1) > 0) return; /* Incomplete parent */ @@ -665,9 +662,9 @@ while (addr->parent != NULL) /* Log the completion of all descendents only when there is no ancestor with the same original address. */ - for (aa = addr->parent; aa != NULL; aa = aa->parent) + for (aa = addr->parent; aa; aa = aa->parent) if (Ustrcmp(aa->address, addr->address) == 0) break; - if (aa != NULL) continue; + if (aa) continue; deliver_msglog("%s %s: children all complete\n", now, addr->address); DEBUG(D_deliver) debug_printf("%s: children all complete\n", addr->address); @@ -676,49 +673,99 @@ while (addr->parent != NULL) +/************************************************* +* Delivery logging support functions * +*************************************************/ + +/* The LOGGING() checks in d_log_interface() are complicated for backwards +compatibility. When outgoing interface logging was originally added, it was +conditional on just incoming_interface (which is off by default). The +outgoing_interface option is on by default to preserve this behaviour, but +you can enable incoming_interface and disable outgoing_interface to get I= +fields on incoming lines only. + +Arguments: + s The log line buffer + sizep Pointer to the buffer size + ptrp Pointer to current index into buffer + addr The address to be logged + +Returns: New value for s +*/ + +static uschar * +d_log_interface(uschar *s, int *sizep, int *ptrp) +{ +if (LOGGING(incoming_interface) && LOGGING(outgoing_interface) + && sending_ip_address) + { + s = string_append(s, sizep, ptrp, 2, US" I=[", sending_ip_address); + s = LOGGING(outgoing_port) + ? string_append(s, sizep, ptrp, 2, US"]:", + string_sprintf("%d", sending_port)) + : string_cat(s, sizep, ptrp, US"]", 1); + } +return s; +} + + static uschar * -d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr) +d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr) { - s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, - US" [", addr->host_used->address, US"]"); - if ((log_extra_selector & LX_outgoing_port) != 0) +s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, + US" [", addr->host_used->address, US"]"); +if (LOGGING(outgoing_port)) + s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d", + addr->host_used->port)); + +#ifdef SUPPORT_SOCKS +if (LOGGING(proxy) && proxy_local_address) + { + s = string_append(s, sizep, ptrp, 3, US" PRX=[", proxy_local_address, US"]"); + if (LOGGING(outgoing_port)) s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d", - addr->host_used->port)); - return s; + proxy_local_port)); + } +#endif + +return d_log_interface(s, sizep, ptrp); } + + + + #ifdef SUPPORT_TLS static uschar * d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) { - if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL) - s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher); - if ((log_extra_selector & LX_tls_certificate_verified) != 0 && - addr->cipher != NULL) - s = string_append(s, sizep, ptrp, 2, US" CV=", - testflag(addr, af_cert_verified) - ? +if (LOGGING(tls_cipher) && addr->cipher) + s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher); +if (LOGGING(tls_certificate_verified) && addr->cipher) + s = string_append(s, sizep, ptrp, 2, US" CV=", + testflag(addr, af_cert_verified) + ? #ifdef EXPERIMENTAL_DANE - testflag(addr, af_dane_verified) - ? "dane" - : + testflag(addr, af_dane_verified) + ? "dane" + : #endif - "yes" - : "no"); - if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL) - s = string_append(s, sizep, ptrp, 3, US" DN=\"", - string_printing(addr->peerdn), US"\""); - return s; + "yes" + : "no"); +if (LOGGING(tls_peerdn) && addr->peerdn) + s = string_append(s, sizep, ptrp, 3, US" DN=\"", + string_printing(addr->peerdn), US"\""); +return s; } #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT uschar * -event_raise(uschar * action, uschar * event, uschar * ev_data) +event_raise(uschar * action, const uschar * event, uschar * ev_data) { uschar * s; if (action) @@ -751,12 +798,14 @@ if (action) return NULL; } -static void -msg_event_raise(uschar * event, address_item * addr) +void +msg_event_raise(const uschar * event, const address_item * addr) { -uschar * save_domain = deliver_domain; +const uschar * save_domain = deliver_domain; uschar * save_local = deliver_localpart; -uschar * save_host = deliver_host; +const uschar * save_host = deliver_host; +const uschar * save_address = deliver_host_address; +const int save_port = deliver_host_port; if (!addr->transport) return; @@ -768,15 +817,19 @@ deliver_localpart = addr->local_part; deliver_host = addr->host_used ? addr->host_used->name : NULL; (void) event_raise(addr->transport->event_action, event, - addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 - ? addr->message : NULL); + addr->host_used + || Ustrcmp(addr->transport->driver_name, "smtp") == 0 + || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 + ? addr->message : NULL); +deliver_host_port = save_port; +deliver_host_address = save_address; deliver_host = save_host; deliver_localpart = save_local; deliver_domain = save_domain; router_name = transport_name = NULL; } -#endif /*EXPERIMENTAL_EVENT*/ +#endif /*DISABLE_EVENT*/ @@ -801,14 +854,14 @@ the log line, and reset the store afterwards. Remote deliveries should always have a pointer to the host item that succeeded; local deliveries can have a pointer to a single host item in their host list, for use by the transport. */ -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT /* presume no successful remote delivery */ lookup_dnssec_authenticated = NULL; #endif s = reset_point = store_get(size); -log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE); +log_address = string_log_address(addr, LOGGING(all_parents), TRUE); if (msg) s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address); else @@ -817,12 +870,19 @@ else s = string_append(s, &size, &ptr, 2, US"> ", log_address); } -if ((log_extra_selector & LX_sender_on_delivery) != 0 || msg) - s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">"); +if (LOGGING(sender_on_delivery) || msg) + s = string_append(s, &size, &ptr, 3, US" F=<", +#ifdef SUPPORT_I18N + testflag(addr, af_utf8_downcvt) + ? string_address_utf8_to_alabel(sender_address, NULL) + : +#endif + sender_address, + US">"); #ifdef EXPERIMENTAL_SRS -if(addr->p.srs_sender) - s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->p.srs_sender, US">"); +if(addr->prop.srs_sender) + s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->prop.srs_sender, US">"); #endif /* You might think that the return path must always be set for a successful @@ -830,20 +890,19 @@ delivery; indeed, I did for some time, until this statement crashed. The case when it is not set is for a delivery to /dev/null which is optimised by not being run at all. */ -if (used_return_path != NULL && - (log_extra_selector & LX_return_path_on_delivery) != 0) +if (used_return_path && LOGGING(return_path_on_delivery)) s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); if (msg) s = string_append(s, &size, &ptr, 2, US" ", msg); /* For a delivery from a system filter, there may not be a router */ -if (addr->router != NULL) +if (addr->router) s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); -if ((log_extra_selector & LX_delivery_size) != 0) +if (LOGGING(delivery_size)) s = string_append(s, &size, &ptr, 2, US" S=", string_sprintf("%d", transport_count)); @@ -853,7 +912,8 @@ if (addr->transport->info->local) { if (addr->host_list) s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); - if (addr->shadow_message != NULL) + s = d_log_interface(s, &size, &ptr); + if (addr->shadow_message) s = string_cat(s, &size, &ptr, addr->shadow_message, Ustrlen(addr->shadow_message)); } @@ -868,7 +928,7 @@ else if (continue_sequence > 1) s = string_cat(s, &size, &ptr, US"*", 1); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT deliver_host_address = addr->host_used->address; deliver_host_port = addr->host_used->port; deliver_host = addr->host_used->name; @@ -890,7 +950,7 @@ else if (addr->auth_id) { s = string_append(s, &size, &ptr, 2, US":", addr->auth_id); - if (log_extra_selector & LX_smtp_mailauth && addr->auth_sndr) + if (LOGGING(smtp_mailauth) && addr->auth_sndr) s = string_append(s, &size, &ptr, 2, US":", addr->auth_sndr); } } @@ -903,15 +963,17 @@ else /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ -if (log_extra_selector & LX_smtp_confirmation && - addr->message && - (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0)) +if ( LOGGING(smtp_confirmation) + && addr->message + && (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0) + ) { - int i; + unsigned i; + unsigned lim = big_buffer_size < 1024 ? big_buffer_size : 1024; uschar *p = big_buffer; uschar *ss = addr->message; *p++ = '\"'; - for (i = 0; i < 256 && ss[i] != 0; i++) /* limit logged amount */ + for (i = 0; i < lim && ss[i] != 0; i++) /* limit logged amount */ { if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */ *p++ = ss[i]; @@ -923,11 +985,11 @@ if (log_extra_selector & LX_smtp_confirmation && /* Time on queue and actual time taken to deliver */ -if ((log_extra_selector & LX_queue_time) != 0) +if (LOGGING(queue_time)) s = string_append(s, &size, &ptr, 2, US" QT=", readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); -if ((log_extra_selector & LX_deliver_time) != 0) +if (LOGGING(deliver_time)) s = string_append(s, &size, &ptr, 2, US" DT=", readconf_printtime(addr->more_errno)); @@ -937,7 +999,7 @@ store we used to build the line after writing it. */ s[ptr] = 0; log_write(0, flags, "%s", s); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (!msg) msg_event_raise(US"msg:delivery", addr); #endif @@ -986,7 +1048,7 @@ transport has disabled it. */ if (driver_type == DTYPE_TRANSPORT) { - if (addr->transport != NULL) + if (addr->transport) { driver_name = addr->transport->name; driver_kind = US" transport"; @@ -996,7 +1058,7 @@ if (driver_type == DTYPE_TRANSPORT) } else if (driver_type == DTYPE_ROUTER) { - if (addr->router != NULL) + if (addr->router) { driver_name = addr->router->name; driver_kind = US" router"; @@ -1012,22 +1074,24 @@ expansion item that has a password setting, and flatten the password. This is a fudge, but I don't know a cleaner way of doing this. (If the item is badly malformed, it won't ever have gone near LDAP.) */ -if (addr->message != NULL) +if (addr->message) { - addr->message = string_printing(addr->message); - if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) && - (Ustrstr(addr->message, "mysql") != NULL || - Ustrstr(addr->message, "pgsql") != NULL || -#ifdef EXPERIMENTAL_REDIS - Ustrstr(addr->message, "redis") != NULL || -#endif - Ustrstr(addr->message, "sqlite") != NULL || - Ustrstr(addr->message, "ldap:") != NULL || - Ustrstr(addr->message, "ldapdn:") != NULL || - Ustrstr(addr->message, "ldapm:") != NULL)) - { - addr->message = string_sprintf("Temporary internal error"); - } + const uschar * s = string_printing(addr->message); + if (s != addr->message) + addr->message = US s; + /* deconst cast ok as string_printing known to have alloc'n'copied */ + if ( ( Ustrstr(s, "failed to expand") != NULL + || Ustrstr(s, "expansion of ") != NULL + ) + && ( Ustrstr(s, "mysql") != NULL + || Ustrstr(s, "pgsql") != NULL + || Ustrstr(s, "redis") != NULL + || Ustrstr(s, "sqlite") != NULL + || Ustrstr(s, "ldap:") != NULL + || Ustrstr(s, "ldapdn:") != NULL + || Ustrstr(s, "ldapm:") != NULL + ) ) + addr->message = string_sprintf("Temporary internal error"); } /* If we used a transport that has one of the "return_output" options set, and @@ -1042,7 +1106,7 @@ on a non-empty file. In any case, we close the message file, because we cannot afford to leave a file-descriptor for one address while processing (maybe very many) others. */ -if (addr->return_file >= 0 && addr->return_filename != NULL) +if (addr->return_file >= 0 && addr->return_filename) { BOOL return_output = FALSE; struct stat statbuf; @@ -1056,46 +1120,44 @@ if (addr->return_file >= 0 && addr->return_filename != NULL) /* Handle logging options */ - if (tb->log_output || (result == FAIL && tb->log_fail_output) || - (result == DEFER && tb->log_defer_output)) + if ( tb->log_output + || result == FAIL && tb->log_fail_output + || result == DEFER && tb->log_defer_output + ) { uschar *s; FILE *f = Ufopen(addr->return_filename, "rb"); - if (f == NULL) + if (!f) log_write(0, LOG_MAIN|LOG_PANIC, "failed to open %s to log output " "from %s transport: %s", addr->return_filename, tb->name, strerror(errno)); else - { - s = US Ufgets(big_buffer, big_buffer_size, f); - if (s != NULL) + if ((s = US Ufgets(big_buffer, big_buffer_size, f))) { uschar *p = big_buffer + Ustrlen(big_buffer); + const uschar * sp; while (p > big_buffer && isspace(p[-1])) p--; *p = 0; - s = string_printing(big_buffer); + sp = string_printing(big_buffer); log_write(0, LOG_MAIN, "<%s>: %s transport output: %s", - addr->address, tb->name, s); + addr->address, tb->name, sp); } (void)fclose(f); - } } /* Handle returning options, but only if there is an address to return the text to. */ - if (sender_address[0] != 0 || addr->p.errors_address != NULL) - { + if (sender_address[0] != 0 || addr->prop.errors_address) if (tb->return_output) { addr->transport_return = result = FAIL; - if (addr->basic_errno == 0 && addr->message == NULL) + if (addr->basic_errno == 0 && !addr->message) addr->message = US"return message generated"; return_output = TRUE; } else if (tb->return_fail_output && result == FAIL) return_output = TRUE; - } } /* Get rid of the file unless it might be returned, but close it in @@ -1126,7 +1188,7 @@ if (result == OK) address_done(addr, now); DEBUG(D_deliver) debug_printf("%s delivered\n", addr->address); - if (addr->parent == NULL) + if (!addr->parent) deliver_msglog("%s %s: %s%s succeeded\n", now, addr->address, driver_name, driver_kind); else @@ -1154,16 +1216,8 @@ if (result == OK) delivery_log(LOG_MAIN, addr, logchar, NULL); #ifdef SUPPORT_TLS - if (tls_out.ourcert) - { - tls_free_cert(tls_out.ourcert); - tls_out.ourcert = NULL; - } - if (tls_out.peercert) - { - tls_free_cert(tls_out.peercert); - tls_out.peercert = NULL; - } + tls_free_cert(&tls_out.ourcert); + tls_free_cert(&tls_out.peercert); tls_out.cipher = NULL; tls_out.peerdn = NULL; tls_out.ocsp = OCSP_NOT_REQ; @@ -1211,8 +1265,8 @@ else if (result == DEFER || result == PANIC) of error number is negative, and all the retry ones are less than any others. */ - unsigned int use_log_selector = (addr->basic_errno <= ERRNO_RETRY_BASE)? - L_retry_defer : 0; + unsigned int use_log_selector = addr->basic_errno <= ERRNO_RETRY_BASE + ? L_retry_defer : 0; /* Build up the line that is used for both the message log and the main log. */ @@ -1222,8 +1276,7 @@ else if (result == DEFER || result == PANIC) /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ - log_address = string_log_address(addr, - (log_write_selector & L_all_parents) != 0, result == OK); + log_address = string_log_address(addr, LOGGING(all_parents), result == OK); s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); @@ -1233,19 +1286,16 @@ else if (result == DEFER || result == PANIC) space, if all routing has been deferred. When a domain has been held, so nothing has been done at all, both variables contain null strings. */ - if (driver_name == NULL) + if (driver_name) { - if (driver_kind != NULL) - s = string_append(s, &size, &ptr, 2, US" ", driver_kind); - } - else - { - if (driver_kind[1] == 't' && addr->router != NULL) + if (driver_kind[1] == 't' && addr->router) s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); Ustrcpy(ss, " ?="); ss[1] = toupper(driver_kind[1]); s = string_append(s, &size, &ptr, 2, ss, driver_name); } + else if (driver_kind) + s = string_append(s, &size, &ptr, 2, US" ", driver_kind); sprintf(CS ss, " defer (%d)", addr->basic_errno); s = string_cat(s, &size, &ptr, ss, Ustrlen(ss)); @@ -1255,11 +1305,19 @@ else if (result == DEFER || result == PANIC) US strerror(addr->basic_errno)); if (addr->host_used) + { s = string_append(s, &size, &ptr, 5, US" H=", addr->host_used->name, US" [", addr->host_used->address, US"]"); + if (LOGGING(outgoing_port)) + { + int port = addr->host_used->port; + s = string_append(s, &size, &ptr, 2, + US":", port == PORT_NONE ? US"25" : string_sprintf("%d", port)); + } + } - if (addr->message != NULL) + if (addr->message) s = string_append(s, &size, &ptr, 2, US": ", addr->message); s[ptr] = 0; @@ -1299,14 +1357,16 @@ else to ignore occurs later, instead of sending a message. Logging of freezing occurs later, just before writing the -H file. */ - if (!testflag(addr, af_ignore_error) && - (addr->special_action == SPECIAL_FREEZE || - (sender_address[0] == 0 && addr->p.errors_address == NULL) - )) + if ( !testflag(addr, af_ignore_error) + && ( addr->special_action == SPECIAL_FREEZE + || (sender_address[0] == 0 && !addr->prop.errors_address) + ) ) { - frozen_info = (addr->special_action == SPECIAL_FREEZE)? US"" : - (sender_local && !local_error_message)? - US" (message created with -f <>)" : US" (delivery error message)"; + frozen_info = addr->special_action == SPECIAL_FREEZE + ? US"" + : sender_local && !local_error_message + ? US" (message created with -f <>)" + : US" (delivery error message)"; deliver_freeze = TRUE; deliver_frozen_at = time(NULL); update_spool = TRUE; @@ -1334,26 +1394,24 @@ else /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ - log_address = string_log_address(addr, - (log_write_selector & L_all_parents) != 0, result == OK); + log_address = string_log_address(addr, LOGGING(all_parents), result == OK); s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); - if ((log_extra_selector & LX_sender_on_delivery) != 0) + if (LOGGING(sender_on_delivery)) s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">"); /* Return path may not be set if no delivery actually happened */ - if (used_return_path != NULL && - (log_extra_selector & LX_return_path_on_delivery) != 0) + if (used_return_path && LOGGING(return_path_on_delivery)) s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); - if (addr->router != NULL) + if (addr->router) s = string_append(s, &size, &ptr, 2, US" R=", addr->router->name); - if (addr->transport != NULL) + if (addr->transport) s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); - if (addr->host_used != NULL) + if (addr->host_used) s = d_hostlog(s, &size, &ptr, addr); #ifdef SUPPORT_TLS @@ -1364,7 +1422,7 @@ else s = string_append(s, &size, &ptr, 2, US": ", US strerror(addr->basic_errno)); - if (addr->message != NULL) + if (addr->message) s = string_append(s, &size, &ptr, 2, US": ", addr->message); s[ptr] = 0; @@ -1372,14 +1430,14 @@ else /* Do the logging. For the message log, "routing failed" for those cases, just to make it clearer. */ - if (driver_name == NULL) - deliver_msglog("%s %s failed for %s\n", now, driver_kind, s); - else + if (driver_name) deliver_msglog("%s %s\n", now, s); + else + deliver_msglog("%s %s failed for %s\n", now, driver_kind, s); log_write(0, LOG_MAIN, "** %s", s); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT msg_event_raise(US"msg:fail:delivery", addr); #endif @@ -1420,7 +1478,7 @@ common_error(BOOL logit, address_item *addr, int code, uschar *format, ...) address_item *addr2; addr->basic_errno = code; -if (format != NULL) +if (format) { va_list ap; uschar buffer[512]; @@ -1432,7 +1490,7 @@ if (format != NULL) addr->message = string_copy(buffer); } -for (addr2 = addr->next; addr2 != NULL; addr2 = addr2->next) +for (addr2 = addr->next; addr2; addr2 = addr2->next) { addr2->basic_errno = code; addr2->message = addr->message; @@ -1463,7 +1521,7 @@ static BOOL check_never_users(uid_t uid, uid_t *nusers) { int i; -if (nusers == NULL) return FALSE; +if (!nusers) return FALSE; for (i = 1; i <= (int)(nusers[0]); i++) if (nusers[i] == uid) return TRUE; return FALSE; } @@ -1496,7 +1554,7 @@ static BOOL findugid(address_item *addr, transport_instance *tp, uid_t *uidp, gid_t *gidp, BOOL *igfp) { -uschar *nuname = NULL; +uschar *nuname; BOOL gid_set = FALSE; /* Default initgroups flag comes from the transport */ @@ -1511,15 +1569,15 @@ if (tp->gid_set) *gidp = tp->gid; gid_set = TRUE; } -else if (tp->expand_gid != NULL) +else if (tp->expand_gid) { - if (route_find_expanded_group(tp->expand_gid, tp->name, US"transport", gidp, - &(addr->message))) gid_set = TRUE; - else + if (!route_find_expanded_group(tp->expand_gid, tp->name, US"transport", gidp, + &(addr->message))) { common_error(FALSE, addr, ERRNO_GIDFAIL, NULL); return FALSE; } + gid_set = TRUE; } /* If the transport did not set a group, see if the router did. */ @@ -1537,7 +1595,7 @@ if (tp->uid_set) *uidp = tp->uid; /* Otherwise, try for an expandable uid field. If it ends up as a numeric id, it does not provide a passwd value from which a gid can be taken. */ -else if (tp->expand_uid != NULL) +else if (tp->expand_uid) { struct passwd *pw; if (!route_find_expanded_user(tp->expand_uid, tp->name, US"transport", &pw, @@ -1546,7 +1604,7 @@ else if (tp->expand_uid != NULL) common_error(FALSE, addr, ERRNO_UIDFAIL, NULL); return FALSE; } - if (!gid_set && pw != NULL) + if (!gid_set && pw) { *gidp = pw->pw_gid; gid_set = TRUE; @@ -1601,12 +1659,12 @@ if (!gid_set) /* Check that the uid is not on the lists of banned uids that may not be used for delivery processes. */ -if (check_never_users(*uidp, never_users)) - nuname = US"never_users"; -else if (check_never_users(*uidp, fixed_never_users)) - nuname = US"fixed_never_users"; - -if (nuname != NULL) +nuname = check_never_users(*uidp, never_users) + ? US"never_users" + : check_never_users(*uidp, fixed_never_users) + ? US"fixed_never_users" + : NULL; +if (nuname) { common_error(TRUE, addr, ERRNO_UIDFAIL, US"User %ld set for %s transport " "is on the %s list", (long int)(*uidp), tp->name, nuname); @@ -1647,14 +1705,13 @@ deliver_set_expansions(addr); size_limit = expand_string_integer(tp->message_size_limit, TRUE); deliver_set_expansions(NULL); -if (expand_string_message != NULL) +if (expand_string_message) { rc = DEFER; - if (size_limit == -1) - addr->message = string_sprintf("failed to expand message_size_limit " - "in %s transport: %s", tp->name, expand_string_message); - else - addr->message = string_sprintf("invalid message_size_limit " + addr->message = size_limit == -1 + ? string_sprintf("failed to expand message_size_limit " + "in %s transport: %s", tp->name, expand_string_message) + : string_sprintf("invalid message_size_limit " "in %s transport: %s", tp->name, expand_string_message); } else if (size_limit > 0 && message_size > size_limit) @@ -1801,19 +1858,19 @@ transport_instance *tp = addr->transport; /* Set up the return path from the errors or sender address. If the transport has its own return path setting, expand it and replace the existing value. */ -if(addr->p.errors_address != NULL) - return_path = addr->p.errors_address; +if(addr->prop.errors_address) + return_path = addr->prop.errors_address; #ifdef EXPERIMENTAL_SRS -else if(addr->p.srs_sender != NULL) - return_path = addr->p.srs_sender; +else if (addr->prop.srs_sender) + return_path = addr->prop.srs_sender; #endif else return_path = sender_address; -if (tp->return_path != NULL) +if (tp->return_path) { uschar *new_return_path = expand_string(tp->return_path); - if (new_return_path == NULL) + if (!new_return_path) { if (!expand_string_forcedfail) { @@ -1841,14 +1898,14 @@ if (!findugid(addr, tp, &uid, &gid, &use_initgroups)) return; home directory set in the address may already be expanded; a flag is set to indicate that. In other cases we must expand it. */ -if ((deliver_home = tp->home_dir) != NULL || /* Set in transport, or */ - ((deliver_home = addr->home_dir) != NULL && /* Set in address and */ - !testflag(addr, af_home_expanded))) /* not expanded */ +if ( (deliver_home = tp->home_dir) /* Set in transport, or */ + || ( (deliver_home = addr->home_dir) /* Set in address and */ + && !testflag(addr, af_home_expanded) /* not expanded */ + ) ) { uschar *rawhome = deliver_home; deliver_home = NULL; /* in case it contains $home */ - deliver_home = expand_string(rawhome); - if (deliver_home == NULL) + if (!(deliver_home = expand_string(rawhome))) { common_error(TRUE, addr, ERRNO_EXPANDFAIL, US"home directory \"%s\" failed " "to expand for %s transport: %s", rawhome, tp->name, @@ -1870,14 +1927,11 @@ all users have access. It is necessary to be in a visible directory for some operating systems when running pipes, as some commands (e.g. "rm" under Solaris 2.5) require this. */ -working_directory = (tp->current_dir != NULL)? - tp->current_dir : addr->current_dir; - -if (working_directory != NULL) +working_directory = tp->current_dir ? tp->current_dir : addr->current_dir; +if (working_directory) { uschar *raw = working_directory; - working_directory = expand_string(raw); - if (working_directory == NULL) + if (!(working_directory = expand_string(raw))) { common_error(TRUE, addr, ERRNO_EXPANDFAIL, US"current directory \"%s\" " "failed to expand for %s transport: %s", raw, tp->name, @@ -1891,15 +1945,17 @@ if (working_directory != NULL) return; } } -else working_directory = (deliver_home == NULL)? US"/" : deliver_home; +else working_directory = deliver_home ? deliver_home : US"/"; /* If one of the return_output flags is set on the transport, create and open a file in the message log directory for the transport to write its output onto. This is mainly used by pipe transports. The file needs to be unique to the address. This feature is not available for shadow transports. */ -if (!shadowing && (tp->return_output || tp->return_fail_output || - tp->log_output || tp->log_fail_output)) +if ( !shadowing + && ( tp->return_output || tp->return_fail_output + || tp->log_output || tp->log_fail_output || tp->log_defer_output + ) ) { uschar *error; addr->return_filename = @@ -1975,20 +2031,18 @@ if ((pid = fork()) == 0) privileged. (Appendfile uses this to expand quota, for example, while able to read private files.) */ - if (addr->transport->setup != NULL) - { + if (addr->transport->setup) switch((addr->transport->setup)(addr->transport, addr, NULL, uid, gid, &(addr->message))) { case DEFER: - addr->transport_return = DEFER; - goto PASS_BACK; + addr->transport_return = DEFER; + goto PASS_BACK; case FAIL: - addr->transport_return = PANIC; - goto PASS_BACK; + addr->transport_return = PANIC; + goto PASS_BACK; } - } /* Ignore SIGINT and SIGTERM during delivery. Also ignore SIGUSR1, as when the process becomes unprivileged, it won't be able to write to the @@ -2014,7 +2068,7 @@ if ((pid = fork()) == 0) { address_item *batched; debug_printf(" home=%s current=%s\n", deliver_home, working_directory); - for (batched = addr->next; batched != NULL; batched = batched->next) + for (batched = addr->next; batched; batched = batched->next) debug_printf("additional batched address: %s\n", batched->address); } @@ -2041,7 +2095,7 @@ if ((pid = fork()) == 0) /* If a transport filter has been specified, set up its argument list. Any errors will get put into the address, and FALSE yielded. */ - if (addr->transport->filter_command != NULL) + if (addr->transport->filter_command) { ok = transport_set_up_command(&transport_filter_argv, addr->transport->filter_command, @@ -2066,20 +2120,20 @@ if ((pid = fork()) == 0) PASS_BACK: if (replicate) replicate_status(addr); - for (addr2 = addr; addr2 != NULL; addr2 = addr2->next) + for (addr2 = addr; addr2; addr2 = addr2->next) { int i; int local_part_length = Ustrlen(addr2->local_part); uschar *s; int ret; - if( (ret = write(pfd[pipe_write], (void *)&(addr2->transport_return), sizeof(int))) != sizeof(int) - || (ret = write(pfd[pipe_write], (void *)&transport_count, sizeof(transport_count))) != sizeof(transport_count) - || (ret = write(pfd[pipe_write], (void *)&(addr2->flags), sizeof(addr2->flags))) != sizeof(addr2->flags) - || (ret = write(pfd[pipe_write], (void *)&(addr2->basic_errno), sizeof(int))) != sizeof(int) - || (ret = write(pfd[pipe_write], (void *)&(addr2->more_errno), sizeof(int))) != sizeof(int) - || (ret = write(pfd[pipe_write], (void *)&(addr2->special_action), sizeof(int))) != sizeof(int) - || (ret = write(pfd[pipe_write], (void *)&(addr2->transport), + if( (ret = write(pfd[pipe_write], &addr2->transport_return, sizeof(int))) != sizeof(int) + || (ret = write(pfd[pipe_write], &transport_count, sizeof(transport_count))) != sizeof(transport_count) + || (ret = write(pfd[pipe_write], &addr2->flags, sizeof(addr2->flags))) != sizeof(addr2->flags) + || (ret = write(pfd[pipe_write], &addr2->basic_errno, sizeof(int))) != sizeof(int) + || (ret = write(pfd[pipe_write], &addr2->more_errno, sizeof(int))) != sizeof(int) + || (ret = write(pfd[pipe_write], &addr2->special_action, sizeof(int))) != sizeof(int) + || (ret = write(pfd[pipe_write], &addr2->transport, sizeof(transport_instance *))) != sizeof(transport_instance *) /* For a file delivery, pass back the local part, in case the original @@ -2087,7 +2141,7 @@ if ((pid = fork()) == 0) logging. */ || (testflag(addr2, af_file) - && ( (ret = write(pfd[pipe_write], (void *)&local_part_length, sizeof(int))) != sizeof(int) + && ( (ret = write(pfd[pipe_write], &local_part_length, sizeof(int))) != sizeof(int) || (ret = write(pfd[pipe_write], addr2->local_part, local_part_length)) != local_part_length ) ) @@ -2099,9 +2153,9 @@ if ((pid = fork()) == 0) for (i = 0, s = addr2->message; i < 2; i++, s = addr2->user_message) { - int message_length = (s == NULL)? 0 : Ustrlen(s) + 1; - if( (ret = write(pfd[pipe_write], (void *)&message_length, sizeof(int))) != sizeof(int) - || (message_length > 0 && (ret = write(pfd[pipe_write], s, message_length)) != message_length) + int message_length = s ? Ustrlen(s) + 1 : 0; + if( (ret = write(pfd[pipe_write], &message_length, sizeof(int))) != sizeof(int) + || message_length > 0 && (ret = write(pfd[pipe_write], s, message_length)) != message_length ) log_write(0, LOG_MAIN|LOG_PANIC, "Failed writing transport results to pipe: %s\n", ret == -1 ? strerror(errno) : "short write"); @@ -2132,41 +2186,40 @@ will remain. Afterwards, close the reading end. */ (void)close(pfd[pipe_write]); -for (addr2 = addr; addr2 != NULL; addr2 = addr2->next) +for (addr2 = addr; addr2; addr2 = addr2->next) { - len = read(pfd[pipe_read], (void *)&status, sizeof(int)); + len = read(pfd[pipe_read], &status, sizeof(int)); if (len > 0) { int i; uschar **sptr; addr2->transport_return = status; - len = read(pfd[pipe_read], (void *)&transport_count, + len = read(pfd[pipe_read], &transport_count, sizeof(transport_count)); - len = read(pfd[pipe_read], (void *)&(addr2->flags), sizeof(addr2->flags)); - len = read(pfd[pipe_read], (void *)&(addr2->basic_errno), sizeof(int)); - len = read(pfd[pipe_read], (void *)&(addr2->more_errno), sizeof(int)); - len = read(pfd[pipe_read], (void *)&(addr2->special_action), sizeof(int)); - len = read(pfd[pipe_read], (void *)&(addr2->transport), + len = read(pfd[pipe_read], &addr2->flags, sizeof(addr2->flags)); + len = read(pfd[pipe_read], &addr2->basic_errno, sizeof(int)); + len = read(pfd[pipe_read], &addr2->more_errno, sizeof(int)); + len = read(pfd[pipe_read], &addr2->special_action, sizeof(int)); + len = read(pfd[pipe_read], &addr2->transport, sizeof(transport_instance *)); if (testflag(addr2, af_file)) { int local_part_length; - len = read(pfd[pipe_read], (void *)&local_part_length, sizeof(int)); - len = read(pfd[pipe_read], (void *)big_buffer, local_part_length); + len = read(pfd[pipe_read], &local_part_length, sizeof(int)); + len = read(pfd[pipe_read], big_buffer, local_part_length); big_buffer[local_part_length] = 0; addr2->local_part = string_copy(big_buffer); } - for (i = 0, sptr = &(addr2->message); i < 2; - i++, sptr = &(addr2->user_message)) + for (i = 0, sptr = &addr2->message; i < 2; i++, sptr = &addr2->user_message) { int message_length; - len = read(pfd[pipe_read], (void *)&message_length, sizeof(int)); + len = read(pfd[pipe_read], &message_length, sizeof(int)); if (message_length > 0) { - len = read(pfd[pipe_read], (void *)big_buffer, message_length); + len = read(pfd[pipe_read], big_buffer, message_length); if (len > 0) *sptr = string_copy(big_buffer); } } @@ -2190,26 +2243,25 @@ in order to record the delivery. */ if (!shadowing) { - for (addr2 = addr; addr2 != NULL; addr2 = addr2->next) - { - if (addr2->transport_return != OK) continue; - - if (testflag(addr2, af_homonym)) - sprintf(CS big_buffer, "%.500s/%s\n", addr2->unique + 3, tp->name); - else - sprintf(CS big_buffer, "%.500s\n", addr2->unique); + for (addr2 = addr; addr2; addr2 = addr2->next) + if (addr2->transport_return == OK) + { + if (testflag(addr2, af_homonym)) + sprintf(CS big_buffer, "%.500s/%s\n", addr2->unique + 3, tp->name); + else + sprintf(CS big_buffer, "%.500s\n", addr2->unique); - /* In the test harness, wait just a bit to let the subprocess finish off - any debug output etc first. */ + /* In the test harness, wait just a bit to let the subprocess finish off + any debug output etc first. */ - if (running_in_test_harness) millisleep(300); + if (running_in_test_harness) millisleep(300); - DEBUG(D_deliver) debug_printf("journalling %s", big_buffer); - len = Ustrlen(big_buffer); - if (write(journal_fd, big_buffer, len) != len) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to update journal for %s: %s", - big_buffer, strerror(errno)); - } + DEBUG(D_deliver) debug_printf("journalling %s", big_buffer); + len = Ustrlen(big_buffer); + if (write(journal_fd, big_buffer, len) != len) + log_write(0, LOG_MAIN|LOG_PANIC, "failed to update journal for %s: %s", + big_buffer, strerror(errno)); + } /* Ensure the journal file is pushed out to disk. */ @@ -2227,7 +2279,6 @@ happens, wait() doesn't recognize the termination of child processes. Exim now resets SIGCHLD to SIG_DFL, but this code should still be robust. */ while ((rc = wait(&status)) != pid) - { if (rc < 0 && errno == ECHILD) /* Process has vanished */ { log_write(0, LOG_MAIN, "%s transport process vanished unexpectedly", @@ -2235,7 +2286,6 @@ while ((rc = wait(&status)) != pid) status = 0; break; } - } if ((status & 0xffff) != 0) { @@ -2248,43 +2298,39 @@ if ((status & 0xffff) != 0) "status 0x%04x: %s %d", addr->transport->driver_name, status, - (msb == 0)? "terminated by signal" : "exit code", + msb == 0 ? "terminated by signal" : "exit code", code); } /* If SPECIAL_WARN is set in the top address, send a warning message. */ -if (addr->special_action == SPECIAL_WARN && - addr->transport->warn_message != NULL) +if (addr->special_action == SPECIAL_WARN && addr->transport->warn_message) { int fd; uschar *warn_message; + pid_t pid; DEBUG(D_deliver) debug_printf("Warning message requested by transport\n"); - warn_message = expand_string(addr->transport->warn_message); - if (warn_message == NULL) + if (!(warn_message = expand_string(addr->transport->warn_message))) log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand \"%s\" (warning " "message for %s transport): %s", addr->transport->warn_message, addr->transport->name, expand_string_message); - else + + else if ((pid = child_open_exim(&fd)) > 0) { - pid_t pid = child_open_exim(&fd); - if (pid > 0) - { - FILE *f = fdopen(fd, "wb"); - if (errors_reply_to != NULL && - !contains_header(US"Reply-To", warn_message)) - fprintf(f, "Reply-To: %s\n", errors_reply_to); - fprintf(f, "Auto-Submitted: auto-replied\n"); - if (!contains_header(US"From", warn_message)) moan_write_from(f); - fprintf(f, "%s", CS warn_message); + FILE *f = fdopen(fd, "wb"); + if (errors_reply_to && !contains_header(US"Reply-To", warn_message)) + fprintf(f, "Reply-To: %s\n", errors_reply_to); + fprintf(f, "Auto-Submitted: auto-replied\n"); + if (!contains_header(US"From", warn_message)) + moan_write_from(f); + fprintf(f, "%s", CS warn_message); - /* Close and wait for child process to complete, without a timeout. */ + /* Close and wait for child process to complete, without a timeout. */ - (void)fclose(f); - (void)child_close(pid, 0); - } + (void)fclose(f); + (void)child_close(pid, 0); } addr->special_action = SPECIAL_NONE; @@ -2293,6 +2339,52 @@ if (addr->special_action == SPECIAL_WARN && + +/* Check transport for the given concurrency limit. Return TRUE if over +the limit (or an expansion failure), else FALSE and if there was a limit, +the key for the hints database used for the concurrency count. */ + +static BOOL +tpt_parallel_check(transport_instance * tp, address_item * addr, uschar ** key) +{ +unsigned max_parallel; + +if (!tp->max_parallel) return FALSE; + +max_parallel = (unsigned) expand_string_integer(tp->max_parallel, TRUE); +if (expand_string_message) + { + log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand max_parallel option " + "in %s transport (%s): %s", tp->name, addr->address, + expand_string_message); + return TRUE; + } + +if (max_parallel > 0) + { + uschar * serialize_key = string_sprintf("tpt-serialize-%s", tp->name); + if (!enq_start(serialize_key, max_parallel)) + { + address_item * next; + DEBUG(D_transport) + debug_printf("skipping tpt %s because concurrency limit %u reached\n", + tp->name, max_parallel); + do + { + next = addr->next; + addr->message = US"concurrency limit reached for transport"; + addr->basic_errno = ERRNO_TRETRY; + post_process_one(addr, DEFER, LOG_MAIN, DTYPE_TRANSPORT, 0); + } while ((addr = next)); + return TRUE; + } + *key = serialize_key; + } +return FALSE; +} + + + /************************************************* * Do local deliveries * *************************************************/ @@ -2316,7 +2408,7 @@ time_t now = time(NULL); /* Loop until we have exhausted the supply of local deliveries */ -while (addr_local != NULL) +while (addr_local) { time_t delivery_start; int deliver_time; @@ -2324,6 +2416,7 @@ while (addr_local != NULL) int logflags = LOG_MAIN; int logchar = dont_deliver? '*' : '='; transport_instance *tp; + uschar * serialize_key = NULL; /* Pick the first undelivered address off the chain */ @@ -2336,15 +2429,13 @@ while (addr_local != NULL) /* An internal disaster if there is no transport. Should not occur! */ - if ((tp = addr->transport) == NULL) + if (!(tp = addr->transport)) { logflags |= LOG_PANIC; disable_logging = FALSE; /* Jic */ - addr->message = - (addr->router != NULL)? - string_sprintf("No transport set by %s router", addr->router->name) - : - string_sprintf("No transport set by system filter"); + addr->message = addr->router + ? string_sprintf("No transport set by %s router", addr->router->name) + : string_sprintf("No transport set by system filter"); post_process_one(addr, DEFER, logflags, DTYPE_TRANSPORT, 0); continue; } @@ -2365,13 +2456,14 @@ while (addr_local != NULL) if either batch_max <= 1 or there aren't any other addresses for local delivery. */ - if (tp->batch_max > 1 && addr_local != NULL) + if (tp->batch_max > 1 && addr_local) { int batch_count = 1; BOOL uses_dom = readconf_depends((driver_instance *)tp, US"domain"); - BOOL uses_lp = (testflag(addr, af_pfr) && - (testflag(addr, af_file) || addr->local_part[0] == '|')) || - readconf_depends((driver_instance *)tp, US"local_part"); + BOOL uses_lp = ( testflag(addr, af_pfr) + && (testflag(addr, af_file) || addr->local_part[0] == '|') + ) + || readconf_depends((driver_instance *)tp, US"local_part"); uschar *batch_id = NULL; address_item **anchor = &addr_local; address_item *last = addr; @@ -2380,12 +2472,12 @@ while (addr_local != NULL) /* Expand the batch_id string for comparison with other addresses. Expansion failure suppresses batching. */ - if (tp->batch_id != NULL) + if (tp->batch_id) { deliver_set_expansions(addr); batch_id = expand_string(tp->batch_id); deliver_set_expansions(NULL); - if (batch_id == NULL) + if (!batch_id) { log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand batch_id option " "in %s transport (%s): %s", tp->name, addr->address, @@ -2409,27 +2501,29 @@ while (addr_local != NULL) same first host if a host list is set */ - while ((next = *anchor) != NULL && batch_count < tp->batch_max) + while ((next = *anchor) && batch_count < tp->batch_max) { BOOL ok = - tp == next->transport && - !previously_transported(next, TRUE) && - (addr->flags & (af_pfr|af_file)) == (next->flags & (af_pfr|af_file)) && - (!uses_lp || Ustrcmp(next->local_part, addr->local_part) == 0) && - (!uses_dom || Ustrcmp(next->domain, addr->domain) == 0) && - same_strings(next->p.errors_address, addr->p.errors_address) && - same_headers(next->p.extra_headers, addr->p.extra_headers) && - same_strings(next->p.remove_headers, addr->p.remove_headers) && - same_ugid(tp, addr, next) && - ((addr->host_list == NULL && next->host_list == NULL) || - (addr->host_list != NULL && next->host_list != NULL && - Ustrcmp(addr->host_list->name, next->host_list->name) == 0)); + tp == next->transport + && !previously_transported(next, TRUE) + && (addr->flags & (af_pfr|af_file)) == (next->flags & (af_pfr|af_file)) + && (!uses_lp || Ustrcmp(next->local_part, addr->local_part) == 0) + && (!uses_dom || Ustrcmp(next->domain, addr->domain) == 0) + && same_strings(next->prop.errors_address, addr->prop.errors_address) + && same_headers(next->prop.extra_headers, addr->prop.extra_headers) + && same_strings(next->prop.remove_headers, addr->prop.remove_headers) + && same_ugid(tp, addr, next) + && ( !addr->host_list && !next->host_list + || addr->host_list + && next->host_list + && Ustrcmp(addr->host_list->name, next->host_list->name) == 0 + ); /* If the transport has a batch_id setting, batch_id will be non-NULL from the expansion outside the loop. Expand for this address and compare. Expansion failure makes this address ineligible for batching. */ - if (ok && batch_id != NULL) + if (ok && batch_id) { uschar *bid; address_item *save_nextnext = next->next; @@ -2438,7 +2532,7 @@ while (addr_local != NULL) next->next = save_nextnext; bid = expand_string(tp->batch_id); deliver_set_expansions(NULL); - if (bid == NULL) + if (!bid) { log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand batch_id option " "in %s transport (%s): %s", tp->name, next->address, @@ -2458,7 +2552,7 @@ while (addr_local != NULL) last = next; batch_count++; } - else anchor = &(next->next); /* Skip the address */ + else anchor = &next->next; /* Skip the address */ } } @@ -2467,13 +2561,13 @@ while (addr_local != NULL) fail them all forthwith. If the expansion fails, or does not yield an integer, defer delivery. */ - if (tp->message_size_limit != NULL) + if (tp->message_size_limit) { int rc = check_message_size(tp, addr); if (rc != OK) { replicate_status(addr); - while (addr != NULL) + while (addr) { addr2 = addr->next; post_process_one(addr, rc, logflags, DTYPE_TRANSPORT, 0); @@ -2491,8 +2585,7 @@ while (addr_local != NULL) of these checks, rather than for all local deliveries, because some local deliveries (e.g. to pipes) can take a substantial time. */ - dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE); - if (dbm_file == NULL) + if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) { DEBUG(D_deliver|D_retry|D_hints_lookup) debug_printf("no retry data available\n"); @@ -2500,7 +2593,7 @@ while (addr_local != NULL) addr2 = addr; addr3 = NULL; - while (addr2 != NULL) + while (addr2) { BOOL ok = TRUE; /* to deliver this address */ uschar *retry_key; @@ -2511,20 +2604,20 @@ while (addr_local != NULL) a routing delay. */ retry_key = string_copy( - (tp->retry_use_local_part)? addr2->address_retry_key : + tp->retry_use_local_part ? addr2->address_retry_key : addr2->domain_retry_key); *retry_key = 'T'; /* Inspect the retry data. If there is no hints file, delivery happens. */ - if (dbm_file != NULL) + if (dbm_file) { dbdata_retry *retry_record = dbfn_read(dbm_file, retry_key); /* If there is no retry record, delivery happens. If there is, remember it exists so it can be deleted after a successful delivery. */ - if (retry_record != NULL) + if (retry_record) { setflag(addr2, af_lt_retry_exists); @@ -2545,9 +2638,9 @@ while (addr_local != NULL) if (queue_running && !deliver_force) { - ok = (now - retry_record->time_stamp > retry_data_expire) || - (now >= retry_record->next_try) || - retry_record->expired; + ok = (now - retry_record->time_stamp > retry_data_expire) + || (now >= retry_record->next_try) + || retry_record->expired; /* If we haven't reached the retry time, there is one more check to do, which is for the ultimate address timeout. */ @@ -2577,18 +2670,37 @@ while (addr_local != NULL) address_item *this = addr2; this->message = US"Retry time not yet reached"; this->basic_errno = ERRNO_LRETRY; - if (addr3 == NULL) addr2 = addr = addr2->next; - else addr2 = addr3->next = addr2->next; + addr2 = addr3 ? (addr3->next = addr2->next) + : (addr = addr2->next); post_process_one(this, DEFER, logflags, DTYPE_TRANSPORT, 0); } } - if (dbm_file != NULL) dbfn_close(dbm_file); + if (dbm_file) dbfn_close(dbm_file); /* If there are no addresses left on the chain, they all deferred. Loop for the next set of addresses. */ - if (addr == NULL) continue; + if (!addr) continue; + + /* If the transport is limited for parallellism, enforce that here. + We use a hints DB entry, incremented here and decremented after + the transport (and any shadow transport) completes. */ + + if (tpt_parallel_check(tp, addr, &serialize_key)) + { + if (expand_string_message) + { + logflags |= LOG_PANIC; + do + { + addr = addr->next; + post_process_one(addr, DEFER, logflags, DTYPE_TRANSPORT, 0); + } while ((addr = addr2)); + } + continue; /* Loop for the next set of addresses. */ + } + /* So, finally, we do have some addresses that can be passed to the transport. Before doing so, set up variables that are relevant to a @@ -2609,18 +2721,19 @@ while (addr_local != NULL) NOTE: if the condition fails because of a lookup defer, there is nothing we can do! */ - if (tp->shadow != NULL && - (tp->shadow_condition == NULL || - expand_check_condition(tp->shadow_condition, tp->name, US"transport"))) + if ( tp->shadow + && ( !tp->shadow_condition + || expand_check_condition(tp->shadow_condition, tp->name, US"transport") + ) ) { transport_instance *stp; address_item *shadow_addr = NULL; address_item **last = &shadow_addr; - for (stp = transports; stp != NULL; stp = stp->next) + for (stp = transports; stp; stp = stp->next) if (Ustrcmp(stp->name, tp->shadow) == 0) break; - if (stp == NULL) + if (!stp) log_write(0, LOG_MAIN|LOG_PANIC, "shadow transport \"%s\" not found ", tp->shadow); @@ -2628,25 +2741,25 @@ while (addr_local != NULL) the shadow_message field a pointer to the shadow_message field of the real address. */ - else for (addr2 = addr; addr2 != NULL; addr2 = addr2->next) - { - if (addr2->transport_return != OK) continue; - addr3 = store_get(sizeof(address_item)); - *addr3 = *addr2; - addr3->next = NULL; - addr3->shadow_message = (uschar *)(&(addr2->shadow_message)); - addr3->transport = stp; - addr3->transport_return = DEFER; - addr3->return_filename = NULL; - addr3->return_file = -1; - *last = addr3; - last = &(addr3->next); - } + else for (addr2 = addr; addr2; addr2 = addr2->next) + if (addr2->transport_return == OK) + { + addr3 = store_get(sizeof(address_item)); + *addr3 = *addr2; + addr3->next = NULL; + addr3->shadow_message = (uschar *) &(addr2->shadow_message); + addr3->transport = stp; + addr3->transport_return = DEFER; + addr3->return_filename = NULL; + addr3->return_file = -1; + *last = addr3; + last = &(addr3->next); + } /* If we found any addresses to shadow, run the delivery, and stick any message back into the shadow_message field in the original. */ - if (shadow_addr != NULL) + if (shadow_addr) { int save_count = transport_count; @@ -2654,26 +2767,32 @@ while (addr_local != NULL) debug_printf(">>>>>>>>>>>>>>>> Shadow delivery >>>>>>>>>>>>>>>>\n"); deliver_local(shadow_addr, TRUE); - for(; shadow_addr != NULL; shadow_addr = shadow_addr->next) + for(; shadow_addr; shadow_addr = shadow_addr->next) { int sresult = shadow_addr->transport_return; - *((uschar **)(shadow_addr->shadow_message)) = (sresult == OK)? - string_sprintf(" ST=%s", stp->name) : - string_sprintf(" ST=%s (%s%s%s)", stp->name, - (shadow_addr->basic_errno <= 0)? - US"" : US strerror(shadow_addr->basic_errno), - (shadow_addr->basic_errno <= 0 || shadow_addr->message == NULL)? - US"" : US": ", - (shadow_addr->message != NULL)? shadow_addr->message : - (shadow_addr->basic_errno <= 0)? US"unknown error" : US""); + *(uschar **)shadow_addr->shadow_message = + sresult == OK + ? string_sprintf(" ST=%s", stp->name) + : string_sprintf(" ST=%s (%s%s%s)", stp->name, + shadow_addr->basic_errno <= 0 + ? US"" + : US strerror(shadow_addr->basic_errno), + shadow_addr->basic_errno <= 0 || !shadow_addr->message + ? US"" + : US": ", + shadow_addr->message + ? shadow_addr->message + : shadow_addr->basic_errno <= 0 + ? US"unknown error" + : US""); DEBUG(D_deliver|D_transport) debug_printf("%s shadow transport returned %s for %s\n", stp->name, - (sresult == OK)? "OK" : - (sresult == DEFER)? "DEFER" : - (sresult == FAIL)? "FAIL" : - (sresult == PANIC)? "PANIC" : "?", + sresult == OK ? "OK" : + sresult == DEFER ? "DEFER" : + sresult == FAIL ? "FAIL" : + sresult == PANIC ? "PANIC" : "?", shadow_addr->address); } @@ -2688,11 +2807,15 @@ while (addr_local != NULL) deliver_set_expansions(NULL); + /* If the transport was parallelism-limited, decrement the hints DB record. */ + + if (serialize_key) enq_end(serialize_key); + /* Now we can process the results of the real transport. We must take each address off the chain first, because post_process_one() puts it on another chain. */ - for (addr2 = addr; addr2 != NULL; addr2 = nextaddr) + for (addr2 = addr; addr2; addr2 = nextaddr) { int result = addr2->transport_return; nextaddr = addr2->next; @@ -2700,10 +2823,10 @@ while (addr_local != NULL) DEBUG(D_deliver|D_transport) debug_printf("%s transport returned %s for %s\n", tp->name, - (result == OK)? "OK" : - (result == DEFER)? "DEFER" : - (result == FAIL)? "FAIL" : - (result == PANIC)? "PANIC" : "?", + result == OK ? "OK" : + result == DEFER ? "DEFER" : + result == FAIL ? "FAIL" : + result == PANIC ? "PANIC" : "?", addr2->address); /* If there is a retry_record, or if delivery is deferred, build a retry @@ -2714,9 +2837,9 @@ while (addr_local != NULL) if (result == DEFER || testflag(addr2, af_lt_retry_exists)) { - int flags = (result == DEFER)? 0 : rf_delete; - uschar *retry_key = string_copy((tp->retry_use_local_part)? - addr2->address_retry_key : addr2->domain_retry_key); + int flags = result == DEFER ? 0 : rf_delete; + uschar *retry_key = string_copy(tp->retry_use_local_part + ? addr2->address_retry_key : addr2->domain_retry_key); *retry_key = 'T'; retry_add_item(addr2, retry_key, flags); } @@ -2732,7 +2855,7 @@ while (addr_local != NULL) if (addr2->transport_return != result) { - for (addr3 = nextaddr; addr3 != NULL; addr3 = addr3->next) + for (addr3 = nextaddr; addr3; addr3 = addr3->next) { addr3->transport_return = addr2->transport_return; addr3->basic_errno = addr2->basic_errno; @@ -2775,40 +2898,41 @@ sort_remote_deliveries(void) { int sep = 0; address_item **aptr = &addr_remote; -uschar *listptr = remote_sort_domains; +const uschar *listptr = remote_sort_domains; uschar *pattern; uschar patbuf[256]; -while (*aptr != NULL && - (pattern = string_nextinlist(&listptr, &sep, patbuf, sizeof(patbuf))) - != NULL) +while ( *aptr + && (pattern = string_nextinlist(&listptr, &sep, patbuf, sizeof(patbuf))) + ) { address_item *moved = NULL; address_item **bptr = &moved; - while (*aptr != NULL) + while (*aptr) { address_item **next; deliver_domain = (*aptr)->domain; /* set $domain */ - if (match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1, + if (match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK) { - aptr = &((*aptr)->next); + aptr = &(*aptr)->next; continue; } - next = &((*aptr)->next); - while (*next != NULL && - (deliver_domain = (*next)->domain, /* Set $domain */ - match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1, - &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) != OK) - next = &((*next)->next); + next = &(*aptr)->next; + while ( *next + && (deliver_domain = (*next)->domain, /* Set $domain */ + match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) != OK + ) + next = &(*next)->next; /* If the batch of non-matchers is at the end, add on any that were extracted further up the chain, and end this iteration. Otherwise, extract them from the chain and hang on the moved chain. */ - if (*next == NULL) + if (!*next) { *next = moved; break; @@ -2818,7 +2942,7 @@ while (*aptr != NULL && *aptr = *next; *next = NULL; bptr = next; - aptr = &((*aptr)->next); + aptr = &(*aptr)->next; } /* If the loop ended because the final address matched, *aptr will @@ -2827,14 +2951,14 @@ while (*aptr != NULL && is, there was a string of non-matching addresses at the end. In this case the extracted addresses have already been added on the end. */ - if (*aptr == NULL) *aptr = moved; + if (!*aptr) *aptr = moved; } DEBUG(D_deliver) { address_item *addr; debug_printf("remote addresses after sorting:\n"); - for (addr = addr_remote; addr != NULL; addr = addr->next) + for (addr = addr_remote; addr; addr = addr->next) debug_printf(" %s\n", addr->address); } } @@ -2961,7 +3085,7 @@ while (!done) /* copy and read header */ memcpy(header, ptr, PIPE_HEADER_SIZE); - header[PIPE_HEADER_SIZE] = '\0'; + header[PIPE_HEADER_SIZE] = '\0'; id = header[0]; subid = header[1]; required = Ustrtol(header + 2, &endc, 10) + PIPE_HEADER_SIZE; /* header + data */ @@ -2974,7 +3098,7 @@ while (!done) } DEBUG(D_deliver) - debug_printf("header read id:%c,subid:%c,size:%s,required:%d,remaining:%d,unfinished:%d\n", + debug_printf("header read id:%c,subid:%c,size:%s,required:%d,remaining:%d,unfinished:%d\n", id, subid, header+2, required, remaining, unfinished); /* is there room for the dataset we want to read ? */ @@ -2987,16 +3111,16 @@ while (!done) break; } - /* we wrote all datasets with atomic write() calls + /* we wrote all datasets with atomic write() calls remaining < required only happens if big_buffer was too small - to get all available data from pipe. unfinished has to be true + to get all available data from pipe. unfinished has to be true as well. */ if (remaining < required) { if (unfinished) continue; msg = string_sprintf("failed to read pipe from transport process " - "%d for transport %s: required size=%d > remaining size=%d and unfinished=false", + "%d for transport %s: required size=%d > remaining size=%d and unfinished=false", pid, addr->transport->driver_name, required, remaining); done = TRUE; break; @@ -3004,7 +3128,7 @@ while (!done) /* step behind the header */ ptr += PIPE_HEADER_SIZE; - + /* Handle each possible type of item, assuming the complete item is available in store. */ @@ -3014,9 +3138,9 @@ while (!done) up by checking the IP address. */ case 'H': - for (h = addrlist->host_list; h != NULL; h = h->next) + for (h = addrlist->host_list; h; h = h->next) { - if (h->address == NULL || Ustrcmp(h->address, ptr+2) != 0) continue; + if (!h->address || Ustrcmp(h->address, ptr+2) != 0) continue; h->status = ptr[0]; h->why = ptr[1]; } @@ -3036,7 +3160,7 @@ while (!done) that a "delete" item is dropped in favour of an "add" item. */ case 'R': - if (addr == NULL) goto ADDR_MISMATCH; + if (!addr) goto ADDR_MISMATCH; DEBUG(D_deliver|D_retry) debug_printf("reading retry information for %s from subprocess\n", @@ -3044,8 +3168,7 @@ while (!done) /* Cut out any "delete" items on the list. */ - for (rp = &(addr->retries); (r = *rp) != NULL; rp = &(r->next)) - { + for (rp = &(addr->retries); (r = *rp); rp = &r->next) if (Ustrcmp(r->key, ptr+1) == 0) /* Found item with same key */ { if ((r->flags & rf_delete) == 0) break; /* It was not "delete" */ @@ -3053,12 +3176,11 @@ while (!done) DEBUG(D_deliver|D_retry) debug_printf(" existing delete item dropped\n"); } - } /* We want to add a delete item only if there is no non-delete item; however we still have to step ptr through the data. */ - if (r == NULL || (*ptr & rf_delete) == 0) + if (!r || (*ptr & rf_delete) == 0) { r = store_get(sizeof(retry_item)); r->next = addr->retries; @@ -3104,7 +3226,7 @@ while (!done) #ifdef SUPPORT_TLS case 'X': - if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */ + if (!addr) goto ADDR_MISMATCH; /* Below, in 'A' handler */ switch (subid) { case '1': @@ -3119,15 +3241,17 @@ while (!done) break; case '2': - addr->peercert = NULL; if (*ptr) (void) tls_import_cert(ptr, &addr->peercert); + else + addr->peercert = NULL; break; case '3': - addr->ourcert = NULL; if (*ptr) (void) tls_import_cert(ptr, &addr->ourcert); + else + addr->ourcert = NULL; break; # ifndef DISABLE_OCSP @@ -3165,14 +3289,14 @@ while (!done) #endif case 'D': - if (addr == NULL) goto ADDR_MISMATCH; + if (!addr) goto ADDR_MISMATCH; memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); ptr += sizeof(addr->dsn_aware); DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); break; case 'A': - if (addr == NULL) + if (!addr) { ADDR_MISMATCH: msg = string_sprintf("address count mismatch for data read from pipe " @@ -3182,41 +3306,79 @@ while (!done) break; } - addr->transport_return = *ptr++; - addr->special_action = *ptr++; - memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno)); - ptr += sizeof(addr->basic_errno); - memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno)); - ptr += sizeof(addr->more_errno); - memcpy(&(addr->flags), ptr, sizeof(addr->flags)); - ptr += sizeof(addr->flags); - addr->message = (*ptr)? string_copy(ptr) : NULL; - while(*ptr++); - addr->user_message = (*ptr)? string_copy(ptr) : NULL; - while(*ptr++); + switch (subid) + { +#ifdef SUPPORT_SOCKS + case '2': /* proxy information; must arrive before A0 and applies to that addr XXX oops*/ + proxy_session = TRUE; /*XXX shouod this be cleared somewhere? */ + if (*ptr == 0) + ptr++; + else + { + proxy_local_address = string_copy(ptr); + while(*ptr++); + memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port)); + ptr += sizeof(proxy_local_port); + } + break; +#endif - /* Always two strings for host information, followed by the port number and DNSSEC mark */ +#ifdef EXPERIMENTAL_DSN_INFO + case '1': /* must arrive before A0, and applies to that addr */ + /* Two strings: smtp_greeting and helo_response */ + addr->smtp_greeting = string_copy(ptr); + while(*ptr++); + addr->helo_response = string_copy(ptr); + while(*ptr++); + break; +#endif - if (*ptr != 0) - { - h = store_get(sizeof(host_item)); - h->name = string_copy(ptr); - while (*ptr++); - h->address = string_copy(ptr); - while(*ptr++); - memcpy(&(h->port), ptr, sizeof(h->port)); - ptr += sizeof(h->port); - h->dnssec = *ptr == '2' ? DS_YES - : *ptr == '1' ? DS_NO - : DS_UNK; - ptr++; - addr->host_used = h; - } - else ptr++; + case '0': + addr->transport_return = *ptr++; + addr->special_action = *ptr++; + memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno)); + ptr += sizeof(addr->basic_errno); + memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno)); + ptr += sizeof(addr->more_errno); + memcpy(&(addr->flags), ptr, sizeof(addr->flags)); + ptr += sizeof(addr->flags); + addr->message = (*ptr)? string_copy(ptr) : NULL; + while(*ptr++); + addr->user_message = (*ptr)? string_copy(ptr) : NULL; + while(*ptr++); - /* Finished with this address */ + /* Always two strings for host information, followed by the port number and DNSSEC mark */ - addr = addr->next; + if (*ptr != 0) + { + h = store_get(sizeof(host_item)); + h->name = string_copy(ptr); + while (*ptr++); + h->address = string_copy(ptr); + while(*ptr++); + memcpy(&(h->port), ptr, sizeof(h->port)); + ptr += sizeof(h->port); + h->dnssec = *ptr == '2' ? DS_YES + : *ptr == '1' ? DS_NO + : DS_UNK; + ptr++; + addr->host_used = h; + } + else ptr++; + + /* Finished with this address */ + + addr = addr->next; + break; + } + break; + + /* Local interface address/port */ + case 'I': + if (*ptr) sending_ip_address = string_copy(ptr); + while (*ptr++) ; + if (*ptr) sending_port = atoi(CS ptr); + while (*ptr++) ; break; /* Z marks the logical end of the data. It is followed by '0' if @@ -3271,7 +3433,7 @@ p->fd = -1; /* If we have finished without error, but haven't had data for every address, something is wrong. */ -if (msg == NULL && addr != NULL) +if (!msg && addr) msg = string_sprintf("insufficient address data read from pipe " "for transport process %d for transport %s", pid, addr->transport->driver_name); @@ -3279,15 +3441,13 @@ if (msg == NULL && addr != NULL) /* If an error message is set, something has gone wrong in getting back the delivery data. Put the message into each address and freeze it. */ -if (msg != NULL) - { - for (addr = addrlist; addr != NULL; addr = addr->next) +if (msg) + for (addr = addrlist; addr; addr = addr->next) { addr->transport_return = DEFER; addr->special_action = SPECIAL_FREEZE; addr->message = msg; } - } /* Return TRUE to indicate we have got all we need from this process, even if it hasn't actually finished yet. */ @@ -3326,16 +3486,14 @@ host_item *h; /* If any host addresses were found to be unusable, add them to the unusable tree so that subsequent deliveries don't try them. */ -for (h = addr->host_list; h != NULL; h = h->next) - { - if (h->address == NULL) continue; - if (h->status >= hstatus_unusable) tree_add_unusable(h); - } +for (h = addr->host_list; h; h = h->next) + if (h->address) + if (h->status >= hstatus_unusable) tree_add_unusable(h); /* Now handle each address on the chain. The transport has placed '=' or '-' into the special_action field for each successful delivery. */ -while (addr != NULL) +while (addr) { address_item *next = addr->next; @@ -3343,10 +3501,11 @@ while (addr != NULL) processing the main hosts and there are fallback hosts available, put the address on the list for fallback delivery. */ - if (addr->transport_return == DEFER && - addr->fallback_hosts != NULL && - !fallback && - msg == NULL) + if ( addr->transport_return == DEFER + && addr->fallback_hosts + && !fallback + && !msg + ) { addr->host_list = addr->fallback_hosts; addr->next = addr_fallback; @@ -3359,7 +3518,7 @@ while (addr != NULL) else { - if (msg != NULL) + if (msg) { addr->message = msg; addr->transport_return = DEFER; @@ -3378,7 +3537,7 @@ the last address, the channel will have been closed down. Now that we have logged that delivery, set continue_sequence to 1 so that any subsequent deliveries don't get "*" incorrectly logged. */ -if (continue_transport == NULL) continue_sequence = 1; +if (!continue_transport) continue_sequence = 1; } @@ -3541,8 +3700,9 @@ for (;;) /* Normally we do not repeat this loop */ readycount > 0 && poffset < remote_max_parallel; poffset++) { - if ((pid = parlist[poffset].pid) != 0 && - FD_ISSET(parlist[poffset].fd, &select_pipes)) + if ( (pid = parlist[poffset].pid) != 0 + && FD_ISSET(parlist[poffset].fd, &select_pipes) + ) { readycount--; if (par_read_pipe(poffset, FALSE)) /* Finished with this pipe */ @@ -3622,7 +3782,7 @@ if ((status & 0xffff) != 0) if (msb != 0 || (code != SIGTERM && code != SIGKILL && code != SIGQUIT)) addrlist->special_action = SPECIAL_FREEZE; - for (addr = addrlist; addr != NULL; addr = addr->next) + for (addr = addrlist; addr; addr = addr->next) { addr->transport_return = DEFER; addr->message = msg; @@ -3671,13 +3831,20 @@ par_reduce(int max, BOOL fallback) while (parcount > max) { address_item *doneaddr = par_wait(); - if (doneaddr == NULL) + if (!doneaddr) { log_write(0, LOG_MAIN|LOG_PANIC, "remote delivery process count got out of step"); parcount = 0; } - else remote_post_process(doneaddr, LOG_MAIN, NULL, fallback); + else + { + transport_instance * tp = doneaddr->transport; + if (tp->max_parallel) + enq_end(string_sprintf("tpt-serialize-%s", tp->name)); + + remote_post_process(doneaddr, LOG_MAIN, NULL, fallback); + } } } @@ -3694,11 +3861,11 @@ int header_length; /* complain to log if someone tries with buffer sizes we can't handle*/ if (size > 99999) -{ - log_write(0, LOG_MAIN|LOG_PANIC_DIE, + { + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed writing transport result to pipe: can't handle buffers > 99999 bytes. truncating!\n"); size = 99999; -} + } /* to keep the write() atomic we build header in writebuffer and copy buf behind */ /* two write() calls would increase the complexity of reading from pipe */ @@ -3706,12 +3873,12 @@ if (size > 99999) /* convert size to human readable string prepended by id and subid */ header_length = snprintf(CS writebuffer, PIPE_HEADER_SIZE+1, "%c%c%05d", id, subid, size); if (header_length != PIPE_HEADER_SIZE) -{ + { log_write(0, LOG_MAIN|LOG_PANIC_DIE, "header snprintf failed\n"); writebuffer[0] = '\0'; -} + } -DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%d,final:%s\n", +DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%d,final:%s\n", id, subid, size, writebuffer); if (buf && size > 0) @@ -3768,13 +3935,13 @@ parcount = 0; /* Number of executing subprocesses */ We use a local variable (parmax) to hold the maximum number of processes; this gets reduced from remote_max_parallel if we can't create enough pipes. */ -if (continue_transport != NULL) remote_max_parallel = 1; +if (continue_transport) remote_max_parallel = 1; parmax = remote_max_parallel; /* If the data for keeping a list of processes hasn't yet been set up, do so. */ -if (parlist == NULL) +if (!parlist) { parlist = store_get(remote_max_parallel * sizeof(pardata)); for (poffset = 0; poffset < remote_max_parallel; poffset++) @@ -3783,7 +3950,7 @@ if (parlist == NULL) /* Now loop for each remote delivery */ -for (delivery_count = 0; addr_remote != NULL; delivery_count++) +for (delivery_count = 0; addr_remote; delivery_count++) { pid_t pid; uid_t uid; @@ -3799,6 +3966,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) address_item *addr = addr_remote; address_item *last = addr; address_item *next; + uschar * panicmsg; + uschar * serialize_key = NULL; /* Pull the first address right off the list. */ @@ -3810,12 +3979,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* If no transport has been set, there has been a big screw-up somewhere. */ - if ((tp = addr->transport) == NULL) + if (!(tp = addr->transport)) { disable_logging = FALSE; /* Jic */ - remote_post_process(addr, LOG_MAIN|LOG_PANIC, - US"No transport set by router", fallback); - continue; + panicmsg = US"No transport set by router"; + goto panic_continue; } /* Check that this base address hasn't previously been delivered to this @@ -3828,7 +3996,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* Force failure if the message is too big. */ - if (tp->message_size_limit != NULL) + if (tp->message_size_limit) { int rc = check_message_size(tp, addr); if (rc != OK) @@ -3851,8 +4019,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) &multi_domain) != OK) { deliver_set_expansions(NULL); - remote_post_process(addr, LOG_MAIN|LOG_PANIC, addr->message, fallback); - continue; + panicmsg = addr->message; + goto panic_continue; } /* Get the maximum it can handle in one envelope, with zero meaning @@ -3901,8 +4069,9 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) the use of these variables, but as it is so likely they will be used when the maximum is 1, we don't bother. Just leave the value alone. */ - if (address_count_max != 1 && - address_count_max < remote_delivery_count/remote_max_parallel) + if ( address_count_max != 1 + && address_count_max < remote_delivery_count/remote_max_parallel + ) { int new_max = remote_delivery_count/remote_max_parallel; int message_max = tp->connection_max_messages; @@ -3927,19 +4096,19 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) and if it might need a per-address check for this, re-evaluate it. */ - while ((next = *anchor) != NULL && address_count < address_count_max) + while ((next = *anchor) && address_count < address_count_max) { BOOL md; if ( (multi_domain || Ustrcmp(next->domain, addr->domain) == 0) && tp == next->transport && same_hosts(next->host_list, addr->host_list) - && same_strings(next->p.errors_address, addr->p.errors_address) - && same_headers(next->p.extra_headers, addr->p.extra_headers) + && same_strings(next->prop.errors_address, addr->prop.errors_address) + && same_headers(next->prop.extra_headers, addr->prop.extra_headers) && same_ugid(tp, next, addr) - && ( next->p.remove_headers == addr->p.remove_headers - || ( next->p.remove_headers != NULL - && addr->p.remove_headers != NULL - && Ustrcmp(next->p.remove_headers, addr->p.remove_headers) == 0 + && ( next->prop.remove_headers == addr->prop.remove_headers + || ( next->prop.remove_headers + && addr->prop.remove_headers + && Ustrcmp(next->prop.remove_headers, addr->prop.remove_headers) == 0 ) ) && ( !multi_domain || ( ( @@ -3966,13 +4135,23 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* If we are acting as an MUA wrapper, all addresses must go in a single transaction. If not, put them back on the chain and yield FALSE. */ - if (mua_wrapper && addr_remote != NULL) + if (mua_wrapper && addr_remote) { last->next = addr_remote; addr_remote = addr; return FALSE; } + /* If the transport is limited for parallellism, enforce that here. + The hints DB entry is decremented in par_reduce(), when we reap the + transport process. */ + + if (tpt_parallel_check(tp, addr, &serialize_key)) + if ((panicmsg = expand_string_message)) + goto panic_continue; + else + continue; /* Loop for the next set of addresses. */ + /* Set up the expansion variables for this set of addresses */ deliver_set_expansions(addr); @@ -3983,29 +4162,26 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* Compute the return path, expanding a new one if required. The old one must be set first, as it might be referred to in the expansion. */ - if(addr->p.errors_address != NULL) - return_path = addr->p.errors_address; + if(addr->prop.errors_address) + return_path = addr->prop.errors_address; #ifdef EXPERIMENTAL_SRS - else if(addr->p.srs_sender != NULL) - return_path = addr->p.srs_sender; + else if(addr->prop.srs_sender) + return_path = addr->prop.srs_sender; #endif else return_path = sender_address; - if (tp->return_path != NULL) + if (tp->return_path) { uschar *new_return_path = expand_string(tp->return_path); - if (new_return_path == NULL) + if (new_return_path) + return_path = new_return_path; + else if (!expand_string_forcedfail) { - if (!expand_string_forcedfail) - { - remote_post_process(addr, LOG_MAIN|LOG_PANIC, - string_sprintf("Failed to expand return path \"%s\": %s", - tp->return_path, expand_string_message), fallback); - continue; - } + panicmsg = string_sprintf("Failed to expand return path \"%s\": %s", + tp->return_path, expand_string_message); + goto enq_continue; } - else return_path = new_return_path; } /* Find the uid, gid, and use_initgroups setting for this transport. Failure @@ -4014,8 +4190,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) if (!findugid(addr, tp, &uid, &gid, &use_initgroups)) { - remote_post_process(addr, LOG_MAIN|LOG_PANIC, NULL, fallback); - continue; + panicmsg = NULL; + goto enq_continue; } /* If this transport has a setup function, call it now so that it gets @@ -4025,7 +4201,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) That is why it is called at this point, before the continue delivery processing, because that might use the fallback hosts. */ - if (tp->setup != NULL) + if (tp->setup) (void)((tp->setup)(addr->transport, addr, NULL, uid, gid, NULL)); /* If this is a run to continue delivery down an already-established @@ -4035,18 +4211,16 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) host is set in the transport. */ continue_more = FALSE; /* In case got set for the last lot */ - if (continue_transport != NULL) + if (continue_transport) { BOOL ok = Ustrcmp(continue_transport, tp->name) == 0; - if (ok && addr->host_list != NULL) + if (ok && addr->host_list) { host_item *h; ok = FALSE; - for (h = addr->host_list; h != NULL; h = h->next) - { + for (h = addr->host_list; h; h = h->next) if (Ustrcmp(h->name, continue_hostname) == 0) { ok = TRUE; break; } - } } /* Addresses not suitable; defer or queue for fallback hosts (which @@ -4055,27 +4229,26 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) if (!ok) { DEBUG(D_deliver) debug_printf("not suitable for continue_transport\n"); - next = addr; + if (serialize_key) enq_end(serialize_key); - if (addr->fallback_hosts != NULL && !fallback) + if (addr->fallback_hosts && !fallback) { - for (;;) + for (next = addr; ; next = next->next) { next->host_list = next->fallback_hosts; DEBUG(D_deliver) debug_printf("%s queued for fallback host(s)\n", next->address); - if (next->next == NULL) break; - next = next->next; + if (!next->next) break; } next->next = addr_fallback; addr_fallback = addr; } else - { - while (next->next != NULL) next = next->next; - next->next = addr_defer; - addr_defer = addr; - } + { + while (next->next) next = next->next; + next->next = addr_defer; + addr_defer = addr; + } continue; } @@ -4084,14 +4257,12 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) the continued host. This tells the transport to leave the channel open, but not to pass it to another delivery process. */ - for (next = addr_remote; next != NULL; next = next->next) + for (next = addr_remote; next; next = next->next) { host_item *h; - for (h = next->host_list; h != NULL; h = h->next) - { + for (h = next->host_list; h; h = h->next) if (Ustrcmp(h->name, continue_hostname) == 0) { continue_more = TRUE; break; } - } } } @@ -4138,9 +4309,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) if (!pipe_done) { - remote_post_process(addr, LOG_MAIN|LOG_PANIC, - string_sprintf("unable to create pipe: %s", strerror(errno)), fallback); - continue; + panicmsg = string_sprintf("unable to create pipe: %s", strerror(errno)); + goto enq_continue; } /* Find a free slot in the pardata list. Must do this after the possible @@ -4148,7 +4318,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) up a slot. */ for (poffset = 0; poffset < remote_max_parallel; poffset++) - if (parlist[poffset].pid == 0) break; + if (parlist[poffset].pid == 0) + break; /* If there isn't one, there has been a horrible disaster. */ @@ -4156,9 +4327,8 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) { (void)close(pfd[pipe_write]); (void)close(pfd[pipe_read]); - remote_post_process(addr, LOG_MAIN|LOG_PANIC, - US"Unexpectedly no free subprocess slot", fallback); - continue; + panicmsg = US"Unexpectedly no free subprocess slot"; + goto enq_continue; } /* Now fork a subprocess to do the remote delivery, but before doing so, @@ -4180,7 +4350,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* Show pids on debug output if parallelism possible */ - if (parmax > 1 && (parcount > 0 || addr_remote != NULL)) + if (parmax > 1 && (parcount > 0 || addr_remote)) { DEBUG(D_any|D_v) debug_selector |= D_pid; DEBUG(D_deliver) debug_printf("Remote delivery process started\n"); @@ -4243,7 +4413,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) if (!(tp->info->code)(addr->transport, addr)) replicate_status(addr); set_process_info("delivering %s (just run %s for %s%s in subprocess)", - message_id, tp->name, addr->address, (addr->next == NULL)? "" : ", ..."); + message_id, tp->name, addr->address, addr->next ? ", ..." : ""); /* Ensure any cached resources that we used are now released */ @@ -4262,9 +4432,9 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* Host unusability information: for most success cases this will be null. */ - for (h = addr->host_list; h != NULL; h = h->next) + for (h = addr->host_list; h; h = h->next) { - if (h->address == NULL || h->status < hstatus_unusable) continue; + if (!h->address || h->status < hstatus_unusable) continue; sprintf(CS big_buffer, "%c%c%s", h->status, h->why, h->address); rmt_dlv_checked_write(fd, 'H', '0', big_buffer, Ustrlen(big_buffer+2) + 3); } @@ -4282,7 +4452,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) item for any client-auth info followed by 'R' items for any retry settings, and finally an 'A' item for the remaining data. */ - for(; addr != NULL; addr = addr->next) + for(; addr; addr = addr->next) { uschar *ptr; retry_item *r; @@ -4297,15 +4467,13 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) #ifdef SUPPORT_TLS if (addr->cipher) { - ptr = big_buffer; - sprintf(CS ptr, "%.128s", addr->cipher); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->cipher) + 1; if (!addr->peerdn) *ptr++ = 0; else { - sprintf(CS ptr, "%.512s", addr->peerdn); - while(*ptr++); + ptr += sprintf(CS ptr, "%.512s", addr->peerdn); + ptr++; } rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer); @@ -4331,9 +4499,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) # ifndef DISABLE_OCSP if (addr->ocsp > OCSP_NOT_REQ) { - ptr = big_buffer; - sprintf(CS ptr, "%c", addr->ocsp + '0'); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%c", addr->ocsp + '0') + 1; rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer); } # endif @@ -4341,23 +4507,17 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) if (client_authenticator) { - ptr = big_buffer; - sprintf(CS big_buffer, "%.64s", client_authenticator); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticator) + 1; rmt_dlv_checked_write(fd, 'C', '1', big_buffer, ptr - big_buffer); } if (client_authenticated_id) { - ptr = big_buffer; - sprintf(CS big_buffer, "%.64s", client_authenticated_id); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_id) + 1; rmt_dlv_checked_write(fd, 'C', '2', big_buffer, ptr - big_buffer); } if (client_authenticated_sender) { - ptr = big_buffer; - sprintf(CS big_buffer, "%.64s", client_authenticated_sender); - while(*ptr++); + ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_sender) + 1; rmt_dlv_checked_write(fd, 'C', '3', big_buffer, ptr - big_buffer); } @@ -4372,16 +4532,15 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) /* Retry information: for most success cases this will be null. */ - for (r = addr->retries; r != NULL; r = r->next) + for (r = addr->retries; r; r = r->next) { - uschar *ptr; sprintf(CS big_buffer, "%c%.500s", r->flags, r->key); ptr = big_buffer + Ustrlen(big_buffer+2) + 3; memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno)); ptr += sizeof(r->basic_errno); memcpy(ptr, &(r->more_errno), sizeof(r->more_errno)); ptr += sizeof(r->more_errno); - if (r->message == NULL) *ptr++ = 0; else + if (!r->message) *ptr++ = 0; else { sprintf(CS ptr, "%.512s", r->message); while(*ptr++); @@ -4389,11 +4548,45 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer); } - /* The rest of the information goes in an 'A' item. */ +#ifdef SUPPORT_SOCKS + if (LOGGING(proxy) && proxy_session) + { + ptr = big_buffer; + if (proxy_local_address) + { + DEBUG(D_deliver) debug_printf("proxy_local_address '%s'\n", proxy_local_address); + ptr = big_buffer + sprintf(CS ptr, "%.128s", proxy_local_address) + 1; + DEBUG(D_deliver) debug_printf("proxy_local_port %d\n", proxy_local_port); + memcpy(ptr, &proxy_local_port, sizeof(proxy_local_port)); + ptr += sizeof(proxy_local_port); + } + else + *ptr++ = '\0'; + rmt_dlv_checked_write(fd, 'A', '2', big_buffer, ptr - big_buffer); + } +#endif +#ifdef EXPERIMENTAL_DSN_INFO +/*um, are they really per-addr? Other per-conn stuff is not (auth, tls). But host_used is! */ + if (addr->smtp_greeting) + { + DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting); + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->smtp_greeting) + 1; + if (addr->helo_response) + { + DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response); + ptr += sprintf(CS ptr, "%.128s", addr->helo_response) + 1; + } + else + *ptr++ = '\0'; + rmt_dlv_checked_write(fd, 'A', '1', big_buffer, ptr - big_buffer); + } +#endif + + /* The rest of the information goes in an 'A0' item. */ + + sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action); ptr = big_buffer + 2; - sprintf(CS big_buffer, "%c%c", addr->transport_return, - addr->special_action); memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno)); ptr += sizeof(addr->basic_errno); memcpy(ptr, &(addr->more_errno), sizeof(addr->more_errno)); @@ -4401,24 +4594,16 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) memcpy(ptr, &(addr->flags), sizeof(addr->flags)); ptr += sizeof(addr->flags); - if (addr->message == NULL) *ptr++ = 0; else - { - sprintf(CS ptr, "%.1024s", addr->message); - while(*ptr++); - } + if (!addr->message) *ptr++ = 0; else + ptr += sprintf(CS ptr, "%.1024s", addr->message) + 1; - if (addr->user_message == NULL) *ptr++ = 0; else - { - sprintf(CS ptr, "%.1024s", addr->user_message); - while(*ptr++); - } + if (!addr->user_message) *ptr++ = 0; else + ptr += sprintf(CS ptr, "%.1024s", addr->user_message) + 1; - if (addr->host_used == NULL) *ptr++ = 0; else + if (!addr->host_used) *ptr++ = 0; else { - sprintf(CS ptr, "%.256s", addr->host_used->name); - while(*ptr++); - sprintf(CS ptr, "%.64s", addr->host_used->address); - while(*ptr++); + ptr += sprintf(CS ptr, "%.256s", addr->host_used->name) + 1; + ptr += sprintf(CS ptr, "%.64s", addr->host_used->address) + 1; memcpy(ptr, &(addr->host_used->port), sizeof(addr->host_used->port)); ptr += sizeof(addr->host_used->port); @@ -4430,12 +4615,25 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer); } + /* Local interface address/port */ +#ifdef EXPERIMENTAL_DSN_INFO + if (sending_ip_address) +#else + if (LOGGING(incoming_interface) && sending_ip_address) +#endif + { + uschar * ptr; + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", sending_ip_address) + 1; + ptr += sprintf(CS ptr, "%d", sending_port) + 1; + rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer); + } + /* Add termination flag, close the pipe, and that's it. The character after 'Z' indicates whether continue_transport is now NULL or not. A change from non-NULL to NULL indicates a problem with a continuing connection. */ - big_buffer[0] = (continue_transport == NULL)? '0' : '1'; + big_buffer[0] = continue_transport ? '1' : '0'; rmt_dlv_checked_write(fd, 'Z', '0', big_buffer, 1); (void)close(fd); exit(EXIT_SUCCESS); @@ -4450,10 +4648,9 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) if (pid < 0) { (void)close(pfd[pipe_read]); - remote_post_process(addr, LOG_MAIN|LOG_PANIC, - string_sprintf("fork failed for remote delivery to %s: %s", - addr->domain, strerror(errno)), fallback); - continue; + panicmsg = string_sprintf("fork failed for remote delivery to %s: %s", + addr->domain, strerror(errno)); + goto enq_continue; } /* Fork succeeded; increment the count, and remember relevant data for @@ -4478,13 +4675,21 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) (continue_transport gets set to NULL) before we consider any other addresses in this message. */ - if (continue_transport != NULL) par_reduce(0, fallback); + if (continue_transport) par_reduce(0, fallback); /* Otherwise, if we are running in the test harness, wait a bit, to let the newly created process get going before we create another process. This should ensure repeatability in the tests. We only need to wait a tad. */ else if (running_in_test_harness) millisleep(500); + + continue; + +enq_continue: + if (serialize_key) enq_end(serialize_key); +panic_continue: + remote_post_process(addr, LOG_MAIN|LOG_PANIC, panicmsg, fallback); + continue; } /* Reached the end of the list of addresses. Wait for all the subprocesses that @@ -4548,7 +4753,7 @@ while(len-- > 0) /* We do the percent hack only for those domains that are listed in percent_hack_domains. A loop is required, to copy with multiple %-hacks. */ -if (percent_hack_domains != NULL) +if (percent_hack_domains) { int rc; uschar *new_address = NULL; @@ -4556,10 +4761,11 @@ if (percent_hack_domains != NULL) deliver_domain = addr->domain; /* set $domain */ - while ((rc = match_isinlist(deliver_domain, &percent_hack_domains, 0, - &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) - == OK && - (t = Ustrrchr(local_part, '%')) != NULL) + while ( (rc = match_isinlist(deliver_domain, (const uschar **)&percent_hack_domains, 0, + &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) + == OK + && (t = Ustrrchr(local_part, '%')) != NULL + ) { new_address = string_copy(local_part); new_address[t - local_part] = '@'; @@ -4571,7 +4777,7 @@ if (percent_hack_domains != NULL) /* If hackery happened, set up new parent and alter the current address. */ - if (new_address != NULL) + if (new_address) { address_item *new_parent = store_get(sizeof(address_item)); *new_parent = *addr; @@ -4617,22 +4823,22 @@ int ptr = 0; uschar *para, *yield; uschar buffer[256]; -if (f == NULL) return NULL; +if (!f) return NULL; -if (Ufgets(buffer, sizeof(buffer), f) == NULL || - Ustrcmp(buffer, "****\n") == 0) return NULL; +if (!Ufgets(buffer, sizeof(buffer), f) || Ustrcmp(buffer, "****\n") == 0) + return NULL; para = store_get(size); for (;;) { para = string_cat(para, &size, &ptr, buffer, Ustrlen(buffer)); - if (Ufgets(buffer, sizeof(buffer), f) == NULL || - Ustrcmp(buffer, "****\n") == 0) break; + if (!Ufgets(buffer, sizeof(buffer), f) || Ustrcmp(buffer, "****\n") == 0) + break; } para[ptr] = 0; -yield = expand_string(para); -if (yield != NULL) return yield; +if ((yield = expand_string(para))) + return yield; log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand string from " "bounce_message_file or warn_message_file (%s): %s", which, @@ -4658,17 +4864,15 @@ Returns: DELIVER_NOT_ATTEMPTED static int continue_closedown(void) { -if (continue_transport != NULL) +if (continue_transport) { transport_instance *t; - for (t = transports; t != NULL; t = t->next) - { + for (t = transports; t; t = t->next) if (Ustrcmp(t->name, continue_transport) == 0) { - if (t->info->closedown != NULL) (t->info->closedown)(t); + if (t->info->closedown) (t->info->closedown)(t); break; } - } } return DELIVER_NOT_ATTEMPTED; } @@ -4701,16 +4905,16 @@ print_address_information(address_item *addr, FILE *f, uschar *si, uschar *sc, BOOL yield = TRUE; uschar *printed = US""; address_item *ancestor = addr; -while (ancestor->parent != NULL) ancestor = ancestor->parent; +while (ancestor->parent) ancestor = ancestor->parent; fprintf(f, "%s", CS si); -if (addr->parent != NULL && testflag(addr, af_hide_child)) +if (addr->parent && testflag(addr, af_hide_child)) { printed = US"an undisclosed address"; yield = FALSE; } -else if (!testflag(addr, af_pfr) || addr->parent == NULL) +else if (!testflag(addr, af_pfr) || !addr->parent) printed = addr->address; else @@ -4730,11 +4934,11 @@ fprintf(f, "%s", CS string_printing(printed)); if (ancestor != addr) { - uschar *original = (ancestor->onetime_parent == NULL)? - ancestor->address : ancestor->onetime_parent; + uschar *original = ancestor->onetime_parent; + if (!original) original= ancestor->address; if (strcmpic(original, printed) != 0) fprintf(f, "%s(%sgenerated from %s)", sc, - (ancestor != addr->parent)? "ultimately " : "", + ancestor != addr->parent ? "ultimately " : "", string_printing(original)); } @@ -4779,15 +4983,12 @@ print_address_error(address_item *addr, FILE *f, uschar *t) int count = Ustrlen(t); uschar *s = testflag(addr, af_pass_message)? addr->message : NULL; -if (s == NULL) - { - if (addr->user_message != NULL) s = addr->user_message; else return; - } +if (!s && !(s = addr->user_message)) + return; fprintf(f, "\n %s", t); -while (*s != 0) - { +while (*s) if (*s == '\\' && s[1] == 'n') { fprintf(f, "\n "); @@ -4804,7 +5005,6 @@ while (*s != 0) count = 0; } } - } } @@ -4816,9 +5016,9 @@ while (*s != 0) a bounce or a warning message. It tries to format the message reasonably as required by RFC 3461 by adding a space after each newline -we assume that this function is only called if addr->host_used is set and if so -a useable addr->message is available containing some Exim description with ": \n" -ending, followed by the L/SMTP error message. +it uses the same logic as print_address_error() above. if af_pass_message is true +and addr->message is set it uses the remote host answer. if not addr->user_message +is used instead if available. Arguments: addr the address @@ -4830,21 +5030,23 @@ Returns: nothing static void print_dsn_diagnostic_code(const address_item *addr, FILE *f) { -uschar * s; - -/* check host_used, af_pass_message flag and addr->message for safety reasons */ -if (!addr->host_used && testflag(addr, af_pass_message) && addr->message) - return; +uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL; -/* search first ": ". we assume to find the remote-MTA answer there */ -DEBUG(D_deliver) - debug_printf("DSN Diagnostic-Code: addr->dsn_message = %s\n", addr->message); -if (!(s = Ustrstr(addr->message, ": "))) - return; /* not found, bail out */ +/* af_pass_message and addr->message set ? print remote host answer */ +if (s) + { + DEBUG(D_deliver) + debug_printf("DSN Diagnostic-Code: addr->message = %s\n", addr->message); -fprintf(f, "Diagnostic-Code: smtp; "); + /* search first ": ". we assume to find the remote-MTA answer there */ + if (!(s = Ustrstr(addr->message, ": "))) + return; /* not found, bail out */ + s += 2; /* skip ": " */ + fprintf(f, "Diagnostic-Code: smtp; "); + } +/* no message available. do nothing */ +else return; -s += 2; /* skip ": " */ while (*s) if (*s == '\\' && s[1] == 'n') { @@ -4881,14 +5083,14 @@ static void do_duplicate_check(address_item **anchor) { address_item *addr; -while ((addr = *anchor) != NULL) +while ((addr = *anchor)) { tree_node *tnode; if (testflag(addr, af_pfr)) { anchor = &(addr->next); } - else if ((tnode = tree_search(tree_duplicates, addr->unique)) != NULL) + else if ((tnode = tree_search(tree_duplicates, addr->unique))) { DEBUG(D_deliver|D_route) debug_printf("%s is a duplicate address: discarded\n", addr->unique); @@ -4959,9 +5161,9 @@ open_db dbblock; open_db *dbm_file; extern int acl_where; -uschar *info = (queue_run_pid == (pid_t)0)? - string_sprintf("delivering %s", id) : - string_sprintf("delivering %s (queue run pid %d)", id, queue_run_pid); +uschar *info = queue_run_pid == (pid_t)0 + ? string_sprintf("delivering %s", id) + : string_sprintf("delivering %s (queue run pid %d)", id, queue_run_pid); /* If the D_process_info bit is on, set_process_info() will output debugging information. If not, we want to show this initial information if D_deliver or @@ -4969,8 +5171,9 @@ D_queue_run is set or in verbose mode. */ set_process_info("%s", info); -if ((debug_selector & D_process_info) == 0 && - (debug_selector & (D_deliver|D_queue_run|D_v)) != 0) +if ( !(debug_selector & D_process_info) + && (debug_selector & (D_deliver|D_queue_run|D_v)) + ) debug_printf("%s\n", info); /* Ensure that we catch any subprocesses that are created. Although Exim @@ -5094,9 +5297,9 @@ Otherwise it might be needed again. */ sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id); jread = Ufopen(spoolname, "rb"); -if (jread != NULL) +if (jread) { - while (Ufgets(big_buffer, big_buffer_size, jread) != NULL) + while (Ufgets(big_buffer, big_buffer_size, jread)) { int n = Ustrlen(big_buffer); big_buffer[n-1] = 0; @@ -5117,7 +5320,7 @@ else if (errno != ENOENT) /* A null recipients list indicates some kind of disaster. */ -if (recipients_list == NULL) +if (!recipients_list) { (void)close(deliver_datafile); deliver_datafile = -1; @@ -5137,8 +5340,9 @@ if (deliver_freeze) tools must be used to deal with it. Logging of this action happens in spool_move_message() and its subfunctions. */ - if (move_frozen_messages && - spool_move_message(id, message_subdir, US"", US"F")) + if ( move_frozen_messages + && spool_move_message(id, message_subdir, US"", US"F") + ) return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ #endif @@ -5170,14 +5374,13 @@ if (deliver_freeze) else { - if ((sender_address[0] == 0 || - auto_thaw <= 0 || - now <= deliver_frozen_at + auto_thaw - ) - && - (!forced || !deliver_force_thaw || !admin_user || - continue_hostname != NULL - )) + if ( ( sender_address[0] == 0 + || auto_thaw <= 0 + || now <= deliver_frozen_at + auto_thaw + ) + && ( !forced || !deliver_force_thaw + || !admin_user || continue_hostname + ) ) { (void)close(deliver_datafile); deliver_datafile = -1; @@ -5225,8 +5428,7 @@ if (message_logs) /* Make a C stream out of it. */ - message_log = fdopen(fd, "a"); - if (message_log == NULL) + if (!(message_log = fdopen(fd, "a"))) { log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't fdopen message log %s: %s", spoolname, strerror(errno)); @@ -5241,8 +5443,8 @@ the addresses. */ if (give_up) { struct passwd *pw = getpwuid(real_uid); - log_write(0, LOG_MAIN, "cancelled by %s", (pw != NULL)? - US pw->pw_name : string_sprintf("uid %ld", (long int)real_uid)); + log_write(0, LOG_MAIN, "cancelled by %s", + pw ? US pw->pw_name : string_sprintf("uid %ld", (long int)real_uid)); process_recipients = RECIP_FAIL; } @@ -5257,7 +5459,7 @@ a result of timeout_frozen_after. If the system filter yields "delivered", then ignore the true recipients of the message. Failure of the filter file is logged, and the delivery attempt fails. */ -else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) +else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) { int rc; int filtertype; @@ -5327,7 +5529,7 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) system_filtering = FALSE; enable_dollar_recipients = FALSE; - if (filter_message != NULL && filter_message[0] == 0) filter_message = NULL; + if (filter_message && filter_message[0] == 0) filter_message = NULL; /* Save the values of the system filter variables so that user filters can use them. */ @@ -5355,8 +5557,8 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) deliver_frozen_at = time(NULL); process_recipients = RECIP_DEFER; frozen_info = string_sprintf(" by the system filter%s%s", - (filter_message == NULL)? US"" : US": ", - (filter_message == NULL)? US"" : filter_message); + filter_message ? US": " : US"", + filter_message ? filter_message : US""); } /* The filter can request that a message be failed. The error message may be @@ -5373,12 +5575,14 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) process_recipients = RECIP_FAIL_FILTER; - if (filter_message != NULL) + if (filter_message) { uschar *logend; colon = US": "; - if (filter_message[0] == '<' && filter_message[1] == '<' && - (logend = Ustrstr(filter_message, ">>")) != NULL) + if ( filter_message[0] == '<' + && filter_message[1] == '<' + && (logend = Ustrstr(filter_message, ">>")) + ) { logmsg = filter_message + 2; loglen = logend - logmsg; @@ -5402,10 +5606,10 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) else if (rc == FF_DELIVERED) { process_recipients = RECIP_IGNORE; - if (addr_new == NULL) - log_write(0, LOG_MAIN, "=> discarded (system filter)"); - else + if (addr_new) log_write(0, LOG_MAIN, "original recipients ignored (system filter)"); + else + log_write(0, LOG_MAIN, "=> discarded (system filter)"); } /* If any new addresses were created by the filter, fake up a "parent" @@ -5414,7 +5618,7 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) pipes, files, and autoreplies, and run them as the filter uid if set, otherwise as the current uid. */ - if (addr_new != NULL) + if (addr_new) { int uid = (system_filter_uid_set)? system_filter_uid : geteuid(); int gid = (system_filter_gid_set)? system_filter_gid : getegid(); @@ -5433,7 +5637,7 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) at the final address. This is used if we go on to add addresses for the original recipients. */ - while (p != NULL) + while (p) { if (parent->child_count == SHRT_MAX) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "system filter generated more " @@ -5484,11 +5688,11 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) /* Now find the actual transport, first expanding the name. We have set address_file or address_pipe above. */ - if (tpname != NULL) + if (tpname) { uschar *tmp = expand_string(tpname); address_file = address_pipe = NULL; - if (tmp == NULL) + if (!tmp) p->message = string_sprintf("failed to expand \"%s\" as a " "system filter transport name", tpname); tpname = tmp; @@ -5499,10 +5703,10 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) type); } - if (tpname != NULL) + if (tpname) { transport_instance *tp; - for (tp = transports; tp != NULL; tp = tp->next) + for (tp = transports; tp; tp = tp->next) { if (Ustrcmp(tp->name, tpname) == 0) { @@ -5510,7 +5714,7 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) break; } } - if (tp == NULL) + if (!tp) p->message = string_sprintf("failed to find \"%s\" transport " "for system filter delivery", tpname); } @@ -5518,11 +5722,11 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT) /* If we couldn't set up a transport, defer the delivery, putting the error on the panic log as well as the main log. */ - if (p->transport == NULL) + if (!p->transport) { address_item *badp = p; p = p->next; - if (addr_last == NULL) addr_new = p; else addr_last->next = p; + if (!addr_last) addr_new = p; else addr_last->next = p; badp->local_part = badp->address; /* Needed for log line */ post_process_one(badp, DEFER, LOG_MAIN|LOG_PANIC, DTYPE_ROUTER, 0); continue; @@ -5560,20 +5764,32 @@ if (process_recipients != RECIP_IGNORE) { for (i = 0; i < recipients_count; i++) { - if (tree_search(tree_nonrecipients, recipients_list[i].address) == NULL) + if (!tree_search(tree_nonrecipients, recipients_list[i].address)) { recipient_item *r = recipients_list + i; address_item *new = deliver_make_addr(r->address, FALSE); - new->p.errors_address = r->errors_to; + new->prop.errors_address = r->errors_to; +#ifdef SUPPORT_I18N + if ((new->prop.utf8_msg = message_smtputf8)) + { + new->prop.utf8_downcvt = message_utf8_downconvert == 1; + new->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + DEBUG(D_deliver) debug_printf("utf8, downconvert %s\n", + new->prop.utf8_downcvt ? "yes" + : new->prop.utf8_downcvt_maybe ? "ifneeded" + : "no"); + } +#endif if (r->pno >= 0) new->onetime_parent = recipients_list[r->pno].address; - /* If DSN support is enabled, set the dsn flags and the original receipt + /* If DSN support is enabled, set the dsn flags and the original receipt to be passed on to other DSN enabled MTAs */ new->dsn_flags = r->dsn_flags & rf_dsnflags; new->dsn_orcpt = r->orcpt; - DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", new->dsn_orcpt, new->dsn_flags); + DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", + new->dsn_orcpt, new->dsn_flags); switch (process_recipients) { @@ -5590,7 +5806,7 @@ if (process_recipients != RECIP_IGNORE) case RECIP_FAIL_FILTER: new->message = - (filter_message == NULL)? US"delivery cancelled" : filter_message; + filter_message ? filter_message : US"delivery cancelled"; setflag(new, af_pass_message); goto RECIP_QUEUE_FAILED; /* below */ @@ -5639,16 +5855,16 @@ if (process_recipients != RECIP_IGNORE) /* Value should be RECIP_ACCEPT; take this as the safe default. */ default: - if (addr_new == NULL) addr_new = new; else addr_last->next = new; + if (!addr_new) addr_new = new; else addr_last->next = new; addr_last = new; break; } -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (process_recipients != RECIP_ACCEPT) { uschar * save_local = deliver_localpart; - uschar * save_domain = deliver_domain; + const uschar * save_domain = deliver_domain; deliver_localpart = expand_string( string_sprintf("${local_part:%s}", new->address)); @@ -5668,14 +5884,11 @@ if (process_recipients != RECIP_IGNORE) DEBUG(D_deliver) { - address_item *p = addr_new; + address_item *p; debug_printf("Delivery address list:\n"); - while (p != NULL) - { - debug_printf(" %s %s\n", p->address, (p->onetime_parent == NULL)? US"" : - p->onetime_parent); - p = p->next; - } + for (p = addr_new; p; p = p->next) + debug_printf(" %s %s\n", p->address, + p->onetime_parent ? p->onetime_parent : US""); } /* Set up the buffers used for copying over the file when delivering. */ @@ -5724,15 +5937,14 @@ deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE); */ header_rewritten = FALSE; /* No headers rewritten yet */ -while (addr_new != NULL) /* Loop until all addresses dealt with */ +while (addr_new) /* Loop until all addresses dealt with */ { address_item *addr, *parent; - dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE); /* Failure to open the retry database is treated the same as if it does not exist. In both cases, dbm_file is NULL. */ - if (dbm_file == NULL) + if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) { DEBUG(D_deliver|D_retry|D_route|D_hints_lookup) debug_printf("no retry data available\n"); @@ -5741,7 +5953,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ /* Scan the current batch of new addresses, to handle pipes, files and autoreplies, and determine which others are ready for routing. */ - while (addr_new != NULL) + while (addr_new) { int rc; uschar *p; @@ -5797,11 +6009,11 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ if (addr->address[0] == '>') { - while (tree_search(tree_duplicates, addr->unique) != NULL) + while (tree_search(tree_duplicates, addr->unique)) addr->unique = string_sprintf(">%s", addr->unique); } - else if ((tnode = tree_search(tree_duplicates, addr->unique)) != NULL) + else if ((tnode = tree_search(tree_duplicates, addr->unique))) { DEBUG(D_deliver|D_route) debug_printf("%s is a duplicate address: discarded\n", addr->address); @@ -5815,7 +6027,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ /* Check for previous delivery */ - if (tree_search(tree_nonrecipients, addr->unique) != NULL) + if (tree_search(tree_nonrecipients, addr->unique)) { DEBUG(D_deliver|D_route) debug_printf("%s was previously delivered: discarded\n", addr->address); @@ -5912,10 +6124,11 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ delivery was forced by hand. */ deliver_domain = addr->domain; /* set $domain */ - if (!forced && hold_domains != NULL && - (rc = match_isinlist(addr->domain, &hold_domains, 0, + if ( !forced && hold_domains + && (rc = match_isinlist(addr->domain, (const uschar **)&hold_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, - NULL)) != FAIL) + NULL)) != FAIL + ) { if (rc == DEFER) { @@ -5937,7 +6150,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ The "unique" field is initialized to the same value as the "address" field, but gets changed here to cope with identically-named descendents. */ - for (parent = addr->parent; parent != NULL; parent = parent->parent) + for (parent = addr->parent; parent; parent = parent->parent) if (strcmpic(addr->address, parent->address) == 0) break; /* If there's an ancestor with the same name, set the homonym flag. This @@ -5947,7 +6160,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ work. This means that siblings or cousins with the same names are treated as duplicates, which is what we want. */ - if (parent != NULL) + if (parent) { setflag(addr, af_homonym); if (parent->unique[0] != '\\') @@ -5965,7 +6178,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ DEBUG(D_deliver|D_route) debug_printf("unique = %s\n", addr->unique); - if (tree_search(tree_nonrecipients, addr->unique) != NULL) + if (tree_search(tree_nonrecipients, addr->unique)) { DEBUG(D_deliver|D_route) debug_printf("%s was previously delivered: discarded\n", addr->unique); @@ -5983,36 +6196,38 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ addr->address_retry_key = string_sprintf("R:%s@%s", addr->local_part, addr->domain); - if (dbm_file == NULL) - domain_retry_record = address_retry_record = NULL; - else + if (dbm_file) { domain_retry_record = dbfn_read(dbm_file, addr->domain_retry_key); - if (domain_retry_record != NULL && - now - domain_retry_record->time_stamp > retry_data_expire) + if ( domain_retry_record + && now - domain_retry_record->time_stamp > retry_data_expire + ) domain_retry_record = NULL; /* Ignore if too old */ address_retry_record = dbfn_read(dbm_file, addr->address_retry_key); - if (address_retry_record != NULL && - now - address_retry_record->time_stamp > retry_data_expire) + if ( address_retry_record + && now - address_retry_record->time_stamp > retry_data_expire + ) address_retry_record = NULL; /* Ignore if too old */ - if (address_retry_record == NULL) + if (!address_retry_record) { uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key, sender_address); address_retry_record = dbfn_read(dbm_file, altkey); - if (address_retry_record != NULL && - now - address_retry_record->time_stamp > retry_data_expire) + if ( address_retry_record + && now - address_retry_record->time_stamp > retry_data_expire) address_retry_record = NULL; /* Ignore if too old */ } } + else + domain_retry_record = address_retry_record = NULL; DEBUG(D_deliver|D_retry) { - if (domain_retry_record == NULL) + if (!domain_retry_record) debug_printf("no domain retry record\n"); - if (address_retry_record == NULL) + if (!address_retry_record) debug_printf("no address retry record\n"); } @@ -6030,7 +6245,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ The reason for not doing the same for address retries is that they normally arise from 4xx responses, not DNS timeouts. */ - if (continue_hostname != NULL && domain_retry_record != NULL) + if (continue_hostname && domain_retry_record) { addr->message = US"reusing SMTP connection skips previous routing defer"; addr->basic_errno = ERRNO_RRETRY; @@ -6069,19 +6284,21 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ which keep the retry record fresh, which can lead to us perpetually deferring messages. */ - else if (((queue_running && !deliver_force) || continue_hostname != NULL) - && - ((domain_retry_record != NULL && - now < domain_retry_record->next_try && - !domain_retry_record->expired) - || - (address_retry_record != NULL && - now < address_retry_record->next_try)) - && - (domain_retry_record != NULL || - address_retry_record == NULL || - !retry_ultimate_address_timeout(addr->address_retry_key, - addr->domain, address_retry_record, now))) + else if ( ( queue_running && !deliver_force + || continue_hostname + ) + && ( ( domain_retry_record + && now < domain_retry_record->next_try + && !domain_retry_record->expired + ) + || ( address_retry_record + && now < address_retry_record->next_try + ) ) + && ( domain_retry_record + || !address_retry_record + || !retry_ultimate_address_timeout(addr->address_retry_key, + addr->domain, address_retry_record, now) + ) ) { addr->message = US"retry time not reached"; addr->basic_errno = ERRNO_RRETRY; @@ -6093,7 +6310,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ else { - if (domain_retry_record != NULL || address_retry_record != NULL) + if (domain_retry_record || address_retry_record) setflag(addr, af_dr_retry_exists); addr->next = addr_route; addr_route = addr; @@ -6105,22 +6322,22 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ /* The database is closed while routing is actually happening. Requests to update it are put on a chain and all processed together at the end. */ - if (dbm_file != NULL) dbfn_close(dbm_file); + if (dbm_file) dbfn_close(dbm_file); /* If queue_domains is set, we don't even want to try routing addresses in those domains. During queue runs, queue_domains is forced to be unset. Optimize by skipping this pass through the addresses if nothing is set. */ - if (!deliver_force && queue_domains != NULL) + if (!deliver_force && queue_domains) { address_item *okaddr = NULL; - while (addr_route != NULL) + while (addr_route) { address_item *addr = addr_route; addr_route = addr->next; deliver_domain = addr->domain; /* set $domain */ - if ((rc = match_isinlist(addr->domain, &queue_domains, 0, + if ((rc = match_isinlist(addr->domain, (const uschar **)&queue_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) != OK) { @@ -6149,28 +6366,30 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ /* Now route those addresses that are not deferred. */ - while (addr_route != NULL) + while (addr_route) { int rc; address_item *addr = addr_route; - uschar *old_domain = addr->domain; + const uschar *old_domain = addr->domain; uschar *old_unique = addr->unique; addr_route = addr->next; addr->next = NULL; /* Just in case some router parameter refers to it. */ - return_path = (addr->p.errors_address != NULL)? - addr->p.errors_address : sender_address; + if (!(return_path = addr->prop.errors_address)) + return_path = sender_address; /* If a router defers an address, add a retry item. Whether or not to use the local part in the key is a property of the router. */ if ((rc = route_address(addr, &addr_local, &addr_remote, &addr_new, &addr_succeed, v_none)) == DEFER) - retry_add_item(addr, (addr->router->retry_use_local_part)? - string_sprintf("R:%s@%s", addr->local_part, addr->domain) : - string_sprintf("R:%s", addr->domain), 0); + retry_add_item(addr, + addr->router->retry_use_local_part + ? string_sprintf("R:%s@%s", addr->local_part, addr->domain) + : string_sprintf("R:%s", addr->domain), + 0); /* Otherwise, if there is an existing retry record in the database, add retry items to delete both forms. We must also allow for the possibility @@ -6212,8 +6431,9 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ has already been delivered, because it's the unique address that finally gets recorded. */ - if (addr->unique != old_unique && - tree_search(tree_nonrecipients, addr->unique) != 0) + if ( addr->unique != old_unique + && tree_search(tree_nonrecipients, addr->unique) != 0 + ) { DEBUG(D_deliver|D_route) debug_printf("%s was previously delivered: " "discarded\n", addr->address); @@ -6229,14 +6449,15 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ to a remote transport, there are no header changes, and the domain was not modified by the router. */ - if (addr_remote == addr && - addr->router->same_domain_copy_routing && - addr->p.extra_headers == NULL && - addr->p.remove_headers == NULL && - old_domain == addr->domain) + if ( addr_remote == addr + && addr->router->same_domain_copy_routing + && !addr->prop.extra_headers + && !addr->prop.remove_headers + && old_domain == addr->domain + ) { address_item **chain = &addr_route; - while (*chain != NULL) + while (*chain) { address_item *addr2 = *chain; if (Ustrcmp(addr2->domain, addr->domain) != 0) @@ -6259,7 +6480,7 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ addr2->transport = addr->transport; addr2->host_list = addr->host_list; addr2->fallback_hosts = addr->fallback_hosts; - addr2->p.errors_address = addr->p.errors_address; + addr2->prop.errors_address = addr->prop.errors_address; copyflag(addr2, addr, af_hide_child | af_local_host_removed); DEBUG(D_deliver|D_route) @@ -6280,38 +6501,23 @@ while (addr_new != NULL) /* Loop until all addresses dealt with */ DEBUG(D_deliver|D_retry|D_route) { - address_item *p = addr_local; + address_item *p; debug_printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); debug_printf("After routing:\n Local deliveries:\n"); - while (p != NULL) - { + for (p = addr_local; p; p = p->next) debug_printf(" %s\n", p->address); - p = p->next; - } - p = addr_remote; debug_printf(" Remote deliveries:\n"); - while (p != NULL) - { + for (p = addr_remote; p; p = p->next) debug_printf(" %s\n", p->address); - p = p->next; - } - p = addr_failed; debug_printf(" Failed addresses:\n"); - while (p != NULL) - { + for (p = addr_failed; p; p = p->next) debug_printf(" %s\n", p->address); - p = p->next; - } - p = addr_defer; debug_printf(" Deferred addresses:\n"); - while (p != NULL) - { + for (p = addr_defer; p; p = p->next) debug_printf(" %s\n", p->address); - p = p->next; - } } /* Free any resources that were cached during routing. */ @@ -6338,18 +6544,19 @@ do_duplicate_check(&addr_remote); remote transport. The check that they all end up in one transaction happens in the do_remote_deliveries() function. */ -if (mua_wrapper && (addr_local != NULL || addr_failed != NULL || - addr_defer != NULL)) +if ( mua_wrapper + && (addr_local || addr_failed || addr_defer) + ) { address_item *addr; uschar *which, *colon, *msg; - if (addr_local != NULL) + if (addr_local) { addr = addr_local; which = US"local"; } - else if (addr_defer != NULL) + else if (addr_defer) { addr = addr_defer; which = US"deferred"; @@ -6360,9 +6567,9 @@ if (mua_wrapper && (addr_local != NULL || addr_failed != NULL || which = US"failed"; } - while (addr->parent != NULL) addr = addr->parent; + while (addr->parent) addr = addr->parent; - if (addr->message != NULL) + if (addr->message) { colon = US": "; msg = addr->message; @@ -6391,14 +6598,16 @@ if (mua_wrapper && (addr_local != NULL || addr_failed != NULL || /* If this is a run to continue deliveries to an external channel that is already set up, defer any local deliveries. */ -if (continue_transport != NULL) +if (continue_transport) { - if (addr_defer == NULL) addr_defer = addr_local; else + if (addr_defer) { address_item *addr = addr_defer; - while (addr->next != NULL) addr = addr->next; + while (addr->next) addr = addr->next; addr->next = addr_local; } + else + addr_defer = addr_local; addr_local = NULL; } @@ -6416,10 +6625,12 @@ remember them for all subsequent deliveries. This can be delayed till later if there is only address to be delivered - if it succeeds the spool write need not happen. */ -if (header_rewritten && - ((addr_local != NULL && - (addr_local->next != NULL || addr_remote != NULL)) || - (addr_remote != NULL && addr_remote->next != NULL))) +if ( header_rewritten + && ( ( addr_local + && (addr_local->next || addr_remote) + ) + || (addr_remote && addr_remote->next) + ) ) { /* Panic-dies on error */ (void)spool_write_header(message_id, SW_DELIVERING, NULL); @@ -6437,7 +6648,7 @@ ultimately updated at the end of processing, the journal is deleted. If a journal is found to exist at the start of delivery, the addresses listed therein are added to the non-recipients. */ -if (addr_local != NULL || addr_remote != NULL) +if (addr_local || addr_remote) { sprintf(CS spoolname, "%s/input/%s/%s-J", spool_directory, message_subdir, id); journal_fd = Uopen(spoolname, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE); @@ -6479,12 +6690,13 @@ for handling fallbacks, though the uid switching will have to be revised. */ to an LHLO command, if is isn't already compiled. This may be used on both local and remote LMTP deliveries. */ -if (regex_IGNOREQUOTA == NULL) regex_IGNOREQUOTA = - regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE); +if (!regex_IGNOREQUOTA) + regex_IGNOREQUOTA = + regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE); /* Handle local deliveries */ -if (addr_local != NULL) +if (addr_local) { DEBUG(D_deliver|D_transport) debug_printf(">>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>\n"); @@ -6496,8 +6708,7 @@ if (addr_local != NULL) so just queue them all. */ if (queue_run_local) - { - while (addr_remote != NULL) + while (addr_remote) { address_item *addr = addr_remote; addr_remote = addr->next; @@ -6506,11 +6717,10 @@ if (queue_run_local) addr->message = US"remote deliveries suppressed"; (void)post_process_one(addr, DEFER, LOG_MAIN, DTYPE_TRANSPORT, 0); } - } /* Handle remote deliveries */ -if (addr_remote != NULL) +if (addr_remote) { DEBUG(D_deliver|D_transport) debug_printf(">>>>>>>>>>>>>>>> Remote deliveries >>>>>>>>>>>>>>>>\n"); @@ -6524,7 +6734,7 @@ if (addr_remote != NULL) do_remote_deliveries is FALSE when mua_wrapper is set and all addresses cannot be delivered in one transaction. */ - if (remote_sort_domains != NULL) sort_remote_deliveries(); + if (remote_sort_domains) sort_remote_deliveries(); if (!do_remote_deliveries(FALSE)) { log_write(0, LOG_MAIN, "** mua_wrapper is set but recipients cannot all " @@ -6541,12 +6751,12 @@ if (addr_remote != NULL) host is used for many domains, so all can be sent in a single transaction (if appropriately configured). */ - if (addr_fallback != NULL && !mua_wrapper) + if (addr_fallback && !mua_wrapper) { DEBUG(D_deliver) debug_printf("Delivering to fallback hosts\n"); addr_remote = addr_fallback; addr_fallback = NULL; - if (remote_sort_domains != NULL) sort_remote_deliveries(); + if (remote_sort_domains) sort_remote_deliveries(); do_remote_deliveries(TRUE); } disable_logging = FALSE; @@ -6574,10 +6784,10 @@ do not ever want to retry, nor do we want to send a bounce message. */ if (mua_wrapper) { - if (addr_defer != NULL) + if (addr_defer) { address_item *addr, *nextaddr; - for (addr = addr_defer; addr != NULL; addr = nextaddr) + for (addr = addr_defer; addr; addr = nextaddr) { log_write(0, LOG_MAIN, "** %s mua_wrapper forced failure for deferred " "delivery", addr->address); @@ -6590,25 +6800,27 @@ if (mua_wrapper) /* Now all should either have succeeded or failed. */ - if (addr_failed == NULL) final_yield = DELIVER_MUA_SUCCEEDED; else + if (!addr_failed) + final_yield = DELIVER_MUA_SUCCEEDED; + else { - uschar *s = (addr_failed->user_message != NULL)? - addr_failed->user_message : addr_failed->message; host_item * host; + uschar *s = addr_failed->user_message; + + if (!s) s = addr_failed->message; fprintf(stderr, "Delivery failed: "); if (addr_failed->basic_errno > 0) { fprintf(stderr, "%s", strerror(addr_failed->basic_errno)); - if (s != NULL) fprintf(stderr, ": "); + if (s) fprintf(stderr, ": "); } if ((host = addr_failed->host_used)) fprintf(stderr, "H=%s [%s]: ", host->name, host->address); - if (s == NULL) - { - if (addr_failed->basic_errno <= 0) fprintf(stderr, "unknown error"); - } - else fprintf(stderr, "%s", CS s); + if (s) + fprintf(stderr, "%s", CS s); + else if (addr_failed->basic_errno <= 0) + fprintf(stderr, "unknown error"); fprintf(stderr, "\n"); final_yield = DELIVER_MUA_FAILED; @@ -6625,35 +6837,39 @@ retry cutoff time has expired for all alternative destinations. Bypass the updating of the database if the -N flag is set, which is a debugging thing that prevents actual delivery. */ -else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); +else if (!dont_deliver) + retry_update(&addr_defer, &addr_failed, &addr_succeed); -/* Send DSN for successful messages */ -addr_dsntmp = addr_succeed; +/* Send DSN for successful messages if requested */ addr_senddsn = NULL; -while(addr_dsntmp != NULL) +for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next) { - DEBUG(D_deliver) - debug_printf("DSN: processing router : %s\n", addr_dsntmp->router->name); - - DEBUG(D_deliver) - debug_printf("DSN: processing successful delivery address: %s\n", addr_dsntmp->address); - /* af_ignore_error not honored here. it's not an error */ - - DEBUG(D_deliver) debug_printf("DSN: Sender_address: %s\n", sender_address); - DEBUG(D_deliver) debug_printf("DSN: orcpt: %s flags: %d\n", addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags); - DEBUG(D_deliver) debug_printf("DSN: envid: %s ret: %d\n", dsn_envid, dsn_ret); - DEBUG(D_deliver) debug_printf("DSN: Final recipient: %s\n", addr_dsntmp->address); - DEBUG(D_deliver) debug_printf("DSN: Remote SMTP server supports DSN: %d\n", addr_dsntmp->dsn_aware); + DEBUG(D_deliver) debug_printf("DSN: processing router : %s\n" + "DSN: processing successful delivery address: %s\n" + "DSN: Sender_address: %s\n" + "DSN: orcpt: %s flags: %d\n" + "DSN: envid: %s ret: %d\n" + "DSN: Final recipient: %s\n" + "DSN: Remote SMTP server supports DSN: %d\n", + addr_dsntmp->router->name, + addr_dsntmp->address, + sender_address, + addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags, + dsn_envid, dsn_ret, + addr_dsntmp->address, + addr_dsntmp->dsn_aware + ); /* send report if next hop not DSN aware or a router flagged "last DSN hop" and a report was requested */ - if (((addr_dsntmp->dsn_aware != dsn_support_yes) || - ((addr_dsntmp->dsn_flags & rf_dsnlasthop) != 0)) - && - (((addr_dsntmp->dsn_flags & rf_dsnflags) != 0) && - ((addr_dsntmp->dsn_flags & rf_notify_success) != 0))) + if ( ( addr_dsntmp->dsn_aware != dsn_support_yes + || addr_dsntmp->dsn_flags & rf_dsnlasthop + ) + && addr_dsntmp->dsn_flags & rf_dsnflags + && addr_dsntmp->dsn_flags & rf_notify_success + ) { /* copy and relink address_item and send report with all of them at once later */ address_item *addr_next; @@ -6663,48 +6879,44 @@ while(addr_dsntmp != NULL) addr_senddsn->next = addr_next; } else - { - DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n"); - } - - addr_dsntmp = addr_dsntmp->next; + DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); } -if (addr_senddsn != NULL) +if (addr_senddsn) { pid_t pid; int fd; - /* create exim process to send message */ + /* create exim process to send message */ pid = child_open_exim(&fd); DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid); - + if (pid < 0) /* Creation of child failed */ { log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " "create child process to send failure message: %s", getpid(), getppid(), strerror(errno)); - DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); - - } + DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); + } else /* Creation of child succeeded */ { FILE *f = fdopen(fd, "wb"); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ int topt = topt_add_return_path | topt_no_body; uschar * bound; - - DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address); - + + DEBUG(D_deliver) + debug_printf("sending error message to: %s\n", sender_address); + /* build unique id for MIME boundary */ bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", bound); - + if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); - + fprintf(f, "Auto-Submitted: auto-generated\n" "From: Mail Delivery System <Mailer-Daemon@%s>\n" "To: %s\n" @@ -6714,14 +6926,13 @@ if (addr_senddsn != NULL) "--%s\n" "Content-type: text/plain; charset=us-ascii\n\n" - + "This message was created automatically by mail delivery software.\n" " ----- The following addresses had successful delivery notifications -----\n", qualify_domain_sender, sender_address, bound, bound); - addr_dsntmp = addr_senddsn; - while(addr_dsntmp) - { + for (addr_dsntmp = addr_senddsn; addr_dsntmp; + addr_dsntmp = addr_dsntmp->next) fprintf(f, "<%s> (relayed %s)\n\n", addr_dsntmp->address, (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 @@ -6730,15 +6941,14 @@ if (addr_senddsn != NULL) ? "to non-DSN-aware mailer" : "via non \"Remote SMTP\" router" ); - addr_dsntmp = addr_dsntmp->next; - } + fprintf(f, "--%s\n" "Content-type: message/delivery-status\n\n" "Reporting-MTA: dns; %s\n", bound, smtp_active_hostname); - if (dsn_envid != NULL) { - /* must be decoded from xtext: see RFC 3461:6.3a */ + if (dsn_envid) + { /* must be decoded from xtext: see RFC 3461:6.3a */ uschar *xdec_envid; if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); @@ -6760,20 +6970,19 @@ if (addr_senddsn != NULL) addr_dsntmp->address); if (addr_dsntmp->host_used && addr_dsntmp->host_used->name) - fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n", + fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n", addr_dsntmp->host_used->name); else - fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n", + fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n", (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP"); - fputc('\n', f); } fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); - + fflush(f); transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ - + /* Write the original email out */ transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); fflush(f); @@ -6791,7 +7000,7 @@ af_ignore_error is set, in which case no action is taken. It is possible for several messages to get sent if there are addresses with different requirements. */ -while (addr_failed != NULL) +while (addr_failed) { pid_t pid; int fd; @@ -6806,7 +7015,7 @@ while (addr_failed != NULL) there may not be a transport (address failed by a router). */ disable_logging = FALSE; - if (addr_failed->transport != NULL) + if (addr_failed->transport) disable_logging = addr_failed->transport->disable_logging; DEBUG(D_deliver) @@ -6828,10 +7037,10 @@ while (addr_failed != NULL) If neither of these cases obtains, something has gone wrong. Log the incident, but then ignore the error. */ - if (sender_address[0] == 0 && addr_failed->p.errors_address == NULL) + if (sender_address[0] == 0 && !addr_failed->prop.errors_address) { - if (!testflag(addr_failed, af_retry_timedout) && - !testflag(addr_failed, af_ignore_error)) + if ( !testflag(addr_failed, af_retry_timedout) + && !testflag(addr_failed, af_ignore_error)) { log_write(0, LOG_MAIN|LOG_PANIC, "internal error: bounce message " "failure is neither frozen nor ignored (it's been ignored)"); @@ -6844,19 +7053,19 @@ while (addr_failed != NULL) mark the recipient done. */ if ( testflag(addr_failed, af_ignore_error) - || ( ((addr_failed->dsn_flags & rf_dsnflags) != 0) - && ((addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure)) - ) - { + || ( addr_failed->dsn_flags & rf_dsnflags + && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure + ) ) + { addr = addr_failed; addr_failed = addr->next; - if (addr->return_filename != NULL) Uunlink(addr->return_filename); + if (addr->return_filename) Uunlink(addr->return_filename); log_write(0, LOG_MAIN, "%s%s%s%s: error ignored", addr->address, - (addr->parent == NULL)? US"" : US" <", - (addr->parent == NULL)? US"" : addr->parent->address, - (addr->parent == NULL)? US"" : US">"); + !addr->parent ? US"" : US" <", + !addr->parent ? US"" : addr->parent->address, + !addr->parent ? US"" : US">"); address_done(addr, logtod); child_done(addr, logtod); @@ -6872,16 +7081,12 @@ while (addr_failed != NULL) else { - bounce_recipient = (addr_failed->p.errors_address == NULL)? - sender_address : addr_failed->p.errors_address; + if (!(bounce_recipient = addr_failed->prop.errors_address)) + bounce_recipient = sender_address; /* Make a subprocess to send a message */ - pid = child_open_exim(&fd); - - /* Creation of child failed */ - - if (pid < 0) + if ((pid = child_open_exim(&fd)) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " "create child process to send failure message: %s", getpid(), getppid(), strerror(errno)); @@ -6911,28 +7116,24 @@ while (addr_failed != NULL) them from the addr_failed chain, and putting them on msgchain. */ paddr = &addr_failed; - for (addr = addr_failed; addr != NULL; addr = *paddr) - { - if (Ustrcmp(bounce_recipient, (addr->p.errors_address == NULL)? - sender_address : addr->p.errors_address) != 0) - { - paddr = &(addr->next); /* Not the same; skip */ - } - else /* The same - dechain */ - { + for (addr = addr_failed; addr; addr = *paddr) + if (Ustrcmp(bounce_recipient, addr->prop.errors_address + ? addr->prop.errors_address : sender_address) == 0) + { /* The same - dechain */ *paddr = addr->next; *pmsgchain = addr; addr->next = NULL; pmsgchain = &(addr->next); } - } + else + paddr = &addr->next; /* Not the same; skip */ /* Include X-Failed-Recipients: for automatic interpretation, but do not let any one header line get too long. We do this by starting a new header every 50 recipients. Omit any addresses for which the "hide_child" flag is set. */ - for (addr = msgchain; addr != NULL; addr = addr->next) + for (addr = msgchain; addr; addr = addr->next) { if (testflag(addr, af_hide_child)) continue; if (rcount >= 50) @@ -6941,16 +7142,18 @@ while (addr_failed != NULL) rcount = 0; } fprintf(f, "%s%s", - (rcount++ == 0)? "X-Failed-Recipients: " : ",\n ", - (testflag(addr, af_pfr) && addr->parent != NULL)? - string_printing(addr->parent->address) : - string_printing(addr->address)); + rcount++ == 0 + ? "X-Failed-Recipients: " + : ",\n ", + testflag(addr, af_pfr) && addr->parent + ? string_printing(addr->parent->address) + : string_printing(addr->address)); } if (rcount > 0) fprintf(f, "\n"); /* Output the standard headers */ - if (errors_reply_to != NULL) + if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); fprintf(f, "Auto-Submitted: auto-replied\n"); moan_write_from(f); @@ -7023,7 +7226,7 @@ wording. */ hidden. */ paddr = &msgchain; - for (addr = msgchain; addr != NULL; addr = *paddr) + for (addr = msgchain; addr; addr = *paddr) { if (print_address_information(addr, f, US" ", US"\n ", US"")) print_address_error(addr, f, US""); @@ -7076,7 +7279,7 @@ wording. */ "The following text was generated during the delivery " "attempt%s:\n", (filecount > 1)? "s" : ""); - for (addr = msgchain; addr != NULL; addr = nextaddr) + for (addr = msgchain; addr; addr = nextaddr) { FILE *fm; address_item *topaddr = addr; @@ -7095,9 +7298,7 @@ wording. */ /* Now copy the file */ - fm = Ufopen(addr->return_filename, "rb"); - - if (fm == NULL) + if (!(fm = Ufopen(addr->return_filename, "rb"))) fprintf(f, " +++ Exim error... failed to open text file: %s\n", strerror(errno)); else @@ -7118,10 +7319,18 @@ wording. */ } /* output machine readable part */ - fprintf(f, "--%s\n" - "Content-type: message/delivery-status\n\n" - "Reporting-MTA: dns; %s\n", - bound, smtp_active_hostname); +#ifdef SUPPORT_I18N + if (message_smtputf8) + fprintf(f, "--%s\n" + "Content-type: message/global-delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); + else +#endif + fprintf(f, "--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); if (dsn_envid) { @@ -7133,19 +7342,35 @@ wording. */ fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n"); } fputc('\n', f); - + for (addr = handled_addr; addr; addr = addr->next) { + host_item * hu; fprintf(f, "Action: failed\n" "Final-Recipient: rfc822;%s\n" "Status: 5.0.0\n", addr->address); - if (addr->host_used && addr->host_used->name) - { - fprintf(f, "Remote-MTA: dns; %s\n", - addr->host_used->name); - print_dsn_diagnostic_code(addr, f); - } + if ((hu = addr->host_used) && hu->name) + { + const uschar * s; + fprintf(f, "Remote-MTA: dns; %s\n", hu->name); +#ifdef EXPERIMENTAL_DSN_INFO + if (hu->address) + { + uschar * p = hu->port == 25 + ? US"" : string_sprintf(":%d", hu->port); + fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); + } + if ((s = addr->smtp_greeting) && *s) + fprintf(f, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s); + if ((s = addr->helo_response) && *s) + fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s); + if ((s = addr->message) && *s) + fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s); +#endif + print_dsn_diagnostic_code(addr, f); + } + fputc('\n', f); } /* Now copy the message, trying to give an intelligible comment if @@ -7156,17 +7381,17 @@ wording. */ emf_text = next_emf(emf, US"copy"); /* add message body - we ignore the intro text from template and add + we ignore the intro text from template and add the text for bounce_return_size_limit at the end. - + bounce_return_message is ignored in case RET= is defined we honor these values otherwise bounce_return_body is honored. - + bounce_return_size_limit is always honored. */ - - fprintf(f, "\n--%s\n", bound); + + fprintf(f, "--%s\n", bound); dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; dsnnotifyhdr = NULL; @@ -7194,11 +7419,17 @@ wording. */ dsnnotifyhdr = dsnlimitmsg; } } - - if (topt & topt_no_body) - fprintf(f,"Content-type: text/rfc822-headers\n\n"); + +#ifdef SUPPORT_I18N + if (message_smtputf8) + fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" + : "Content-type: message/global\n\n", + f); else - fprintf(f,"Content-type: message/rfc822\n\n"); +#endif + fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n" + : "Content-type: message/rfc822\n\n", + f); fflush(f); transport_filter_argv = NULL; /* Just in case */ @@ -7206,11 +7437,11 @@ wording. */ transport_write_message(NULL, fileno(f), topt, 0, dsnnotifyhdr, NULL, NULL, NULL, NULL, 0); fflush(f); - + /* we never add the final text. close the file */ if (emf) (void)fclose(emf); - + fprintf(f, "\n--%s--\n", bound); /* Close the file, which should send an EOF to the child process @@ -7235,7 +7466,7 @@ wording. */ if (rc != 0) { uschar *s = US""; - if (now - received_time < retry_maximum_timeout && addr_defer == NULL) + if (now - received_time < retry_maximum_timeout && !addr_defer) { addr_defer = (address_item *)(+1); deliver_freeze = TRUE; @@ -7255,7 +7486,7 @@ wording. */ else { - for (addr = handled_addr; addr != NULL; addr = addr->next) + for (addr = handled_addr; addr; addr = addr->next) { address_done(addr, logtod); child_done(addr, logtod); @@ -7277,7 +7508,7 @@ DELIVERY_TIDYUP: message log if so configured, and we are using them. Otherwise, sling it. Then delete the message itself. */ -if (addr_defer == NULL) +if (!addr_defer) { if (message_logs) { @@ -7298,11 +7529,9 @@ if (addr_defer == NULL) "msglog.OLD directory", spoolname); } else - { if (Uunlink(spoolname) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", spoolname, strerror(errno)); - } } /* Remove the two message files. */ @@ -7318,7 +7547,7 @@ if (addr_defer == NULL) /* Log the end of this message, with queue time if requested. */ - if ((log_extra_selector & LX_queue_time_overall) != 0) + if (LOGGING(queue_time_overall)) log_write(0, LOG_MAIN, "Completed QT=%s", readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); else @@ -7327,10 +7556,10 @@ if (addr_defer == NULL) /* Unset deliver_freeze so that we won't try to move the spool files further down */ deliver_freeze = FALSE; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT (void) event_raise(event_action, US"msg:complete", NULL); #endif -} + } /* If there are deferred addresses, we are keeping this message because it is not yet completed. Lose any temporary files that were catching output from @@ -7365,35 +7594,37 @@ else if (addr_defer != (address_item *)(+1)) uschar *recipients = US""; BOOL delivery_attempted = FALSE; - deliver_domain = testflag(addr_defer, af_pfr)? - addr_defer->parent->domain : addr_defer->domain; + deliver_domain = testflag(addr_defer, af_pfr) + ? addr_defer->parent->domain : addr_defer->domain; - for (addr = addr_defer; addr != NULL; addr = addr->next) + for (addr = addr_defer; addr; addr = addr->next) { address_item *otaddr; if (addr->basic_errno > ERRNO_RETRY_BASE) delivery_attempted = TRUE; - if (deliver_domain != NULL) + if (deliver_domain) { - uschar *d = (testflag(addr, af_pfr))? addr->parent->domain : addr->domain; + const uschar *d = testflag(addr, af_pfr) + ? addr->parent->domain : addr->domain; /* The domain may be unset for an address that has never been routed because the system filter froze the message. */ - if (d == NULL || Ustrcmp(d, deliver_domain) != 0) deliver_domain = NULL; + if (!d || Ustrcmp(d, deliver_domain) != 0) + deliver_domain = NULL; } - if (addr->return_filename != NULL) Uunlink(addr->return_filename); + if (addr->return_filename) Uunlink(addr->return_filename); /* Handle the case of one-time aliases. If any address in the ancestry of this one is flagged, ensure it is in the recipients list, suitably flagged, and that its parent is marked delivered. */ - for (otaddr = addr; otaddr != NULL; otaddr = otaddr->parent) - if (otaddr->onetime_parent != NULL) break; + for (otaddr = addr; otaddr; otaddr = otaddr->parent) + if (otaddr->onetime_parent) break; - if (otaddr != NULL) + if (otaddr) { int i; int t = recipients_count; @@ -7414,7 +7645,7 @@ else if (addr_defer != (address_item *)(+1)) DEBUG(D_deliver) debug_printf("one_time: adding %s in place of %s\n", otaddr->address, otaddr->parent->address); receive_add_recipient(otaddr->address, t); - recipients_list[recipients_count-1].errors_to = otaddr->p.errors_address; + recipients_list[recipients_count-1].errors_to = otaddr->prop.errors_address; tree_add_nonrecipient(otaddr->parent->address); update_spool = TRUE; } @@ -7425,20 +7656,18 @@ else if (addr_defer != (address_item *)(+1)) list of recipients for a warning message. */ if (sender_address[0] != 0) - { - if (addr->p.errors_address == NULL) + if (addr->prop.errors_address) { - if (Ustrstr(recipients, sender_address) == NULL) + if (Ustrstr(recipients, addr->prop.errors_address) == NULL) recipients = string_sprintf("%s%s%s", recipients, - (recipients[0] == 0)? "" : ",", sender_address); + (recipients[0] == 0)? "" : ",", addr->prop.errors_address); } else { - if (Ustrstr(recipients, addr->p.errors_address) == NULL) + if (Ustrstr(recipients, sender_address) == NULL) recipients = string_sprintf("%s%s%s", recipients, - (recipients[0] == 0)? "" : ",", addr->p.errors_address); + (recipients[0] == 0)? "" : ",", sender_address); } - } } /* Send a warning message if the conditions are right. If the condition check @@ -7453,7 +7682,7 @@ else if (addr_defer != (address_item *)(+1)) ) && delay_warning[1] > 0 && sender_address[0] != 0 - && ( delay_warning_condition == NULL + && ( !delay_warning_condition || expand_check_condition(delay_warning_condition, US"delay_warning", US"option") ) @@ -7521,17 +7750,14 @@ else if (addr_defer != (address_item *)(+1)) uschar * bound; if (warn_message_file) - { - wmf = Ufopen(warn_message_file, "rb"); - if (wmf == NULL) + if (!(wmf = Ufopen(warn_message_file, "rb"))) log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for warning " "message texts: %s", warn_message_file, strerror(errno)); - } warnmsg_recipients = recipients; - warnmsg_delay = (queue_time < 120*60)? - string_sprintf("%d minutes", show_time/60): - string_sprintf("%d hours", show_time/3600); + warnmsg_delay = queue_time < 120*60 + ? string_sprintf("%d minutes", show_time/60) + : string_sprintf("%d hours", show_time/3600); if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); @@ -7580,7 +7806,7 @@ else if (addr_defer != (address_item *)(+1)) "The message identifier is: %s\n", warnmsg_delay, primary_hostname, message_id); - for (h = header_list; h != NULL; h = h->next) + for (h = header_list; h; h = h->next) if (strncmpic(h->text, US"Subject:", 8) == 0) fprintf(f, "The subject of the message is: %s", h->text + 9); else if (strncmpic(h->text, US"Date:", 5) == 0) @@ -7631,7 +7857,7 @@ else if (addr_defer != (address_item *)(+1)) "Reporting-MTA: dns; %s\n", bound, smtp_active_hostname); - + if (dsn_envid) { @@ -7644,24 +7870,25 @@ else if (addr_defer != (address_item *)(+1)) } fputc('\n', f); - while (addr_dsndefer) + for ( ; addr_dsndefer; addr_dsndefer = addr_dsndefer->next) { if (addr_dsndefer->dsn_orcpt) - fprintf(f,"Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); + fprintf(f, "Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); - fprintf(f,"Action: delayed\n"); - fprintf(f,"Final-Recipient: rfc822;%s\n", addr_dsndefer->address); - fprintf(f,"Status: 4.0.0\n"); + fprintf(f, "Action: delayed\n" + "Final-Recipient: rfc822;%s\n" + "Status: 4.0.0\n", + addr_dsndefer->address); if (addr_dsndefer->host_used && addr_dsndefer->host_used->name) { - fprintf(f,"Remote-MTA: dns; %s\n", + fprintf(f, "Remote-MTA: dns; %s\n", addr_dsndefer->host_used->name); print_dsn_diagnostic_code(addr_dsndefer, f); } - addr_dsndefer = addr_dsndefer->next; + fputc('\n', f); } - fprintf(f, "\n--%s\n" + fprintf(f, "--%s\n" "Content-type: text/rfc822-headers\n\n", bound); @@ -7713,7 +7940,7 @@ else if (addr_defer != (address_item *)(+1)) if (deliver_freeze) { - if (freeze_tell != NULL && freeze_tell[0] != 0 && !local_error_message) + if (freeze_tell && freeze_tell[0] != 0 && !local_error_message) { uschar *s = string_copy(frozen_info); uschar *ss = Ustrstr(s, " by the system filter: "); @@ -7839,6 +8066,11 @@ if (!regex_PRDR) regex_PRDR = regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); #endif +#ifdef SUPPORT_I18N +if (!regex_UTF8) regex_UTF8 = + regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE); +#endif + if (!regex_DSN) regex_DSN = regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); @@ -7847,6 +8079,41 @@ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA = } +uschar * +deliver_get_sender_address (uschar * id) +{ +int rc; +uschar * new_sender_address, + * save_sender_address; + +if (!spool_open_datafile(id)) + return NULL; + +/* Save and restore the global sender_address. I'm not sure if we should +not save/restore all the other global variables too, because +spool_read_header() may change all of them. But OTOH, when this +deliver_get_sender_address() gets called, the current message is done +already and nobody needs the globals anymore. (HS12, 2015-08-21) */ + +sprintf(CS spoolname, "%s-H", id); +save_sender_address = sender_address; + +rc = spool_read_header(spoolname, TRUE, TRUE); + +new_sender_address = sender_address; +sender_address = save_sender_address; + +if (rc != spool_read_OK) + return NULL; + +assert(new_sender_address); + +(void)close(deliver_datafile); +deliver_datafile = -1; + +return new_sender_address; +} + /* vi: aw ai sw=2 */ /* End of deliver.c */ diff --git a/src/src/dkim.c b/src/src/dkim.c index 171fcccdb..3fa43fc3d 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge, 1995 - 2007 */ +/* Copyright (c) University of Cambridge, 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for DKIM support. Other DKIM relevant code is in @@ -14,567 +14,608 @@ #include "pdkim/pdkim.h" -pdkim_ctx *dkim_verify_ctx = NULL; +pdkim_ctx *dkim_verify_ctx = NULL; pdkim_signature *dkim_signatures = NULL; -pdkim_signature *dkim_cur_sig = NULL; - -int dkim_exim_query_dns_txt(char *name, char *answer) { - dns_answer dnsa; - dns_scan dnss; - dns_record *rr; - - lookup_dnssec_authenticated = NULL; - if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL; - - /* Search for TXT record */ - for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; - rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) - if (rr->type == T_TXT) break; - - /* Copy record content to the answer buffer */ - if (rr != NULL) { +pdkim_signature *dkim_cur_sig = NULL; + +static int +dkim_exim_query_dns_txt(char *name, char *answer) +{ +dns_answer dnsa; +dns_scan dnss; +dns_record *rr; + +lookup_dnssec_authenticated = NULL; +if (dns_lookup(&dnsa, US name, T_TXT, NULL) != DNS_SUCCEED) + return PDKIM_FAIL; + +/* Search for TXT record */ + +for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); + rr; + rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) + if (rr->type == T_TXT) + { int rr_offset = 0; int answer_offset = 0; - while (rr_offset < rr->size) { - uschar len = (rr->data)[rr_offset++]; - snprintf(answer+(answer_offset), - PDKIM_DNS_TXT_MAX_RECLEN-(answer_offset), - "%.*s", (int)len, (char *)((rr->data)+rr_offset)); - rr_offset+=len; - answer_offset+=len; - if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN) { - return PDKIM_FAIL; + + /* Copy record content to the answer buffer */ + + while (rr_offset < rr->size) + { + uschar len = rr->data[rr_offset++]; + snprintf(answer + answer_offset, + PDKIM_DNS_TXT_MAX_RECLEN - answer_offset, + "%.*s", (int)len, (char *) (rr->data + rr_offset)); + rr_offset += len; + answer_offset += len; + if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN) + return PDKIM_FAIL; } + return PDKIM_OK; } - } - else return PDKIM_FAIL; - return PDKIM_OK; +return PDKIM_FAIL; } -void dkim_exim_verify_init(void) { +void +dkim_exim_verify_init(void) +{ +/* Free previous context if there is one */ - /* Free previous context if there is one */ - if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx); +if (dkim_verify_ctx) + pdkim_free_ctx(dkim_verify_ctx); - /* Create new context */ - dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP, - &dkim_exim_query_dns_txt - ); - - if (dkim_verify_ctx != NULL) { - dkim_collect_input = TRUE; - pdkim_set_debug_stream(dkim_verify_ctx,debug_file); - } - else dkim_collect_input = FALSE; +/* Create new context */ +dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt); +dkim_collect_input = !!dkim_verify_ctx; } -void dkim_exim_verify_feed(uschar *data, int len) { - if (dkim_collect_input && - pdkim_feed(dkim_verify_ctx, - (char *)data, - len) != PDKIM_OK) dkim_collect_input = FALSE; +void +dkim_exim_verify_feed(uschar * data, int len) +{ +if ( dkim_collect_input + && pdkim_feed(dkim_verify_ctx, (char *)data, len) != PDKIM_OK) + dkim_collect_input = FALSE; } -void dkim_exim_verify_finish(void) { - pdkim_signature *sig = NULL; - int dkim_signers_size = 0; - int dkim_signers_ptr = 0; - dkim_signers = NULL; +void +dkim_exim_verify_finish(void) +{ +pdkim_signature *sig = NULL; +int dkim_signers_size = 0; +int dkim_signers_ptr = 0; +dkim_signers = NULL; - /* Delete eventual previous signature chain */ - dkim_signatures = NULL; +/* Delete eventual previous signature chain */ - /* If we have arrived here with dkim_collect_input == FALSE, it - means there was a processing error somewhere along the way. - Log the incident and disable futher verification. */ - if (!dkim_collect_input) { - log_write(0, LOG_MAIN, "DKIM: Error while running this message through validation, disabling signature verification."); - dkim_disable_verify = TRUE; - return; +dkim_signatures = NULL; + +/* If we have arrived here with dkim_collect_input == FALSE, it +means there was a processing error somewhere along the way. +Log the incident and disable futher verification. */ + +if (!dkim_collect_input) + { + log_write(0, LOG_MAIN, + "DKIM: Error while running this message through validation," + " disabling signature verification."); + dkim_disable_verify = TRUE; + return; } - dkim_collect_input = FALSE; - /* Finish DKIM operation and fetch link to signatures chain */ - if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return; - - sig = dkim_signatures; - while (sig != NULL) { - int size = 0; - int ptr = 0; - /* Log a line for each signature */ - uschar *logmsg = string_append(NULL, &size, &ptr, 5, - - string_sprintf( "d=%s s=%s c=%s/%s a=%s ", - sig->domain, - sig->selector, - (sig->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed", - (sig->canon_body == PDKIM_CANON_SIMPLE)?"simple":"relaxed", - (sig->algo == PDKIM_ALGO_RSA_SHA256)?"rsa-sha256":"rsa-sha1" - ), - ((sig->identity != NULL)? - string_sprintf("i=%s ", sig->identity) - : - US"" - ), - ((sig->created > 0)? - string_sprintf("t=%lu ", sig->created) - : - US"" - ), - ((sig->expires > 0)? - string_sprintf("x=%lu ", sig->expires) - : - US"" - ), - ((sig->bodylength > -1)? - string_sprintf("l=%lu ", sig->bodylength) - : - US"" - ) - ); - - switch(sig->verify_status) { - case PDKIM_VERIFY_NONE: - logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]"); +dkim_collect_input = FALSE; + +/* Finish DKIM operation and fetch link to signatures chain */ + +if (pdkim_feed_finish(dkim_verify_ctx, &dkim_signatures) != PDKIM_OK) + return; + +for (sig = dkim_signatures; sig; sig = sig->next) + { + int size = 0; + int ptr = 0; + + /* Log a line for each signature */ + + uschar *logmsg = string_append(NULL, &size, &ptr, 5, + string_sprintf("d=%s s=%s c=%s/%s a=%s b=%d ", + sig->domain, + sig->selector, + sig->canon_headers == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", + sig->canon_body == PDKIM_CANON_SIMPLE ? "simple" : "relaxed", + sig->algo == PDKIM_ALGO_RSA_SHA256 ? "rsa-sha256" : "rsa-sha1", + sig->sigdata_len * 8 + ), + + sig->identity ? string_sprintf("i=%s ", sig->identity) : US"", + sig->created > 0 ? string_sprintf("t=%lu ", sig->created) : US"", + sig->expires > 0 ? string_sprintf("x=%lu ", sig->expires) : US"", + sig->bodylength > -1 ? string_sprintf("l=%lu ", sig->bodylength) : US"" + ); + + switch (sig->verify_status) + { + case PDKIM_VERIFY_NONE: + logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]"); break; - case PDKIM_VERIFY_INVALID: - logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - "); - switch (sig->verify_ext_status) { - case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: - logmsg = string_append(logmsg, &size, &ptr, 1, "public key record (currently?) unavailable]"); - break; - case PDKIM_VERIFY_INVALID_BUFFER_SIZE: - logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]"); - break; - case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: - logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]"); - break; - default: - logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]"); - } + + case PDKIM_VERIFY_INVALID: + logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - "); + switch (sig->verify_ext_status) + { + case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: + logmsg = string_append(logmsg, &size, &ptr, 1, + "public key record (currently?) unavailable]"); + break; + + case PDKIM_VERIFY_INVALID_BUFFER_SIZE: + logmsg = string_append(logmsg, &size, &ptr, 1, + "overlong public key record]"); + break; + + case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: + logmsg = string_append(logmsg, &size, &ptr, 1, + "syntax error in public key record]"); + break; + + default: + logmsg = string_append(logmsg, &size, &ptr, 1, + "unspecified problem]"); + } break; - case PDKIM_VERIFY_FAIL: - logmsg = string_append(logmsg, &size, &ptr, 1, "[verification failed - "); - switch (sig->verify_ext_status) { - case PDKIM_VERIFY_FAIL_BODY: - logmsg = string_append(logmsg, &size, &ptr, 1, "body hash mismatch (body probably modified in transit)]"); - break; - case PDKIM_VERIFY_FAIL_MESSAGE: - logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]"); - break; - default: - logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]"); - } + + case PDKIM_VERIFY_FAIL: + logmsg = + string_append(logmsg, &size, &ptr, 1, "[verification failed - "); + switch (sig->verify_ext_status) + { + case PDKIM_VERIFY_FAIL_BODY: + logmsg = string_append(logmsg, &size, &ptr, 1, + "body hash mismatch (body probably modified in transit)]"); + break; + + case PDKIM_VERIFY_FAIL_MESSAGE: + logmsg = string_append(logmsg, &size, &ptr, 1, + "signature did not verify (headers probably modified in transit)]"); + break; + + default: + logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]"); + } break; - case PDKIM_VERIFY_PASS: - logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]"); + + case PDKIM_VERIFY_PASS: + logmsg = + string_append(logmsg, &size, &ptr, 1, "[verification succeeded]"); break; } - logmsg[ptr] = '\0'; - log_write(0, LOG_MAIN, "DKIM: %s", logmsg); + logmsg[ptr] = '\0'; + log_write(0, LOG_MAIN, "DKIM: %s", logmsg); + + /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */ + + dkim_signers = string_append(dkim_signers, + &dkim_signers_size, + &dkim_signers_ptr, 2, sig->domain, ":"); - /* Build a colon-separated list of signing domains (and identities, if present) in dkim_signers */ + if (sig->identity) dkim_signers = string_append(dkim_signers, - &dkim_signers_size, - &dkim_signers_ptr, - 2, - sig->domain, - ":" - ); - - if (sig->identity != NULL) { - dkim_signers = string_append(dkim_signers, - &dkim_signers_size, - &dkim_signers_ptr, - 2, - sig->identity, - ":" - ); - } + &dkim_signers_size, + &dkim_signers_ptr, 2, sig->identity, ":"); - /* Process next signature */ - sig = sig->next; + /* Process next signature */ } - /* NULL-terminate and chop the last colon from the domain list */ - if (dkim_signers != NULL) { - dkim_signers[dkim_signers_ptr] = '\0'; - if (Ustrlen(dkim_signers) > 0) - dkim_signers[Ustrlen(dkim_signers)-1] = '\0'; +/* NULL-terminate and chop the last colon from the domain list */ + +if (dkim_signers) + { + dkim_signers[dkim_signers_ptr] = '\0'; + if (Ustrlen(dkim_signers) > 0) + dkim_signers[Ustrlen(dkim_signers) - 1] = '\0'; } } -void dkim_exim_acl_setup(uschar *id) { - pdkim_signature *sig = dkim_signatures; - dkim_cur_sig = NULL; - dkim_cur_signer = id; - if (dkim_disable_verify || - !id || !dkim_verify_ctx) return; - /* Find signature to run ACL on */ - while (sig != NULL) { - uschar *cmp_val = NULL; - if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity; - else cmp_val = (uschar *)sig->domain; - if (cmp_val && (strcmpic(cmp_val,id) == 0)) { - dkim_cur_sig = sig; - /* The "dkim_domain" and "dkim_selector" expansion variables have - related globals, since they are used in the signing code too. - Instead of inventing separate names for verification, we set - them here. This is easy since a domain and selector is guaranteed - to be in a signature. The other dkim_* expansion items are - dynamically fetched from dkim_cur_sig at expansion time (see - function below). */ - dkim_signing_domain = (uschar *)sig->domain; - dkim_signing_selector = (uschar *)sig->selector; - return; +void +dkim_exim_acl_setup(uschar * id) +{ +pdkim_signature * sig; +uschar * cmp_val; + +dkim_cur_sig = NULL; +dkim_cur_signer = id; + +if (dkim_disable_verify || !id || !dkim_verify_ctx) + return; + +/* Find signature to run ACL on */ + +for (sig = dkim_signatures; sig; sig = sig->next) + if ( (cmp_val = Ustrchr(id, '@') != NULL ? US sig->identity : US sig->domain) + && strcmpic(cmp_val, id) == 0 + ) + { + dkim_cur_sig = sig; + + /* The "dkim_domain" and "dkim_selector" expansion variables have + related globals, since they are used in the signing code too. + Instead of inventing separate names for verification, we set + them here. This is easy since a domain and selector is guaranteed + to be in a signature. The other dkim_* expansion items are + dynamically fetched from dkim_cur_sig at expansion time (see + function below). */ + + dkim_signing_domain = US sig->domain; + dkim_signing_selector = US sig->selector; + dkim_key_length = sig->sigdata_len * 8; + return; } - sig = sig->next; - } } -uschar *dkim_exim_expand_query(int what) { +static uschar * +dkim_exim_expand_defaults(int what) +{ +switch (what) + { + case DKIM_ALGO: return US""; + case DKIM_BODYLENGTH: return US"9999999999999"; + case DKIM_CANON_BODY: return US""; + case DKIM_CANON_HEADERS: return US""; + case DKIM_COPIEDHEADERS: return US""; + case DKIM_CREATED: return US"0"; + case DKIM_EXPIRES: return US"9999999999999"; + case DKIM_HEADERNAMES: return US""; + case DKIM_IDENTITY: return US""; + case DKIM_KEY_GRANULARITY: return US"*"; + case DKIM_KEY_SRVTYPE: return US"*"; + case DKIM_KEY_NOTES: return US""; + case DKIM_KEY_TESTING: return US"0"; + case DKIM_NOSUBDOMAINS: return US"0"; + case DKIM_VERIFY_STATUS: return US"none"; + case DKIM_VERIFY_REASON: return US""; + default: return US""; + } +} - if (!dkim_verify_ctx || - dkim_disable_verify || - !dkim_cur_sig) return dkim_exim_expand_defaults(what); - switch(what) { - case DKIM_ALGO: - switch(dkim_cur_sig->algo) { - case PDKIM_ALGO_RSA_SHA1: - return US"rsa-sha1"; - case PDKIM_ALGO_RSA_SHA256: - default: - return US"rsa-sha256"; +uschar * +dkim_exim_expand_query(int what) +{ +if (!dkim_verify_ctx || dkim_disable_verify || !dkim_cur_sig) + return dkim_exim_expand_defaults(what); + +switch (what) + { + case DKIM_ALGO: + switch (dkim_cur_sig->algo) + { + case PDKIM_ALGO_RSA_SHA1: return US"rsa-sha1"; + case PDKIM_ALGO_RSA_SHA256: + default: return US"rsa-sha256"; } - case DKIM_BODYLENGTH: - return (dkim_cur_sig->bodylength >= 0)? - (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength) - :dkim_exim_expand_defaults(what); - case DKIM_CANON_BODY: - switch(dkim_cur_sig->canon_body) { - case PDKIM_CANON_RELAXED: - return US"relaxed"; - case PDKIM_CANON_SIMPLE: - default: - return US"simple"; - } - case DKIM_CANON_HEADERS: - switch(dkim_cur_sig->canon_headers) { - case PDKIM_CANON_RELAXED: - return US"relaxed"; - case PDKIM_CANON_SIMPLE: - default: - return US"simple"; + + case DKIM_BODYLENGTH: + return dkim_cur_sig->bodylength >= 0 + ? string_sprintf(OFF_T_FMT, (LONGLONG_T) dkim_cur_sig->bodylength) + : dkim_exim_expand_defaults(what); + + case DKIM_CANON_BODY: + switch (dkim_cur_sig->canon_body) + { + case PDKIM_CANON_RELAXED: return US"relaxed"; + case PDKIM_CANON_SIMPLE: + default: return US"simple"; } - case DKIM_COPIEDHEADERS: - return dkim_cur_sig->copiedheaders? - (uschar *)(dkim_cur_sig->copiedheaders) - :dkim_exim_expand_defaults(what); - case DKIM_CREATED: - return (dkim_cur_sig->created > 0)? - (uschar *)string_sprintf("%llu",dkim_cur_sig->created) - :dkim_exim_expand_defaults(what); - case DKIM_EXPIRES: - return (dkim_cur_sig->expires > 0)? - (uschar *)string_sprintf("%llu",dkim_cur_sig->expires) - :dkim_exim_expand_defaults(what); - case DKIM_HEADERNAMES: - return dkim_cur_sig->headernames? - (uschar *)(dkim_cur_sig->headernames) - :dkim_exim_expand_defaults(what); - case DKIM_IDENTITY: - return dkim_cur_sig->identity? - (uschar *)(dkim_cur_sig->identity) - :dkim_exim_expand_defaults(what); - case DKIM_KEY_GRANULARITY: - return dkim_cur_sig->pubkey? - (dkim_cur_sig->pubkey->granularity? - (uschar *)(dkim_cur_sig->pubkey->granularity) - :dkim_exim_expand_defaults(what) - ) - :dkim_exim_expand_defaults(what); - case DKIM_KEY_SRVTYPE: - return dkim_cur_sig->pubkey? - (dkim_cur_sig->pubkey->srvtype? - (uschar *)(dkim_cur_sig->pubkey->srvtype) - :dkim_exim_expand_defaults(what) - ) - :dkim_exim_expand_defaults(what); - case DKIM_KEY_NOTES: - return dkim_cur_sig->pubkey? - (dkim_cur_sig->pubkey->notes? - (uschar *)(dkim_cur_sig->pubkey->notes) - :dkim_exim_expand_defaults(what) - ) - :dkim_exim_expand_defaults(what); - case DKIM_KEY_TESTING: - return dkim_cur_sig->pubkey? - (dkim_cur_sig->pubkey->testing? - US"1" - :dkim_exim_expand_defaults(what) - ) - :dkim_exim_expand_defaults(what); - case DKIM_NOSUBDOMAINS: - return dkim_cur_sig->pubkey? - (dkim_cur_sig->pubkey->no_subdomaining? - US"1" - :dkim_exim_expand_defaults(what) - ) - :dkim_exim_expand_defaults(what); - case DKIM_VERIFY_STATUS: - switch(dkim_cur_sig->verify_status) { - case PDKIM_VERIFY_INVALID: - return US"invalid"; - case PDKIM_VERIFY_FAIL: - return US"fail"; - case PDKIM_VERIFY_PASS: - return US"pass"; - case PDKIM_VERIFY_NONE: - default: - return US"none"; + + case DKIM_CANON_HEADERS: + switch (dkim_cur_sig->canon_headers) + { + case PDKIM_CANON_RELAXED: return US"relaxed"; + case PDKIM_CANON_SIMPLE: + default: return US"simple"; + } + + case DKIM_COPIEDHEADERS: + return dkim_cur_sig->copiedheaders + ? US dkim_cur_sig->copiedheaders : dkim_exim_expand_defaults(what); + + case DKIM_CREATED: + return dkim_cur_sig->created > 0 + ? string_sprintf("%llu", dkim_cur_sig->created) + : dkim_exim_expand_defaults(what); + + case DKIM_EXPIRES: + return dkim_cur_sig->expires > 0 + ? string_sprintf("%llu", dkim_cur_sig->expires) + : dkim_exim_expand_defaults(what); + + case DKIM_HEADERNAMES: + return dkim_cur_sig->headernames + ? US dkim_cur_sig->headernames : dkim_exim_expand_defaults(what); + + case DKIM_IDENTITY: + return dkim_cur_sig->identity + ? US dkim_cur_sig->identity : dkim_exim_expand_defaults(what); + + case DKIM_KEY_GRANULARITY: + return dkim_cur_sig->pubkey + ? dkim_cur_sig->pubkey->granularity + ? US dkim_cur_sig->pubkey->granularity + : dkim_exim_expand_defaults(what) + : dkim_exim_expand_defaults(what); + + case DKIM_KEY_SRVTYPE: + return dkim_cur_sig->pubkey + ? dkim_cur_sig->pubkey->srvtype + ? US dkim_cur_sig->pubkey->srvtype + : dkim_exim_expand_defaults(what) + : dkim_exim_expand_defaults(what); + + case DKIM_KEY_NOTES: + return dkim_cur_sig->pubkey + ? dkim_cur_sig->pubkey->notes + ? US dkim_cur_sig->pubkey->notes + : dkim_exim_expand_defaults(what) + : dkim_exim_expand_defaults(what); + + case DKIM_KEY_TESTING: + return dkim_cur_sig->pubkey + ? dkim_cur_sig->pubkey->testing + ? US"1" + : dkim_exim_expand_defaults(what) + : dkim_exim_expand_defaults(what); + + case DKIM_NOSUBDOMAINS: + return dkim_cur_sig->pubkey + ? dkim_cur_sig->pubkey->no_subdomaining + ? US"1" + : dkim_exim_expand_defaults(what) + : dkim_exim_expand_defaults(what); + + case DKIM_VERIFY_STATUS: + switch (dkim_cur_sig->verify_status) + { + case PDKIM_VERIFY_INVALID: return US"invalid"; + case PDKIM_VERIFY_FAIL: return US"fail"; + case PDKIM_VERIFY_PASS: return US"pass"; + case PDKIM_VERIFY_NONE: + default: return US"none"; } - case DKIM_VERIFY_REASON: - switch (dkim_cur_sig->verify_ext_status) { - case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: - return US"pubkey_unavailable"; - case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: - return US"pubkey_syntax"; - case PDKIM_VERIFY_FAIL_BODY: - return US"bodyhash_mismatch"; - case PDKIM_VERIFY_FAIL_MESSAGE: - return US"signature_incorrect"; + + case DKIM_VERIFY_REASON: + switch (dkim_cur_sig->verify_ext_status) + { + case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: + return US"pubkey_unavailable"; + case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: return US"pubkey_syntax"; + case PDKIM_VERIFY_FAIL_BODY: return US"bodyhash_mismatch"; + case PDKIM_VERIFY_FAIL_MESSAGE: return US"signature_incorrect"; } - default: - return US""; + + default: + return US""; } } -uschar *dkim_exim_expand_defaults(int what) { - switch(what) { - case DKIM_ALGO: return US""; - case DKIM_BODYLENGTH: return US"9999999999999"; - case DKIM_CANON_BODY: return US""; - case DKIM_CANON_HEADERS: return US""; - case DKIM_COPIEDHEADERS: return US""; - case DKIM_CREATED: return US"0"; - case DKIM_EXPIRES: return US"9999999999999"; - case DKIM_HEADERNAMES: return US""; - case DKIM_IDENTITY: return US""; - case DKIM_KEY_GRANULARITY: return US"*"; - case DKIM_KEY_SRVTYPE: return US"*"; - case DKIM_KEY_NOTES: return US""; - case DKIM_KEY_TESTING: return US"0"; - case DKIM_NOSUBDOMAINS: return US"0"; - case DKIM_VERIFY_STATUS: return US"none"; - case DKIM_VERIFY_REASON: return US""; - default: return US""; +uschar * +dkim_exim_sign(int dkim_fd, uschar * dkim_private_key, + const uschar * dkim_domain, uschar * dkim_selector, + uschar * dkim_canon, uschar * dkim_sign_headers) +{ +int sep = 0; +uschar *seen_items = NULL; +int seen_items_size = 0; +int seen_items_offset = 0; +uschar itembuf[256]; +uschar *dkim_canon_expanded; +uschar *dkim_sign_headers_expanded; +uschar *dkim_private_key_expanded; +pdkim_ctx *ctx = NULL; +uschar *rc = NULL; +uschar *sigbuf = NULL; +int sigsize = 0; +int sigptr = 0; +pdkim_signature *signature; +int pdkim_canon; +int pdkim_rc; +int sread; +char buf[4096]; +int save_errno = 0; +int old_pool = store_pool; + +store_pool = POOL_MAIN; + +if (!(dkim_domain = expand_cstring(dkim_domain))) + { + /* expansion error, do not send message. */ + log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand " + "dkim_domain: %s", expand_string_message); + rc = NULL; + goto CLEANUP; } -} +/* Set $dkim_domain expansion variable to each unique domain in list. */ + +while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, + itembuf, sizeof(itembuf)))) + { + if (!dkim_signing_domain || dkim_signing_domain[0] == '\0') + continue; + + /* Only sign once for each domain, no matter how often it + appears in the expanded list. */ + + if (seen_items) + { + const uschar *seen_items_list = seen_items; + if (match_isinlist(dkim_signing_domain, + &seen_items_list, 0, NULL, NULL, MCL_STRING, TRUE, + NULL) == OK) + continue; + + seen_items = + string_append(seen_items, &seen_items_size, &seen_items_offset, 1, ":"); + } + + seen_items = + string_append(seen_items, &seen_items_size, &seen_items_offset, 1, + dkim_signing_domain); + seen_items[seen_items_offset] = '\0'; + + /* Set up $dkim_selector expansion variable. */ -uschar *dkim_exim_sign(int dkim_fd, - uschar *dkim_private_key, - uschar *dkim_domain, - uschar *dkim_selector, - uschar *dkim_canon, - uschar *dkim_sign_headers) { - int sep = 0; - uschar *seen_items = NULL; - int seen_items_size = 0; - int seen_items_offset = 0; - uschar itembuf[256]; - uschar *dkim_canon_expanded; - uschar *dkim_sign_headers_expanded; - uschar *dkim_private_key_expanded; - pdkim_ctx *ctx = NULL; - uschar *rc = NULL; - uschar *sigbuf = NULL; - int sigsize = 0; - int sigptr = 0; - pdkim_signature *signature; - int pdkim_canon; - int pdkim_rc; - int sread; - char buf[4096]; - int save_errno = 0; - int old_pool = store_pool; - - store_pool = POOL_MAIN; - - dkim_domain = expand_string(dkim_domain); - if (dkim_domain == NULL) { + if (!(dkim_signing_selector = expand_string(dkim_selector))) + { + log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand " + "dkim_selector: %s", expand_string_message); + rc = NULL; + goto CLEANUP; + } + + /* Get canonicalization to use */ + + dkim_canon_expanded = dkim_canon ? expand_string(dkim_canon) : US"relaxed"; + if (!dkim_canon_expanded) + { /* expansion error, do not send message. */ - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_domain: %s", expand_string_message); + log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand " + "dkim_canon: %s", expand_string_message); rc = NULL; goto CLEANUP; - } + } - /* Set $dkim_domain expansion variable to each unique domain in list. */ - while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep, - itembuf, - sizeof(itembuf))) != NULL) { - if (!dkim_signing_domain || (dkim_signing_domain[0] == '\0')) continue; - /* Only sign once for each domain, no matter how often it - appears in the expanded list. */ - if (seen_items != NULL) { - uschar *seen_items_list = seen_items; - if (match_isinlist(dkim_signing_domain, - &seen_items_list,0,NULL,NULL,MCL_STRING,TRUE,NULL) == OK) - continue; - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":"); + if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0) + pdkim_canon = PDKIM_CANON_RELAXED; + else if (Ustrcmp(dkim_canon_expanded, "simple") == 0) + pdkim_canon = PDKIM_CANON_SIMPLE; + else + { + log_write(0, LOG_MAIN, + "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n", + dkim_canon_expanded); + pdkim_canon = PDKIM_CANON_RELAXED; } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,dkim_signing_domain); - seen_items[seen_items_offset] = '\0'; - - /* Set up $dkim_selector expansion variable. */ - dkim_signing_selector = expand_string(dkim_selector); - if (dkim_signing_selector == NULL) { - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_selector: %s", expand_string_message); + + dkim_sign_headers_expanded = NULL; + if (dkim_sign_headers) + if (!(dkim_sign_headers_expanded = expand_string(dkim_sign_headers))) + { + log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand " + "dkim_sign_headers: %s", expand_string_message); rc = NULL; goto CLEANUP; + } + /* else pass NULL, which means default header list */ + + /* Get private key to use. */ + + if (!(dkim_private_key_expanded = expand_string(dkim_private_key))) + { + log_write(0, LOG_MAIN | LOG_PANIC, "failed to expand " + "dkim_private_key: %s", expand_string_message); + rc = NULL; + goto CLEANUP; } - /* Get canonicalization to use */ - dkim_canon_expanded = expand_string(dkim_canon?dkim_canon:US"relaxed"); - if (dkim_canon_expanded == NULL) { - /* expansion error, do not send message. */ - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_canon: %s", expand_string_message); + if ( Ustrlen(dkim_private_key_expanded) == 0 + || Ustrcmp(dkim_private_key_expanded, "0") == 0 + || Ustrcmp(dkim_private_key_expanded, "false") == 0 + ) + continue; /* don't sign, but no error */ + + if (dkim_private_key_expanded[0] == '/') + { + int privkey_fd = 0; + + /* Looks like a filename, load the private key. */ + + memset(big_buffer, 0, big_buffer_size); + privkey_fd = open(CS dkim_private_key_expanded, O_RDONLY); + if (privkey_fd < 0) + { + log_write(0, LOG_MAIN | LOG_PANIC, "unable to open " + "private key file for reading: %s", + dkim_private_key_expanded); rc = NULL; goto CLEANUP; - } - if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0) - pdkim_canon = PDKIM_CANON_RELAXED; - else if (Ustrcmp(dkim_canon_expanded, "simple") == 0) - pdkim_canon = PDKIM_CANON_SIMPLE; - else { - log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon_expanded); - pdkim_canon = PDKIM_CANON_RELAXED; - } - - if (dkim_sign_headers) { - dkim_sign_headers_expanded = expand_string(dkim_sign_headers); - if (dkim_sign_headers_expanded == NULL) { - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_sign_headers: %s", expand_string_message); - rc = NULL; - goto CLEANUP; } - } - else { - /* pass NULL, which means default header list */ - dkim_sign_headers_expanded = NULL; - } - /* Get private key to use. */ - dkim_private_key_expanded = expand_string(dkim_private_key); - if (dkim_private_key_expanded == NULL) { - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "dkim_private_key: %s", expand_string_message); + if (read(privkey_fd, big_buffer, big_buffer_size - 2) < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s", + dkim_private_key_expanded); rc = NULL; goto CLEANUP; - } - if ( (Ustrlen(dkim_private_key_expanded) == 0) || - (Ustrcmp(dkim_private_key_expanded,"0") == 0) || - (Ustrcmp(dkim_private_key_expanded,"false") == 0) ) { - /* don't sign, but no error */ - continue; - } - - if (dkim_private_key_expanded[0] == '/') { - int privkey_fd = 0; - /* Looks like a filename, load the private key. */ - memset(big_buffer,0,big_buffer_size); - privkey_fd = open(CS dkim_private_key_expanded,O_RDONLY); - if (privkey_fd < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, "unable to open " - "private key file for reading: %s", dkim_private_key_expanded); - rc = NULL; - goto CLEANUP; - } - if (read(privkey_fd,big_buffer,(big_buffer_size-2)) < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s", - dkim_private_key_expanded); - rc = NULL; - goto CLEANUP; } - (void)close(privkey_fd); - dkim_private_key_expanded = big_buffer; - } - ctx = pdkim_init_sign(PDKIM_INPUT_SMTP, - (char *)dkim_signing_domain, - (char *)dkim_signing_selector, - (char *)dkim_private_key_expanded - ); - - pdkim_set_debug_stream(ctx,debug_file); - - pdkim_set_optional(ctx, - (char *)dkim_sign_headers_expanded, - NULL, - pdkim_canon, - pdkim_canon, - -1, - PDKIM_ALGO_RSA_SHA256, - 0, - 0); - - lseek(dkim_fd, 0, SEEK_SET); - while((sread = read(dkim_fd,&buf,4096)) > 0) { - if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) { - rc = NULL; - goto CLEANUP; - } + (void) close(privkey_fd); + dkim_private_key_expanded = big_buffer; } - /* Handle failed read above. */ - if (sread == -1) { - debug_printf("DKIM: Error reading -K file.\n"); - save_errno = errno; + + ctx = pdkim_init_sign( (char *) dkim_signing_domain, + (char *) dkim_signing_selector, + (char *) dkim_private_key_expanded); + pdkim_set_optional(ctx, + (char *) dkim_sign_headers_expanded, + NULL, + pdkim_canon, + pdkim_canon, -1, PDKIM_ALGO_RSA_SHA256, 0, 0); + + lseek(dkim_fd, 0, SEEK_SET); + + while ((sread = read(dkim_fd, &buf, 4096)) > 0) + if (pdkim_feed(ctx, buf, sread) != PDKIM_OK) + { rc = NULL; goto CLEANUP; + } + + /* Handle failed read above. */ + if (sread == -1) + { + debug_printf("DKIM: Error reading -K file.\n"); + save_errno = errno; + rc = NULL; + goto CLEANUP; } - pdkim_rc = pdkim_feed_finish(ctx,&signature); - if (pdkim_rc != PDKIM_OK) { - log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc); - rc = NULL; - goto CLEANUP; + if ((pdkim_rc = pdkim_feed_finish(ctx, &signature)) != PDKIM_OK) + { + log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc); + rc = NULL; + goto CLEANUP; } - sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2, - US signature->signature_header, - US"\r\n"); + sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2, + US signature->signature_header, US"\r\n"); - pdkim_free_ctx(ctx); - ctx = NULL; + pdkim_free_ctx(ctx); + ctx = NULL; } - if (sigbuf != NULL) { - sigbuf[sigptr] = '\0'; - rc = sigbuf; - } else - rc = US""; - - CLEANUP: - if (ctx != NULL) - pdkim_free_ctx(ctx); - store_pool = old_pool; - errno = save_errno; - return rc; +if (sigbuf) + { + sigbuf[sigptr] = '\0'; + rc = sigbuf; + } +else + rc = US""; + +CLEANUP: +if (ctx) + pdkim_free_ctx(ctx); +store_pool = old_pool; +errno = save_errno; +return rc; } #endif diff --git a/src/src/dkim.h b/src/src/dkim.h index 15a7e60a5..39a0408a1 100644 --- a/src/src/dkim.h +++ b/src/src/dkim.h @@ -2,16 +2,15 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge, 1995 - 2007 */ +/* Copyright (c) University of Cambridge, 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -uschar *dkim_exim_sign(int,uschar *,uschar *,uschar *,uschar *,uschar *); +uschar *dkim_exim_sign(int, uschar *, const uschar *, uschar *, uschar *, uschar *); void dkim_exim_verify_init(void); void dkim_exim_verify_feed(uschar *, int); void dkim_exim_verify_finish(void); void dkim_exim_acl_setup(uschar *); uschar *dkim_exim_expand_query(int); -uschar *dkim_exim_expand_defaults(int); #define DKIM_ALGO 1 #define DKIM_BODYLENGTH 2 diff --git a/src/src/dns.c b/src/src/dns.c index 2968fba19..e6e4fb6b3 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -10,16 +10,6 @@ #include "exim.h" -/* Function declaration needed for mutual recursion when A6 records -are supported. */ - -#if HAVE_IPV6 -#ifdef SUPPORT_A6 -static void dns_complete_a6(dns_address ***, dns_answer *, dns_record *, - int, uschar *); -#endif -#endif - /************************************************* * Fake DNS resolver * @@ -33,7 +23,7 @@ If not, it passes its arguments on to res_search(). The fake nameserver may also return a code specifying that the name should be passed on. Background: the original test suite required a real nameserver to carry the -test zones, whereas the new test suit has the fake server for portability. This +test zones, whereas the new test suite has the fake server for portability. This code supports both. Arguments: @@ -46,11 +36,10 @@ Returns: length of returned data, or -1 on error (h_errno set) */ static int -fakens_search(uschar *domain, int type, uschar *answerptr, int size) +fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { int len = Ustrlen(domain); int asize = size; /* Locally modified */ -uschar *endname; uschar name[256]; uschar utilname[256]; uschar *aptr = answerptr; /* Locally modified */ @@ -61,38 +50,11 @@ struct stat statbuf; if (domain[len - 1] == '.') len--; Ustrncpy(name, domain, len); name[len] = 0; -endname = name + len; - -/* This code, for forcing TRY_AGAIN and NO_RECOVERY, is here so that it works -for the old test suite that uses a real nameserver. When the old test suite is -eventually abandoned, this code could be moved into the fakens utility. */ - -if (len >= 14 && Ustrcmp(endname - 14, "test.again.dns") == 0) - { - int delay = Uatoi(name); /* digits at the start of the name */ - DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n", - name, dns_text_type(type)); - if (delay > 0) - { - DEBUG(D_dns) debug_printf("delaying %d seconds\n", delay); - sleep(delay); - } - h_errno = TRY_AGAIN; - return -1; - } - -if (len >= 13 && Ustrcmp(endname - 13, "test.fail.dns") == 0) - { - DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n", - name, dns_text_type(type)); - h_errno = NO_RECOVERY; - return -1; - } /* Look for the fakens utility, and if it exists, call it. */ -(void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens", - spool_directory); +(void)string_format(utilname, sizeof(utilname), "%s/bin/fakens", + config_main_directory); if (stat(CS utilname, &statbuf) >= 0) { @@ -100,11 +62,10 @@ if (stat(CS utilname, &statbuf) >= 0) int infd, outfd, rc; uschar *argv[5]; - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", - name, dns_text_type(type)); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type)); argv[0] = utilname; - argv[1] = spool_directory; + argv[1] = config_main_directory; argv[2] = name; argv[3] = dns_text_type(type); argv[4] = NULL; @@ -123,6 +84,13 @@ if (stat(CS utilname, &statbuf) >= 0) asize -= rc; /* may need to be passed on to res_search(). */ } + /* If we ran out of output buffer before exhausting the return, + carry on reading and counting it. */ + + if (asize == 0) + while ((rc = read(outfd, name, sizeof(name))) > 0) + len += rc; + if (rc < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s", strerror(errno)); @@ -139,6 +107,10 @@ if (stat(CS utilname, &statbuf) >= 0) DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n"); } } +else + { + DEBUG(D_dns) debug_printf("fakens (%s) not found\n", utilname); + } /* fakens utility not found, or it returned "pass on" */ @@ -257,9 +229,9 @@ Returns: nothing */ void -dns_build_reverse(uschar *string, uschar *buffer) +dns_build_reverse(const uschar *string, uschar *buffer) { -uschar *p = string + Ustrlen(string); +const uschar *p = string + Ustrlen(string); uschar *pp = buffer; /* Handle IPv4 address */ @@ -271,7 +243,7 @@ if (Ustrchr(string, ':') == NULL) int i; for (i = 0; i < 4; i++) { - uschar *ppp = p; + const uschar *ppp = p; while (ppp > string && ppp[-1] != '.') ppp--; Ustrncpy(pp, ppp, p - ppp); pp += p - ppp; @@ -416,7 +388,8 @@ from the following bytes. */ dnss->aptr += namelen; GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */ -dnss->aptr += 6; /* Don't want class or TTL */ +dnss->aptr += 2; /* Don't want class */ +GETLONG(dnss->srr.ttl, dnss->aptr); /* TTL */ GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */ dnss->srr.data = dnss->aptr; /* The record's data follows */ dnss->aptr += dnss->srr.size; /* Advance to next RR */ @@ -428,6 +401,33 @@ return &(dnss->srr); } +/* Extract the AUTHORITY information from the answer. If the +answer isn't authoritive (AA not set), we do not extract anything. + +The AUTHORITIVE section contains NS records if +the name in question was found, it contains a SOA record +otherwise. (This is just from experience and some tests, is there +some spec?) + +We've cycle through the AUTHORITY section, since it may contain +other records (e.g. NSEC3) too. */ + +static const uschar * +dns_extract_auth_name(const dns_answer * dnsa) /* FIXME: const dns_answer */ +{ +dns_scan dnss; +dns_record * rr; +HEADER * h = (HEADER *) dnsa->answer; + +if (!h->nscount || !h->aa) return NULL; +for (rr = dns_next_rr((dns_answer*) dnsa, &dnss, RESET_AUTHORITY); + rr; + rr = dns_next_rr((dns_answer*) dnsa, &dnss, RESET_NEXT)) + if (rr->type == (h->ancount ? T_NS : T_SOA)) return rr->name; +return NULL; +} + + /************************************************* @@ -436,33 +436,74 @@ return &(dnss->srr); /* We do not perform DNSSEC work ourselves; if the administrator has installed a verifying resolver which sets AD as appropriate, though, we'll use that. -(AD = Authentic Data) +(AD = Authentic Data, AA = Authoritive Answer) Argument: pointer to dns answer block Returns: bool indicating presence of AD bit */ BOOL -dns_is_secure(dns_answer *dnsa) +dns_is_secure(const dns_answer * dnsa) { #ifdef DISABLE_DNSSEC DEBUG(D_dns) debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n"); return FALSE; #else -HEADER *h = (HEADER *)dnsa->answer; -return h->ad ? TRUE : FALSE; +HEADER * h = (HEADER *) dnsa->answer; +const uschar * auth_name; +const uschar * trusted; + +if (h->ad) return TRUE; + +/* If the resolver we ask is authoritive for the domain in question, it +* may not set the AD but the AA bit. If we explicitly trust +* the resolver for that domain (via a domainlist in dns_trust_aa), +* we return TRUE to indicate a secure answer. +*/ + +if ( !h->aa + || !dns_trust_aa + || !(trusted = expand_string(dns_trust_aa)) + || !*trusted + || !(auth_name = dns_extract_auth_name(dnsa)) + || OK != match_isinlist(auth_name, &trusted, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) + ) + return FALSE; + +DEBUG(D_dns) debug_printf("DNS faked the AD bit " + "(got AA and matched with dns_trust_aa (%s in %s))\n", + auth_name, dns_trust_aa); + +return TRUE; #endif } static void dns_set_insecure(dns_answer * dnsa) { +#ifndef DISABLE_DNSSEC HEADER * h = (HEADER *)dnsa->answer; h->ad = 0; +#endif } +/************************************************ + * Check whether the AA bit is set * + * We need this to warn if we requested AD * + * from an authoritive server * + ************************************************/ +BOOL +dns_is_aa(const dns_answer *dnsa) +{ +#ifdef DISABLE_DNSSEC +return FALSE; +#else +return ((HEADER*)dnsa->answer)->aa; +#endif +} @@ -518,19 +559,17 @@ Returns: the return code */ static int -dns_return(uschar *name, int type, int rc) +dns_return(const uschar * name, int type, int rc) { res_state resp = os_get_dns_resolver_res(); tree_node *node = store_get_perm(sizeof(tree_node) + 290); sprintf(CS node->name, "%.255s-%s-%lx", name, dns_text_type(type), - resp->options); + (unsigned long) resp->options); node->data.val = rc; (void)tree_insertnode(&tree_dns_fails, node); return rc; } - - /************************************************* * Do basic DNS lookup * *************************************************/ @@ -557,11 +596,11 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_basic_lookup(dns_answer *dnsa, uschar *name, int type) +dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type) { #ifndef STAND_ALONE int rc = -1; -uschar *save; +const uschar *save_domain; #endif res_state resp = os_get_dns_resolver_res(); @@ -574,7 +613,7 @@ have many addresses in the same domain. We rely on the resolver and name server caching for successful lookups. */ sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type), - resp->options); + (unsigned long) resp->options); previous = tree_search(tree_dns_fails, node_name); if (previous != NULL) { @@ -587,6 +626,25 @@ if (previous != NULL) return previous->data.val; } +#ifdef SUPPORT_I18N +/* Convert all names to a-label form before doing lookup */ + { + uschar * alabel; + uschar * errstr = NULL; + DEBUG(D_dns) if (string_is_utf8(name)) + debug_printf("convert utf8 '%s' to alabel for for lookup\n", name); + if ((alabel = string_domain_utf8_to_alabel(name, &errstr)), errstr) + { + DEBUG(D_dns) + debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s\n", name, + errstr); + host_find_failed_syntax = TRUE; + return DNS_NOMATCH; + } + name = alabel; + } +#endif + /* If configured, check the hygene of the name passed to lookup. Otherwise, although DNS lookups may give REFUSED at the lower level, some resolvers turn this into TRY_AGAIN, which is silly. Give a NOMATCH return, since such @@ -605,7 +663,7 @@ For SRV records, we omit the initial _smtp._tcp. components at the start. */ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) { - uschar *checkname = name; + const uschar *checkname = name; int ovector[3*(EXPAND_MAXN+1)]; dns_pattern_init(); @@ -619,7 +677,7 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) while (*checkname++ != '.'); } - if (pcre_exec(regex_check_dns_names, NULL, CS checkname, Ustrlen(checkname), + if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname), 0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0) { DEBUG(D_dns) @@ -641,22 +699,16 @@ the IP address instead of returning -1 with h_error=HOST_NOT_FOUND. Some nameservers are also believed to do this. It is, of course, contrary to the specification of the DNS, so we lock it out. */ -if (( - #ifdef SUPPORT_A6 - type == T_A6 || - #endif - type == T_A || type == T_AAAA) && - string_is_ip_address(name, NULL) != 0) +if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0) return DNS_NOMATCH; /* If we are running in the test harness, instead of calling the normal resolver (res_search), we call fakens_search(), which recognizes certain special domains, and interfaces to a fake nameserver for certain special zones. */ -if (running_in_test_harness) - dnsa->answerlen = fakens_search(name, type, dnsa->answer, MAXPACKET); -else - dnsa->answerlen = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET); +dnsa->answerlen = running_in_test_harness + ? fakens_search(name, type, dnsa->answer, MAXPACKET) + : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET); if (dnsa->answerlen > MAXPACKET) { @@ -677,12 +729,12 @@ if (dnsa->answerlen < 0) switch (h_errno) name, dns_text_type(type)); /* Cut this out for various test programs */ - #ifndef STAND_ALONE - save = deliver_domain; - deliver_domain = name; /* set $domain */ - rc = match_isinlist(name, &dns_again_means_nonexist, 0, NULL, NULL, +#ifndef STAND_ALONE + save_domain = deliver_domain; + deliver_domain = string_copy(name); /* set $domain */ + rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL); - deliver_domain = save; + deliver_domain = save_domain; if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); @@ -692,9 +744,9 @@ if (dnsa->answerlen < 0) switch (h_errno) "DNS_NOMATCH\n", name); return dns_return(name, type, DNS_NOMATCH); - #else /* For stand-alone tests */ +#else /* For stand-alone tests */ return dns_return(name, type, DNS_AGAIN); - #endif +#endif case NO_RECOVERY: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" @@ -738,7 +790,8 @@ won't return any. If fully_qualified_name is not NULL, set it to point to the full name returned by the resolver, if this is different to what it is given, unless the returned name starts with "*" as some nameservers seem to be returning -wildcards in this form. +wildcards in this form. In international mode "different" means "alabel +forms are different". Arguments: dnsa pointer to dns_answer structure @@ -755,7 +808,8 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_lookup(dns_answer *dnsa, uschar *name, int type, uschar **fully_qualified_name) +dns_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { int i; const uschar *orig_name = name; @@ -782,7 +836,7 @@ for (i = 0; i < 10; i++) cname_rr.data = type_rr.data = NULL; for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) { if (rr->type == type) @@ -798,18 +852,19 @@ for (i = 0; i < 10; i++) if (i == 0 && fully_qualified_name != NULL) { - if (cname_rr.data != NULL) - { - if (Ustrcmp(cname_rr.name, *fully_qualified_name) != 0 && - cname_rr.name[0] != '*') - *fully_qualified_name = string_copy_dnsdomain(cname_rr.name); - } - else if (type_rr.data != NULL) - { - if (Ustrcmp(type_rr.name, *fully_qualified_name) != 0 && - type_rr.name[0] != '*') - *fully_qualified_name = string_copy_dnsdomain(type_rr.name); - } + uschar * rr_name = cname_rr.data ? cname_rr.name + : type_rr.data ? type_rr.name : NULL; + if ( rr_name + && Ustrcmp(rr_name, *fully_qualified_name) != 0 + && rr_name[0] != '*' +#ifdef SUPPORT_I18N + && ( !string_is_utf8(*fully_qualified_name) + || Ustrcmp(rr_name, + string_domain_utf8_to_alabel(*fully_qualified_name, NULL)) != 0 + ) +#endif + ) + *fully_qualified_name = string_copy_dnsdomain(rr_name); } /* If any data records of the correct type were found, we are done. */ @@ -828,7 +883,7 @@ for (i = 0; i < 10; i++) if (cname_rr.data == NULL) return DNS_FAIL; datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256); + cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data)); if (datalen < 0) return DNS_FAIL; name = data; @@ -854,7 +909,7 @@ return DNS_FAIL; * Do a DNS lookup and handle virtual types * ************************************************/ -/* This function handles some invented "lookup types" that synthesize feature +/* This function handles some invented "lookup types" that synthesize features not available in the basic types. The special types all have negative values. Positive type values are passed straight on to dns_lookup(). @@ -873,176 +928,181 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_special_lookup(dns_answer *dnsa, uschar *name, int type, - uschar **fully_qualified_name) +dns_special_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { -if (type >= 0) return dns_lookup(dnsa, name, type, fully_qualified_name); - -/* The "mx hosts only" type doesn't require any special action here */ - -if (type == T_MXH) return dns_lookup(dnsa, name, T_MX, fully_qualified_name); - -/* Find nameservers for the domain or the nearest enclosing zone, excluding the -root servers. */ - -if (type == T_ZNS) +switch (type) { - uschar *d = name; - while (d != 0) + /* The "mx hosts only" type doesn't require any special action here */ + case T_MXH: + return dns_lookup(dnsa, name, T_MX, fully_qualified_name); + + /* Find nameservers for the domain or the nearest enclosing zone, excluding + the root servers. */ + case T_ZNS: + type = T_NS; + /* FALLTHROUGH */ + case T_SOA: { - int rc = dns_lookup(dnsa, d, T_NS, fully_qualified_name); - if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; - while (*d != 0 && *d != '.') d++; - if (*d++ == 0) break; + const uschar *d = name; + while (d != 0) + { + int rc = dns_lookup(dnsa, d, type, fully_qualified_name); + if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; + while (*d != 0 && *d != '.') d++; + if (*d++ == 0) break; + } + return DNS_NOMATCH; } - return DNS_NOMATCH; - } - -/* Try to look up the Client SMTP Authorization SRV record for the name. If -there isn't one, search from the top downwards for a CSA record in a parent -domain, which might be making assertions about subdomains. If we find a record -we set fully_qualified_name to whichever lookup succeeded, so that the caller -can tell whether to look at the explicit authorization field or the subdomain -assertion field. */ - -if (type == T_CSA) - { - uschar *srvname, *namesuff, *tld, *p; - int priority, weight, port; - int limit, rc, i; - BOOL ipv6; - dns_record *rr; - dns_scan dnss; - - DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); - srvname = string_sprintf("_client._smtp.%s", name); - rc = dns_lookup(dnsa, srvname, T_SRV, NULL); - if (rc == DNS_SUCCEED || rc == DNS_AGAIN) + /* Try to look up the Client SMTP Authorization SRV record for the name. If + there isn't one, search from the top downwards for a CSA record in a parent + domain, which might be making assertions about subdomains. If we find a record + we set fully_qualified_name to whichever lookup succeeded, so that the caller + can tell whether to look at the explicit authorization field or the subdomain + assertion field. */ + case T_CSA: { - if (rc == DNS_SUCCEED) *fully_qualified_name = name; - return rc; - } - - /* Search for CSA subdomain assertion SRV records from the top downwards, - starting with the 2nd level domain. This order maximizes cache-friendliness. - We skip the top level domains to avoid loading their nameservers and because - we know they'll never have CSA SRV records. */ - - namesuff = Ustrrchr(name, '.'); - if (namesuff == NULL) return DNS_NOMATCH; - tld = namesuff + 1; - ipv6 = FALSE; - limit = dns_csa_search_limit; + uschar *srvname, *namesuff, *tld, *p; + int priority, weight, port; + int limit, rc, i; + BOOL ipv6; + dns_record *rr; + dns_scan dnss; - /* Use more appropriate search parameters if we are in the reverse DNS. */ + DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); - if (strcmpic(namesuff, US".arpa") == 0) - { - if (namesuff - 8 > name && strcmpic(namesuff - 8, US".in-addr.arpa") == 0) - { - namesuff -= 8; - tld = namesuff + 1; - limit = 3; - } - else if (namesuff - 4 > name && strcmpic(namesuff - 4, US".ip6.arpa") == 0) + srvname = string_sprintf("_client._smtp.%s", name); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_SUCCEED || rc == DNS_AGAIN) { - namesuff -= 4; - tld = namesuff + 1; - ipv6 = TRUE; - limit = 3; + if (rc == DNS_SUCCEED) *fully_qualified_name = string_copy(name); + return rc; } - } - - DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); - - /* Do not perform the search if the top level or 2nd level domains do not - exist. This is quite common, and when it occurs all the search queries would - go to the root or TLD name servers, which is not friendly. So we check the - AUTHORITY section; if it contains the root's SOA record or the TLD's SOA then - the TLD or the 2LD (respectively) doesn't exist and we can skip the search. - If the TLD and the 2LD exist but the explicit CSA record lookup failed, then - the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */ - - if (rc == DNS_NOMATCH) - { - /* This is really gross. The successful return value from res_search() is - the packet length, which is stored in dnsa->answerlen. If we get a - negative DNS reply then res_search() returns -1, which causes the bounds - checks for name decompression to fail when it is treated as a packet - length, which in turn causes the authority search to fail. The correct - packet length has been lost inside libresolv, so we have to guess a - replacement value. (The only way to fix this properly would be to - re-implement res_search() and res_query() so that they don't muddle their - success and packet length return values.) For added safety we only reset - the packet length if the packet header looks plausible. */ - - HEADER *h = (HEADER *)dnsa->answer; - if (h->qr == 1 && h->opcode == QUERY && h->tc == 0 - && (h->rcode == NOERROR || h->rcode == NXDOMAIN) - && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 - && ntohs(h->nscount) >= 1) - dnsa->answerlen = MAXPACKET; - - for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - if (rr->type != T_SOA) continue; - else if (strcmpic(rr->name, US"") == 0 || - strcmpic(rr->name, tld) == 0) return DNS_NOMATCH; - else break; - } - for (i = 0; i < limit; i++) - { - if (ipv6) + /* Search for CSA subdomain assertion SRV records from the top downwards, + starting with the 2nd level domain. This order maximizes cache-friendliness. + We skip the top level domains to avoid loading their nameservers and because + we know they'll never have CSA SRV records. */ + + namesuff = Ustrrchr(name, '.'); + if (namesuff == NULL) return DNS_NOMATCH; + tld = namesuff + 1; + ipv6 = FALSE; + limit = dns_csa_search_limit; + + /* Use more appropriate search parameters if we are in the reverse DNS. */ + + if (strcmpic(namesuff, US".arpa") == 0) + if (namesuff - 8 > name && strcmpic(namesuff - 8, US".in-addr.arpa") == 0) + { + namesuff -= 8; + tld = namesuff + 1; + limit = 3; + } + else if (namesuff - 4 > name && strcmpic(namesuff - 4, US".ip6.arpa") == 0) + { + namesuff -= 4; + tld = namesuff + 1; + ipv6 = TRUE; + limit = 3; + } + + DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); + + /* Do not perform the search if the top level or 2nd level domains do not + exist. This is quite common, and when it occurs all the search queries would + go to the root or TLD name servers, which is not friendly. So we check the + AUTHORITY section; if it contains the root's SOA record or the TLD's SOA then + the TLD or the 2LD (respectively) doesn't exist and we can skip the search. + If the TLD and the 2LD exist but the explicit CSA record lookup failed, then + the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */ + + if (rc == DNS_NOMATCH) { - /* Scan through the IPv6 reverse DNS in chunks of 16 bits worth of IP - address, i.e. 4 hex chars and 4 dots, i.e. 8 chars. */ - namesuff -= 8; - if (namesuff <= name) return DNS_NOMATCH; + /* This is really gross. The successful return value from res_search() is + the packet length, which is stored in dnsa->answerlen. If we get a + negative DNS reply then res_search() returns -1, which causes the bounds + checks for name decompression to fail when it is treated as a packet + length, which in turn causes the authority search to fail. The correct + packet length has been lost inside libresolv, so we have to guess a + replacement value. (The only way to fix this properly would be to + re-implement res_search() and res_query() so that they don't muddle their + success and packet length return values.) For added safety we only reset + the packet length if the packet header looks plausible. */ + + HEADER *h = (HEADER *)dnsa->answer; + if (h->qr == 1 && h->opcode == QUERY && h->tc == 0 + && (h->rcode == NOERROR || h->rcode == NXDOMAIN) + && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 + && ntohs(h->nscount) >= 1) + dnsa->answerlen = MAXPACKET; + + for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) + ) + if (rr->type != T_SOA) continue; + else if (strcmpic(rr->name, US"") == 0 || + strcmpic(rr->name, tld) == 0) return DNS_NOMATCH; + else break; } - else - /* Find the start of the preceding domain name label. */ - do - if (--namesuff <= name) return DNS_NOMATCH; - while (*namesuff != '.'); - - DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); - - srvname = string_sprintf("_client._smtp.%s", namesuff + 1); - rc = dns_lookup(dnsa, srvname, T_SRV, NULL); - if (rc == DNS_AGAIN) return rc; - if (rc != DNS_SUCCEED) continue; - /* Check that the SRV record we have found is worth returning. We don't - just return the first one we find, because some lower level SRV record - might make stricter assertions than its parent domain. */ - - for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + for (i = 0; i < limit; i++) { - if (rr->type != T_SRV) continue; - - /* Extract the numerical SRV fields (p is incremented) */ - p = rr->data; - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); - - /* Check the CSA version number */ - if (priority != 1) continue; - - /* If it's making an interesting assertion, return this response. */ - if (port & 1) - { - *fully_qualified_name = namesuff + 1; - return DNS_SUCCEED; - } + if (ipv6) + { + /* Scan through the IPv6 reverse DNS in chunks of 16 bits worth of IP + address, i.e. 4 hex chars and 4 dots, i.e. 8 chars. */ + namesuff -= 8; + if (namesuff <= name) return DNS_NOMATCH; + } + else + /* Find the start of the preceding domain name label. */ + do + if (--namesuff <= name) return DNS_NOMATCH; + while (*namesuff != '.'); + + DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); + + srvname = string_sprintf("_client._smtp.%s", namesuff + 1); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_AGAIN) return rc; + if (rc != DNS_SUCCEED) continue; + + /* Check that the SRV record we have found is worth returning. We don't + just return the first one we find, because some lower level SRV record + might make stricter assertions than its parent domain. */ + + for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + { + if (rr->type != T_SRV) continue; + + /* Extract the numerical SRV fields (p is incremented) */ + p = rr->data; + GETSHORT(priority, p); + GETSHORT(weight, p); weight = weight; /* compiler quietening */ + GETSHORT(port, p); + + /* Check the CSA version number */ + if (priority != 1) continue; + + /* If it's making an interesting assertion, return this response. */ + if (port & 1) + { + *fully_qualified_name = namesuff + 1; + return DNS_SUCCEED; + } + } } + return DNS_NOMATCH; } - return DNS_NOMATCH; + + default: + if (type >= 0) + return dns_lookup(dnsa, name, type, fully_qualified_name); } /* Control should never reach here */ @@ -1052,218 +1112,51 @@ return DNS_FAIL; -/* Support for A6 records has been commented out since they were demoted to -experimental status at IETF 51. */ - -#if HAVE_IPV6 && defined(SUPPORT_A6) - -/************************************************* -* Search DNS block for prefix RRs * -*************************************************/ - -/* Called from dns_complete_a6() to search an additional section or a main -answer section for required prefix records to complete an IPv6 address obtained -from an A6 record. For each prefix record, a recursive call to dns_complete_a6 -is made, with a new copy of the address so far. - -Arguments: - dnsa the DNS answer block - which RESET_ADDITIONAL or RESET_ANSWERS - name name of prefix record - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - bits number of bits we have already got - bitvec the bits we have already got - -Returns: TRUE if any records were found -*/ - -static BOOL -dns_find_prefix(dns_answer *dnsa, int which, uschar *name, dns_address - ***yptrptr, int bits, uschar *bitvec) -{ -BOOL yield = FALSE; -dns_record *rr; -dns_scan dnss; - -for (rr = dns_next_rr(dnsa, &dnss, which); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - { - uschar cbitvec[16]; - if (rr->type != T_A6 || strcmpic(rr->name, name) != 0) continue; - yield = TRUE; - memcpy(cbitvec, bitvec, sizeof(cbitvec)); - dns_complete_a6(yptrptr, dnsa, rr, bits, cbitvec); - } - -return yield; -} - - - -/************************************************* -* Follow chains of A6 records * -*************************************************/ - -/* A6 records may be incomplete, with pointers to other records containing more -bits of the address. There can be a tree structure, leading to a number of -addresses originating from a single initial A6 record. - -Arguments: - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - dnsa the current DNS answer block - rr the RR we have at present - bits number of bits we have already got - bitvec the bits we have already got - -Returns: nothing -*/ - -static void -dns_complete_a6(dns_address ***yptrptr, dns_answer *dnsa, dns_record *rr, - int bits, uschar *bitvec) -{ -static uschar bitmask[] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 }; -uschar *p = (uschar *)(rr->data); -int prefix_len, suffix_len; -int i, j, k; -uschar *chainptr; -uschar chain[264]; -dns_answer cdnsa; - -/* The prefix length is the first byte. It defines the prefix which is missing -from the data in this record as a number of bits. Zero means this is the end of -a chain. The suffix is the data in this record; only sufficient bytes to hold -it are supplied. There may be zero bytes. We have to ignore trailing bits that -we have already obtained from earlier RRs in the chain. */ - -prefix_len = *p++; /* bits */ -suffix_len = (128 - prefix_len + 7)/8; /* bytes */ - -/* If the prefix in this record is greater than the prefix in the previous -record in the chain, we have to ignore the record (RFC 2874). */ - -if (prefix_len > 128 - bits) return; - -/* In this little loop, the number of bits up to and including the current byte -is held in k. If we have none of the bits in this byte, we can just or it into -the current data. If we have all of the bits in this byte, we skip it. -Otherwise, some masking has to be done. */ - -for (i = suffix_len - 1, j = 15, k = 8; i >= 0; i--) - { - int required = k - bits; - if (required >= 8) bitvec[j] |= p[i]; - else if (required > 0) bitvec[j] |= p[i] & bitmask[required]; - j--; /* I tried putting these in the "for" statement, but gcc muttered */ - k += 8; /* about computed values not being used. */ - } - -/* If the prefix_length is zero, we are at the end of a chain. Build a -dns_address item with the current data, hang it onto the end of the chain, -adjust the hanging pointer, and we are done. */ - -if (prefix_len == 0) - { - dns_address *new = store_get(sizeof(dns_address) + 50); - inet_ntop(AF_INET6, bitvec, CS new->address, 50); - new->next = NULL; - **yptrptr = new; - *yptrptr = &(new->next); - return; - } - -/* Prefix length is not zero. Reset the number of bits that we have collected -so far, and extract the chain name. */ - -bits = 128 - prefix_len; -p += suffix_len; - -chainptr = chain; -while ((i = *p++) != 0) - { - if (chainptr != chain) *chainptr++ = '.'; - memcpy(chainptr, p, i); - chainptr += i; - p += i; - } -*chainptr = 0; -chainptr = chain; - -/* Now scan the current DNS response record to see if the additional section -contains the records we want. This processing can be cut out for testing -purposes. */ - -if (dns_find_prefix(dnsa, RESET_ADDITIONAL, chainptr, yptrptr, bits, bitvec)) - return; - -/* No chain records were found in the current DNS response block. Do a new DNS -lookup to try to find these records. This opens up the possibility of DNS -failures. We ignore them at this point; if all branches of the tree fail, there -will be no addresses at the end. */ - -if (dns_lookup(&cdnsa, chainptr, T_A6, NULL) == DNS_SUCCEED) - (void)dns_find_prefix(&cdnsa, RESET_ANSWERS, chainptr, yptrptr, bits, bitvec); -} -#endif /* HAVE_IPV6 && defined(SUPPORT_A6) */ - - /************************************************* * Get address(es) from DNS record * *************************************************/ -/* The record type is either T_A for an IPv4 address or T_AAAA (or T_A6 when -supported) for an IPv6 address. In the A6 case, there may be several addresses, -generated by following chains. A recursive function does all the hard work. A6 -records now look like passing into history, so the code is only included when -explicitly asked for. +/* The record type is either T_A for an IPv4 address or T_AAAA for an IPv6 address. Argument: dnsa the DNS answer block rr the RR -Returns: pointer a chain of dns_address items +Returns: pointer to a chain of dns_address items; NULL when the dnsa was overrun */ dns_address * dns_address_from_rr(dns_answer *dnsa, dns_record *rr) { -dns_address *yield = NULL; - -#if HAVE_IPV6 && defined(SUPPORT_A6) -dns_address **yieldptr = &yield; -uschar bitvec[16]; -#else -dnsa = dnsa; /* Stop picky compilers warning */ -#endif +dns_address * yield = NULL; +uschar * dnsa_lim = dnsa->answer + dnsa->answerlen; if (rr->type == T_A) { - uschar *p = (uschar *)(rr->data); - yield = store_get(sizeof(dns_address) + 20); - (void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - yield->next = NULL; + uschar *p = US rr->data; + if (p + 4 <= dnsa_lim) + { + yield = store_get(sizeof(dns_address) + 20); + (void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + yield->next = NULL; + } } #if HAVE_IPV6 -#ifdef SUPPORT_A6 -else if (rr->type == T_A6) - { - memset(bitvec, 0, sizeof(bitvec)); - dns_complete_a6(&yieldptr, dnsa, rr, 0, bitvec); - } -#endif /* SUPPORT_A6 */ - else { - yield = store_get(sizeof(dns_address) + 50); - inet_ntop(AF_INET6, (uschar *)(rr->data), CS yield->address, 50); - yield->next = NULL; + if (rr->data + 16 <= dnsa_lim) + { + struct in6_addr in6; + int i; + for (i = 0; i < 16; i++) in6.s6_addr[i] = rr->data[i]; + yield = store_get(sizeof(dns_address) + 50); + inet_ntop(AF_INET6, &in6, CS yield->address, 50); + yield->next = NULL; + } } #endif /* HAVE_IPV6 */ diff --git a/src/src/drtables.c b/src/src/drtables.c index c2d866850..ec4e72683 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -53,6 +53,10 @@ set to NULL for those that are not compiled into the binary. */ #include "auths/spa.h" #endif +#ifdef AUTH_TLS +#include "auths/tls.h" +#endif + auth_info auths_available[] = { /* Checking by an expansion condition on plain text */ @@ -155,6 +159,20 @@ auth_info auths_available[] = { }, #endif +#ifdef AUTH_TLS + { + US"tls", /* lookup name */ + auth_tls_options, + &auth_tls_options_count, + &auth_tls_option_defaults, + sizeof(auth_tls_options_block), + auth_tls_init, /* init function */ + auth_tls_server, /* server function */ + NULL, /* client function */ + NULL /* diagnostic function */ + }, +#endif + { US"", NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL } }; @@ -447,15 +465,15 @@ extern lookup_module_info sqlite_lookup_module_info; #ifdef EXPERIMENTAL_SPF extern lookup_module_info spf_lookup_module_info; #endif -#ifdef EXPERIMENTAL_REDIS -extern lookup_module_info redis_lookup_module_info; -#endif #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2 extern lookup_module_info pgsql_lookup_module_info; #endif #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2 extern lookup_module_info passwd_lookup_module_info; #endif +#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 +extern lookup_module_info redis_lookup_module_info; +#endif #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2 extern lookup_module_info oracle_lookup_module_info; #endif @@ -558,7 +576,7 @@ void init_lookup_list(void) addlookupmodule(NULL, &pgsql_lookup_module_info); #endif -#ifdef EXPERIMENTAL_REDIS +#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 addlookupmodule(NULL, &redis_lookup_module_info); #endif diff --git a/src/src/enq.c b/src/src/enq.c index 6cd243c4c..7f7bebc68 100644 --- a/src/src/enq.c +++ b/src/src/enq.c @@ -20,17 +20,20 @@ /* This function is called when a host is listed for serialization of connections. It is also called when ETRN is listed for serialization. We open the misc database and look for a record, which implies an existing connection -or ETRN run. If not found, create one and return TRUE. +or ETRN run. If increasing the count would take us past the given limit +value return FALSE. If not, bump it and return TRUE. If not found, create +one with value 1 and return TRUE. Arguments: key string on which to serialize + lim parallelism limit Returns: TRUE if OK to proceed; FALSE otherwise */ BOOL -enq_start(uschar *key) +enq_start(uschar *key, unsigned lim) { dbdata_serialize *serial_record; dbdata_serialize new_record; @@ -44,25 +47,31 @@ deliberate; the dbfn_open() function - which is an Exim function - always tries to create if it can't open a read/write file. It expects only O_RDWR or O_RDONLY as its argument. */ -dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE); -if (dbm_file == NULL) return FALSE; +if (!(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) + return FALSE; /* See if there is a record for this host or queue run; if there is, we cannot proceed with the connection unless the record is very old. */ serial_record = dbfn_read(dbm_file, key); -if (serial_record != NULL && time(NULL) - serial_record->time_stamp < 6*60*60) +if (serial_record && time(NULL) - serial_record->time_stamp < 6*60*60) { - dbfn_close(dbm_file); - DEBUG(D_transport) debug_printf("outstanding serialization record for %s\n", - key); - return FALSE; + if (serial_record->count >= lim) + { + dbfn_close(dbm_file); + DEBUG(D_transport) debug_printf("outstanding serialization record for %s\n", + key); + return FALSE; + } + new_record.count = serial_record->count + 1; } +else + new_record.count = 1; -/* We can proceed - insert a new record or update the old one. At present -the count field is not used; just set it to 1. */ +/* We can proceed - insert a new record or update the old one. */ -new_record.count = 1; +DEBUG(D_transport) debug_printf("write serialization record for %s val %d\n", + key, new_record.count); dbfn_write(dbm_file, key, &new_record, (int)sizeof(dbdata_serialize)); dbfn_close(dbm_file); return TRUE; @@ -88,12 +97,25 @@ enq_end(uschar *key) { open_db dbblock; open_db *dbm_file; +dbdata_serialize *serial_record; DEBUG(D_transport) debug_printf("end serialized: %s\n", key); -dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE); -if (dbm_file == NULL) return; -dbfn_delete(dbm_file, key); +if ( !(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)) + || !(serial_record = dbfn_read(dbm_file, key)) + ) + return; +if (--serial_record->count > 0) + { + DEBUG(D_transport) debug_printf("write serialization record for %s val %d\n", + key, serial_record->count); + dbfn_write(dbm_file, key, serial_record, (int)sizeof(dbdata_serialize)); + } +else + { + DEBUG(D_transport) debug_printf("remove serialization record for %s\n", key); + dbfn_delete(dbm_file, key); + } dbfn_close(dbm_file); } diff --git a/src/src/exicyclog.src b/src/src/exicyclog.src index 01d1f2feb..1559a4794 100644 --- a/src/src/exicyclog.src +++ b/src/src/exicyclog.src @@ -282,28 +282,34 @@ done if [ $keep -gt 99 ]; then first=001; else first=01; fi +# Grab our pid ro avoid race in file creation +ourpid=$$ + if [ -f $mainlog ]; then $mv $mainlog $mainlog.$first $chown $user:$group $mainlog.$first - $touch $mainlog - $chown $user:$group $mainlog - $chmod 640 $mainlog + $touch $mainlog.$ourpid + $chown $user:$group $mainlog.$ourpid + $chmod 640 $mainlog.$ourpid + $mv $mainlog.$ourpid $mainlog fi if [ -f $rejectlog ]; then $mv $rejectlog $rejectlog.$first $chown $user:$group $rejectlog.$first - $touch $rejectlog - $chown $user:$group $rejectlog - $chmod 640 $rejectlog + $touch $rejectlog.$ourpid + $chown $user:$group $rejectlog.$ourpid + $chmod 640 $rejectlog.$ourpid + $mv $rejectlog.$ourpid $rejectlog fi if [ -f $paniclog ]; then $mv $paniclog $paniclog.$first $chown $user:$group $paniclog.$first - $touch $paniclog - $chown $user:$group $paniclog - $chmod 640 $paniclog + $touch $paniclog.$ourpid + $chown $user:$group $paniclog.$ourpid + $chmod 640 $paniclog.$ourpid + $mv $paniclog.$ourpid $paniclog fi # Now scan the (0)02 and later files, compressing where necessary, and diff --git a/src/src/exigrep.src b/src/src/exigrep.src index 419fcb54c..bb994d769 100644 --- a/src/src/exigrep.src +++ b/src/src/exigrep.src @@ -2,7 +2,7 @@ use strict; -# Copyright (c) 2007-2014 University of Cambridge. +# Copyright (c) 2007-2015 University of Cambridge. # See the file NOTICE for conditions of use and distribution. # Except when they appear in comments, the following placeholders in this @@ -224,7 +224,7 @@ if (@ARGV) foreach (@ARGV) { my $filename = $_; - if ($filename =~ /\.(?:COMPRESS_SUFFIX)$/o) + if (-x 'ZCAT_COMMAND' && $filename =~ /\.(?:COMPRESS_SUFFIX)$/o) { open(LOG, "ZCAT_COMMAND $filename |") || die "Unable to zcat $filename: $!\n"; diff --git a/src/src/exim.c b/src/src/exim.c index 3ac03e92e..28617a510 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -131,10 +131,11 @@ Returns: TRUE or FALSE */ BOOL -regex_match_and_setup(const pcre *re, uschar *subject, int options, int setup) +regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup) { int ovector[3*(EXPAND_MAXN+1)]; -int n = pcre_exec(re, NULL, CS subject, Ustrlen(subject), 0, +uschar * s = string_copy(subject); /* de-constifying */ +int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0, PCRE_EOPT | options, ovector, sizeof(ovector)/sizeof(int)); BOOL yield = n >= 0; if (n == 0) n = EXPAND_MAXN + 1; @@ -144,7 +145,7 @@ if (yield) expand_nmax = (setup < 0)? 0 : setup + 1; for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2) { - expand_nstring[expand_nmax] = subject + ovector[nn]; + expand_nstring[expand_nmax] = s + ovector[nn]; expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn]; } expand_nmax--; @@ -229,7 +230,7 @@ to disrupt whatever is going on outside the signal handler. */ if (fd < 0) return; -{int dummy = write(fd, process_info, process_info_len); dummy = dummy; } +(void)write(fd, process_info, process_info_len); (void)close(fd); } @@ -412,11 +413,11 @@ if (exim_tvcmp(&now_tv, then_tv) <= 0) { if (!running_in_test_harness) { - debug_printf("tick check: %lu.%06lu %lu.%06lu\n", + debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n", then_tv->tv_sec, (long) then_tv->tv_usec, now_tv.tv_sec, (long) now_tv.tv_usec); - debug_printf("waiting %lu.%06lu\n", itval.it_value.tv_sec, - (long) itval.it_value.tv_usec); + debug_printf("waiting " TIME_T_FMT ".%06lu\n", + itval.it_value.tv_sec, (long) itval.it_value.tv_usec); } } @@ -813,18 +814,33 @@ fprintf(f, "Support for:"); #ifdef WITH_CONTENT_SCAN fprintf(f, " Content_Scanning"); #endif +#ifdef WITH_OLD_DEMIME + fprintf(f, " Old_Demime"); +#endif #ifndef DISABLE_DKIM fprintf(f, " DKIM"); #endif -#ifdef WITH_OLD_DEMIME - fprintf(f, " Old_Demime"); +#ifndef DISABLE_DNSSEC + fprintf(f, " DNSSEC"); #endif -#ifndef DISABLE_PRDR - fprintf(f, " PRDR"); +#ifndef DISABLE_EVENT + fprintf(f, " Event"); +#endif +#ifdef SUPPORT_I18N + fprintf(f, " I18N"); #endif #ifndef DISABLE_OCSP fprintf(f, " OCSP"); #endif +#ifndef DISABLE_PRDR + fprintf(f, " PRDR"); +#endif +#ifdef SUPPORT_PROXY + fprintf(f, " PROXY"); +#endif +#ifdef SUPPORT_SOCKS + fprintf(f, " SOCKS"); +#endif #ifdef EXPERIMENTAL_SPF fprintf(f, " Experimental_SPF"); #endif @@ -843,14 +859,8 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_DMARC fprintf(f, " Experimental_DMARC"); #endif -#ifdef EXPERIMENTAL_PROXY - fprintf(f, " Experimental_Proxy"); -#endif -#ifdef EXPERIMENTAL_EVENT - fprintf(f, " Experimental_Event"); -#endif -#ifdef EXPERIMENTAL_REDIS - fprintf(f, " Experimental_Redis"); +#ifdef EXPERIMENTAL_DSN_INFO + fprintf(f, " Experimental_DSN_info"); #endif fprintf(f, "\n"); @@ -894,6 +904,9 @@ fprintf(f, "Lookups (built-in):"); #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2 fprintf(f, " pgsql"); #endif +#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 + fprintf(f, " redis"); +#endif #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2 fprintf(f, " sqlite"); #endif @@ -927,6 +940,9 @@ fprintf(f, "Authenticators:"); #ifdef AUTH_SPA fprintf(f, " spa"); #endif +#ifdef AUTH_TLS + fprintf(f, " tls"); +#endif fprintf(f, "\n"); fprintf(f, "Routers:"); @@ -1015,12 +1031,13 @@ DEBUG(D_any) do { #ifdef SUPPORT_TLS tls_version_report(f); #endif +#ifdef SUPPORT_I18N + utf8_version_report(f); +#endif - for (authi = auths_available; *authi->driver_name != '\0'; ++authi) { - if (authi->version_report) { + for (authi = auths_available; *authi->driver_name != '\0'; ++authi) + if (authi->version_report) (*authi->version_report)(f); - } - } /* PCRE_PRERELEASE is either defined and empty or a bare sequence of characters; unless it's an ancient version of PCRE in which case it @@ -1040,10 +1057,8 @@ DEBUG(D_any) do { init_lookup_list(); for (i = 0; i < lookup_list_count; i++) - { if (lookup_list[i]->version_report) lookup_list[i]->version_report(f); - } #ifdef WHITELIST_D_MACROS fprintf(f, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); @@ -1477,6 +1492,7 @@ BOOL f_end_dot = FALSE; BOOL deliver_give_up = FALSE; BOOL list_queue = FALSE; BOOL list_options = FALSE; +BOOL list_config = FALSE; BOOL local_queue_only; BOOL more = TRUE; BOOL one_msg_action = FALSE; @@ -1593,8 +1609,9 @@ if (!route_findgroup(US CONFIGURE_GROUPNAME, &config_gid)) } #endif -/* In the Cygwin environment, some initialization needs doing. It is fudged -in by means of this macro. */ +/* In the Cygwin environment, some initialization used to need doing. +It was fudged in by means of this macro; now no longer but we'll leave +it in case of others. */ #ifdef OS_INIT OS_INIT @@ -1627,6 +1644,10 @@ if (log_buffer == NULL) exit(EXIT_FAILURE); } +/* Initialize the default log options. */ + +bits_set(log_selector, log_selector_size, log_default); + /* Set log_stderr to stderr, provided that stderr exists. This gets reset to NULL when the daemon is run and the file is closed. We have to use this indirection, because some systems don't allow writing to the variable "stderr". @@ -1737,6 +1758,8 @@ regex_whitelisted_macro = regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); #endif +for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; + /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic @@ -2134,9 +2157,19 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "P") == 0) { - list_options = TRUE; - debug_selector |= D_v; - debug_file = stderr; + /* -bP config: we need to setup here, because later, + * when list_options is checked, the config is read already */ + if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0) + { + list_config = TRUE; + readconf_save_config(version_string); + } + else + { + list_options = TRUE; + debug_selector |= D_v; + debug_file = stderr; + } } /* -brt: Test retry configuration lookup */ @@ -2307,7 +2340,7 @@ for (i = 1; i < argc; i++) if (nr_configs) { int sep = 0; - uschar *list = argrest; + const uschar *list = argrest; uschar *filename; while (trusted_config && (filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) != NULL) @@ -2439,8 +2472,8 @@ for (i = 1; i < argc; i++) argrest++; } if (*argrest != 0) - decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options, - debug_options_count, US"debug", 0); + decode_bits(&selector, 1, debug_notall, argrest, + debug_options, debug_options_count, US"debug", 0); debug_selector = selector; } break; @@ -2512,7 +2545,7 @@ for (i = 1; i < argc; i++) case 'f': { - int start, end; + int dummy_start, dummy_end; uschar *errmess; if (*argrest == 0) { @@ -2520,9 +2553,7 @@ for (i = 1; i < argc; i++) { badarg = TRUE; break; } } if (*argrest == 0) - { sender_address = string_sprintf(""); /* Ensure writeable memory */ - } else { uschar *temp = argrest + Ustrlen(argrest) - 1; @@ -2530,8 +2561,15 @@ for (i = 1; i < argc; i++) if (temp >= argrest && *temp == '.') f_end_dot = TRUE; allow_domain_literals = TRUE; strip_trailing_dot = TRUE; - sender_address = parse_extract_address(argrest, &errmess, &start, &end, - &sender_address_domain, TRUE); +#ifdef SUPPORT_I18N + allow_utf8_domains = TRUE; +#endif + sender_address = parse_extract_address(argrest, &errmess, + &dummy_start, &dummy_end, &sender_address_domain, TRUE); +#ifdef SUPPORT_I18N + message_smtputf8 = string_is_utf8(sender_address); + allow_utf8_domains = FALSE; +#endif allow_domain_literals = FALSE; strip_trailing_dot = FALSE; if (sender_address == NULL) @@ -3698,6 +3736,10 @@ is equivalent to the ability to modify a setuid binary! This needs to happen before we read the main configuration. */ init_lookup_list(); +#ifdef SUPPORT_I18N +if (running_in_test_harness) smtputf8_advertise_hosts = NULL; +#endif + /* Read the main runtime configuration data; this gives up if there is a failure. It leaves the configuration file open so that the subsequent configuration data for delivery can be read if needed. */ @@ -3766,14 +3808,17 @@ else /* Handle the decoding of logging options. */ -decode_bits(&log_write_selector, &log_extra_selector, 0, 0, +decode_bits(log_selector, log_selector_size, log_notall, log_selector_string, log_options, log_options_count, US"log", 0); DEBUG(D_any) { + int i; debug_printf("configuration file is %s\n", config_main_filename); - debug_printf("log selectors = %08x %08x\n", log_write_selector, - log_extra_selector); + debug_printf("log selectors ="); + for (i = 0; i < log_selector_size; i++) + debug_printf(" %08x", log_selector[i]); + debug_printf("\n"); } /* If domain literals are not allowed, check the sender address that was @@ -3980,7 +4025,7 @@ a debugging feature for finding out what arguments certain MUAs actually use. Don't attempt it if logging is disabled, or if listing variables or if verifying/testing addresses or expansions. */ -if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0) +if (((debug_selector & D_any) != 0 || LOGGING(arguments)) && really_exim && !list_options && !checking) { int i; @@ -3994,7 +4039,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0) for (i = 0; i < argc; i++) { int len = Ustrlen(argv[i]); - uschar *printing; + const uschar *printing; uschar *quote; if (p + len + 8 >= big_buffer + big_buffer_size) { @@ -4006,7 +4051,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0) printing = string_printing(argv[i]); if (printing[0] == 0) quote = US"\""; else { - uschar *pp = printing; + const uschar *pp = printing; quote = US""; while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; } } @@ -4015,7 +4060,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0) while (*p) p++; } - if ((log_extra_selector & LX_arguments) != 0) + if (LOGGING(arguments)) log_write(0, LOG_MAIN, "%s", big_buffer); else debug_printf("%s\n", big_buffer); @@ -4503,6 +4548,13 @@ if (list_options) exim_exit(EXIT_SUCCESS); } +if (list_config) + { + set_process_info("listing config"); + readconf_print(US"config", NULL, FALSE); + exim_exit(EXIT_SUCCESS); + } + /* Handle a request to deliver one or more messages that are already on the queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with @@ -4999,8 +5051,9 @@ if (host_checking) "**** This is not for real!\n\n", sender_host_address); + memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (verify_check_host(&hosts_connection_nolog) == OK) - log_write_selector &= ~L_smtp_connection; + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails, @@ -5069,6 +5122,9 @@ if (mua_wrapper) deliver_drop_privilege = TRUE; queue_smtp = FALSE; queue_smtp_domains = NULL; +#ifdef SUPPORT_I18N + message_utf8_downconvert = -1; /* convert-if-needed */ +#endif } @@ -5170,8 +5226,9 @@ if (smtp_input) { smtp_in = stdin; smtp_out = stdout; + memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (verify_check_host(&hosts_connection_nolog) == OK) - log_write_selector &= ~L_smtp_connection; + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); if (!smtp_start_session()) { @@ -5347,7 +5404,6 @@ while (more) if (recipients_max > 0 && ++rcount > recipients_max && !extract_recipients) - { if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: too many recipients\n"); @@ -5359,11 +5415,22 @@ while (more) moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)? errors_sender_rc : EXIT_FAILURE; } - } +#ifdef SUPPORT_I18N + { + BOOL b = allow_utf8_domains; + allow_utf8_domains = TRUE; +#endif recipient = parse_extract_address(s, &errmess, &start, &end, &domain, FALSE); +#ifdef SUPPORT_I18N + if (string_is_utf8(recipient)) + message_smtputf8 = TRUE; + else + allow_utf8_domains = b; + } +#endif if (domain == 0 && !allow_unqualified_recipient) { recipient = NULL; @@ -5463,9 +5530,7 @@ while (more) return_path = string_copy(sender_address); } else - { printf("Return-path = %s\n", (return_path[0] == 0)? US"<>" : return_path); - } printf("Sender = %s\n", (sender_address[0] == 0)? US"<>" : sender_address); receive_add_recipient( diff --git a/src/src/exim.h b/src/src/exim.h index fb48a43d3..f94f00b97 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -418,7 +418,7 @@ iconv(). It's os.h file defines ICONV_ARG2_TYPE. For the rest, define a default here. */ #ifndef ICONV_ARG2_TYPE -# define ICONV_ARG2_TYPE const char ** +# define ICONV_ARG2_TYPE char ** #endif /* One OS uses a different type for the 5th argument of getsockopt */ @@ -591,4 +591,9 @@ default to EDQUOT if it exists, otherwise ENOSPC. */ #endif #endif +/* DANE w/o DNSSEC is useless */ +#if defined(EXPERIMENTAL_DANE) && defined(DISABLE_DNSSEC) + #undef DISABLE_DNSSEC +#endif + /* End of exim.h */ diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index ce06f16e7..7babc643e 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -88,10 +88,10 @@ Returns: the value of the character escape */ int -string_interpret_escape(uschar **pp) +string_interpret_escape(const uschar **pp) { int ch; -uschar *p = *pp; +const uschar *p = *pp; ch = *(++p); if (isdigit(ch) && ch != '8' && ch != '9') { @@ -329,7 +329,7 @@ while (Ufgets(line, max_insize, f) != NULL) keystart = t; while (*s != 0 && *s != '\"') { - if (*s == '\\') *t++ = string_interpret_escape(&s); + if (*s == '\\') *t++ = string_interpret_escape((const uschar **)&s); else *t++ = *s; s++; } diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 124303abe..417a42db6 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -356,15 +356,19 @@ Returns: a pointer to the retrieved record, or */ void * -dbfn_read_with_length(open_db *dbblock, uschar *key, int *length) +dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) { void *yield; EXIM_DATUM key_datum, result_datum; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; @@ -396,16 +400,20 @@ Returns: the yield of the underlying dbm or db "write" function. If this */ int -dbfn_write(open_db *dbblock, uschar *key, void *ptr, int length) +dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length) { EXIM_DATUM key_datum, value_datum; dbdata_generic *gptr = (dbdata_generic *)ptr; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; EXIM_DATUM_DATA(value_datum) = CS ptr; EXIM_DATUM_SIZE(value_datum) = length; return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum); @@ -426,12 +434,16 @@ Returns: the yield of the underlying dbm or db "delete" function. */ int -dbfn_delete(open_db *dbblock, uschar *key) +dbfn_delete(open_db *dbblock, const uschar *key) { +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); EXIM_DATUM key_datum; EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; return EXIM_DBDEL(dbblock->dbptr, key_datum); } @@ -630,19 +642,6 @@ while (key != NULL) printf("\n"); } - /* Old-style domain record, without separate timestamps. This code can - eventually be thrown away, say in 5 years' time (it's now Feb 2003). */ - - else - { - printf("%s %s callout=%s postmaster=%s random=%s\n", - print_time(((dbdata_generic *)value)->time_stamp), - keybuffer, - print_cache(callout->result), - print_cache(callout->postmaster_result), - print_cache(callout->random_result)); - } - break; case type_ratelimit: diff --git a/src/src/eximon.src b/src/src/eximon.src index fac24208c..d461ccffa 100644 --- a/src/src/eximon.src +++ b/src/src/eximon.src @@ -4,7 +4,7 @@ # The build process concatenates on the front of this various settings from # os-specific files and from the user's configuration file. -# Copyright (c) 2004 - 2012 University of Cambridge. +# Copyright (c) 2004 - 2015 University of Cambridge. # See the file NOTICE for conditions of use and distribution. # Except when they appear in comments, the following placeholders in this @@ -79,11 +79,12 @@ LOG_FILE_PATH=`$EXIM_PATH -C $config -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 -# it where the log data is. Otherwise, remove any occurrences of -# "syslog:" or ":syslog" (spaces allowed in various places) and look at the -# remainder of the entry. If it's null, the default is "mainlog" in the -# "log" directory in the spool directory. Otherwise, set the name from the -# given path. +# it where the log data is. If log_file_path is unset (i.e. empty) the default +# is "mainlog" in the "log" directory in the spool directory. Otherwise, +# remove any occurrences of "syslog:" or ":syslog" (spaces allowed in various +# places) and look at the remainder of the entry. If it's null, check whether +# LOG_FILE_NAME was set a compile time and contains a path. Otherwise fall +# back to the default path. if [ "$EXIMON_LOG_FILE_PATH" != "" ] ; then LOG_FILE_NAME="$EXIMON_LOG_FILE_PATH" @@ -94,6 +95,8 @@ elif [ "$LOG_FILE_PATH" = "syslog" ] ; then echo MAIL.INFO syslog messages into a separate file, you can point eximon at echo that file with the EXIMON_LOG_FILE_PATH environment variable. echo \*\*\* +elif [ "$LOG_FILE_PATH" = "" ] ; then + LOG_FILE_NAME=$SPOOL_DIRECTORY/log/mainlog else LOG_FILE_NAME=`echo $LOG_FILE_PATH | \ sed -e 's/ *: *syslog *: */:/' \ @@ -101,7 +104,17 @@ else -e 's/^ *syslog *: *//' \ -e 's/%s/main/'` if [ "$LOG_FILE_NAME" = "" ] ; then - LOG_FILE_NAME=$SPOOL_DIRECTORY/log/mainlog + COMPILETIMEDEFAULT=`$EXIM_PATH -C /dev/null -bP log_file_path | \ + sed -e 's/.*=[ ]*//' \ + -e 's/ *: *syslog *: */:/' \ + -e 's/ *: *syslog *$//' \ + -e 's/^ *syslog *: *//' \ + -e 's/%s/main/'` + if [ "$COMPILETIMEDEFAULT" != "" ] ; then + LOG_FILE_NAME="$COMPILETIMEDEFAULT" + else + LOG_FILE_NAME=$SPOOL_DIRECTORY/log/mainlog + fi fi fi diff --git a/src/src/exiqgrep.src b/src/src/exiqgrep.src index afecbffee..2c52f137f 100644 --- a/src/src/exiqgrep.src +++ b/src/src/exiqgrep.src @@ -44,6 +44,7 @@ if ($^O eq 'darwin') { # aka MacOS X }; getopts('hf:r:y:o:s:C:zxlibRca',\%opt); +if ($ARGV[0]) { &help; exit;} if ($opt{h}) { &help; exit;} if ($opt{a}) { $eargs = '-bp'; } if ($opt{C} && -e $opt{C} && -f $opt{C} && -R $opt{C}) { $eargs .= ' -C '.$opt{C}; } diff --git a/src/src/expand.c b/src/src/expand.c index bf79a8222..a51dac570 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -13,8 +13,8 @@ /* Recursively called function */ -static uschar *expand_string_internal(uschar *, BOOL, uschar **, BOOL, BOOL, BOOL *); -static int_eximarith_t expanded_string_integer(uschar *, BOOL); +static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *); +static int_eximarith_t expanded_string_integer(const uschar *, BOOL); #ifdef STAND_ALONE #ifndef SUPPORT_CRYPTEQ @@ -94,10 +94,6 @@ bcrypt ({CRYPT}$2a$). -#ifndef nelements -# define nelements(arr) (sizeof(arr) / sizeof(*arr)) -#endif - /************************************************* * Local statics and tables * *************************************************/ @@ -109,11 +105,15 @@ static uschar *item_table[] = { US"acl", US"certextract", US"dlfunc", + US"env", US"extract", US"filter", US"hash", US"hmac", US"if", +#ifdef SUPPORT_I18N + US"imapfolder", +#endif US"length", US"listextract", US"lookup", @@ -135,11 +135,15 @@ enum { EITEM_ACL, EITEM_CERTEXTRACT, EITEM_DLFUNC, + EITEM_ENV, EITEM_EXTRACT, EITEM_FILTER, EITEM_HASH, EITEM_HMAC, EITEM_IF, +#ifdef SUPPORT_I18N + EITEM_IMAPFOLDER, +#endif EITEM_LENGTH, EITEM_LISTEXTRACT, EITEM_LOOKUP, @@ -168,7 +172,14 @@ static uschar *op_table_underscore[] = { US"quote_local_part", US"reverse_ip", US"time_eval", - US"time_interval"}; + US"time_interval" +#ifdef SUPPORT_I18N + ,US"utf8_domain_from_alabel", + US"utf8_domain_to_alabel", + US"utf8_localpart_from_alabel", + US"utf8_localpart_to_alabel" +#endif + }; enum { EOP_FROM_UTF8, @@ -176,7 +187,14 @@ enum { EOP_QUOTE_LOCAL_PART, EOP_REVERSE_IP, EOP_TIME_EVAL, - EOP_TIME_INTERVAL }; + EOP_TIME_INTERVAL +#ifdef SUPPORT_I18N + ,EOP_UTF8_DOMAIN_FROM_ALABEL, + EOP_UTF8_DOMAIN_TO_ALABEL, + EOP_UTF8_LOCALPART_FROM_ALABEL, + EOP_UTF8_LOCALPART_TO_ALABEL +#endif + }; static uschar *op_table_main[] = { US"address", @@ -192,6 +210,8 @@ static uschar *op_table_main[] = { US"hash", US"hex2b64", US"hexquote", + US"ipv6denorm", + US"ipv6norm", US"l", US"lc", US"length", @@ -217,7 +237,7 @@ static uschar *op_table_main[] = { US"utf8clean" }; enum { - EOP_ADDRESS = sizeof(op_table_underscore)/sizeof(uschar *), + EOP_ADDRESS = nelem(op_table_underscore), EOP_ADDRESSES, EOP_BASE62, EOP_BASE62D, @@ -230,6 +250,8 @@ enum { EOP_HASH, EOP_HEX2B64, EOP_HEXQUOTE, + EOP_IPV6DENORM, + EOP_IPV6NORM, EOP_L, EOP_LC, EOP_LENGTH, @@ -444,6 +466,7 @@ static var_entry var_table[] = { { "bounce_return_size_limit", vtype_int, &bounce_return_size_limit }, { "caller_gid", vtype_gid, &real_gid }, { "caller_uid", vtype_uid, &real_uid }, + { "callout_address", vtype_stringptr, &callout_address }, { "compile_date", vtype_stringptr, &version_date }, { "compile_number", vtype_stringptr, &version_cnumber }, { "config_dir", vtype_stringptr, &config_main_directory }, @@ -470,6 +493,7 @@ static var_entry var_table[] = { { "dkim_headernames", vtype_dkim, (void *)DKIM_HEADERNAMES }, { "dkim_identity", vtype_dkim, (void *)DKIM_IDENTITY }, { "dkim_key_granularity",vtype_dkim, (void *)DKIM_KEY_GRANULARITY }, + { "dkim_key_length", vtype_int, &dkim_key_length }, { "dkim_key_nosubdomains",vtype_dkim, (void *)DKIM_NOSUBDOMAINS }, { "dkim_key_notes", vtype_dkim, (void *)DKIM_KEY_NOTES }, { "dkim_key_srvtype", vtype_dkim, (void *)DKIM_KEY_SRVTYPE }, @@ -492,7 +516,7 @@ static var_entry var_table[] = { { "dnslist_value", vtype_stringptr, &dnslist_value }, { "domain", vtype_stringptr, &deliver_domain }, { "domain_data", vtype_stringptr, &deliver_domain_data }, -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT { "event_data", vtype_stringptr, &event_data }, /*XXX want to use generic vars for as many of these as possible*/ @@ -549,6 +573,9 @@ static var_entry var_table[] = { { "message_id", vtype_stringptr, &message_id }, { "message_linecount", vtype_int, &message_linecount }, { "message_size", vtype_int, &message_size }, +#ifdef SUPPORT_I18N + { "message_smtputf8", vtype_bool, &message_smtputf8 }, +#endif #ifdef WITH_CONTENT_SCAN { "mime_anomaly_level", vtype_int, &mime_anomaly_level }, { "mime_anomaly_text", vtype_stringptr, &mime_anomaly_text }, @@ -584,13 +611,16 @@ static var_entry var_table[] = { { "parent_domain", vtype_stringptr, &deliver_domain_parent }, { "parent_local_part", vtype_stringptr, &deliver_localpart_parent }, { "pid", vtype_pid, NULL }, +#ifndef DISABLE_PRDR + { "prdr_requested", vtype_bool, &prdr_requested }, +#endif { "primary_hostname", vtype_stringptr, &primary_hostname }, -#ifdef EXPERIMENTAL_PROXY - { "proxy_host_address", vtype_stringptr, &proxy_host_address }, - { "proxy_host_port", vtype_int, &proxy_host_port }, +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) + { "proxy_external_address",vtype_stringptr, &proxy_external_address }, + { "proxy_external_port", vtype_int, &proxy_external_port }, + { "proxy_local_address", vtype_stringptr, &proxy_local_address }, + { "proxy_local_port", vtype_int, &proxy_local_port }, { "proxy_session", vtype_bool, &proxy_session }, - { "proxy_target_address",vtype_stringptr, &proxy_target_address }, - { "proxy_target_port", vtype_int, &proxy_target_port }, #endif { "prvscheck_address", vtype_stringptr, &prvscheck_address }, { "prvscheck_keynum", vtype_stringptr, &prvscheck_keynum }, @@ -625,6 +655,7 @@ static var_entry var_table[] = { { "sender_address_local_part", vtype_localpart, &sender_address }, { "sender_data", vtype_stringptr, &sender_data }, { "sender_fullhost", vtype_stringptr, &sender_fullhost }, + { "sender_helo_dnssec", vtype_bool, &sender_helo_dnssec }, { "sender_helo_name", vtype_stringptr, &sender_helo_name }, { "sender_host_address", vtype_stringptr, &sender_host_address }, { "sender_host_authenticated",vtype_stringptr, &sender_host_authenticated }, @@ -655,6 +686,7 @@ static var_entry var_table[] = { { "sn8", vtype_filter_int, &filter_sn[8] }, { "sn9", vtype_filter_int, &filter_sn[9] }, #ifdef WITH_CONTENT_SCAN + { "spam_action", vtype_stringptr, &spam_action }, { "spam_bar", vtype_stringptr, &spam_bar }, { "spam_report", vtype_stringptr, &spam_report }, { "spam_score", vtype_stringptr, &spam_score }, @@ -737,7 +769,7 @@ static var_entry var_table[] = { { "warnmsg_recipients", vtype_stringptr, &warnmsg_recipients } }; -static int var_table_size = sizeof(var_table)/sizeof(var_entry); +static int var_table_size = nelem(var_table); static uschar var_buffer[256]; static BOOL malformed_header; @@ -974,8 +1006,8 @@ Note: The test for *s != 0 in the while loop is necessary because Ustrchr() yields non-NULL if the character is zero (which is not something I expected). */ -static uschar * -read_name(uschar *name, int max, uschar *s, uschar *extras) +static const uschar * +read_name(uschar *name, int max, const uschar *s, uschar *extras) { int ptr = 0; while (*s != 0 && (isalnum(*s) || Ustrchr(extras, *s) != NULL)) @@ -1008,8 +1040,8 @@ Arguments: Returns: a pointer to the first character after the header name */ -static uschar * -read_header_name(uschar *name, int max, uschar *s) +static const uschar * +read_header_name(uschar *name, int max, const uschar *s) { int prelen = Ustrchr(name, '_') - name + 1; int ptr = Ustrlen(name) - prelen; @@ -1046,6 +1078,14 @@ while (isdigit(*s)) *n = *n * 10 + (*s++ - '0'); return s; } +static const uschar * +read_cnumber(int *n, const uschar *s) +{ +*n = 0; +while (isdigit(*s)) *n = *n * 10 + (*s++ - '0'); +return s; +} + /************************************************* @@ -1063,7 +1103,7 @@ Returns: NULL if the subfield was not found, or */ static uschar * -expand_getkeyed(uschar *key, uschar *s) +expand_getkeyed(uschar *key, const uschar *s) { int length = Ustrlen(key); while (isspace(*s)) s++; @@ -1074,7 +1114,7 @@ while (*s != 0) { int dkeylength; uschar *data; - uschar *dkey = s; + const uschar *dkey = s; while (*s != 0 && *s != '=' && !isspace(*s)) s++; dkeylength = s - dkey; @@ -1185,9 +1225,9 @@ return fieldtext; static uschar * -expand_getlistele(int field, uschar * list) +expand_getlistele(int field, const uschar * list) { -uschar * tlist= list; +const uschar * tlist= list; int sep= 0; uschar dummy; @@ -1235,7 +1275,7 @@ certfield * cp; if (!(vp = find_var_ent(certvar))) { - expand_string_message = + expand_string_message = string_sprintf("no variable named \"%s\"", certvar); return NULL; /* Unknown variable name */ } @@ -1243,7 +1283,7 @@ if (!(vp = find_var_ent(certvar))) want to do that in future */ if (vp->type != vtype_cert) { - expand_string_message = + expand_string_message = string_sprintf("\"%s\" is not a certificate", certvar); return NULL; /* Unknown variable name */ } @@ -1254,7 +1294,7 @@ if (*field >= '0' && *field <= '9') return tls_cert_ext_by_oid(*(void **)vp->value, field, 0); for(cp = certfields; - cp < certfields + nelements(certfields); + cp < certfields + nelem(certfields); cp++) if (Ustrncmp(cp->name, field, cp->namelen) == 0) { @@ -1263,7 +1303,7 @@ for(cp = certfields; return (*cp->getfn)( *(void **)vp->value, modifier ); } -expand_string_message = +expand_string_message = string_sprintf("bad field selector \"%s\" for certextract", field); return NULL; } @@ -1424,7 +1464,7 @@ unsigned long int total = 0; /* no overflow */ while (*s != 0) { - if (i == 0) i = sizeof(prime)/sizeof(int) - 1; + if (i == 0) i = nelem(prime) - 1; total += prime[i--] * (unsigned int)(*s++); } @@ -1695,7 +1735,14 @@ if (Ustrncmp(name, "auth", 4) == 0) uschar *endptr; int n = Ustrtoul(name + 4, &endptr, 10); if (*endptr == 0 && n != 0 && n <= AUTH_VARS) - return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1]; + return !auth_vars[n-1] ? US"" : auth_vars[n-1]; + } +else if (Ustrncmp(name, "regex", 5) == 0) + { + uschar *endptr; + int n = Ustrtoul(name + 5, &endptr, 10); + if (*endptr == 0 && n != 0 && n <= REGEX_VARS) + return !regex_vars[n-1] ? US"" : regex_vars[n-1]; } /* For all other variables, search the table */ @@ -1937,11 +1984,11 @@ Returns: 0 OK; string pointer updated */ static int -read_subs(uschar **sub, int n, int m, uschar **sptr, BOOL skipping, +read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping, BOOL check_end, uschar *name, BOOL *resetok) { int i; -uschar *s = *sptr; +const uschar *s = *sptr; while (isspace(*s)) s++; for (i = 0; i < n; i++) @@ -2017,15 +2064,15 @@ static int eval_acl(uschar ** sub, int nsub, uschar ** user_msgp) { int i; -uschar *tmp; int sav_narg = acl_narg; int ret; +uschar * dummy_logmsg; extern int acl_where; -if(--nsub > sizeof(acl_arg)/sizeof(*acl_arg)) nsub = sizeof(acl_arg)/sizeof(*acl_arg); +if(--nsub > nelem(acl_arg)) nsub = nelem(acl_arg); for (i = 0; i < nsub && sub[i+1]; i++) { - tmp = acl_arg[i]; + uschar * tmp = acl_arg[i]; acl_arg[i] = sub[i+1]; /* place callers args in the globals */ sub[i+1] = tmp; /* stash the old args using our caller's storage */ } @@ -2042,7 +2089,7 @@ DEBUG(D_expand) acl_narg>0 ? acl_arg[0] : US"<none>", acl_narg>1 ? " +more" : ""); -ret = acl_eval(acl_where, sub[0], user_msgp, &tmp); +ret = acl_eval(acl_where, sub[0], user_msgp, &dummy_logmsg); for (i = 0; i < nsub; i++) acl_arg[i] = sub[i+1]; /* restore old args */ @@ -2073,8 +2120,8 @@ Returns: a pointer to the first character after the condition, or NULL after an error */ -static uschar * -eval_condition(uschar *s, BOOL *resetok, BOOL *yield) +static const uschar * +eval_condition(const uschar *s, BOOL *resetok, BOOL *yield) { BOOL testfor = TRUE; BOOL tempcond, combined_cond; @@ -2084,7 +2131,7 @@ int i, rc, cond_type, roffset; int_eximarith_t num[2]; struct stat statbuf; uschar name[256]; -uschar *sub[10]; +const uschar *sub[10]; const pcre *re; const uschar *rerror; @@ -2124,7 +2171,7 @@ if (name[0] == 0) /* Find which condition we are dealing with, and switch on it */ -cond_type = chop_match(name, cond_table, sizeof(cond_table)/sizeof(uschar *)); +cond_type = chop_match(name, cond_table, nelem(cond_table)); switch(cond_type) { /* def: tests for a non-empty variable, or for the existence of a header. If @@ -2304,6 +2351,7 @@ switch(cond_type) case ECOND_ACL: /* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} */ { + uschar *sub[10]; uschar *user_msg; BOOL cond = FALSE; int size = 0; @@ -2312,7 +2360,7 @@ switch(cond_type) while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; /*}*/ - switch(read_subs(sub, sizeof(sub)/sizeof(*sub), 1, + switch(read_subs(sub, nelem(sub), 1, &s, yield == NULL, TRUE, US"acl", resetok)) { case 1: expand_string_message = US"too few arguments or bracketing " @@ -2322,7 +2370,7 @@ switch(cond_type) } *resetok = FALSE; - if (yield != NULL) switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + if (yield != NULL) switch(eval_acl(sub, nelem(sub), &user_msg)) { case OK: cond = TRUE; @@ -2354,29 +2402,32 @@ switch(cond_type) in their own set of braces. */ case ECOND_SASLAUTHD: - #ifndef CYRUS_SASLAUTHD_SOCKET - goto COND_FAILED_NOT_COMPILED; - #else - while (isspace(*s)) s++; - if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ - switch(read_subs(sub, 4, 2, &s, yield == NULL, TRUE, US"saslauthd", resetok)) - { - case 1: expand_string_message = US"too few arguments or bracketing " - "error for saslauthd"; - case 2: - case 3: return NULL; - } - if (sub[2] == NULL) sub[3] = NULL; /* realm if no service */ - if (yield != NULL) +#ifndef CYRUS_SASLAUTHD_SOCKET + goto COND_FAILED_NOT_COMPILED; +#else { - int rc; - rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3], - &expand_string_message); - if (rc == ERROR || rc == DEFER) return NULL; - *yield = (rc == OK) == testfor; + uschar *sub[4]; + while (isspace(*s)) s++; + if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ + switch(read_subs(sub, nelem(sub), 2, &s, yield == NULL, TRUE, US"saslauthd", + resetok)) + { + case 1: expand_string_message = US"too few arguments or bracketing " + "error for saslauthd"; + case 2: + case 3: return NULL; + } + if (sub[2] == NULL) sub[3] = NULL; /* realm if no service */ + if (yield != NULL) + { + int rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3], + &expand_string_message); + if (rc == ERROR || rc == DEFER) return NULL; + *yield = (rc == OK) == testfor; + } + return s; } - return s; - #endif /* CYRUS_SASLAUTHD_SOCKET */ +#endif /* CYRUS_SASLAUTHD_SOCKET */ /* symbolic operators for numeric and string comparison, and a number of @@ -2754,6 +2805,7 @@ switch(cond_type) case ECOND_INLIST: case ECOND_INLISTI: { + const uschar * list = sub[1]; int sep = 0; uschar *save_iterate_item = iterate_item; int (*compare)(const uschar *, const uschar *); @@ -2761,12 +2813,10 @@ switch(cond_type) DEBUG(D_expand) debug_printf("condition: %s\n", name); tempcond = FALSE; - if (cond_type == ECOND_INLISTI) - compare = strcmpic; - else - compare = (int (*)(const uschar *, const uschar *)) strcmp; + compare = cond_type == ECOND_INLISTI + ? strcmpic : (int (*)(const uschar *, const uschar *)) strcmp; - while ((iterate_item = string_nextinlist(&sub[1], &sep, NULL, 0)) != NULL) + while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0))) if (compare(sub[0], iterate_item) == 0) { tempcond = TRUE; @@ -2844,6 +2894,7 @@ switch(cond_type) case ECOND_FORALL: case ECOND_FORANY: { + const uschar * list; int sep = 0; uschar *save_iterate_item = iterate_item; @@ -2883,7 +2934,8 @@ switch(cond_type) } if (yield != NULL) *yield = !testfor; - while ((iterate_item = string_nextinlist(&sub[0], &sep, NULL, 0)) != NULL) + list = sub[0]; + while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)) != NULL) { DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, iterate_item); if (!eval_condition(sub[1], resetok, &tempcond)) @@ -3101,11 +3153,11 @@ Returns: 0 OK; lookup_value has been reset to save_lookup */ static int -process_yesno(BOOL skipping, BOOL yes, uschar *save_lookup, uschar **sptr, +process_yesno(BOOL skipping, BOOL yes, uschar *save_lookup, const uschar **sptr, uschar **yieldptr, int *sizeptr, int *ptrptr, uschar *type, BOOL *resetok) { int rc = 0; -uschar *s = *sptr; /* Local value */ +const uschar *s = *sptr; /* Local value */ uschar *sub1, *sub2; /* If there are no following strings, we substitute the contents of $value for @@ -3183,7 +3235,8 @@ inside another lookup or if or extract. */ else if (*s != '}') { uschar name[256]; - s = read_name(name, sizeof(name), s, US"_"); + /* deconst cast ok here as source is s anyway */ + s = US read_name(name, sizeof(name), s, US"_"); if (Ustrcmp(name, "fail") == 0) { if (!yes && !skipping) @@ -3752,14 +3805,14 @@ Returns: NULL if expansion fails: */ static uschar * -expand_string_internal(uschar *string, BOOL ket_ends, uschar **left, +expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left, BOOL skipping, BOOL honour_dollar, BOOL *resetok_p) { int ptr = 0; int size = Ustrlen(string)+ 64; int item_type; uschar *yield = store_get(size); -uschar *s = string; +const uschar *s = string; uschar *save_expand_nstring[EXPAND_MAXN+1]; int save_expand_nlength[EXPAND_MAXN+1]; BOOL resetok = TRUE; @@ -3787,7 +3840,7 @@ while (*s != 0) if (s[1] == 'N') { - uschar *t = s + 2; + const uschar * t = s + 2; for (s = t; *s != 0; s++) if (*s == '\\' && s[1] == 'N') break; yield = string_cat(yield, &size, &ptr, t, s - t); if (*s != 0) s += 2; @@ -3903,7 +3956,7 @@ while (*s != 0) if (isdigit(*s)) { int n; - s = read_number(&n, s); + s = read_cnumber(&n, s); if (n >= 0 && n <= expand_nmax) yield = string_cat(yield, &size, &ptr, expand_nstring[n], expand_nlength[n]); @@ -3924,7 +3977,7 @@ while (*s != 0) if (isdigit((*(++s)))) { int n; - s = read_number(&n, s); /*{*/ + s = read_cnumber(&n, s); /*{*/ if (*s++ != '}') { /*{*/ expand_string_message = US"} expected after number"; @@ -3947,7 +4000,7 @@ while (*s != 0) OK. */ s = read_name(name, sizeof(name), s, US"_-"); - item_type = chop_match(name, item_table, sizeof(item_table)/sizeof(uschar *)); + item_type = chop_match(name, item_table, nelem(item_table)); switch(item_type) { @@ -3966,7 +4019,8 @@ while (*s != 0) uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; - switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl", &resetok)) + switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, US"acl", + &resetok)) { case 1: goto EXPAND_FAILED_CURLY; case 2: @@ -3975,7 +4029,7 @@ while (*s != 0) if (skipping) continue; resetok = FALSE; - switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + switch(eval_acl(sub, nelem(sub), &user_msg)) { case OK: case FAIL: @@ -4001,7 +4055,7 @@ while (*s != 0) case EITEM_IF: { BOOL cond = FALSE; - uschar *next_s; + const uschar *next_s; int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); @@ -4041,6 +4095,43 @@ while (*s != 0) continue; } +#ifdef SUPPORT_I18N + case EITEM_IMAPFOLDER: + { /* ${imapfolder {name}{sep]{specials}} */ + uschar *sub_arg[3]; + uschar *encoded; + + switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name, + &resetok)) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + + if (sub_arg[1] == NULL) /* One argument */ + { + sub_arg[1] = US"/"; /* default separator */ + sub_arg[2] = NULL; + } + else if (Ustrlen(sub_arg[1]) != 1) + { + expand_string_message = + string_sprintf( + "IMAP folder separator must be one character, found \"%s\"", + sub_arg[1]); + goto EXPAND_FAILED; + } + + if (!(encoded = imap_utf7_encode(sub_arg[0], headers_charset, + sub_arg[1][0], sub_arg[2], &expand_string_message))) + goto EXPAND_FAILED; + if (!skipping) + yield = string_cat(yield, &size, &ptr, encoded, Ustrlen(encoded)); + continue; + } +#endif + /* Handle database lookups unless locked out. If "skipping" is TRUE, we are expanding an internal string that isn't actually going to be used. All we need to do is check the syntax, so don't do a lookup at all. Preserve the @@ -4053,7 +4144,8 @@ while (*s != 0) int stype, partial, affixlen, starflags; int expand_setup = 0; int nameptr = 0; - uschar *key, *filename, *affix; + uschar *key, *filename; + const uschar *affix; uschar *save_lookup_value = lookup_value; int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); @@ -4771,7 +4863,7 @@ while (*s != 0) { FILE *f; uschar *arg; - uschar **argv; + const uschar **argv; pid_t pid; int fd_in, fd_out; int lsize = 0; @@ -4809,7 +4901,7 @@ while (*s != 0) /* Create the child process, making it a group leader. */ - pid = child_open(argv, NULL, 0077, &fd_in, &fd_out, TRUE); + pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE); if (pid < 0) { @@ -5129,7 +5221,7 @@ while (*s != 0) { int ovector[3*(EXPAND_MAXN+1)]; int n = pcre_exec(re, NULL, CS subject, slen, moffset + moffsetextra, - PCRE_EOPT | emptyopt, ovector, sizeof(ovector)/sizeof(int)); + PCRE_EOPT | emptyopt, ovector, nelem(ovector)); int nn; uschar *insert; @@ -5471,7 +5563,7 @@ while (*s != 0) int sep = 0; int save_ptr = ptr; uschar outsep[2] = { '\0', '\0' }; - uschar *list, *expr, *temp; + const uschar *list, *expr, *temp; uschar *save_iterate_item = iterate_item; uschar *save_lookup_value = lookup_value; @@ -5484,11 +5576,12 @@ while (*s != 0) if (item_type == EITEM_REDUCE) { + uschar * t; while (isspace(*s)) s++; if (*s++ != '{') goto EXPAND_FAILED_CURLY; - temp = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); - if (temp == NULL) goto EXPAND_FAILED; - lookup_value = temp; + t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); + if (!t) goto EXPAND_FAILED; + lookup_value = t; if (*s++ != '}') goto EXPAND_FAILED_CURLY; } @@ -5509,9 +5602,7 @@ while (*s != 0) if (temp != NULL) s = temp; } else - { temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); - } if (temp == NULL) { @@ -5569,7 +5660,8 @@ while (*s != 0) else { - temp = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok); + uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok); + temp = t; if (temp == NULL) { iterate_item = save_iterate_item; @@ -5579,7 +5671,7 @@ while (*s != 0) } if (item_type == EITEM_REDUCE) { - lookup_value = temp; /* Update the value of $value */ + lookup_value = t; /* Update the value of $value */ continue; /* and continue the iteration */ } } @@ -5642,10 +5734,9 @@ while (*s != 0) case EITEM_SORT: { int sep = 0; - uschar *srclist, *cmp, *xtract; + const uschar *srclist, *cmp, *xtract; uschar *srcitem; - uschar *dstlist = NULL; - uschar *dstkeylist = NULL; + const uschar *dstlist = NULL, *dstkeylist = NULL; uschar * tmp; uschar *save_iterate_item = iterate_item; @@ -5787,12 +5878,12 @@ while (*s != 0) #define EXPAND_DLFUNC_MAX_ARGS 8 case EITEM_DLFUNC: - #ifndef EXPAND_DLFUNC - expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/ - "is not included in this binary"; - goto EXPAND_FAILED; +#ifndef EXPAND_DLFUNC + expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/ + "is not included in this binary"; + goto EXPAND_FAILED; - #else /* EXPAND_DLFUNC */ +#else /* EXPAND_DLFUNC */ { tree_node *t; exim_dlfunc_t *func; @@ -5878,7 +5969,39 @@ while (*s != 0) goto EXPAND_FAILED; } } - #endif /* EXPAND_DLFUNC */ +#endif /* EXPAND_DLFUNC */ + + case EITEM_ENV: /* ${env {name} {val_if_found} {val_if_unfound}} */ + { + uschar * key; + uschar *save_lookup_value = lookup_value; + + while (isspace(*s)) s++; + if (*s != '{') /*}*/ + goto EXPAND_FAILED; + + key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + if (!key) goto EXPAND_FAILED; /*{*/ + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + lookup_value = US getenv(CS key); + + switch(process_yesno( + skipping, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + &size, /* output size */ + &ptr, /* output current point */ + US"env", /* condition type */ + &resetok)) + { + case 1: goto EXPAND_FAILED; /* when all is well, the */ + case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ + } + continue; + } } /* EITEM_* switch */ /* Control reaches here if the name is not recognized as one of the more @@ -5899,13 +6022,12 @@ while (*s != 0) the arguments and then scan the main table. */ if ((c = chop_match(name, op_table_underscore, - sizeof(op_table_underscore)/sizeof(uschar *))) < 0) + nelem(op_table_underscore))) < 0) { arg = Ustrchr(name, '_'); if (arg != NULL) *arg = 0; - c = chop_match(name, op_table_main, - sizeof(op_table_main)/sizeof(uschar *)); - if (c >= 0) c += sizeof(op_table_underscore)/sizeof(uschar *); + c = chop_match(name, op_table_main, nelem(op_table_main)); + if (c >= 0) c += nelem(op_table_underscore); if (arg != NULL) *arg++ = '_'; /* Put back for error messages */ } @@ -5919,7 +6041,7 @@ while (*s != 0) case EOP_SHA256: if (s[1] == '$') { - uschar * s1 = s; + const uschar * s1 = s; sub = expand_string_internal(s+2, TRUE, &s1, skipping, FALSE, &resetok); if (!sub) goto EXPAND_FAILED; /*{*/ @@ -6150,7 +6272,7 @@ while (*s != 0) uschar * cp; uschar buffer[256]; - while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++; + while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++; cp = string_sprintf("%d", cnt); yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp)); continue; @@ -6162,7 +6284,7 @@ while (*s != 0) case EOP_LISTNAMED: { tree_node *t = NULL; - uschar * list; + const uschar * list; int sep = 0; uschar * item; uschar * suffix = US""; @@ -6289,6 +6411,39 @@ while (*s != 0) continue; } + case EOP_IPV6NORM: + case EOP_IPV6DENORM: + { + int type = string_is_ip_address(sub, NULL); + int binary[4]; + uschar buffer[44]; + + switch (type) + { + case 6: + (void) host_aton(sub, binary); + break; + + case 4: /* convert to IPv4-mapped IPv6 */ + binary[0] = binary[1] = 0; + binary[2] = 0x0000ffff; + (void) host_aton(sub, binary+3); + break; + + case 0: + expand_string_message = + string_sprintf("\"%s\" is not an IP address", sub); + goto EXPAND_FAILED; + } + + yield = string_cat(yield, &size, &ptr, buffer, + c == EOP_IPV6NORM + ? ipv6_nmtoa(binary, buffer) + : host_nmtoa(4, binary, -1, buffer, ':') + ); + continue; + } + case EOP_ADDRESS: case EOP_LOCAL_PART: case EOP_DOMAIN: @@ -6481,7 +6636,7 @@ while (*s != 0) case EOP_RFC2047: { uschar buffer[2048]; - uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset, + const uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset, buffer, sizeof(buffer), FALSE); yield = string_cat(yield, &size, &ptr, string, Ustrlen(string)); continue; @@ -6522,7 +6677,7 @@ while (*s != 0) } /* replace illegal UTF-8 sequences by replacement character */ - + #define UTF8_REPLACEMENT_CHAR US"?" case EOP_UTF8CLEAN: @@ -6531,7 +6686,7 @@ while (*s != 0) int bytes_left = 0; long codepoint = -1; uschar seq_buff[4]; /* accumulate utf-8 here */ - + while (*sub != 0) { int complete = 0; @@ -6540,16 +6695,13 @@ while (*s != 0) if (bytes_left) { if ((c & 0xc0) != 0x80) - { /* wrong continuation byte; invalidate all bytes */ complete = 1; /* error */ - } else { codepoint = (codepoint << 6) | (c & 0x3f); seq_buff[index++] = c; if (--bytes_left == 0) /* codepoint complete */ - { if(codepoint > 0x10FFFF) /* is it too large? */ complete = -1; /* error (RFC3629 limit) */ else @@ -6557,7 +6709,6 @@ while (*s != 0) yield = string_cat(yield, &size, &ptr, seq_buff, seq_len); index = 0; } - } } } else /* no bytes left: new sequence */ @@ -6600,18 +6751,80 @@ while (*s != 0) yield = string_cat(yield, &size, &ptr, UTF8_REPLACEMENT_CHAR, 1); } if ((complete == 1) && ((c & 0x80) == 0)) - { /* ASCII character follows incomplete sequence */ + /* ASCII character follows incomplete sequence */ yield = string_cat(yield, &size, &ptr, &c, 1); - } } continue; } +#ifdef SUPPORT_I18N + case EOP_UTF8_DOMAIN_TO_ALABEL: + { + uschar * error = NULL; + uschar * s = string_domain_utf8_to_alabel(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting utf8 (%s) to alabel: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } + + case EOP_UTF8_DOMAIN_FROM_ALABEL: + { + uschar * error = NULL; + uschar * s = string_domain_alabel_to_utf8(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting alabel (%s) to utf8: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } + + case EOP_UTF8_LOCALPART_TO_ALABEL: + { + uschar * error = NULL; + uschar * s = string_localpart_utf8_to_alabel(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting utf8 (%s) to alabel: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + DEBUG(D_expand) debug_printf("yield: '%s'\n", yield); + continue; + } + + case EOP_UTF8_LOCALPART_FROM_ALABEL: + { + uschar * error = NULL; + uschar * s = string_localpart_alabel_to_utf8(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting alabel (%s) to utf8: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } +#endif /* EXPERIMENTAL_INTERNATIONAL */ + /* escape turns all non-printing characters into escape sequences. */ case EOP_ESCAPE: { - uschar *t = string_printing(sub); + const uschar *t = string_printing(sub); yield = string_cat(yield, &size, &ptr, t, Ustrlen(t)); continue; } @@ -6999,23 +7212,35 @@ return (Ustrpbrk(string, "$\\") == NULL)? string : +const uschar * +expand_cstring(const uschar *string) +{ +search_find_defer = FALSE; +malformed_header = FALSE; +return (Ustrpbrk(string, "$\\") == NULL)? string : + expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL); +} + + + /************************************************* * Expand and copy * *************************************************/ /* Now and again we want to expand a string and be sure that the result is in a new bit of store. This function does that. +Since we know it has been copied, the de-const cast is safe. Argument: the string to be expanded Returns: the expanded string, always in a new bit of store, or NULL */ uschar * -expand_string_copy(uschar *string) +expand_string_copy(const uschar *string) { -uschar *yield = expand_string(string); +const uschar *yield = expand_cstring(string); if (yield == string) yield = string_copy(string); -return yield; +return US yield; } @@ -7062,7 +7287,7 @@ Returns: the integer value, or */ static int_eximarith_t -expanded_string_integer(uschar *s, BOOL isplus) +expanded_string_integer(const uschar *s, BOOL isplus) { int_eximarith_t value; uschar *msg = US"invalid integer \"%s\""; @@ -7216,7 +7441,7 @@ regex_match_and_setup(const pcre *re, uschar *subject, int options, int setup) { int ovector[3*(EXPAND_MAXN+1)]; int n = pcre_exec(re, NULL, subject, Ustrlen(subject), 0, PCRE_EOPT|options, - ovector, sizeof(ovector)/sizeof(int)); + ovector, nelem(ovector)); BOOL yield = n >= 0; if (n == 0) n = EXPAND_MAXN + 1; if (yield) @@ -7257,22 +7482,22 @@ for (i = 1; i < argc; i++) if (Ustrspn(argv[i], "abcdefghijklmnopqrtsuvwxyz0123456789-.:/") == Ustrlen(argv[i])) { - #ifdef LOOKUP_LDAP +#ifdef LOOKUP_LDAP eldap_default_servers = argv[i]; - #endif - #ifdef LOOKUP_MYSQL +#endif +#ifdef LOOKUP_MYSQL mysql_servers = argv[i]; - #endif - #ifdef LOOKUP_PGSQL +#endif +#ifdef LOOKUP_PGSQL pgsql_servers = argv[i]; - #endif - #ifdef EXPERIMENTAL_REDIS +#endif +#ifdef LOOKUP_REDIS redis_servers = argv[i]; - #endif +#endif } - #ifdef EXIM_PERL +#ifdef EXIM_PERL else opt_perl_startup = argv[i]; - #endif +#endif } printf("Testing string expansion: debug_level = %d\n\n", debug_level); diff --git a/src/src/filter.c b/src/src/filter.c index 7132d22ed..bba1a520e 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -355,7 +355,7 @@ while (*(++ptr) != 0 && *ptr != '\"' && *ptr != '\n') } } - *bp++ = string_interpret_escape(&ptr); + *bp++ = string_interpret_escape(CUSS &ptr); } } @@ -1821,7 +1821,7 @@ while (commands != NULL) set in a system filter and to the local address in user filters. */ addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */ - addr->p.errors_address = (s == NULL)? + addr->prop.errors_address = (s == NULL)? s : string_copy(s); /* Default is NULL */ if (commands->noerror) setflag(addr, af_ignore_error); addr->next = *generated; @@ -2021,7 +2021,7 @@ while (commands != NULL) { int sep = 0; uschar *ss; - uschar *list = s; + const uschar *list = s; uschar buffer[128]; while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) @@ -2057,7 +2057,7 @@ while (commands != NULL) DEFERFREEZEFAIL: fmsg = expargs[0]; if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, " ... (truncated)"); - fmsg = string_printing(fmsg); + fmsg = US string_printing(fmsg); *error_pointer = fmsg; if (filter_test != FTEST_NONE) @@ -2351,7 +2351,7 @@ while (commands != NULL) case testprint_command: if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) { - uschar *s = string_printing(expargs[0]); + const uschar *s = string_printing(expargs[0]); if (filter_test == FTEST_NONE) debug_printf("Filter: testprint: %s\n", s); else diff --git a/src/src/functions.h b/src/src/functions.h index c5f6d5ce1..3b700873a 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -45,15 +45,15 @@ extern uschar * tls_cert_fprt_sha256(void *); extern int tls_client_start(int, host_item *, address_item *, transport_instance * -#ifdef EXPERIMENTAL_DANE +# ifdef EXPERIMENTAL_DANE , dns_answer * -#endif +# endif ); extern void tls_close(BOOL, BOOL); extern int tls_export_cert(uschar *, size_t, void *); extern int tls_feof(void); extern int tls_ferror(void); -extern void tls_free_cert(void *); +extern void tls_free_cert(void **); extern int tls_getc(void); extern int tls_import_cert(const uschar *, void **); extern int tls_read(BOOL, uschar *, size_t); @@ -66,11 +66,11 @@ extern void tls_version_report(FILE *); # ifndef USE_GNUTLS extern BOOL tls_openssl_options_parse(uschar *, long *); # endif -extern uschar * tls_field_from_dn(uschar *, uschar *); -extern BOOL tls_is_name_for_cert(uschar *, void *); +extern uschar * tls_field_from_dn(uschar *, const uschar *); +extern BOOL tls_is_name_for_cert(const uschar *, void *); # ifdef EXPERIMENTAL_DANE -extern int tlsa_lookup(host_item *, dns_answer *, BOOL, BOOL *); +extern int tlsa_lookup(const host_item *, dns_answer *, BOOL, BOOL *); # endif #endif /*SUPPORT_TLS*/ @@ -86,11 +86,11 @@ extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); extern uschar *auth_b64encode(uschar *, int); extern int auth_b64decode(uschar *, uschar **); -extern int auth_call_pam(uschar *, uschar **); +extern int auth_call_pam(const uschar *, uschar **); extern int auth_call_pwcheck(uschar *, uschar **); -extern int auth_call_radius(uschar *, uschar **); -extern int auth_call_saslauthd(uschar *, uschar *, uschar *, uschar *, - uschar **); +extern int auth_call_radius(const uschar *, uschar **); +extern int auth_call_saslauthd(const uschar *, const uschar *, + const uschar *, const uschar *, uschar **); extern int auth_check_serv_cond(auth_instance *); extern int auth_check_some_cond(auth_instance *, uschar *, uschar *, int); @@ -99,11 +99,14 @@ extern int auth_get_no64_data(uschar **, uschar *); extern uschar *auth_xtextencode(uschar *, int); extern int auth_xtextdecode(uschar *, uschar **); +extern void bits_clear(unsigned int *, size_t, int *); +extern void bits_set(unsigned int *, size_t, int *); + extern void cancel_cutthrough_connection(const char *); -extern int check_host(void *, uschar *, uschar **, uschar **); +extern int check_host(void *, const uschar *, const uschar **, uschar **); extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...); -extern pid_t child_open_uid(uschar **, uschar **, int, uid_t *, gid_t *, - int *, int *, uschar *, BOOL); +extern pid_t child_open_uid(const uschar **, const uschar **, int, + uid_t *, gid_t *, int *, int *, uschar *, BOOL); extern uschar *cutthrough_finaldot(void); extern BOOL cutthrough_flush_send(void); extern BOOL cutthrough_headers_send(void); @@ -118,13 +121,13 @@ extern int dcc_process(uschar **); #endif extern void debug_logging_activate(uschar *, uschar *); -extern void debug_print_argv(uschar **); +extern void debug_print_argv(const uschar **); extern void debug_print_ids(uschar *); extern void debug_print_string(uschar *); extern void debug_print_tree(tree_node *); extern void debug_vprintf(const char *, va_list); -extern void decode_bits(unsigned int *, unsigned int *, - int, int, uschar *, bit_table *, int, uschar *, int); +extern void decode_bits(unsigned int *, size_t, int *, + uschar *, bit_table *, int, uschar *, int); extern address_item *deliver_make_addr(uschar *, BOOL); extern void deliver_init(void); extern void delivery_log(int, address_item *, int, uschar *); @@ -133,6 +136,9 @@ extern void deliver_msglog(const char *, ...) PRINTF_FUNCTION(1,2); extern void deliver_set_expansions(address_item *); extern int deliver_split_address(address_item *); extern void deliver_succeeded(address_item *); + +extern uschar *deliver_get_sender_address (uschar *id); + #ifdef WITH_OLD_DEMIME extern int demime(uschar **); #endif @@ -143,22 +149,24 @@ extern BOOL dkim_transport_write_message(address_item *, int, int, int, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *); #endif extern dns_address *dns_address_from_rr(dns_answer *, dns_record *); -extern int dns_basic_lookup(dns_answer *, uschar *, int); -extern void dns_build_reverse(uschar *, uschar *); +extern int dns_basic_lookup(dns_answer *, const uschar *, int); +extern void dns_build_reverse(const uschar *, uschar *); extern void dns_init(BOOL, BOOL, BOOL); -extern BOOL dns_is_secure(dns_answer *); -extern int dns_lookup(dns_answer *, uschar *, int, uschar **); +extern BOOL dns_is_aa(const dns_answer *); +extern BOOL dns_is_secure(const dns_answer *); +extern int dns_lookup(dns_answer *, const uschar *, int, const uschar **); extern void dns_pattern_init(void); -extern int dns_special_lookup(dns_answer *, uschar *, int, uschar **); +extern int dns_special_lookup(dns_answer *, const uschar *, int, const uschar **); extern dns_record *dns_next_rr(dns_answer *, dns_scan *, int); extern uschar *dns_text_type(int); extern void dscp_list_to_stream(FILE *); extern BOOL dscp_lookup(const uschar *, int, int *, int *, int *); extern void enq_end(uschar *); -extern BOOL enq_start(uschar *); -#ifdef EXPERIMENTAL_EVENT -extern uschar *event_raise(uschar *, uschar *, uschar *); +extern BOOL enq_start(uschar *, unsigned); +#ifndef DISABLE_EVENT +extern uschar *event_raise(uschar *, const uschar *, uschar *); +extern void msg_event_raise(const uschar *, const address_item *); #endif extern void exim_exit(int); extern void exim_nullstd(void); @@ -169,8 +177,9 @@ extern int exp_bool(address_item *addr, uschar *mtype, uschar *mname, unsigned dgb_opt, uschar *oname, BOOL bvalue, uschar *svalue, BOOL *rvalue); extern BOOL expand_check_condition(uschar *, uschar *, uschar *); -extern uschar *expand_string(uschar *); -extern uschar *expand_string_copy(uschar *); +extern uschar *expand_string(uschar *); /* public, cannot make const */ +extern const uschar *expand_cstring(const uschar *); /* ... so use this one */ +extern uschar *expand_string_copy(const uschar *); extern int_eximarith_t expand_string_integer(uschar *, BOOL); extern void modify_variable(uschar *, void *); @@ -188,16 +197,17 @@ extern int header_checkname(header_line *, BOOL); extern BOOL header_match(uschar *, BOOL, BOOL, string_item *, int, ...); extern int host_address_extract_port(uschar *); extern uschar *host_and_ident(BOOL); -extern int host_aton(uschar *, int *); -extern void host_build_hostlist(host_item **, uschar *, BOOL); -extern ip_address_item *host_build_ifacelist(uschar *, uschar *); +extern int host_aton(const uschar *, int *); +extern void host_build_hostlist(host_item **, const uschar *, BOOL); +extern ip_address_item *host_build_ifacelist(const uschar *, uschar *); extern void host_build_log_info(void); extern void host_build_sender_fullhost(void); -extern BOOL host_find_byname(host_item *, uschar *, int, uschar **, BOOL); -extern int host_find_bydns(host_item *, uschar *, int, uschar *, uschar *, - uschar *, uschar *, uschar *, uschar **, BOOL *); +extern BOOL host_find_byname(host_item *, const uschar *, int, + const uschar **, BOOL); +extern int host_find_bydns(host_item *, const uschar *, int, uschar *, uschar *, + uschar *, const dnssec_domains *, const uschar **, BOOL *); extern ip_address_item *host_find_interfaces(void); -extern BOOL host_is_in_net(uschar *, uschar *, int); +extern BOOL host_is_in_net(const uschar *, const uschar *, int); extern BOOL host_is_tls_on_connect_port(int); extern int host_item_get_port(host_item *); extern void host_mask(int, int *, int); @@ -206,16 +216,26 @@ extern int host_nmtoa(int, int *, int, uschar *, int); extern uschar *host_ntoa(int, const void *, uschar *, int *); extern int host_scan_for_local_hosts(host_item *, host_item **, BOOL *); +extern uschar *imap_utf7_encode(uschar *, const uschar *, + uschar, uschar *, uschar **); + extern void invert_address(uschar *, uschar *); +extern int ip_addr(void *, int, const uschar *, int); extern int ip_bind(int, int, uschar *, int); extern int ip_connect(int, int, const uschar *, int, int); extern int ip_connectedsocket(int, const uschar *, int, int, int, host_item *, uschar **); extern int ip_get_address_family(int); -extern void ip_keepalive(int, uschar *, BOOL); +extern void ip_keepalive(int, const uschar *, BOOL); extern int ip_recv(int, uschar *, int, int); extern int ip_socket(int, int); +extern int ip_tcpsocket(const uschar *, uschar **, int); +extern int ip_unixsocket(const uschar *, uschar **); +extern int ip_streamsocket(const uschar *, uschar **, int); + +extern int ipv6_nmtoa(int *, uschar *); + extern uschar *local_part_quote(uschar *); extern int log_create(uschar *); extern int log_create_as_exim(uschar *); @@ -226,15 +246,15 @@ extern int malware(const uschar *, int); extern int malware_in_file(uschar *); extern void malware_init(void); #endif -extern int match_address_list(uschar *, BOOL, BOOL, uschar **, - unsigned int *, int, int, uschar **); -extern int match_check_list(uschar **, int, tree_node **, unsigned int **, - int(*)(void *, uschar *, uschar **, uschar **), void *, int, - uschar *, uschar **); -extern int match_isinlist(uschar *, uschar **, int, tree_node **, - unsigned int *, int, BOOL, uschar **); -extern int match_check_string(uschar *, uschar *, int, BOOL, BOOL, BOOL, - uschar **); +extern int match_address_list(const uschar *, BOOL, BOOL, const uschar **, + unsigned int *, int, int, const uschar **); +extern int match_check_list(const uschar **, int, tree_node **, unsigned int **, + int(*)(void *, const uschar *, const uschar **, uschar **), void *, int, + const uschar *, const uschar **); +extern int match_isinlist(const uschar *, const uschar **, int, tree_node **, + unsigned int *, int, BOOL, const uschar **); +extern int match_check_string(const uschar *, const uschar *, int, BOOL, BOOL, BOOL, + const uschar **); extern void md5_end(md5 *, const uschar *, int, uschar *); extern void md5_mid(md5 *, const uschar *); extern void md5_start(md5 *); @@ -243,8 +263,8 @@ extern void millisleep(int); struct mime_boundary_context; extern int mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *, uschar **, uschar **); -extern int mime_decode(uschar **); -extern int mime_regex(uschar **); +extern int mime_decode(const uschar **); +extern int mime_regex(const uschar **); #endif extern uschar *moan_check_errorcopy(uschar *); extern BOOL moan_skipped_syntax_errors(uschar *, error_block *, uschar *, @@ -261,12 +281,12 @@ extern void open_cutthrough_connection( address_item * addr ); extern uschar *parse_extract_address(uschar *, uschar **, int *, int *, int *, BOOL); extern int parse_forward_list(uschar *, int, address_item **, uschar **, - uschar *, uschar *, error_block **); + const uschar *, uschar *, error_block **); extern uschar *parse_find_address_end(uschar *, BOOL); extern uschar *parse_find_at(uschar *); -extern uschar *parse_fix_phrase(uschar *, int, uschar *, int); +extern const uschar *parse_fix_phrase(const uschar *, int, uschar *, int); extern uschar *parse_message_id(uschar *, uschar **, uschar **); -extern uschar *parse_quote_2047(uschar *, int, uschar *, uschar *, int, BOOL); +extern const uschar *parse_quote_2047(const uschar *, int, uschar *, uschar *, int, BOOL); extern uschar *parse_date_time(uschar *str, time_t *t); extern int vaguely_random_number(int); #ifdef SUPPORT_TLS @@ -293,8 +313,9 @@ extern void readconf_print(uschar *, uschar *, BOOL); extern uschar *readconf_printtime(int); extern uschar *readconf_readname(uschar *, int, uschar *); extern int readconf_readtime(const uschar *, int, BOOL); -extern void readconf_rest(); -extern uschar *readconf_retry_error(uschar *, uschar *, int *, int *); +extern void readconf_rest(void); +extern uschar *readconf_retry_error(const uschar *, const uschar *, int *, int *); +extern void readconf_save_config(const uschar *); extern void read_message_body(BOOL); extern void receive_bomb_out(uschar *, uschar *); extern BOOL receive_check_fs(int); @@ -303,20 +324,21 @@ extern BOOL receive_msg(BOOL); extern int receive_statvfs(BOOL, int *); extern void receive_swallow_smtp(void); #ifdef WITH_CONTENT_SCAN -extern int regex(uschar **); +extern int regex(const uschar **); #endif -extern BOOL regex_match_and_setup(const pcre *, uschar *, int, int); +extern BOOL regex_match_and_setup(const pcre *, const uschar *, int, int); extern const pcre *regex_must_compile(const uschar *, BOOL, BOOL); extern void retry_add_item(address_item *, uschar *, int); -extern BOOL retry_check_address(uschar *, host_item *, uschar *, BOOL, +extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, uschar **, uschar **); -extern retry_config *retry_find_config(uschar *, uschar *, int, int); -extern BOOL retry_ultimate_address_timeout(uschar *, uschar *, +extern retry_config *retry_find_config(const uschar *, const uschar *, int, int); +extern BOOL retry_ultimate_address_timeout(uschar *, const uschar *, dbdata_retry *, time_t); extern void retry_update(address_item **, address_item **, address_item **); extern uschar *rewrite_address(uschar *, BOOL, BOOL, rewrite_rule *, int); extern uschar *rewrite_address_qualify(uschar *, BOOL); -extern header_line *rewrite_header(header_line *, uschar *, uschar *, +extern header_line *rewrite_header(header_line *, + const uschar *, const uschar *, rewrite_rule *, int, BOOL); extern uschar *rewrite_one(uschar *, int, BOOL *, BOOL, uschar *, rewrite_rule *); @@ -325,10 +347,10 @@ extern uschar *rfc2047_decode2(uschar *, BOOL, uschar *, int, int *, int *, uschar **); extern int route_address(address_item *, address_item **, address_item **, address_item **, address_item **, int); -extern int route_check_prefix(uschar *, uschar *); -extern int route_check_suffix(uschar *, uschar *); +extern int route_check_prefix(const uschar *, const uschar *); +extern int route_check_suffix(const uschar *, const uschar *); extern BOOL route_findgroup(uschar *, gid_t *); -extern BOOL route_finduser(uschar *, struct passwd **, uid_t *); +extern BOOL route_finduser(const uschar *, struct passwd **, uid_t *); extern BOOL route_find_expanded_group(uschar *, uschar *, uschar *, gid_t *, uschar **); extern BOOL route_find_expanded_user(uschar *, uschar *, uschar *, @@ -336,10 +358,10 @@ extern BOOL route_find_expanded_user(uschar *, uschar *, uschar *, extern void route_init(void); extern void route_tidyup(void); -extern uschar *search_find(void *, uschar *, uschar *, int, uschar *, int, +extern uschar *search_find(void *, uschar *, uschar *, int, const uschar *, int, int, int *); -extern int search_findtype(uschar *, int); -extern int search_findtype_partial(uschar *, int *, uschar **, int *, +extern int search_findtype(const uschar *, int); +extern int search_findtype_partial(const uschar *, int *, const uschar **, int *, int *); extern void *search_open(uschar *, int, int, uid_t *, gid_t *); extern void search_tidyup(void); @@ -352,15 +374,14 @@ extern int sieve_interpret(uschar *, int, uschar *, uschar *, uschar *, extern void sigalrm_handler(int); extern BOOL smtp_buffered(void); extern void smtp_closedown(uschar *); -extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar * -#ifdef EXPERIMENTAL_EVENT - , uschar * -#endif - ); +extern int smtp_connect(host_item *, int, int, uschar *, int, + transport_instance *); +extern int smtp_sock_connect(host_item *, int, int, uschar *, + transport_instance * tb, int); extern int smtp_feof(void); extern int smtp_ferror(void); extern uschar *smtp_get_connection_info(void); -extern BOOL smtp_get_interface(uschar *, int, address_item *, BOOL *, +extern BOOL smtp_get_interface(uschar *, int, address_item *, uschar **, uschar *); extern BOOL smtp_get_port(uschar *, address_item *, int *, uschar *); extern int smtp_getc(void); @@ -377,7 +398,7 @@ extern int smtp_ungetc(int); extern BOOL smtp_verify_helo(void); extern int smtp_write_command(smtp_outblock *, BOOL, const char *, ...) PRINTF_FUNCTION(3,4); #ifdef WITH_CONTENT_SCAN -extern int spam(uschar **); +extern int spam(const uschar **); extern FILE *spool_mbox(unsigned long *, const uschar *); #endif extern BOOL spool_move_message(uschar *, uschar *, uschar *, uschar *); @@ -391,23 +412,34 @@ extern int stdin_ferror(void); extern int stdin_ungetc(int); extern uschar *string_append(uschar *, int *, int *, int, ...); extern uschar *string_append_listele(uschar *, uschar, const uschar *); +extern uschar *string_append_listele_n(uschar *, uschar, const uschar *, unsigned); extern uschar *string_base62(unsigned long int); extern uschar *string_cat(uschar *, int *, int *, const uschar *, int); extern uschar *string_copy_dnsdomain(uschar *); -extern uschar *string_copy_malloc(uschar *); -extern uschar *string_copylc(uschar *); +extern uschar *string_copy_malloc(const uschar *); +extern uschar *string_copylc(const uschar *); extern uschar *string_copynlc(uschar *, int); -extern uschar *string_dequote(uschar **); +extern uschar *string_dequote(const uschar **); extern BOOL string_format(uschar *, int, const char *, ...) ALMOST_PRINTF(3,4); extern uschar *string_format_size(int, uschar *); -extern int string_interpret_escape(uschar **); +extern int string_interpret_escape(const uschar **); extern int string_is_ip_address(const uschar *, int *); +#ifdef SUPPORT_I18N +extern BOOL string_is_utf8(const uschar *); +#endif extern uschar *string_log_address(address_item *, BOOL, BOOL); -extern uschar *string_nextinlist(uschar **, int *, uschar *, int); +extern uschar *string_nextinlist(const uschar **, int *, uschar *, int); extern uschar *string_open_failed(int, const char *, ...) PRINTF_FUNCTION(2,3); -extern uschar *string_printing2(uschar *, BOOL); +extern const uschar *string_printing2(const uschar *, BOOL); extern uschar *string_split_message(uschar *); extern uschar *string_unprinting(uschar *); +#ifdef SUPPORT_I18N +extern uschar *string_address_utf8_to_alabel(const uschar *, uschar **); +extern uschar *string_domain_alabel_to_utf8(const uschar *, uschar **); +extern uschar *string_domain_utf8_to_alabel(const uschar *, uschar **); +extern uschar *string_localpart_alabel_to_utf8(const uschar *, uschar **); +extern uschar *string_localpart_utf8_to_alabel(const uschar *, uschar **); +#endif extern BOOL string_vformat(uschar *, int, const char *, va_list); extern int strcmpic(const uschar *, const uschar *); extern int strncmpic(const uschar *, const uschar *, int); @@ -416,14 +448,14 @@ extern uschar *strstric(uschar *, uschar *, BOOL); extern uschar *tod_stamp(int); extern void tls_modify_variables(tls_support *); -extern BOOL transport_check_waiting(uschar *, uschar *, int, uschar *, - BOOL *); +extern BOOL transport_check_waiting(const uschar *, const uschar *, int, uschar *, + BOOL *, oicf, void*); extern void transport_init(void); -extern BOOL transport_pass_socket(uschar *, uschar *, uschar *, uschar *, +extern BOOL transport_pass_socket(const uschar *, const uschar *, const uschar *, uschar *, int); extern uschar *transport_rcpt_address(address_item *, BOOL); -extern BOOL transport_set_up_command(uschar ***, uschar *, BOOL, int, - address_item *, uschar *, uschar **); +extern BOOL transport_set_up_command(const uschar ***, uschar *, + BOOL, int, address_item *, uschar *, uschar **); extern void transport_update_waiting(host_item *, uschar *); extern BOOL transport_write_block(int, uschar *, int); extern BOOL transport_write_string(int, const char *, ...); @@ -435,17 +467,20 @@ extern void tree_add_duplicate(uschar *, address_item *); extern void tree_add_nonrecipient(uschar *); extern void tree_add_unusable(host_item *); extern int tree_insertnode(tree_node **, tree_node *); -extern tree_node *tree_search(tree_node *, uschar *); +extern tree_node *tree_search(tree_node *, const uschar *); extern void tree_write(tree_node *, FILE *); extern void tree_walk(tree_node *, void (*)(uschar*, uschar*, void*), void *); #ifdef WITH_CONTENT_SCAN extern void unspool_mbox(void); #endif +#ifdef SUPPORT_I18N +extern void utf8_version_report(FILE *); +#endif extern int verify_address(address_item *, FILE *, int, int, int, int, uschar *, uschar *, BOOL *); -extern int verify_check_dnsbl(uschar **); +extern int verify_check_dnsbl(int, const uschar **, uschar **); extern int verify_check_header_address(uschar **, uschar **, int, int, int, uschar *, uschar *, int, int *); extern int verify_check_headers(uschar **); @@ -453,8 +488,8 @@ extern int verify_check_header_names_ascii(uschar **); extern int verify_check_host(uschar **); extern int verify_check_notblind(void); extern int verify_check_given_host(uschar **, host_item *); -extern int verify_check_this_host(uschar **, unsigned int *, uschar*, - uschar *, uschar **); +extern int verify_check_this_host(const uschar **, unsigned int *, + const uschar*, const uschar *, const uschar **); extern address_item *verify_checked_sender(uschar *); extern void verify_get_ident(int); extern BOOL verify_sender(int *, uschar **); diff --git a/src/src/globals.c b/src/src/globals.c index a8670e414..14a821a19 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* All the global variables are defined together in this one module, so @@ -83,7 +83,7 @@ uschar *oracle_servers = NULL; uschar *pgsql_servers = NULL; #endif -#ifdef EXPERIMENTAL_REDIS +#ifdef LOOKUP_REDIS uschar *redis_servers = NULL; #endif @@ -148,7 +148,7 @@ uschar *gnutls_require_kx = NULL; uschar *gnutls_require_proto = NULL; uschar *openssl_options = NULL; const pcre *regex_STARTTLS = NULL; -uschar *tls_advertise_hosts = NULL; /* This is deliberate */ +uschar *tls_advertise_hosts = US"*"; uschar *tls_certificate = NULL; uschar *tls_crl = NULL; /* This default matches NSS DH_MAX_P_BITS value at current time (2012), because @@ -156,9 +156,10 @@ that's the interop problem which has been observed: GnuTLS suggesting a higher bit-count as "NORMAL" (2432) and Thunderbird dropping connection. */ int tls_dh_max_bits = 2236; uschar *tls_dhparam = NULL; -#ifndef DISABLE_OCSP +uschar *tls_eccurve = US"prime256v1"; +# ifndef DISABLE_OCSP uschar *tls_ocsp_file = NULL; -#endif +# endif BOOL tls_offered = FALSE; uschar *tls_privatekey = NULL; BOOL tls_remember_esmtp = FALSE; @@ -166,6 +167,8 @@ uschar *tls_require_ciphers = NULL; uschar *tls_try_verify_hosts = NULL; uschar *tls_verify_certificates= US"system"; uschar *tls_verify_hosts = NULL; +#else /*!SUPPORT_TLS*/ +uschar *tls_advertise_hosts = NULL; #endif #ifndef DISABLE_PRDR @@ -175,6 +178,10 @@ BOOL prdr_requested = FALSE; const pcre *regex_PRDR = NULL; #endif +#ifdef SUPPORT_I18N +const pcre *regex_UTF8 = NULL; +#endif + /* Input-reading functions for messages, so we can use special ones for incoming TCP/IP. The defaults use stdin. We never need these for any stand-alone tests. */ @@ -192,24 +199,24 @@ BOOL (*receive_smtp_buffered)(void) = NULL; /* Only used for SMTP */ when verifying one address while routing/verifying another. We have to have the size explicit, because it is referenced from more than one module. */ -uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT] = { - &deliver_address_data, - &deliver_domain, - &deliver_domain_data, - &deliver_domain_orig, - &deliver_domain_parent, - &deliver_localpart, - &deliver_localpart_data, - &deliver_localpart_orig, - &deliver_localpart_parent, - &deliver_localpart_prefix, - &deliver_localpart_suffix, - (uschar **)(&deliver_recipients), - &deliver_host, - &deliver_home, - &address_file, - &address_pipe, - &self_hostname, +const uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT] = { + CUSS &deliver_address_data, + CUSS &deliver_domain, + CUSS &deliver_domain_data, + CUSS &deliver_domain_orig, + CUSS &deliver_domain_parent, + CUSS &deliver_localpart, + CUSS &deliver_localpart_data, + CUSS &deliver_localpart_orig, + CUSS &deliver_localpart_parent, + CUSS &deliver_localpart_prefix, + CUSS &deliver_localpart_suffix, + CUSS (uschar **)(&deliver_recipients), + CUSS &deliver_host, + CUSS &deliver_home, + CUSS &address_file, + CUSS &address_pipe, + CUSS &self_hostname, NULL }; int address_expansions_count = sizeof(address_expansions)/sizeof(uschar **); @@ -349,13 +356,17 @@ address_item address_defaults = { NULL, /* return_filename */ NULL, /* self_hostname */ NULL, /* shadow_message */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS NULL, /* cipher */ NULL, /* ourcert */ NULL, /* peercert */ NULL, /* peerdn */ OCSP_NOT_REQ, /* ocsp */ - #endif +#endif +#ifdef EXPERIMENTAL_DSN_INFO + NULL, /* smtp_greeting */ + NULL, /* helo_response */ +#endif NULL, /* authenticator */ NULL, /* auth_id */ NULL, /* auth_sndr */ @@ -384,6 +395,9 @@ address_item address_defaults = { #ifdef EXPERIMENTAL_SRS NULL, /* srs_sender */ #endif +#ifdef SUPPORT_I18N + FALSE, /* utf8 */ +#endif } }; @@ -466,6 +480,7 @@ int bounce_return_size_limit = 100*1024; uschar *bounce_sender_authentication = NULL; int bsmtp_transaction_linecount = 0; +uschar *callout_address = NULL; int callout_cache_domain_positive_expire = 7*24*60*60; int callout_cache_domain_negative_expire = 3*60*60; int callout_cache_positive_expire = 24*60*60; @@ -506,8 +521,11 @@ int continue_sequence = 1; uschar *continue_transport = NULL; uschar *csa_status = NULL; -BOOL cutthrough_delivery = FALSE; -int cutthrough_fd = -1; +cut_t cutthrough = { + FALSE, /* delivery: when to attempt */ + -1, /* fd: open connection */ + 0, /* nrcpt: number of addresses */ +}; BOOL daemon_listen = FALSE; uschar *daemon_smtp_port = US"smtp"; @@ -525,40 +543,45 @@ uschar *dccifd_options = US"header"; BOOL debug_daemon = FALSE; int debug_fd = -1; FILE *debug_file = NULL; -bit_table debug_options[] = { - { US"acl", D_acl }, - { US"all", D_all }, - { US"auth", D_auth }, - { US"deliver", D_deliver }, - { US"dns", D_dns }, - { US"dnsbl", D_dnsbl }, - { US"exec", D_exec }, - { US"expand", D_expand }, - { US"filter", D_filter }, - { US"hints_lookup", D_hints_lookup }, - { US"host_lookup", D_host_lookup }, - { US"ident", D_ident }, - { US"interface", D_interface }, - { US"lists", D_lists }, - { US"load", D_load }, - { US"local_scan", D_local_scan }, - { US"lookup", D_lookup }, - { US"memory", D_memory }, - { US"pid", D_pid }, - { US"process_info", D_process_info }, - { US"queue_run", D_queue_run }, - { US"receive", D_receive }, - { US"resolver", D_resolver }, - { US"retry", D_retry }, - { US"rewrite", D_rewrite }, - { US"route", D_route }, - { US"timestamp", D_timestamp }, - { US"tls", D_tls }, - { US"transport", D_transport }, - { US"uid", D_uid }, - { US"verify", D_verify } +int debug_notall[] = { + Di_memory, + -1 +}; +bit_table debug_options[] = { /* must be in alphabetical order */ + BIT_TABLE(D, acl), + BIT_TABLE(D, all), + BIT_TABLE(D, auth), + BIT_TABLE(D, deliver), + BIT_TABLE(D, dns), + BIT_TABLE(D, dnsbl), + BIT_TABLE(D, exec), + BIT_TABLE(D, expand), + BIT_TABLE(D, filter), + BIT_TABLE(D, hints_lookup), + BIT_TABLE(D, host_lookup), + BIT_TABLE(D, ident), + BIT_TABLE(D, interface), + BIT_TABLE(D, lists), + BIT_TABLE(D, load), + BIT_TABLE(D, local_scan), + BIT_TABLE(D, lookup), + BIT_TABLE(D, memory), + BIT_TABLE(D, pid), + BIT_TABLE(D, process_info), + BIT_TABLE(D, queue_run), + BIT_TABLE(D, receive), + BIT_TABLE(D, resolver), + BIT_TABLE(D, retry), + BIT_TABLE(D, rewrite), + BIT_TABLE(D, route), + BIT_TABLE(D, timestamp), + BIT_TABLE(D, tls), + BIT_TABLE(D, transport), + BIT_TABLE(D, uid), + BIT_TABLE(D, verify), }; -int debug_options_count = sizeof(debug_options)/sizeof(bit_table); +int debug_options_count = nelem(debug_options); + unsigned int debug_selector = 0; int delay_warning[DELAY_WARNING_SIZE] = { DELAY_WARNING_SIZE, 1, 24*60*60 }; uschar *delay_warning_condition= @@ -570,18 +593,18 @@ uschar *delay_warning_condition= BOOL delivery_date_remove = TRUE; uschar *deliver_address_data = NULL; int deliver_datafile = -1; -uschar *deliver_domain = NULL; +const uschar *deliver_domain = NULL; uschar *deliver_domain_data = NULL; -uschar *deliver_domain_orig = NULL; -uschar *deliver_domain_parent = NULL; +const uschar *deliver_domain_orig = NULL; +const uschar *deliver_domain_parent = NULL; BOOL deliver_drop_privilege = FALSE; BOOL deliver_firsttime = FALSE; BOOL deliver_force = FALSE; BOOL deliver_freeze = FALSE; time_t deliver_frozen_at = 0; uschar *deliver_home = NULL; -uschar *deliver_host = NULL; -uschar *deliver_host_address = NULL; +const uschar *deliver_host = NULL; +const uschar *deliver_host_address = NULL; int deliver_host_port = 0; uschar *deliver_in_buffer = NULL; ino_t deliver_inode = 0; @@ -614,13 +637,14 @@ BOOL disable_ipv6 = FALSE; BOOL disable_logging = FALSE; #ifndef DISABLE_DKIM +BOOL dkim_collect_input = FALSE; uschar *dkim_cur_signer = NULL; +BOOL dkim_disable_verify = FALSE; +int dkim_key_length = 0; uschar *dkim_signers = NULL; uschar *dkim_signing_domain = NULL; uschar *dkim_signing_selector = NULL; uschar *dkim_verify_signers = US"$dkim_signers"; -BOOL dkim_collect_input = FALSE; -BOOL dkim_disable_verify = FALSE; #endif #ifdef EXPERIMENTAL_DMARC BOOL dmarc_has_been_checked = FALSE; @@ -646,6 +670,7 @@ uschar *dns_ipv4_lookup = NULL; int dns_retrans = 0; int dns_retry = 0; int dns_dnssec_ok = -1; /* <0 = not coerced */ +uschar *dns_trust_aa = NULL; int dns_use_edns0 = -1; /* <0 = not coerced */ uschar *dnslist_domain = NULL; uschar *dnslist_matched = NULL; @@ -665,11 +690,11 @@ uschar *errors_copy = NULL; int error_handling = ERRORS_SENDER; uschar *errors_reply_to = NULL; int errors_sender_rc = EXIT_FAILURE; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT uschar *event_action = NULL; /* expansion for delivery events */ uschar *event_data = NULL; /* auxilary data variable for event */ int event_defer_errno = 0; -uschar *event_name = NULL; /* event name variable */ +const uschar *event_name = NULL; /* event name variable */ #endif @@ -801,78 +826,93 @@ uid_t local_user_uid = (uid_t)(-1); tree_node *localpartlist_anchor= NULL; int localpartlist_count = 0; uschar *log_buffer = NULL; -unsigned int log_extra_selector = LX_default; + +int log_default[] = { /* for initializing log_selector */ + Li_acl_warn_skipped, + Li_connection_reject, + Li_delay_delivery, + Li_dnslist_defer, + Li_etrn, + Li_host_lookup_failed, + Li_lost_incoming_connection, + Li_outgoing_interface, /* see d_log_interface in deliver.c */ + Li_queue_run, + Li_rejected_header, + Li_retry_defer, + Li_sender_verify_fail, + Li_size_reject, + Li_skip_delivery, + Li_smtp_confirmation, + Li_tls_certificate_verified, + Li_tls_cipher, + -1 +}; + uschar *log_file_path = US LOG_FILE_PATH "\0<--------------Space to patch log_file_path->"; -/* Those log options with L_xxx identifiers have values less than 0x800000 and -are the ones that get put into log_write_selector. They can be used in calls to -log_write() to test for the bit. The options with LX_xxx identifiers have -values greater than 0x80000000 and are put into log_extra_selector (without the -top bit). They are never used in calls to log_write(), but are tested -independently. This separation became necessary when the number of log -selectors was getting close to filling a 32-bit word. */ - -/* Note that this list must be in alphabetical order. */ - -bit_table log_options[] = { - { US"8bitmime", LX_8bitmime }, - { US"acl_warn_skipped", LX_acl_warn_skipped }, - { US"address_rewrite", L_address_rewrite }, - { US"all", L_all }, - { US"all_parents", L_all_parents }, - { US"arguments", LX_arguments }, - { US"connection_reject", L_connection_reject }, - { US"delay_delivery", L_delay_delivery }, - { US"deliver_time", LX_deliver_time }, - { US"delivery_size", LX_delivery_size }, - { US"dnslist_defer", L_dnslist_defer }, - { US"etrn", L_etrn }, - { US"host_lookup_failed", L_host_lookup_failed }, - { US"ident_timeout", LX_ident_timeout }, - { US"incoming_interface", LX_incoming_interface }, - { US"incoming_port", LX_incoming_port }, - { US"lost_incoming_connection", L_lost_incoming_connection }, - { US"outgoing_port", LX_outgoing_port }, - { US"pid", LX_pid }, -#ifdef EXPERIMENTAL_PROXY - { US"proxy", LX_proxy }, +int log_notall[] = { + -1 +}; +bit_table log_options[] = { /* must be in alphabetical order */ + BIT_TABLE(L, 8bitmime), + BIT_TABLE(L, acl_warn_skipped), + BIT_TABLE(L, address_rewrite), + BIT_TABLE(L, all), + BIT_TABLE(L, all_parents), + BIT_TABLE(L, arguments), + BIT_TABLE(L, connection_reject), + BIT_TABLE(L, delay_delivery), + BIT_TABLE(L, deliver_time), + BIT_TABLE(L, delivery_size), + BIT_TABLE(L, dnslist_defer), + BIT_TABLE(L, etrn), + BIT_TABLE(L, host_lookup_failed), + BIT_TABLE(L, ident_timeout), + BIT_TABLE(L, incoming_interface), + BIT_TABLE(L, incoming_port), + BIT_TABLE(L, lost_incoming_connection), + BIT_TABLE(L, outgoing_interface), + BIT_TABLE(L, outgoing_port), + BIT_TABLE(L, pid), +#if defined(SUPPORT_PROXY) || defined (SUPPORT_SOCKS) + BIT_TABLE(L, proxy), #endif - { US"queue_run", L_queue_run }, - { US"queue_time", LX_queue_time }, - { US"queue_time_overall", LX_queue_time_overall }, - { US"received_recipients", LX_received_recipients }, - { US"received_sender", LX_received_sender }, - { US"rejected_header", LX_rejected_header }, - { US"rejected_headers", LX_rejected_header }, - { US"retry_defer", L_retry_defer }, - { US"return_path_on_delivery", LX_return_path_on_delivery }, - { US"sender_on_delivery", LX_sender_on_delivery }, - { US"sender_verify_fail", LX_sender_verify_fail }, - { US"size_reject", L_size_reject }, - { US"skip_delivery", L_skip_delivery }, - { US"smtp_confirmation", LX_smtp_confirmation }, - { US"smtp_connection", L_smtp_connection }, - { US"smtp_incomplete_transaction", L_smtp_incomplete_transaction }, - { US"smtp_mailauth", LX_smtp_mailauth }, - { US"smtp_no_mail", LX_smtp_no_mail }, - { US"smtp_protocol_error", L_smtp_protocol_error }, - { US"smtp_syntax_error", L_smtp_syntax_error }, - { US"subject", LX_subject }, - { US"tls_certificate_verified", LX_tls_certificate_verified }, - { US"tls_cipher", LX_tls_cipher }, - { US"tls_peerdn", LX_tls_peerdn }, - { US"tls_sni", LX_tls_sni }, - { US"unknown_in_list", LX_unknown_in_list } + BIT_TABLE(L, queue_run), + BIT_TABLE(L, queue_time), + BIT_TABLE(L, queue_time_overall), + BIT_TABLE(L, received_recipients), + BIT_TABLE(L, received_sender), + BIT_TABLE(L, rejected_header), + { US"rejected_headers", Li_rejected_header }, + BIT_TABLE(L, retry_defer), + BIT_TABLE(L, return_path_on_delivery), + BIT_TABLE(L, sender_on_delivery), + BIT_TABLE(L, sender_verify_fail), + BIT_TABLE(L, size_reject), + BIT_TABLE(L, skip_delivery), + BIT_TABLE(L, smtp_confirmation), + BIT_TABLE(L, smtp_connection), + BIT_TABLE(L, smtp_incomplete_transaction), + BIT_TABLE(L, smtp_mailauth), + BIT_TABLE(L, smtp_no_mail), + BIT_TABLE(L, smtp_protocol_error), + BIT_TABLE(L, smtp_syntax_error), + BIT_TABLE(L, subject), + BIT_TABLE(L, tls_certificate_verified), + BIT_TABLE(L, tls_cipher), + BIT_TABLE(L, tls_peerdn), + BIT_TABLE(L, tls_sni), + BIT_TABLE(L, unknown_in_list), }; +int log_options_count = nelem(log_options); -int log_options_count = sizeof(log_options)/sizeof(bit_table); int log_reject_target = 0; +unsigned int log_selector[log_selector_size]; /* initialized in main() */ uschar *log_selector_string = NULL; FILE *log_stderr = NULL; BOOL log_testing_mode = FALSE; BOOL log_timezone = FALSE; -unsigned int log_write_selector= L_default; uschar *login_sender_address = NULL; uschar *lookup_dnssec_authenticated = NULL; int lookup_open_max = 25; @@ -903,6 +943,10 @@ int message_linecount = 0; BOOL message_logs = TRUE; int message_size = 0; uschar *message_size_limit = US"50M"; +#ifdef SUPPORT_I18N +BOOL message_smtputf8 = FALSE; +int message_utf8_downconvert = 0; /* -1 ifneeded; 0 never; 1 always */ +#endif uschar message_subdir[2] = { 0, 0 }; uschar *message_reference = NULL; @@ -957,14 +1001,14 @@ int process_info_len = 0; uschar *process_log_path = NULL; BOOL prod_requires_admin = TRUE; -#ifdef EXPERIMENTAL_PROXY -uschar *proxy_host_address = US""; -int proxy_host_port = 0; -uschar *proxy_required_hosts = US""; +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) +uschar *hosts_proxy = US""; +uschar *proxy_external_address = US""; +int proxy_external_port = 0; +uschar *proxy_local_address = US""; +int proxy_local_port = 0; BOOL proxy_session = FALSE; BOOL proxy_session_failed = FALSE; -uschar *proxy_target_address = US""; -int proxy_target_port = 0; #endif uschar *prvscheck_address = NULL; @@ -972,7 +1016,7 @@ uschar *prvscheck_keynum = NULL; uschar *prvscheck_result = NULL; -uschar *qualify_domain_recipient = NULL; +const uschar *qualify_domain_recipient = NULL; uschar *qualify_domain_sender = NULL; BOOL queue_2stage = FALSE; uschar *queue_domains = NULL; @@ -1054,8 +1098,9 @@ const pcre *regex_From = NULL; const pcre *regex_IGNOREQUOTA = NULL; const pcre *regex_PIPELINING = NULL; const pcre *regex_SIZE = NULL; -const pcre *regex_smtp_code = NULL; const pcre *regex_ismsgid = NULL; +const pcre *regex_smtp_code = NULL; +uschar *regex_vars[REGEX_VARS]; #ifdef WHITELIST_D_MACROS const pcre *regex_whitelisted_macro = NULL; #endif @@ -1154,7 +1199,9 @@ router_instance router_defaults = { NULL, /* fallback_hostlist */ NULL, /* transport instance */ NULL, /* pass_router */ - NULL /* redirect_router */ + NULL, /* redirect_router */ + + { NULL, NULL }, /* dnssec_domains {require,request} */ }; uschar *router_name = NULL; @@ -1185,6 +1232,7 @@ uschar *sender_address_unrewritten = NULL; uschar *sender_data = NULL; unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; uschar *sender_fullhost = NULL; +BOOL sender_helo_dnssec = FALSE; uschar *sender_helo_name = NULL; uschar **sender_host_aliases = &no_aliases; uschar *sender_host_address = NULL; @@ -1213,6 +1261,7 @@ uschar *sending_ip_address = NULL; int sending_port = -1; SIGNAL_BOOL sigalrm_seen = FALSE; uschar **sighup_argv = NULL; +int slow_lookup_log = 0; /* millisecs, zero disables */ int smtp_accept_count = 0; BOOL smtp_accept_keepalive = TRUE; int smtp_accept_max = 20; @@ -1267,11 +1316,15 @@ int smtp_rlr_limit = 0; int smtp_rlr_threshold = INT_MAX; BOOL smtp_use_pipelining = FALSE; BOOL smtp_use_size = FALSE; +#ifdef SUPPORT_I18N +uschar *smtputf8_advertise_hosts = US"*"; /* overridden under test-harness */ +#endif #ifdef WITH_CONTENT_SCAN uschar *spamd_address = US"127.0.0.1 783"; uschar *spam_bar = NULL; uschar *spam_report = NULL; +uschar *spam_action = NULL; uschar *spam_score = NULL; uschar *spam_score_int = NULL; #endif @@ -1374,6 +1427,7 @@ transport_instance transport_defaults = { NULL, /* remove_headers */ NULL, /* return_path */ NULL, /* debug_string */ + NULL, /* max_parallel */ NULL, /* message_size_limit */ NULL, /* headers_rewrite */ NULL, /* rewrite_rules */ @@ -1392,7 +1446,7 @@ transport_instance transport_defaults = { FALSE, /* log_defer_output */ TRUE_UNSET /* retry_use_local_part: BOOL, but set neither 1 nor 0 so can detect unset */ -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT ,NULL /* event_action */ #endif }; @@ -1400,7 +1454,7 @@ transport_instance transport_defaults = { int transport_count; uschar *transport_name = NULL; int transport_newlines; -uschar **transport_filter_argv = NULL; +const uschar **transport_filter_argv = NULL; int transport_filter_timeout; BOOL transport_filter_timed_out = FALSE; int transport_write_timeout= 0; @@ -1447,8 +1501,8 @@ uschar *uucp_from_sender = US"$1"; uschar *verify_mode = NULL; uschar *version_copyright = - US"Copyright (c) University of Cambridge, 1995 - 2014\n" - "(c) The Exim Maintainers and contributors in ACKNOWLEDGMENTS file, 2007 - 2014"; + US"Copyright (c) University of Cambridge, 1995 - 2015\n" + "(c) The Exim Maintainers and contributors in ACKNOWLEDGMENTS file, 2007 - 2015"; uschar *version_date = US"?"; uschar *version_cnumber = US"????"; uschar *version_string = US"?"; diff --git a/src/src/globals.h b/src/src/globals.h index 5495f54db..9ae78a920 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Almost all the global variables are defined together in this one header, so @@ -62,7 +62,7 @@ extern uschar *oracle_servers; /* List of servers and connect info */ extern uschar *pgsql_servers; /* List of servers and connect info */ #endif -#ifdef EXPERIMENTAL_REDIS +#ifdef LOOKUP_REDIS extern uschar *redis_servers; /* List of servers and connect info */ #endif @@ -112,15 +112,15 @@ extern uschar *gnutls_require_kx; /* So some can be avoided */ extern uschar *gnutls_require_proto; /* So some can be avoided */ extern uschar *openssl_options; /* OpenSSL compatibility options */ extern const pcre *regex_STARTTLS; /* For recognizing STARTTLS settings */ -extern uschar *tls_advertise_hosts; /* host for which TLS is advertised */ extern uschar *tls_certificate; /* Certificate file */ extern uschar *tls_channelbinding_b64; /* string of base64 channel binding */ extern uschar *tls_crl; /* CRL File */ extern int tls_dh_max_bits; /* don't accept higher lib suggestions */ extern uschar *tls_dhparam; /* DH param file */ -#ifndef DISABLE_OCSP +extern uschar *tls_eccurve; /* EC curve */ +# ifndef DISABLE_OCSP extern uschar *tls_ocsp_file; /* OCSP stapling proof file */ -#endif +# endif extern BOOL tls_offered; /* Server offered TLS */ extern uschar *tls_privatekey; /* Private key file */ extern BOOL tls_remember_esmtp; /* For YAEB */ @@ -129,6 +129,7 @@ extern uschar *tls_try_verify_hosts; /* Optional client verification */ extern uschar *tls_verify_certificates;/* Path for certificates to check */ extern uschar *tls_verify_hosts; /* Mandatory client verification */ #endif +extern uschar *tls_advertise_hosts; /* host for which TLS is advertised */ extern uschar *dsn_envid; /* DSN envid string */ extern int dsn_ret; /* DSN ret type*/ @@ -150,7 +151,7 @@ extern BOOL (*receive_smtp_buffered)(void); the size of this vector set explicitly, because it is referenced from more than one module. */ -extern uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT]; +extern const uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT]; /* General global variables */ @@ -254,6 +255,7 @@ extern int bounce_return_size_limit; /* Max amount to return */ extern uschar *bounce_sender_authentication; /* AUTH address for bounces */ extern int bsmtp_transaction_linecount; /* Start of last transaction */ +extern uschar *callout_address; /* Address used for a malware/spamd/verify etc. callout */ extern int callout_cache_domain_positive_expire; /* Time for positive domain callout cache records to expire */ extern int callout_cache_domain_negative_expire; /* Time for negative domain callout cache records to expire */ extern int callout_cache_positive_expire; /* Time for positive callout cache records to expire */ @@ -291,8 +293,16 @@ extern int continue_sequence; /* Sequence num for continued delivery */ extern uschar *continue_transport; /* Transport for continued delivery */ extern uschar *csa_status; /* Client SMTP Authorization result */ -extern BOOL cutthrough_delivery; /* Deliver in foreground */ -extern int cutthrough_fd; /* Connection for ditto */ + +typedef struct { + BOOL delivery; /* When to attempt */ + int fd; /* Open connection */ + int nrcpt; /* Count of addresses */ + uschar * interface; /* (address of) */ + host_item host; /* Host used */ + address_item addr; /* (Chain of) addresses */ +} cut_t; +extern cut_t cutthrough; /* Deliver-concurrently */ extern BOOL daemon_listen; /* True if listening required */ extern uschar *daemon_smtp_port; /* Can be a list of ports */ @@ -310,6 +320,7 @@ extern uschar *dccifd_options; /* options for the dccifd daemon */ extern BOOL debug_daemon; /* Debug the daemon process only */ extern int debug_fd; /* The fd for debug_file */ extern FILE *debug_file; /* Where to write debugging info */ +extern int debug_notall[]; /* Debug options excluded from +all */ extern bit_table debug_options[]; /* Table of debug options */ extern int debug_options_count; /* Size of table */ extern int delay_warning[]; /* Times between warnings */ @@ -318,19 +329,19 @@ extern BOOL delivery_date_remove; /* Remove delivery-date headers */ extern uschar *deliver_address_data; /* Arbitrary data for an address */ extern int deliver_datafile; /* FD for data part of message */ -extern uschar *deliver_domain; /* The local domain for delivery */ +extern const uschar *deliver_domain; /* The local domain for delivery */ extern uschar *deliver_domain_data; /* From domain lookup */ -extern uschar *deliver_domain_orig; /* The original local domain for delivery */ -extern uschar *deliver_domain_parent; /* The parent domain for delivery */ +extern const uschar *deliver_domain_orig; /* The original local domain for delivery */ +extern const uschar *deliver_domain_parent; /* The parent domain for delivery */ extern BOOL deliver_drop_privilege; /* TRUE for unprivileged delivery */ extern BOOL deliver_firsttime; /* True for first delivery attempt */ extern BOOL deliver_force; /* TRUE if delivery was forced */ extern BOOL deliver_freeze; /* TRUE if delivery is frozen */ extern time_t deliver_frozen_at; /* Time of freezing */ extern uschar *deliver_home; /* Home directory for pipes */ -extern uschar *deliver_host; /* (First) host for routed local deliveries */ +extern const uschar *deliver_host; /* (First) host for routed local deliveries */ /* Remote host for filter */ -extern uschar *deliver_host_address; /* Address for remote delivery filter */ +extern const uschar *deliver_host_address; /* Address for remote delivery filter */ extern int deliver_host_port; /* Address for remote delivery filter */ extern uschar *deliver_in_buffer; /* Buffer for copying file */ extern ino_t deliver_inode; /* Inode for appendfile */ @@ -363,13 +374,14 @@ extern BOOL disable_ipv6; /* Don't do any IPv6 things */ extern BOOL disable_logging; /* Disables log writing when TRUE */ #ifndef DISABLE_DKIM +extern BOOL dkim_collect_input; /* Runtime flag that tracks wether SMTP input is fed to DKIM validation */ extern uschar *dkim_cur_signer; /* Expansion variable, holds the current "signer" domain or identity during a acl_smtp_dkim run */ +extern BOOL dkim_disable_verify; /* Set via ACL control statement. When set, DKIM verification is disabled for the current message */ +extern int dkim_key_length; /* Expansion variable, length of signing key in bits */ extern uschar *dkim_signers; /* Expansion variable, holds colon-separated list of domains and identities that have signed a message */ extern uschar *dkim_signing_domain; /* Expansion variable, domain used for signing a message. */ extern uschar *dkim_signing_selector; /* Expansion variable, selector used for signing a message. */ extern uschar *dkim_verify_signers; /* Colon-separated list of domains for each of which we call the DKIM ACL */ -extern BOOL dkim_collect_input; /* Runtime flag that tracks wether SMTP input is fed to DKIM validation */ -extern BOOL dkim_disable_verify; /* Set via ACL control statement. When set, DKIM verification is disabled for the current message */ #endif #ifdef EXPERIMENTAL_DMARC extern BOOL dmarc_has_been_checked; /* Global variable to check if test has been called yet */ @@ -395,6 +407,7 @@ extern int dns_dane_ok; /* Ok to use DANE when checking TLS authe extern int dns_retrans; /* Retransmission time setting */ extern int dns_retry; /* Number of retries */ extern int dns_dnssec_ok; /* When constructing DNS query, set DO flag */ +extern uschar *dns_trust_aa; /* DNSSEC trust AA as AD */ extern int dns_use_edns0; /* Coerce EDNS0 support on/off in resolver. */ extern uschar *dnslist_domain; /* DNS (black) list domain */ extern uschar *dnslist_matched; /* DNS (black) list matched key */ @@ -418,11 +431,11 @@ extern uschar *errors_copy; /* For taking copies of errors */ extern uschar *errors_reply_to; /* Reply-to for error messages */ extern int errors_sender_rc; /* Return after message to sender*/ -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT extern uschar *event_action; /* expansion for delivery events */ extern uschar *event_data; /* event data */ extern int event_defer_errno; /* error number set when a remote delivery is deferred with a host error */ -extern uschar *event_name; /* event classification */ +extern const uschar *event_name; /* event classification */ #endif extern gid_t exim_gid; /* To be used with exim_uid */ @@ -521,16 +534,17 @@ extern uid_t local_user_uid; /* As it says; may be set in routers */ extern tree_node *localpartlist_anchor;/* Tree of defined localpart lists */ extern int localpartlist_count; /* Number defined */ extern uschar *log_buffer; /* For constructing log entries */ -extern unsigned int log_extra_selector;/* Bit map of logging options other than used by log_write() */ +extern int log_default[]; /* Initialization list for log_selector */ extern uschar *log_file_path; /* If unset, use default */ +extern int log_notall[]; /* Log options excluded from +all */ extern bit_table log_options[]; /* Table of options */ extern int log_options_count; /* Size of table */ extern int log_reject_target; /* Target log for ACL rejections */ +extern unsigned int log_selector[]; /* Bit map of logging options */ extern uschar *log_selector_string; /* As supplied in the config */ extern FILE *log_stderr; /* Copy of stderr for log use, or NULL */ extern BOOL log_testing_mode; /* TRUE in various testing modes */ extern BOOL log_timezone; /* TRUE to include the timezone in log lines */ -extern unsigned int log_write_selector;/* Bit map of logging options for log_write() */ extern uschar *login_sender_address; /* The actual sender address */ extern lookup_info **lookup_list; /* Array of pointers to available lookups */ extern int lookup_list_count; /* Number of entries in the list */ @@ -562,6 +576,11 @@ extern int message_linecount; /* As it says */ extern BOOL message_logs; /* TRUE to write message logs */ extern int message_size; /* Size of message */ extern uschar *message_size_limit; /* As it says */ +#ifdef SUPPORT_I18N +extern BOOL message_smtputf8; /* Internationalized mail handling */ +extern int message_utf8_downconvert; /* convert from utf8 */ +const extern pcre *regex_UTF8; /* For recognizing SMTPUTF8 settings */ +#endif extern uschar message_subdir[]; /* Subdirectory for messages */ extern uschar *message_reference; /* Reference for error messages */ @@ -626,21 +645,21 @@ extern int process_info_len; extern uschar *process_log_path; /* Alternate path */ extern BOOL prod_requires_admin; /* TRUE if prodding requires admin */ -#ifdef EXPERIMENTAL_PROXY -extern uschar *proxy_host_address; /* IP of host being proxied */ -extern int proxy_host_port; /* Port of host being proxied */ -extern uschar *proxy_required_hosts; /* Hostlist which (require) use proxy protocol */ +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) +extern uschar *hosts_proxy; /* Hostlist which (require) use proxy protocol */ +extern uschar *proxy_external_address; /* IP of remote interface of proxy */ +extern int proxy_external_port; /* Port on remote interface of proxy */ +extern uschar *proxy_local_address; /* IP of local interface of proxy */ +extern int proxy_local_port; /* Port on local interface of proxy */ extern BOOL proxy_session; /* TRUE if receiving mail from valid proxy */ extern BOOL proxy_session_failed; /* TRUE if required proxy negotiation failed */ -extern uschar *proxy_target_address; /* IP of proxy server inbound */ -extern int proxy_target_port; /* Port of proxy server inbound */ #endif extern uschar *prvscheck_address; /* Set during prvscheck expansion item */ extern uschar *prvscheck_keynum; /* Set during prvscheck expansion item */ extern uschar *prvscheck_result; /* Set during prvscheck expansion item */ -extern uschar *qualify_domain_recipient; /* Domain to qualify recipients with */ +extern const uschar *qualify_domain_recipient; /* Domain to qualify recipients with */ extern uschar *qualify_domain_sender; /* Domain to qualify senders with */ extern BOOL queue_2stage; /* Run queue in 2-stage manner */ extern uschar *queue_domains; /* Queue these domains */ @@ -700,8 +719,9 @@ extern const pcre *regex_From; /* For recognizing "From_" lines */ extern const pcre *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */ extern const pcre *regex_PIPELINING; /* For recognizing PIPELINING */ extern const pcre *regex_SIZE; /* For recognizing SIZE settings */ -extern const pcre *regex_smtp_code; /* For recognizing SMTP codes */ extern const pcre *regex_ismsgid; /* Compiled r.e. for message it */ +extern const pcre *regex_smtp_code; /* For recognizing SMTP codes */ +extern uschar *regex_vars[]; /* $regexN variables */ #ifdef WHITELIST_D_MACROS extern const pcre *regex_whitelisted_macro; /* For -D macro values */ #endif @@ -742,6 +762,7 @@ extern uschar *sender_address_unrewritten; /* Set if rewritten by verify */ extern uschar *sender_data; /* lookup result for senders */ extern unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for sender domain */ extern uschar *sender_fullhost; /* Sender host name + address */ +extern BOOL sender_helo_dnssec; /* True if HELO verify used DNS and was DNSSEC */ extern uschar *sender_helo_name; /* Host name from HELO/EHLO */ extern uschar **sender_host_aliases; /* Points to list of alias names */ extern unsigned int sender_host_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for incoming host */ @@ -764,6 +785,7 @@ extern uschar *sending_ip_address; /* Address of outgoing (SMTP) interface * extern int sending_port; /* Port of outgoing interface */ extern SIGNAL_BOOL sigalrm_seen; /* Flag for sigalrm_handler */ extern uschar **sighup_argv; /* Args for re-execing after SIGHUP */ +extern int slow_lookup_log; /* Log DNS lookups taking loger than N millisecs */ extern int smtp_accept_count; /* Count of connections */ extern BOOL smtp_accept_keepalive; /* Set keepalive on incoming */ extern int smtp_accept_max; /* Max SMTP connections */ @@ -814,11 +836,15 @@ extern int smtp_rlr_limit; /* Max delay */ extern int smtp_rlr_threshold; /* Threshold for RCPT rate limit */ extern BOOL smtp_use_pipelining; /* Global for passed connections */ extern BOOL smtp_use_size; /* Global for passed connections */ +#ifdef SUPPORT_I18N +extern uschar *smtputf8_advertise_hosts; /* ingress control */ +#endif #ifdef WITH_CONTENT_SCAN extern uschar *spamd_address; /* address for the spamassassin daemon */ extern uschar *spam_bar; /* the spam "bar" (textual representation of spam_score) */ extern uschar *spam_report; /* the spamd report (multiline) */ +extern uschar *spam_action; /* the spamd recommended-action */ extern uschar *spam_score; /* the spam score (float) */ extern uschar *spam_score_int; /* spam_score * 10 (int) */ #endif @@ -887,7 +913,7 @@ extern BOOL timestamps_utc; /* Use UTC for all times */ extern uschar *transport_name; /* Name of transport last started */ extern int transport_count; /* Count of bytes transported */ extern int transport_newlines; /* Accurate count of number of newline chars transported */ -extern uschar **transport_filter_argv; /* For on-the-fly filtering */ +extern const uschar **transport_filter_argv; /* For on-the-fly filtering */ extern int transport_filter_timeout; /* Timeout for same */ extern BOOL transport_filter_timed_out; /* True if it did */ diff --git a/src/src/host.c b/src/src/host.c index 1ef732727..90ba852d8 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or @@ -94,6 +94,53 @@ random_seed = 1103515245 * random_seed + 12345; return (unsigned int)(random_seed >> 16) % limit; } +/************************************************* +* Wrappers for logging lookup times * +*************************************************/ + +/* When the 'slow_lookup_log' variable is enabled, these wrappers will +write to the log file all (potential) dns lookups that take more than +slow_lookup_log milliseconds +*/ + +static void +log_long_lookup(const uschar * type, const uschar * data, unsigned long msec) +{ +log_write(0, LOG_MAIN, "Long %s lookup for '%s': %lu msec", + type, data, msec); +} + + +/* returns the current system epoch time in milliseconds. */ +static unsigned long +get_time_in_ms() +{ +struct timeval tmp_time; +unsigned long seconds, microseconds; + +gettimeofday(&tmp_time, NULL); +seconds = (unsigned long) tmp_time.tv_sec; +microseconds = (unsigned long) tmp_time.tv_usec; +return seconds*1000 + microseconds/1000; +} + + +static int +dns_lookup_timerwrap(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) +{ +int retval; +unsigned long time_msec; + +if (!slow_lookup_log) + return dns_lookup(dnsa, name, type, fully_qualified_name); + +time_msec = get_time_in_ms(); +retval = dns_lookup(dnsa, name, type, fully_qualified_name); +if ((time_msec = get_time_in_ms() - time_msec) > slow_lookup_log) + log_long_lookup(US"name", name, time_msec); +return retval; +} /************************************************* @@ -101,8 +148,7 @@ return (unsigned int)(random_seed >> 16) % limit; *************************************************/ /* This function is called instead of gethostbyname(), gethostbyname2(), or -getipnodebyname() when running in the test harness. It recognizes the name -"manyhome.test.ex" and generates a humungous number of IP addresses. It also +getipnodebyname() when running in the test harness. . It also recognizes an unqualified "localhost" and forces it to the appropriate loopback address. IP addresses are treated as literals. For other names, it uses the DNS to find the host name. In the test harness, this means it will access only the @@ -118,7 +164,7 @@ Returns: a hostent structure or NULL for an error */ static struct hostent * -host_fake_gethostbyname(uschar *name, int af, int *error_num) +host_fake_gethostbyname(const uschar *name, int af, int *error_num) { #if HAVE_IPV6 int alen = (af == AF_INET)? sizeof(struct in_addr):sizeof(struct in6_addr); @@ -127,7 +173,7 @@ int alen = sizeof(struct in_addr); #endif int ipa; -uschar *lname = name; +const uschar *lname = name; uschar *adds; uschar **alist; struct hostent *yield; @@ -139,34 +185,6 @@ DEBUG(D_host_lookup) debug_printf("using host_fake_gethostbyname for %s (%s)\n", name, (af == AF_INET)? "IPv4" : "IPv6"); -/* Handle the name that needs a vast number of IP addresses */ - -if (Ustrcmp(name, "manyhome.test.ex") == 0 && af == AF_INET) - { - int i, j; - yield = store_get(sizeof(struct hostent)); - alist = store_get(2049 * sizeof(char *)); - adds = store_get(2048 * alen); - yield->h_name = CS name; - yield->h_aliases = NULL; - yield->h_addrtype = af; - yield->h_length = alen; - yield->h_addr_list = CSS alist; - for (i = 104; i <= 111; i++) - { - for (j = 0; j <= 255; j++) - { - *alist++ = adds; - *adds++ = 10; - *adds++ = 250; - *adds++ = i; - *adds++ = j; - } - } - *alist = NULL; - return yield; - } - /* Handle unqualified "localhost" */ if (Ustrcmp(name, "localhost") == 0) @@ -217,7 +235,7 @@ if (ipa != 0) else { int type = (af == AF_INET)? T_A:T_AAAA; - int rc = dns_lookup(&dnsa, lname, type, NULL); + int rc = dns_lookup_timerwrap(&dnsa, lname, type, NULL); int count = 0; lookup_dnssec_authenticated = NULL; @@ -233,11 +251,10 @@ else } for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) - { - if (rr->type == type) count++; - } + if (rr->type == type) + count++; yield = store_get(sizeof(struct hostent)); alist = store_get((count + 1) * sizeof(char **)); @@ -250,14 +267,14 @@ else yield->h_addr_list = CSS alist; for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { int i, n; int x[4]; dns_address *da; if (rr->type != type) continue; - da = dns_address_from_rr(&dnsa, rr); + if (!(da = dns_address_from_rr(&dnsa, rr))) break; *alist++ = adds; n = host_aton(da->address, x); for (i = 0; i < n; i++) @@ -295,19 +312,18 @@ Returns: nothing */ void -host_build_hostlist(host_item **anchor, uschar *list, BOOL randomize) +host_build_hostlist(host_item **anchor, const uschar *list, BOOL randomize) { int sep = 0; int fake_mx = MX_NONE; /* This value is actually -1 */ uschar *name; -uschar buffer[1024]; if (list == NULL) return; if (randomize) fake_mx--; /* Start at -2 for randomizing */ *anchor = NULL; -while ((name = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) +while ((name = string_nextinlist(&list, &sep, NULL, 0)) != NULL) { host_item *h; @@ -318,7 +334,7 @@ while ((name = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) } h = store_get(sizeof(host_item)); - h->name = string_copy(name); + h->name = name; h->address = NULL; h->port = PORT_NONE; h->mx = fake_mx; @@ -444,7 +460,7 @@ Returns: a port number or PORT_NONE int host_item_get_port(host_item *h) { -uschar *p; +const uschar *p; int port, x; int len = Ustrlen(h->name); @@ -528,7 +544,7 @@ use this directly as the first item for Received: because it ain't an RFC 2822 domain. Sigh. */ address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port); -if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0) +if (!LOGGING(incoming_port) || sender_host_port <= 0) *(Ustrrchr(address, ':')) = 0; /* If there's no EHLO/HELO data, we can't show it. */ @@ -679,8 +695,7 @@ else { uschar *flag = useflag? US"H=" : US""; uschar *iface = US""; - if ((log_extra_selector & LX_incoming_interface) != 0 && - interface_address != NULL) + if (LOGGING(incoming_interface) && interface_address != NULL) iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port); if (sender_ident == NULL) (void)string_format(big_buffer, big_buffer_size, "%s%s%s", @@ -718,7 +733,7 @@ Returns: a chain of ip_address_items, each containing to a textual */ ip_address_item * -host_build_ifacelist(uschar *list, uschar *name) +host_build_ifacelist(const uschar *list, uschar *name) { int sep = 0; uschar *s; @@ -811,9 +826,9 @@ ip_address_item *running_interfaces = NULL; if (local_interface_data == NULL) { void *reset_item = store_get(0); - ip_address_item *dlist = host_build_ifacelist(local_interfaces, + ip_address_item *dlist = host_build_ifacelist(CUS local_interfaces, US"local_interfaces"); - ip_address_item *xlist = host_build_ifacelist(extra_local_interfaces, + ip_address_item *xlist = host_build_ifacelist(CUS extra_local_interfaces, US"extra_local_interfaces"); ip_address_item *ipa; @@ -972,7 +987,7 @@ Returns: the number of ints used */ int -host_aton(uschar *address, int *bin) +host_aton(const uschar *address, int *bin) { int x[4]; int v4offset = 0; @@ -984,8 +999,8 @@ supported. */ if (Ustrchr(address, ':') != NULL) { - uschar *p = address; - uschar *component[8]; + const uschar *p = address; + const uschar *component[8]; BOOL ipv4_ends = FALSE; int ci = 0; int nulloffset = 0; @@ -1131,20 +1146,14 @@ if (count == 1) { j = binary[0]; for (i = 24; i >= 0; i -= 8) - { - sprintf(CS tt, "%d.", (j >> i) & 255); - while (*tt) tt++; - } + tt += sprintf(CS tt, "%d.", (j >> i) & 255); } else - { for (i = 0; i < 4; i++) { j = binary[i]; - sprintf(CS tt, "%04x%c%04x%c", (j >> 16) & 0xffff, sep, j & 0xffff, sep); - while (*tt) tt++; + tt += sprintf(CS tt, "%04x%c%04x%c", (j >> 16) & 0xffff, sep, j & 0xffff, sep); } - } tt--; /* lose final separator */ @@ -1160,6 +1169,63 @@ return tt - buffer; } +/* Like host_nmtoa() but: ipv6-only, canonical output, no mask + +Arguments: + binary points to the ints + buffer big enough to hold the result + +Returns: the number of characters placed in buffer, not counting + the final nul. +*/ + +int +ipv6_nmtoa(int * binary, uschar * buffer) +{ +int i, j, k; +uschar * c = buffer; +uschar * d = NULL; /* shut insufficiently "clever" compiler up */ + +for (i = 0; i < 4; i++) + { /* expand to text */ + j = binary[i]; + c += sprintf(CS c, "%x:%x:", (j >> 16) & 0xffff, j & 0xffff); + } + +for (c = buffer, k = -1, i = 0; i < 8; i++) + { /* find longest 0-group sequence */ + if (*c == '0') /* must be "0:" */ + { + uschar * s = c; + j = i; + while (c[2] == '0') i++, c += 2; + if (i-j > k) + { + k = i-j; /* length of sequence */ + d = s; /* start of sequence */ + } + } + while (*++c != ':') ; + c++; + } + +c[-1] = '\0'; /* drop trailing colon */ + +/* debug_printf("%s: D k %d <%s> <%s>\n", __FUNCTION__, k, d, d + 2*(k+1)); */ +if (k >= 0) + { /* collapse */ + c = d + 2*(k+1); + if (d == buffer) c--; /* need extra colon */ + *d++ = ':'; /* 1st 0 */ + while ((*d++ = *c++)) ; + } +else + d = c; + +return d - buffer; +} + + /************************************************* * Check port for tls_on_connect * @@ -1179,7 +1245,7 @@ host_is_tls_on_connect_port(int port) { int sep = 0; uschar buffer[32]; -uschar *list = tls_in.on_connect_ports; +const uschar *list = tls_in.on_connect_ports; uschar *s; uschar *end; @@ -1214,7 +1280,7 @@ Returns: */ BOOL -host_is_in_net(uschar *host, uschar *net, int maskoffset) +host_is_in_net(const uschar *host, const uschar *net, int maskoffset) { int i; int address[4]; @@ -1329,9 +1395,9 @@ for (h = host; h != last->next; h = h->next) if (hosts_treat_as_local != NULL) { int rc; - uschar *save = deliver_domain; + const uschar *save = deliver_domain; deliver_domain = h->name; /* set $domain */ - rc = match_isinlist(string_copylc(h->name), &hosts_treat_as_local, 0, + rc = match_isinlist(string_copylc(h->name), CUSS &hosts_treat_as_local, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save; if (rc == OK) goto FOUND_LOCAL; @@ -1455,6 +1521,9 @@ int len; uschar *s, *t; struct hostent *hosts; struct in_addr addr; +unsigned long time_msec; + +if (slow_lookup_log) time_msec = get_time_in_ms(); /* Lookup on IPv6 system */ @@ -1490,6 +1559,11 @@ addr.s_addr = (S_ADDR_TYPE)inet_addr(CS sender_host_address); hosts = gethostbyaddr(CS(&addr), sizeof(addr), AF_INET); #endif +if ( slow_lookup_log + && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log + ) + log_long_lookup(US"name", sender_host_address, time_msec); + /* Failed to look up the host. */ if (hosts == NULL) @@ -1590,7 +1664,7 @@ uschar *hname, *save_hostname; uschar **aliases; uschar buffer[256]; uschar *ordername; -uschar *list = host_lookup_order; +const uschar *list = host_lookup_order; dns_record *rr; dns_answer dnsa; dns_scan dnss; @@ -1615,14 +1689,13 @@ if (running_in_test_harness && /* Do lookups directly in the DNS or via gethostbyaddr() (or equivalent), in the order specified by the host_lookup_order option. */ -while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) - != NULL) +while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) { if (strcmpic(ordername, US"bydns") == 0) { dns_init(FALSE, FALSE, FALSE); /* dnssec ctrl by dns_dnssec_ok glbl */ dns_build_reverse(sender_host_address, buffer); - rc = dns_lookup(&dnsa, buffer, T_PTR, NULL); + rc = dns_lookup_timerwrap(&dnsa, buffer, T_PTR, NULL); /* The first record we come across is used for the name; others are considered to be aliases. We have to scan twice, in order to find out the @@ -1637,8 +1710,6 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) int count = 0; int old_pool = store_pool; - /* Ideally we'd check DNSSEC both forward and reverse, but we use the - gethost* routines for forward, so can't do that unless/until we rewrite. */ sender_host_dnssec = dns_is_secure(&dnsa); DEBUG(D_dns) debug_printf("Reverse DNS security status: %s\n", @@ -1647,11 +1718,10 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) store_pool = POOL_PERM; /* Save names in permanent storage */ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) - { - if (rr->type == T_PTR) count++; - } + if (rr->type == T_PTR) + count++; /* Get store for the list of aliases. For compatibility with gethostbyaddr, we make an empty list if there are none. */ @@ -1661,7 +1731,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) /* Re-scan and extract the names */ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { uschar *s = NULL; @@ -1686,8 +1756,8 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) "empty name: treated as non-existent host name\n"); continue; } - if (sender_host_name == NULL) sender_host_name = s; - else *aptr++ = s; + if (!sender_host_name) sender_host_name = s; + else *aptr++ = s; while (*s != 0) { *s = tolower(*s); s++; } } @@ -1766,21 +1836,30 @@ for (hname = sender_host_name; hname != NULL; hname = *aliases++) int rc; BOOL ok = FALSE; host_item h; + dnssec_domains d; + h.next = NULL; h.name = hname; h.mx = MX_NONE; h.address = NULL; + d.request = sender_host_dnssec ? US"*" : NULL;; + d.require = NULL; - /* When called with the last argument FALSE, host_find_byname() won't return - HOST_FOUND_LOCAL. If the incoming address is an IPv4 address expressed in - IPv6 format, we must compare the IPv4 part to any IPv4 addresses. */ - - if ((rc = host_find_byname(&h, NULL, 0, NULL, FALSE)) == HOST_FOUND) + if ( (rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A, + NULL, NULL, NULL, &d, NULL, NULL)) == HOST_FOUND + || rc == HOST_FOUND_LOCAL + ) { host_item *hh; HDEBUG(D_host_lookup) debug_printf("checking addresses for %s\n", hname); + + /* If the forward lookup was not secure we cancel the is-secure variable */ + + DEBUG(D_dns) debug_printf("Forward DNS security status: %s\n", + h.dnssec == DS_YES ? "DNSSEC verified (AD)" : "unverified"); + if (h.dnssec != DS_YES) sender_host_dnssec = FALSE; + for (hh = &h; hh != NULL; hh = hh->next) - { if (host_is_in_net(hh->address, sender_host_address, 0)) { HDEBUG(D_host_lookup) debug_printf(" %s OK\n", hh->address); @@ -1788,10 +1867,8 @@ for (hname = sender_host_name; hname != NULL; hname = *aliases++) break; } else - { HDEBUG(D_host_lookup) debug_printf(" %s\n", hh->address); - } - } + if (!ok) HDEBUG(D_host_lookup) debug_printf("no IP address for %s matched %s\n", hname, sender_host_address); @@ -1804,9 +1881,7 @@ for (hname = sender_host_name; hname != NULL; hname = *aliases++) return DEFER; } else - { HDEBUG(D_host_lookup) debug_printf("no IP addresses found for %s\n", hname); - } /* If this name is no good, and it's the sender name, set it null pro tem; if it's an alias, just remove it from the list. */ @@ -1892,8 +1967,8 @@ Returns: HOST_FIND_FAILED Failed to find the host or domain */ int -host_find_byname(host_item *host, uschar *ignore_target_hosts, int flags, - uschar **fully_qualified_name, BOOL local_host_check) +host_find_byname(host_item *host, const uschar *ignore_target_hosts, int flags, + const uschar **fully_qualified_name, BOOL local_host_check) { int i, yield, times; uschar **addrlist; @@ -1903,22 +1978,12 @@ BOOL temp_error = FALSE; int af; #endif -/* If we are in the test harness, a name ending in .test.again.dns always -forces a temporary error response, unless the name is in -dns_again_means_nonexist. */ - -if (running_in_test_harness) - { - uschar *endname = host->name + Ustrlen(host->name); - if (Ustrcmp(endname - 14, "test.again.dns") == 0) goto RETURN_AGAIN; - } - /* Make sure DNS options are set as required. This appears to be necessary in some circumstances when the get..byname() function actually calls the DNS. */ dns_init((flags & HOST_FIND_QUALIFY_SINGLE) != 0, (flags & HOST_FIND_SEARCH_PARENTS) != 0, - FALSE); /*XXX dnssec? */ + FALSE); /* Cannot retrieve dnssec status so do not request */ /* In an IPv6 world, unless IPv6 has been disabled, we need to scan for both kinds of address, so go round the loop twice. Note that we have ensured that @@ -1932,8 +1997,8 @@ lookups here (except when testing standalone). */ #else if (disable_ipv6 || (dns_ipv4_lookup != NULL && - match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) == OK)) + match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) == OK)) #endif { af = AF_INET; times = 1; } @@ -1962,11 +2027,14 @@ for (i = 1; i <= times; BOOL ipv4_addr; int error_num = 0; struct hostent *hostdata; + unsigned long time_msec = 0; /* compiler quietening */ #ifdef STAND_ALONE printf("Looking up: %s\n", host->name); #endif + if (slow_lookup_log) time_msec = get_time_in_ms(); + #if HAVE_IPV6 if (running_in_test_harness) hostdata = host_fake_gethostbyname(host->name, af, &error_num); @@ -1990,17 +2058,21 @@ for (i = 1; i <= times; } #endif /* HAVE_IPV6 */ + if ( slow_lookup_log + && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log) + log_long_lookup(US"name", host->name, time_msec); + if (hostdata == NULL) { uschar *error; switch (error_num) { case HOST_NOT_FOUND: error = US"HOST_NOT_FOUND"; break; - case TRY_AGAIN: error = US"TRY_AGAIN"; break; - case NO_RECOVERY: error = US"NO_RECOVERY"; break; - case NO_DATA: error = US"NO_DATA"; break; + case TRY_AGAIN: error = US"TRY_AGAIN"; break; + case NO_RECOVERY: error = US"NO_RECOVERY"; break; + case NO_DATA: error = US"NO_DATA"; break; #if NO_DATA != NO_ADDRESS - case NO_ADDRESS: error = US"NO_ADDRESS"; break; + case NO_ADDRESS: error = US"NO_ADDRESS"; break; #endif default: error = US"?"; break; } @@ -2116,7 +2188,7 @@ yield = local_host_check? HDEBUG(D_host_lookup) { - host_item *h; + const host_item *h; if (fully_qualified_name != NULL) debug_printf("fully qualified name = %s\n", *fully_qualified_name); debug_printf("%s looked up these IP addresses:\n", @@ -2146,9 +2218,9 @@ RETURN_AGAIN: { #ifndef STAND_ALONE int rc; - uschar *save = deliver_domain; + const uschar *save = deliver_domain; deliver_domain = host->name; /* set $domain */ - rc = match_isinlist(host->name, &dns_again_means_nonexist, 0, NULL, NULL, + rc = match_isinlist(host->name, CUSS &dns_again_means_nonexist, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save; if (rc == OK) @@ -2196,7 +2268,8 @@ Arguments: fully_qualified_name if not NULL, return fully qualified name here if the contents are different (i.e. it must be preset to something) - dnnssec_require if TRUE check the DNS result AD bit + dnssec_request if TRUE request the AD bit + dnssec_require if TRUE require the AD bit Returns: HOST_FIND_FAILED couldn't find A record HOST_FIND_AGAIN try again later @@ -2206,7 +2279,8 @@ Returns: HOST_FIND_FAILED couldn't find A record static int set_address_from_dns(host_item *host, host_item **lastptr, - uschar *ignore_target_hosts, BOOL allow_ip, uschar **fully_qualified_name, + const uschar *ignore_target_hosts, BOOL allow_ip, + const uschar **fully_qualified_name, BOOL dnssec_request, BOOL dnssec_require) { dns_record *rr; @@ -2231,27 +2305,22 @@ if (allow_ip && string_is_ip_address(host->name, NULL) != 0) return HOST_FOUND; } -/* On an IPv6 system, unless IPv6 is disabled, go round the loop up to three -times, looking for A6 and AAAA records the first two times. However, unless +/* On an IPv6 system, unless IPv6 is disabled, go round the loop up to twice, +looking for AAAA records the first time. However, unless doing standalone testing, we force an IPv4 lookup if the domain matches -dns_ipv4_lookup is set. Since A6 records look like being abandoned, support -them only if explicitly configured to do so. On an IPv4 system, go round the +dns_ipv4_lookup is set. On an IPv4 system, go round the loop once only, looking only for A records. */ #if HAVE_IPV6 #ifndef STAND_ALONE if (disable_ipv6 || (dns_ipv4_lookup != NULL && - match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) == OK)) + match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) == OK)) i = 0; /* look up A records only */ else #endif /* STAND_ALONE */ - #ifdef SUPPORT_A6 - i = 2; /* look up A6 and AAAA and A records */ - #else i = 1; /* look up AAAA and A records */ - #endif /* SUPPORT_A6 */ /* The IPv4 world */ @@ -2261,17 +2330,24 @@ loop once only, looking only for A records. */ for (; i >= 0; i--) { - static int types[] = { T_A, T_AAAA, T_A6 }; + static int types[] = { T_A, T_AAAA }; int type = types[i]; int randoffset = (i == 0)? 500 : 0; /* Ensures v6 sorts before v4 */ dns_answer dnsa; dns_scan dnss; - int rc = dns_lookup(&dnsa, host->name, type, fully_qualified_name); + int rc = dns_lookup_timerwrap(&dnsa, host->name, type, fully_qualified_name); lookup_dnssec_authenticated = !dnssec_request ? NULL : dns_is_secure(&dnsa) ? US"yes" : US"no"; - /* We want to return HOST_FIND_AGAIN if one of the A, A6, or AAAA lookups + DEBUG(D_dns) + if ( (dnssec_request || dnssec_require) + && !dns_is_secure(&dnsa) + && dns_is_aa(&dnsa) + ) + debug_printf("DNS lookup of %.256s (A/AAAA) requested AD, but got AA\n", host->name); + + /* We want to return HOST_FIND_AGAIN if one of the A or AAAA lookups fails or times out, but not if another one succeeds. (In the early IPv6 days there are name servers that always fail on AAAA, but are happy to give out an A record. We want to proceed with that A record.) */ @@ -2280,13 +2356,13 @@ for (; i >= 0; i--) { if (i == 0) /* Just tried for an A record, i.e. end of loop */ { - if (host->address != NULL) return HOST_FOUND; /* A6 or AAAA was found */ + if (host->address != NULL) return HOST_FOUND; /* AAAA was found */ if (rc == DNS_AGAIN || rc == DNS_FAIL || v6_find_again) return HOST_FIND_AGAIN; return HOST_FIND_FAILED; /* DNS_NOMATCH or DNS_NODATA */ } - /* Tried for an A6 or AAAA record: remember if this was a temporary + /* Tried for an AAAA record: remember if this was a temporary error, and look for the next record type. */ if (rc != DNS_NOMATCH && rc != DNS_NODATA) v6_find_again = TRUE; @@ -2307,7 +2383,7 @@ for (; i >= 0; i--) { log_write(L_host_lookup_failed, LOG_MAIN, "dnssec fail on %s for %.256s", - i>1 ? "A6" : i>0 ? "AAAA" : "A", host->name); + i>0 ? "AAAA" : "A", host->name); continue; } if (host->dnssec == DS_YES) /* set in host_find_bydns() */ @@ -2321,30 +2397,27 @@ for (; i >= 0; i--) /* Lookup succeeded: fill in the given host item with the first non-ignored address found; create additional items for any others. A single A6 record - may generate more than one address. */ + may generate more than one address. The lookup had a chance to update the + fqdn; we do not want any later times round the loop to do so. */ + + fully_qualified_name = NULL; for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { if (rr->type == type) { - /* dns_address *da = dns_address_from_rr(&dnsa, rr); */ - - dns_address *da; - da = dns_address_from_rr(&dnsa, rr); + dns_address *da = dns_address_from_rr(&dnsa, rr); DEBUG(D_host_lookup) - { - if (da == NULL) - debug_printf("no addresses extracted from A6 RR for %s\n", + if (!da) debug_printf("no addresses extracted from A6 RR for %s\n", host->name); - } /* This loop runs only once for A and AAAA records, but may run several times for an A6 record that generated multiple addresses. */ - for (; da != NULL; da = da->next) + for (; da; da = da->next) { #ifndef STAND_ALONE if (ignore_target_hosts != NULL && @@ -2431,10 +2504,10 @@ for (; i >= 0; i--) } } -/* Control gets here only if the third lookup (the A record) succeeded. +/* Control gets here only if the econdookup (the A record) succeeded. However, the address may not be filled in if it was ignored. */ -return (host->address == NULL)? HOST_IGNORED : HOST_FOUND; +return host->address ? HOST_FOUND : HOST_IGNORED; } @@ -2464,8 +2537,8 @@ Arguments: srv_service when SRV used, the service name srv_fail_domains DNS errors for these domains => assume nonexist mx_fail_domains DNS errors for these domains => assume nonexist - dnssec_request_domains => make dnssec request - dnssec_require_domains => ditto and nonexist failures + dnssec_d.request => make dnssec request: domainlist + dnssec_d.require => ditto and nonexist failures fully_qualified_name if not NULL, return fully-qualified name removed set TRUE if local host was removed from the list @@ -2481,10 +2554,10 @@ Returns: HOST_FIND_FAILED Failed to find the host or domain; */ int -host_find_bydns(host_item *host, uschar *ignore_target_hosts, int whichrrs, +host_find_bydns(host_item *host, const uschar *ignore_target_hosts, int whichrrs, uschar *srv_service, uschar *srv_fail_domains, uschar *mx_fail_domains, - uschar *dnssec_request_domains, uschar *dnssec_require_domains, - uschar **fully_qualified_name, BOOL *removed) + const dnssec_domains *dnssec_d, + const uschar **fully_qualified_name, BOOL *removed) { host_item *h, *last; dns_record *rr; @@ -2493,11 +2566,13 @@ int ind_type = 0; int yield; dns_answer dnsa; dns_scan dnss; -BOOL dnssec_require = match_isinlist(host->name, &dnssec_require_domains, +BOOL dnssec_require = dnssec_d + && match_isinlist(host->name, CUSS &dnssec_d->require, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK; BOOL dnssec_request = dnssec_require - || match_isinlist(host->name, &dnssec_request_domains, - 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK; + || ( dnssec_d + && match_isinlist(host->name, CUSS &dnssec_d->request, + 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK); dnssec_status_t dnssec; /* Set the default fully qualified name to the incoming name, initialize the @@ -2507,12 +2582,11 @@ that gets set for DNS syntax check errors. */ if (fully_qualified_name != NULL) *fully_qualified_name = host->name; dns_init((whichrrs & HOST_FIND_QUALIFY_SINGLE) != 0, (whichrrs & HOST_FIND_SEARCH_PARENTS) != 0, - dnssec_request - ); + dnssec_request); host_find_failed_syntax = FALSE; /* First, if requested, look for SRV records. The service name is given; we -assume TCP progocol. DNS domain names are constrained to a maximum of 256 +assume TCP protocol. DNS domain names are constrained to a maximum of 256 characters, so the code below should be safe. */ if ((whichrrs & HOST_FIND_BY_SRV) != 0) @@ -2531,7 +2605,13 @@ if ((whichrrs & HOST_FIND_BY_SRV) != 0) dnssec = DS_UNK; lookup_dnssec_authenticated = NULL; - rc = dns_lookup(&dnsa, buffer, ind_type, &temp_fully_qualified_name); + rc = dns_lookup_timerwrap(&dnsa, buffer, ind_type, CUSS &temp_fully_qualified_name); + + DEBUG(D_dns) + if ((dnssec_request || dnssec_require) + & !dns_is_secure(&dnsa) + & dns_is_aa(&dnsa)) + debug_printf("DNS lookup of %.256s (SRV) requested AD, but got AA\n", host->name); if (dnssec_request) { @@ -2556,8 +2636,8 @@ if ((whichrrs & HOST_FIND_BY_SRV) != 0) if (rc == DNS_FAIL || rc == DNS_AGAIN) { #ifndef STAND_ALONE - if (match_isinlist(host->name, &srv_fail_domains, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK) + if (match_isinlist(host->name, CUSS &srv_fail_domains, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) != OK) #endif { yield = HOST_FIND_AGAIN; goto out; } DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " @@ -2577,12 +2657,18 @@ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0) ind_type = T_MX; dnssec = DS_UNK; lookup_dnssec_authenticated = NULL; - rc = dns_lookup(&dnsa, host->name, ind_type, fully_qualified_name); + rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name); + + DEBUG(D_dns) + if ((dnssec_request || dnssec_require) + & !dns_is_secure(&dnsa) + & dns_is_aa(&dnsa)) + debug_printf("DNS lookup of %.256s (MX) requested AD, but got AA\n", host->name); if (dnssec_request) { if (dns_is_secure(&dnsa)) - { + { DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name); dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } @@ -2608,8 +2694,8 @@ if (rc != DNS_SUCCEED && (whichrrs & HOST_FIND_BY_MX) != 0) case DNS_FAIL: case DNS_AGAIN: #ifndef STAND_ALONE - if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK) + if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) != OK) #endif { yield = HOST_FIND_AGAIN; goto out; } DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " @@ -3083,9 +3169,6 @@ dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */ return yield; } - - - /************************************************* ************************************************** * Stand-alone test program * @@ -3176,6 +3259,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) else { int flags = whichrrs; + dnssec_domains d; h.name = buffer; h.next = NULL; @@ -3188,12 +3272,13 @@ while (Ufgets(buffer, 256, stdin) != NULL) if (qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE; if (search_parents) flags |= HOST_FIND_SEARCH_PARENTS; + d.request = request_dnssec ? &h.name : NULL; + d.require = require_dnssec ? &h.name : NULL; + rc = byname ? host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE) : host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL, - request_dnssec ? &h.name : NULL, - require_dnssec ? &h.name : NULL, - &fully_qualified_name, NULL); + &d, &fully_qualified_name, NULL); if (rc == HOST_FIND_FAILED) printf("Failed\n"); else if (rc == HOST_FIND_AGAIN) printf("Again\n"); diff --git a/src/src/imap_utf7.c b/src/src/imap_utf7.c new file mode 100644 index 000000000..dcccaeef8 --- /dev/null +++ b/src/src/imap_utf7.c @@ -0,0 +1,210 @@ +#include "exim.h" + +#ifdef SUPPORT_I18N + +uschar * +imap_utf7_encode(uschar *string, const uschar *charset, uschar sep, + uschar *specials, uschar **error) +{ +static uschar encode_base64[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; +int ptr = 0; +int size = 0; +size_t slen; +uschar *sptr, *yield = NULL; +int i = 0, j; /* compiler quietening */ +uschar c = 0; /* compiler quietening */ +BOOL base64mode = FALSE; +BOOL lastsep = FALSE; +uschar utf16buf[256]; +uschar *utf16ptr; +uschar *s; +uschar outbuf[256]; +uschar *outptr = outbuf; +#if HAVE_ICONV +iconv_t icd; +#endif + +if (!specials) specials = US""; + +/* Pass over the string. If it consists entirely of "normal" characters + (possibly with leading seps), return it as is. */ +for (s = string; *s; s++) + { + if (s == string && *s == sep) + string++; + if ( *s >= 0x7f + || *s < 0x20 + || strchr("./&", *s) + || *s == sep + || Ustrchr(specials, *s) + ) + break; + } + +if (!*s) + return string; + +sptr = string; +slen = Ustrlen(string); + +#if HAVE_ICONV +if ((icd = iconv_open("UTF-16BE", CCS charset)) == (iconv_t)-1) + { + *error = string_sprintf( + "imapfolder: iconv_open(\"UTF-16BE\", \"%s\") failed: %s%s", + charset, strerror(errno), + errno == EINVAL ? " (maybe unsupported conversion)" : ""); + return NULL; + } +#endif + +while (slen > 0) + { +#if HAVE_ICONV + size_t left = sizeof(utf16buf); + utf16ptr = utf16buf; + + if ( iconv(icd, (ICONV_ARG2_TYPE)&sptr, &slen, CSS &utf16ptr, &left) + == (size_t)-1 + && errno != E2BIG + ) + { + *error = string_sprintf("imapfolder: iconv() failed to convert from %s: %s", + charset, strerror(errno)); + iconv_close(icd); + return NULL; + } +#else + for (utf16ptr = utf16buf; + slen > 0 && (utf16ptr - utf16buf) < sizeof(utf16buf); + utf16ptr += 2, slen--, sptr++) + { + *utf16ptr = *sptr; + *(utf16ptr+1) = '\0'; + } +#endif + + s = utf16buf; + while (s < utf16ptr) + { + /* Now encode utf16buf as modified UTF-7 */ + if ( s[0] != 0 + || s[1] >= 0x7f + || s[1] < 0x20 + || (Ustrchr(specials, s[1]) && s[1] != sep) + ) + { + lastsep = FALSE; + /* Encode as modified BASE64 */ + if (!base64mode) + { + *outptr++ = '&'; + base64mode = TRUE; + i = 0; + } + + for (j = 0; j < 2; j++, s++) switch (i++) + { + case 0: + /* Top 6 bits of the first octet */ + *outptr++ = encode_base64[(*s >> 2) & 0x3F]; + c = (*s & 0x03); break; + case 1: + /* Bottom 2 bits of the first octet, and top 4 bits of the second */ + *outptr++ = encode_base64[(c << 4) | ((*s >> 4) & 0x0F)]; + c = (*s & 0x0F); break; + case 2: + /* Bottom 4 bits of the second octet and top 2 bits of the third */ + *outptr++ = encode_base64[(c << 2) | ((*s >> 6) & 0x03)]; + /* Bottom 6 bits of the third octet */ + *outptr++ = encode_base64[*s & 0x3F]; + i = 0; + } + } + + else if ( (s[1] != '.' && s[1] != '/') + || s[1] == sep + ) + { + /* Encode as self (almost) */ + if (base64mode) + { + switch (i) + { + case 1: + /* Remaining bottom 2 bits of the last octet */ + *outptr++ = encode_base64[c << 4]; + break; + case 2: + /* Remaining bottom 4 bits of the last octet */ + *outptr++ = encode_base64[c << 2]; + } + *outptr++ = '-'; + base64mode = FALSE; + } + + if (*++s == sep) + { + if (!lastsep) + { + *outptr++ = '.'; + lastsep = TRUE; + } + } + else + { + *outptr++ = *s; + if (*s == '&') + *outptr++ = '-'; + lastsep = FALSE; + } + + s++; + } + else + { + *error = string_sprintf("imapfolder: illegal character '%c'", s[1]); + if (yield) store_reset(yield); + return NULL; + } + + if (outptr > outbuf + sizeof(outbuf) - 3) + { + yield = string_cat(yield, &size, &ptr, outbuf, outptr - outbuf); + outptr = outbuf; + } + + } + } /* End of input string */ + +if (base64mode) + { + switch (i) + { + case 1: + /* Remaining bottom 2 bits of the last octet */ + *outptr++ = encode_base64[c << 4]; + break; + case 2: + /* Remaining bottom 4 bits of the last octet */ + *outptr++ = encode_base64[c << 2]; + } + *outptr++ = '-'; + } + +#if HAVE_ICONV +iconv_close(icd); +#endif + +yield = string_cat(yield, &size, &ptr, outbuf, outptr - outbuf); +if (yield[ptr-1] == '.') + ptr--; +yield[ptr] = '\0'; + +return yield; +} + +#endif /* whole file */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/ip.c b/src/src/ip.c index 91b74e20e..1e3875aef 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for doing things with sockets. With the advent of IPv6 this has @@ -97,24 +97,11 @@ ip_addrinfo(const uschar *address, struct sockaddr_in6 *saddr) * Bind socket to interface and port * *************************************************/ -/* This function binds a socket to a local interface address and port. For a -wildcard IPv6 bind, the address is ":". - -Arguments: - sock the socket - af AF_INET or AF_INET6 - the socket type - address the IP address, in text form - port the IP port (host order) - -Returns: the result of bind() -*/ - int -ip_bind(int sock, int af, uschar *address, int port) +ip_addr(void * sin_, int af, const uschar * address, int port) { -int s_len; -union sockaddr_46 sin; -memset(&sin, 0, sizeof(sin)); +union sockaddr_46 * sin = sin_; +memset(sin, 0, sizeof(*sin)); /* Setup code when using an IPv6 socket. The wildcard address is ":", to ensure an IPv6 socket is used. */ @@ -124,15 +111,13 @@ if (af == AF_INET6) { if (address[0] == ':' && address[1] == 0) { - sin.v6.sin6_family = AF_INET6; - sin.v6.sin6_addr = in6addr_any; + sin->v6.sin6_family = AF_INET6; + sin->v6.sin6_addr = in6addr_any; } else - { - ip_addrinfo(address, &sin.v6); /* Panic-dies on error */ - } - sin.v6.sin6_port = htons(port); - s_len = sizeof(sin.v6); + ip_addrinfo(address, &sin->v6); /* Panic-dies on error */ + sin->v6.sin6_port = htons(port); + return sizeof(sin->v6); } else #else /* HAVE_IPv6 */ @@ -142,17 +127,34 @@ af = af; /* Avoid compiler warning */ /* Setup code when using IPv4 socket. The wildcard address is "". */ { - sin.v4.sin_family = AF_INET; - sin.v4.sin_port = htons(port); - s_len = sizeof(sin.v4); - if (address[0] == 0) - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY; - else - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CS address); + sin->v4.sin_family = AF_INET; + sin->v4.sin_port = htons(port); + sin->v4.sin_addr.s_addr = address[0] == 0 + ? (S_ADDR_TYPE)INADDR_ANY + : (S_ADDR_TYPE)inet_addr(CS address); + return sizeof(sin->v4); } +} + -/* Now we can call the bind() function */ +/* This function binds a socket to a local interface address and port. For a +wildcard IPv6 bind, the address is ":". + +Arguments: + sock the socket + af AF_INET or AF_INET6 - the socket type + address the IP address, in text form + port the IP port (host order) + +Returns: the result of bind() +*/ + +int +ip_bind(int sock, int af, uschar *address, int port) +{ +union sockaddr_46 sin; +int s_len = ip_addr(&sin, af, address, port); return bind(sock, (struct sockaddr *)&sin, s_len); } @@ -226,24 +228,25 @@ alarm(0); can't think of any other way of doing this. It converts a connection refused into a timeout if the timeout is set to 999999. */ -if (running_in_test_harness) +if (running_in_test_harness && save_errno == ECONNREFUSED && timeout == 999999) { - if (save_errno == ECONNREFUSED && timeout == 999999) - { - rc = -1; - save_errno = EINTR; - sigalrm_seen = TRUE; - } + rc = -1; + save_errno = EINTR; + sigalrm_seen = TRUE; } /* Success */ -if (rc >= 0) return 0; +if (rc >= 0) + { + callout_address = string_sprintf("[%s]:%d", address, port); + return 0; + } /* A failure whose error code is "Interrupted system call" is in fact an externally applied timeout if the signal handler has been run. */ -errno = (save_errno == EINTR && sigalrm_seen)? ETIMEDOUT : save_errno; +errno = save_errno == EINTR && sigalrm_seen ? ETIMEDOUT : save_errno; return -1; } @@ -310,8 +313,8 @@ else if (string_is_ip_address(hostname, NULL) != 0) else { shost.name = string_copy(hostname); - if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, - FALSE) != HOST_FOUND) + if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE, + NULL, FALSE) != HOST_FOUND) { *errstr = string_sprintf("no IP address found for host %s", shost.name); return -1; @@ -337,7 +340,8 @@ for (h = &shost; h != NULL; h = h->next) { if (fd != fd6) close(fd6); if (fd != fd4) close(fd4); - if (connhost) { + if (connhost) + { h->port = port; *connhost = *h; connhost->next = NULL; @@ -354,6 +358,63 @@ bad: } +int +ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo) +{ +int scan; +uschar hostname[256]; +unsigned int portlow, porthigh; + +/* extract host and port part */ +scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh); +if (scan != 3) + { + if (scan != 2) + { + *errstr = string_sprintf("invalid socket '%s'", hostport); + return -1; + } + porthigh = portlow; + } + +return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh, + tmo, NULL, errstr); +} + +int +ip_unixsocket(const uschar * path, uschar ** errstr) +{ +int sock; +struct sockaddr_un server; + +if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + *errstr = US"can't open UNIX socket."; + return -1; + } + +server.sun_family = AF_UNIX; +Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1); +server.sun_path[sizeof(server.sun_path)-1] = '\0'; +if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) + { + int err = errno; + (void)close(sock); + *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s", + path, strerror(err)); + return -1; + } +callout_address = string_copy(path); +return sock; +} + +int +ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo) +{ +return *spec == '/' + ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo); +} + /************************************************* * Set keepalive on a socket * *************************************************/ @@ -369,7 +430,7 @@ Returns: nothing */ void -ip_keepalive(int sock, uschar *address, BOOL torf) +ip_keepalive(int sock, const uschar *address, BOOL torf) { int fodder = 1; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, @@ -395,23 +456,22 @@ BOOL fd_ready(int fd, int timeout) { fd_set select_inset; -struct timeval tv; time_t start_recv = time(NULL); +int time_left = timeout; int rc; -if (timeout <= 0) +if (time_left <= 0) { errno = ETIMEDOUT; return FALSE; } /* Wait until the socket is ready */ -for (;;) +do { + struct timeval tv = { time_left, 0 }; FD_ZERO (&select_inset); FD_SET (fd, &select_inset); - tv.tv_sec = timeout; - tv.tv_usec = 0; /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/ rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv); @@ -423,17 +483,17 @@ for (;;) Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making the interrupt not at all rare. Since the timeout is typically more than 2 minutes, the effect was to block the timeout completely. To prevent this - happening again, we do an explicit time test. */ + happening again, we do an explicit time test and adjust the timeout + accordingly */ if (rc < 0 && errno == EINTR) { DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n"); - if (time(NULL) - start_recv < timeout) continue; - DEBUG(D_transport) debug_printf("total wait time exceeds timeout\n"); - } - /* Handle a timeout, and treat any other select error as a timeout, including - an EINTR when we have been in this loop for longer than timeout. */ + /* Watch out, 'continue' jumps to the condition, not to the loops top */ + time_left = timeout - (time(NULL) - start_recv); + if (time_left > 0) continue; + } if (rc <= 0) { @@ -441,10 +501,10 @@ for (;;) return FALSE; } - /* If the socket is ready, break out of the loop. */ - - if (FD_ISSET(fd, &select_inset)) break; + /* Checking the FD_ISSET is not enough, if we're interrupted, the + select_inset may still contain the 'input'. */ } +while (rc < 0 || !FD_ISSET(fd, &select_inset)); return TRUE; } @@ -647,13 +707,9 @@ while (last > first) return TRUE; } else if (c > 0) - { first = middle + 1; - } else - { last = middle; - } } return FALSE; } @@ -668,3 +724,5 @@ for (i=0; i < dscp_table_size; ++i) /* End of ip.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/local_scan.h b/src/src/local_scan.h index b160205b3..bca14bcaf 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This file is the header that is the only Exim header to be included in the @@ -98,8 +98,8 @@ ABI is changed in a non backward compatible way. The minor number is increased each time a new feature is added (in a way that doesn't break backward compatibility). */ -#define LOCAL_SCAN_ABI_VERSION_MAJOR 1 -#define LOCAL_SCAN_ABI_VERSION_MINOR 1 +#define LOCAL_SCAN_ABI_VERSION_MAJOR 2 +#define LOCAL_SCAN_ABI_VERSION_MINOR 0 #define LOCAL_SCAN_ABI_VERSION \ LOCAL_SCAN_ABI_VERSION_MAJOR.LOCAL_SCAN_ABI_VERSION_MINOR @@ -189,7 +189,7 @@ extern int smtp_fflush(void); extern void smtp_printf(const char *, ...) PRINTF_FUNCTION(1,2); extern void smtp_vprintf(const char *, va_list); extern uschar *string_copy(const uschar *); -extern uschar *string_copyn(uschar *, int); +extern uschar *string_copyn(const uschar *, int); extern uschar *string_sprintf(const char *, ...) ALMOST_PRINTF(1,2); /* End of local_scan.h */ diff --git a/src/src/log.c b/src/src/log.c index c80c34751..558c000d7 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for writing log files. The code for maintaining datestamped @@ -556,6 +556,23 @@ return total_written; } + +static void +set_file_path(void) +{ +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))) + { + if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; + file_path = string_copy(t); + break; + } +} + + + /************************************************* * Write message to log file * *************************************************/ @@ -596,7 +613,7 @@ If a message_id exists, we include it after the timestamp. Arguments: selector write to main log or LOG_INFO only if this value is zero, or if - its bit is set in log_write_selector + its bit is set in log_selector[0] flags each bit indicates some independent action: LOG_SENDER add raw sender to the message LOG_RECIPIENTS add raw recipients list to message @@ -668,13 +685,13 @@ 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[0] != 0) + if (*log_file_path) { int sep = ':'; /* Fixed separator - outside use */ uschar *s; - uschar *ss = log_file_path; + const uschar *ss = log_file_path; logging_mode = 0; - while ((s = string_nextinlist(&ss,&sep,log_buffer,LOG_BUFFER_SIZE)) != NULL) + while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) { if (Ustrcmp(s, "syslog") == 0) logging_mode |= LOG_MODE_SYSLOG; @@ -685,10 +702,8 @@ if (!path_inspected) /* If a non-empty path is given, use it */ - if (s[0] != 0) - { + 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 @@ -696,17 +711,7 @@ if (!path_inspected) use the ultimate default in the spool directory. */ else - { - uschar *t; - uschar *tt = US LOG_FILE_PATH; - while ((t = string_nextinlist(&tt,&sep,log_buffer,LOG_BUFFER_SIZE)) - != NULL) - { - if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; - file_path = string_copy(t); - break; - } - } /* Empty item in log_file_path */ + set_file_path(); /* Empty item in log_file_path */ } /* First non-syslog item in log_file_path */ } /* Scan of log_file_path */ } @@ -729,10 +734,8 @@ if (!path_inspected) should work since we have now set up the routing. */ if (multiple) - { log_write(0, LOG_MAIN|LOG_PANIC, "More than one path given in log_file_path: using %s", file_path); - } } /* If debugging, show all log entries, but don't show headers. Do it all @@ -746,15 +749,12 @@ DEBUG(D_any|D_v) Ustrcpy(ptr, "LOG:"); ptr += 4; - /* Show the options that were passed into the call. These are those whose - flag values do not have the 0x80000000 bit in them. Note that this - automatically exclude the "all" setting. */ + /* Show the selector that was passed into the call. */ for (i = 0; i < log_options_count; i++) { - unsigned int bit = log_options[i].bit; - if ((bit & 0x80000000) != 0) continue; - if ((selector & bit) != 0) + unsigned int bitnum = log_options[i].bit; + if (bitnum < BITWORDSIZE && selector == BIT(bitnum)) { *ptr++ = ' '; Ustrcpy(ptr, log_options[i].name); @@ -806,7 +806,7 @@ ptr = log_buffer; sprintf(CS ptr, "%s ", tod_stamp(tod_log)); while(*ptr) ptr++; -if ((log_extra_selector & LX_pid) != 0) +if (LOGGING(pid)) { sprintf(CS ptr, "[%d] ", (int)getpid()); while (*ptr) ptr++; @@ -866,7 +866,7 @@ or unless there is no log_stderr (expn called from daemon, for example). */ if (!really_exim || log_testing_mode) { if (debug_selector == 0 && log_stderr != NULL && - (selector == 0 || (selector & log_write_selector) != 0)) + (selector == 0 || (selector & log_selector[0]) != 0)) { if (host_checking) fprintf(log_stderr, "LOG: %s", CS(log_buffer + 20)); /* no timestamp */ @@ -884,7 +884,7 @@ has been renamed. Therefore, do a stat() and see if the inode has changed, and if so, re-open. */ if ((flags & LOG_MAIN) != 0 && - (selector == 0 || (selector & log_write_selector) != 0)) + (selector == 0 || (selector & log_selector[0]) != 0)) { if ((logging_mode & LOG_MODE_SYSLOG) != 0 && (syslog_duplication || (flags & (LOG_REJECT|LOG_PANIC)) == 0)) @@ -953,7 +953,7 @@ if ((flags & LOG_REJECT) != 0) { header_line *h; - if (header_list != NULL && (log_extra_selector & LX_rejected_header) != 0) + if (header_list != NULL && LOGGING(rejected_header)) { if (recipients_count > 0) { @@ -1139,6 +1139,35 @@ syslog_open = FALSE; /************************************************* +* Multi-bit set or clear * +*************************************************/ + +/* These functions take a list of bit indexes (terminated by -1) and +clear or set the corresponding bits in the selector. + +Arguments: + selector address of the bit string + selsize number of words in the bit string + bits list of bits to set +*/ + +void +bits_clear(unsigned int *selector, size_t selsize, int *bits) +{ +for(; *bits != -1; ++bits) + BIT_CLEAR(selector, selsize, *bits); +} + +void +bits_set(unsigned int *selector, size_t selsize, int *bits) +{ +for(; *bits != -1; ++bits) + BIT_SET(selector, selsize, *bits); +} + + + +/************************************************* * Decode bit settings for log/debug * *************************************************/ @@ -1148,13 +1177,9 @@ also recognizes a numeric setting of the form =<number>, but this is not intended for user use. It's an easy way for Exim to pass the debug settings when it is re-exec'ed. -The log options are held in two unsigned ints (because there became too many -for one). The top bit in the table means "put in 2nd selector". This does not -yet apply to debug options, so the "=" facility sets only the first selector. - -The "all" selector, which must be equal to 0xffffffff, is recognized specially. -It sets all the bits in both selectors. However, there is a facility for then -unsetting certain bits, because we want to turn off "memory" in the debug case. +The option table is a list of names and bit indexes. The index -1 +means "set all bits, except for those listed in notall". The notall +list is terminated by -1. The action taken for bad values varies depending upon why we're here. For log messages, or if the debugging is triggered from config, then we write @@ -1162,10 +1187,9 @@ to the log on the way out. For debug setting triggered from the command-line, we treat it as an unknown option: error message to stderr and die. Arguments: - selector1 address of the first bit string - selector2 address of the second bit string, or NULL - notall1 bits to exclude from "all" for selector1 - notall2 bits to exclude from "all" for selector2 + selector address of the bit string + selsize number of words in the bit string + notall list of bits to exclude from "all" string the configured string options the table of option names count size of table @@ -1176,9 +1200,8 @@ Returns: nothing on success - bomb out on failure */ void -decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1, - int notall2, uschar *string, bit_table *options, int count, uschar *which, - int flags) +decode_bits(unsigned int *selector, size_t selsize, int *notall, + uschar *string, bit_table *options, int count, uschar *which, int flags) { uschar *errmsg; if (string == NULL) return; @@ -1186,7 +1209,8 @@ if (string == NULL) return; if (*string == '=') { char *end; /* Not uschar */ - *selector1 = strtoul(CS string+1, &end, 0); + memset(selector, 0, sizeof(*selector)*selsize); + *selector = strtoul(CS string+1, &end, 0); if (*end == 0) return; errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which, string); @@ -1229,40 +1253,22 @@ else for(;;) if (middle->name[len] != 0) c = -1; else { unsigned int bit = middle->bit; - unsigned int *selector; - - /* The value with all bits set means "force all bits in both selectors" - in the case where two are being handled. However, the top bit in the - second selector is never set. When setting, some bits can be excluded. - */ - - if (bit == 0xffffffff) - { - if (adding) - { - *selector1 = 0xffffffff ^ notall1; - if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2; - } - else - { - *selector1 = 0; - if (selector2 != NULL) *selector2 = 0; - } - } - - /* Otherwise, the 0x80000000 bit means "this value, without the top - bit, belongs in the second selector". */ - else - { - if ((bit & 0x80000000) != 0) - { - selector = selector2; - bit &= 0x7fffffff; - } - else selector = selector1; - if (adding) *selector |= bit; else *selector &= ~bit; - } + if (bit == -1) + { + if (adding) + { + memset(selector, -1, sizeof(*selector)*selsize); + bits_clear(selector, selsize, notall); + } + else + memset(selector, 0, sizeof(*selector)*selsize); + } + else if (adding) + BIT_SET(selector, selsize, bit); + else + BIT_CLEAR(selector, selsize, bit); + break; /* Out of loop to match selector name */ } } @@ -1332,10 +1338,14 @@ if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL)) debug_selector = D_default; if (opts) - { - decode_bits(&debug_selector, NULL, D_memory, 0, opts, + decode_bits(&debug_selector, 1, debug_notall, opts, debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG); - } + +/* When activating from a transport process we may never have logged at all +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(); open_log(&fd, lt_debug, tag_name); diff --git a/src/src/lookupapi.h b/src/src/lookupapi.h index 2a270714b..03de8f675 100644 --- a/src/src/lookupapi.h +++ b/src/src/lookupapi.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -30,11 +30,11 @@ typedef struct lookup_info { int (*find)( /* find function */ void *, /* handle */ uschar *, /* file name or NULL */ - uschar *, /* key or query */ + const uschar *, /* key or query */ int, /* length of key or query */ uschar **, /* for returning answer */ uschar **, /* for error message */ - BOOL *); /* to request cache cleanup */ + uint *); /* cache TTL, sconds */ void (*close)( /* close function */ void *); /* handle */ void (*tidy)(void); /* tidy function */ @@ -46,9 +46,10 @@ typedef struct lookup_info { } lookup_info; /* This magic number is used by the following lookup_module_info structure - for checking API compatibility. It's equivalent to the string"LMM2" */ -#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4d32 + for checking API compatibility. It used to be equivalent to the string"LMM3" */ +#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4933 /* Version 2 adds: version_report */ +/* Version 3 change: non/cache becomes TTL in seconds */ typedef struct lookup_module_info { uint magic; diff --git a/src/src/lookups/README b/src/src/lookups/README index 98905dc5c..31fea6448 100644 --- a/src/src/lookups/README +++ b/src/src/lookups/README @@ -122,12 +122,15 @@ DEFER. The arguments are: uschar **errmsg where to put an error message on failure; this is initially set to "", and should be left as that for a standard "entry not found" error - BOOL *do_cache the lookup should set this to FALSE when it changes data. - This is TRUE by default. When set to FALSE the cache tree + uint *do_cache the lookup should set this to 0 when it changes data. + This is MAXINT by default. When set to 0 the cache tree of the current search handle will be cleaned and the current result will NOT be cached. Currently the mysql and pgsql lookups use this when UPDATE/INSERT queries are executed. + If set to a nonzero number of seconds, the cached value + becomes unusable after this time. Currently the dnsdb + lookup uses this to support the TTL value. Even though the key is zero-terminated, the length is passed because in the common case it has been computed already and is often needed. diff --git a/src/src/lookups/cdb.c b/src/src/lookups/cdb.c index fda14fbd9..ba925dc12 100644 --- a/src/src/lookups/cdb.c +++ b/src/src/lookups/cdb.c @@ -18,6 +18,9 @@ * Changed over to using unsigned chars * Makes use of lf_check_file() for file checking * -------------------------------------------------------------- + * Modified by The Exim Maintainers 2015: + * const propagation + * -------------------------------------------------------------- * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -272,11 +275,11 @@ cdb_check(void *handle, static int cdb_find(void *handle, uschar *filename, - uschar *keystring, + const uschar *keystring, int key_len, uschar **result, uschar **errmsg, - BOOL *do_cache) + uint *do_cache) { struct cdb_state * cdbp = handle; uint32 item_key_len, diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index 3dec7a716..b8c42d596 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -86,8 +86,8 @@ return rc == 0; the keylength in order to include the terminating zero. */ static int -dbmdb_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +dbmdb_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { EXIM_DB *d = (EXIM_DB *)handle; EXIM_DATUM key, data; @@ -119,8 +119,8 @@ return FAIL; /* See local README for interface description */ int -static dbmnz_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +static dbmnz_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { return dbmdb_find(handle, filename, keystring, length-1, result, errmsg, do_cache); @@ -139,11 +139,11 @@ return dbmdb_find(handle, filename, keystring, length-1, result, errmsg, */ static int -dbmjz_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +dbmjz_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { uschar *key_item, *key_buffer, *key_p; -uschar *key_elems = keystring; +const uschar *key_elems = keystring; int buflen, bufleft, key_item_len, sep = 0; /* To a first approximation, the size of the lookup key needs to be about, diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 5a82b340d..70e6c8c63 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -34,9 +34,6 @@ static const char *type_names[] = { #if HAVE_IPV6 "a+", "aaaa", - #ifdef SUPPORT_A6 - "a6", - #endif #endif "cname", "csa", @@ -44,6 +41,7 @@ static const char *type_names[] = { "mxh", "ns", "ptr", + "soa", "spf", "srv", "tlsa", @@ -56,9 +54,6 @@ static int type_values[] = { #if HAVE_IPV6 T_ADDRESSES, /* Private type for AAAA + A */ T_AAAA, - #ifdef SUPPORT_A6 - T_A6, - #endif #endif T_CNAME, T_CSA, /* Private type for "Client SMTP Authorization". */ @@ -66,6 +61,7 @@ static int type_values[] = { T_MXH, /* Private type for "MX hostnames" */ T_NS, T_PTR, + T_SOA, T_SPF, T_SRV, T_TLSA, @@ -108,27 +104,34 @@ no separator. With neither of these options specified, only the first item is output. Similarly for "SPF" records, but the default for joining multiple items in one SPF record is the empty string, for direct concatenation. -(c) If the next sequence of characters is 'defer_FOO' followed by a comma, -the defer behaviour is set to FOO. The possible behaviours are: 'strict', where -any defer causes the whole lookup to defer; 'lax', where a defer causes the -whole lookup to defer only if none of the DNS queries succeeds; and 'never', -where all defers are as if the lookup failed. The default is 'lax'. +(c) Options, all comma-terminated, in any order. Any unrecognised option +terminates option processing. Recognised options are: -(d) Another optional comma-sep field: 'dnssec_FOO', with 'strict', 'lax' -and 'never' (default); can appear before or after (c). The meanings are +- 'defer_FOO': set the defer behaviour to FOO. The possible behaviours are: +'strict', where any defer causes the whole lookup to defer; 'lax', where a defer +causes the whole lookup to defer only if none of the DNS queries succeeds; and +'never', where all defers are as if the lookup failed. The default is 'lax'. + +- 'dnssec_FOO', with 'strict', 'lax' and 'never' (default). The meanings are require, try and don't-try dnssec respectively. -(e) If the next sequence of characters is a sequence of letters and digits +- 'retrans_VAL', set the timeout value. VAL is an Exim time specification +(eg "5s"). The default is set by the main configuration option 'dns_retrans'. + +- 'retry_VAL', set the retry count on timeouts. VAL is an integer. The +default is set by the main configuration option "dns_retry". + +(d) If the next sequence of characters is a sequence of letters and digits followed by '=', it is interpreted as the name of the DNS record type. The default is "TXT". -(f) Then there follows list of domain names. This is a generalized Exim list, +(e) Then there follows list of domain names. This is a generalized Exim list, which may start with '<' in order to set a specific separator. The default separator, as always, is colon. */ static int -dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +dnsdb_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { int rc; int size = 256; @@ -136,12 +139,13 @@ int ptr = 0; int sep = 0; int defer_mode = PASS; int dnssec_mode = OK; +int save_retrans = dns_retrans; +int save_retry = dns_retry; int type; int failrc = FAIL; -uschar *outsep = US"\n"; -uschar *outsep2 = NULL; +const uschar *outsep = CUS"\n"; +const uschar *outsep2 = NULL; uschar *equals, *domain, *found; -uschar buffer[256]; /* Because we're the working in the search pool, we try to reclaim as much store as possible later, so we preallocate the result here */ @@ -180,58 +184,62 @@ if (*keystring == '>') /* Check for a modifier keyword. */ -while ( strncmpic(keystring, US"defer_", 6) == 0 - || strncmpic(keystring, US"dnssec_", 7) == 0 - ) +for (;;) { if (strncmpic(keystring, US"defer_", 6) == 0) { keystring += 6; if (strncmpic(keystring, US"strict", 6) == 0) - { - defer_mode = DEFER; - keystring += 6; - } + { defer_mode = DEFER; keystring += 6; } else if (strncmpic(keystring, US"lax", 3) == 0) - { - defer_mode = PASS; - keystring += 3; - } + { defer_mode = PASS; keystring += 3; } else if (strncmpic(keystring, US"never", 5) == 0) - { - defer_mode = OK; - keystring += 5; - } + { defer_mode = OK; keystring += 5; } else { *errmsg = US"unsupported dnsdb defer behaviour"; return DEFER; } } - else + else if (strncmpic(keystring, US"dnssec_", 7) == 0) { keystring += 7; if (strncmpic(keystring, US"strict", 6) == 0) - { - dnssec_mode = DEFER; - keystring += 6; - } + { dnssec_mode = DEFER; keystring += 6; } else if (strncmpic(keystring, US"lax", 3) == 0) + { dnssec_mode = PASS; keystring += 3; } + else if (strncmpic(keystring, US"never", 5) == 0) + { dnssec_mode = OK; keystring += 5; } + else { - dnssec_mode = PASS; - keystring += 3; + *errmsg = US"unsupported dnsdb dnssec behaviour"; + return DEFER; } - else if (strncmpic(keystring, US"never", 5) == 0) + } + else if (strncmpic(keystring, US"retrans_", 8) == 0) + { + int timeout_sec; + if ((timeout_sec = readconf_readtime(keystring += 8, ',', FALSE)) <= 0) { - dnssec_mode = OK; - keystring += 5; + *errmsg = US"unsupported dnsdb timeout value"; + return DEFER; } - else + dns_retrans = timeout_sec; + while (*keystring != ',') keystring++; + } + else if (strncmpic(keystring, US"retry_", 6) == 0) + { + int retries; + if ((retries = (int)strtol(CCS keystring + 6, CSS &keystring, 0)) < 0) { - *errmsg = US"unsupported dnsdb dnssec behaviour"; + *errmsg = US"unsupported dnsdb retry count"; return DEFER; } + dns_retry = retries; } + else + break; + while (isspace(*keystring)) keystring++; if (*keystring++ != ',') { @@ -307,8 +315,7 @@ if (!outsep2) switch(type) /* Now scan the list and do a lookup for each item */ -while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) - != NULL) +while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) { uschar rbuffer[256]; int searchtype = (type == T_CSA)? T_SRV : /* record type we want */ @@ -345,19 +352,13 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) #if HAVE_IPV6 if (type == T_ADDRESSES) /* NB cannot happen unless HAVE_IPV6 */ { - if (searchtype == T_ADDRESSES) -# if defined(SUPPORT_A6) - searchtype = T_A6; -# else - searchtype = T_AAAA; -# endif - else if (searchtype == T_A6) searchtype = T_AAAA; + if (searchtype == T_ADDRESSES) searchtype = T_AAAA; else if (searchtype == T_AAAA) searchtype = T_A; - rc = dns_special_lookup(&dnsa, domain, searchtype, &found); + rc = dns_special_lookup(&dnsa, domain, searchtype, CUSS &found); } else #endif - rc = dns_special_lookup(&dnsa, domain, type, &found); + rc = dns_special_lookup(&dnsa, domain, type, CUSS &found); lookup_dnssec_authenticated = dnssec_mode==OK ? NULL : dns_is_secure(&dnsa) ? US"yes" : US"no"; @@ -369,6 +370,8 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) { if (defer_mode == DEFER) { + dns_retrans = save_retrans; + dns_retry = save_retry; dns_init(FALSE, FALSE, FALSE); /* clr dnssec bit */ return DEFER; /* always defer */ } @@ -385,19 +388,13 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) { if (rr->type != searchtype) continue; - /* There may be several addresses from an A6 record. Put the configured - separator between them, just as for between several records. However, A6 - support is not normally configured these days. */ + if (*do_cache > rr->ttl) + *do_cache = rr->ttl; - if (type == T_A || - #ifdef SUPPORT_A6 - type == T_A6 || - #endif - type == T_AAAA || - type == T_ADDRESSES) + if (type == T_A || type == T_AAAA || type == T_ADDRESSES) { dns_address *da; - for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next) + for (da = dns_address_from_rr(&dnsa, rr); da; da = da->next) { if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1); yield = string_cat(yield, &size, &ptr, da->address, @@ -440,7 +437,7 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) uint16_t i, payload_length; uschar s[MAX_TLSA_EXPANDED_SIZE]; uschar * sp = s; - uschar *p = (uschar *)(rr->data); + uschar * p = US rr->data; usage = *p++; selector = *p++; @@ -453,72 +450,75 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) for (i=0; i < payload_length && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4); i++) - { sp += sprintf(CS sp, "%02x", (unsigned char)p[i]); - } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); } - else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */ + else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */ { int priority, weight, port; uschar s[264]; - uschar *p = (uschar *)(rr->data); - - if (type == T_MXH) - { - /* mxh ignores the priority number and includes only the hostnames */ - GETSHORT(priority, p); - } - else if (type == T_MX) - { - GETSHORT(priority, p); - sprintf(CS s, "%d%c", priority, *outsep2); - yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); - } - else if (type == T_SRV) - { - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); - sprintf(CS s, "%d%c%d%c%d%c", priority, *outsep2, - weight, *outsep2, port, *outsep2); - yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); - } - else if (type == T_CSA) - { - /* See acl_verify_csa() for more comments about CSA. */ - - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); - - if (priority != 1) continue; /* CSA version must be 1 */ - - /* If the CSA record we found is not the one we asked for, analyse - the subdomain assertions in the port field, else analyse the direct - authorization status in the weight field. */ - - if (found != domain) - { - if (port & 1) *s = 'X'; /* explicit authorization required */ - else *s = '?'; /* no subdomain assertions here */ - } - else - { - if (weight < 2) *s = 'N'; /* not authorized */ - else if (weight == 2) *s = 'Y'; /* authorized */ - else if (weight == 3) *s = '?'; /* unauthorizable */ - else continue; /* invalid */ - } - - s[1] = ' '; - yield = string_cat(yield, &size, &ptr, s, 2); - } + uschar * p = US rr->data; + + switch (type) + { + case T_MXH: + /* mxh ignores the priority number and includes only the hostnames */ + GETSHORT(priority, p); + break; + + case T_MX: + GETSHORT(priority, p); + sprintf(CS s, "%d%c", priority, *outsep2); + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + break; + + case T_SRV: + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + sprintf(CS s, "%d%c%d%c%d%c", priority, *outsep2, + weight, *outsep2, port, *outsep2); + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + break; + + case T_CSA: + /* See acl_verify_csa() for more comments about CSA. */ + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + + if (priority != 1) continue; /* CSA version must be 1 */ + + /* If the CSA record we found is not the one we asked for, analyse + the subdomain assertions in the port field, else analyse the direct + authorization status in the weight field. */ + + if (Ustrcmp(found, domain) != 0) + { + if (port & 1) *s = 'X'; /* explicit authorization required */ + else *s = '?'; /* no subdomain assertions here */ + } + else + { + if (weight < 2) *s = 'N'; /* not authorized */ + else if (weight == 2) *s = 'Y'; /* authorized */ + else if (weight == 3) *s = '?'; /* unauthorizable */ + else continue; /* invalid */ + } + + s[1] = ' '; + yield = string_cat(yield, &size, &ptr, s, 2); + break; + + default: + break; + } /* GETSHORT() has advanced the pointer to the target domain. */ rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p, - (DN_EXPAND_ARG4_TYPE)(s), sizeof(s)); + (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); /* If an overlong response was received, the data will have been truncated and dn_expand may fail. */ @@ -530,6 +530,32 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) break; } else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + + if (type == T_SOA && outsep2 != NULL) + { + unsigned long serial, refresh, retry, expire, minimum; + + p += rc; + yield = string_cat(yield, &size, &ptr, outsep2, 1); + + rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p, + (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); + if (rc < 0) + { + log_write(0, LOG_MAIN, "responsible-mailbox truncated: type=%s " + "domain=%s", dns_text_type(type), domain); + break; + } + else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + + p += rc; + GETLONG(serial, p); GETLONG(refresh, p); + GETLONG(retry, p); GETLONG(expire, p); GETLONG(minimum, p); + sprintf(CS s, "%c%lu%c%lu%c%lu%c%lu%c%lu", + *outsep2, serial, *outsep2, refresh, + *outsep2, retry, *outsep2, expire, *outsep2, minimum); + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + } } } /* Loop for list of returned records */ @@ -545,6 +571,8 @@ store_reset(yield + ptr + 1); /* If ptr == 0 we have not found anything. Otherwise, insert the terminating zero and return the result. */ +dns_retrans = save_retrans; +dns_retry = save_retry; dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */ if (ptr == 0) return failrc; diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index 990b69c8b..9f7dd8da0 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* The idea for this code came from Matthew Byng-Maddick, but his original has @@ -66,8 +66,8 @@ scanning the directory, as it is hopefully faster to let the OS do the scanning for us. */ int -static dsearch_find(void *handle, uschar *dirname, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +static dsearch_find(void *handle, uschar *dirname, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { struct stat statbuf; int save_errno; diff --git a/src/src/lookups/ibase.c b/src/src/lookups/ibase.c index 23e1dea60..3c679c6cf 100644 --- a/src/src/lookups/ibase.c +++ b/src/src/lookups/ibase.c @@ -451,7 +451,7 @@ deferred with a retryable error. */ static int ibase_find(void *handle, uschar * filename, uschar * query, int length, - uschar ** result, uschar ** errmsg, BOOL *do_cache) + uschar ** result, uschar ** errmsg, uint *do_cache) { int sep = 0; uschar *server; @@ -577,7 +577,7 @@ static lookup_info _lookup_info = { #ifdef DYNLOOKUP #define ibase_lookup_module_info _lookup_module_info #endif - + static lookup_info *_lookup_list[] = { &_lookup_info }; lookup_module_info ibase_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 }; diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index ef7ed9e34..fe67e7f0a 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Many thanks to Stuart Lynne for contributing the original code for this @@ -130,9 +130,10 @@ Returns: OK or FAIL or DEFER */ static int -perform_ldap_search(uschar *ldap_url, uschar *server, int s_port, int search_type, - uschar **res, uschar **errmsg, BOOL *defer_break, uschar *user, uschar *password, - int sizelimit, int timelimit, int tcplimit, int dereference, void *referrals) +perform_ldap_search(const uschar *ldap_url, uschar *server, int s_port, + int search_type, uschar **res, uschar **errmsg, BOOL *defer_break, + uschar *user, uschar *password, int sizelimit, int timelimit, int tcplimit, + int dereference, void *referrals) { LDAPURLDesc *ludp = NULL; LDAPMessage *result = NULL; @@ -155,7 +156,7 @@ uschar *error1 = NULL; /* string representation of errcode (static) */ uschar *error2 = NULL; /* error message from the server */ uschar *matched = NULL; /* partially matched DN */ -int attr_count = 0; +int attrs_requested = 0; int error_yield = DEFER; int msgid; int rc, ldap_rc, ldap_parse_rc; @@ -247,7 +248,7 @@ if (host != NULL) /* Count the attributes; we need this later to tell us how to format results */ for (attrp = USS ludp->lud_attrs; attrp != NULL && *attrp != NULL; attrp++) - attr_count++; + attrs_requested++; /* See if we can find a cached connection to this host. The port is not relevant for ldapi. The host name pointer is set to NULL if no host was given @@ -712,10 +713,15 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == LDAP_RES_SEARCH_ENTRY) { LDAPMessage *e; + int valuecount; /* We can see an attr spread across several + entries. If B is derived from A and we request + A and the directory contains both, A and B, + then we get two entries, one for A and one for B. + Here we just count the values per entry */ - DEBUG(D_lookup) debug_printf("ldap_result loop\n"); + DEBUG(D_lookup) debug_printf("LDAP result loop\n"); - for(e = ldap_first_entry(lcp->ld, result); + for(e = ldap_first_entry(lcp->ld, result), valuecount = 0; e != NULL; e = ldap_next_entry(lcp->ld, e)) { @@ -764,15 +770,21 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == /* Otherwise, loop through the entry, grabbing attribute values. If there's only one attribute being retrieved, no attribute name is given, and the - result is not quoted. Multiple values are separated by (comma, space). + result is not quoted. Multiple values are separated by (comma). If more than one attribute is being retrieved, the data is given as a - sequence of name=value pairs, with the value always in quotes. If there are - multiple values, they are given within the quotes, comma separated. */ + sequence of name=value pairs, separated by (space), with the value always in quotes. + If there are multiple values, they are given within the quotes, comma separated. */ else for (attr = US ldap_first_attribute(lcp->ld, e, &ber); attr != NULL; attr = US ldap_next_attribute(lcp->ld, e, ber)) { + DEBUG(D_lookup) debug_printf("LDAP attr loop\n"); + + /* In case of attrs_requested == 1 we just count the values, in all other cases + (0, >1) we count the values per attribute */ + if (attrs_requested != 1) valuecount = 0; + if (attr[0] != 0) { /* Get array of values for this attribute. */ @@ -780,7 +792,8 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == if ((firstval = values = USS ldap_get_values(lcp->ld, e, CS attr)) != NULL) { - if (attr_count != 1) + + if (attrs_requested != 1) { if (insert_space) data = string_cat(data, &size, &ptr, US" ", 1); @@ -794,22 +807,23 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == { uschar *value = *values; int len = Ustrlen(value); + ++valuecount; + + DEBUG(D_lookup) debug_printf("LDAP value loop %s:%s\n", attr, value); - DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value); + /* In case we requested one attribute only but got several times + into that attr loop, we need to append the additional values. + (This may happen if you derive attributeTypes B and C from A and + then query for A.) In all other cases we detect the different + attribute and append only every non first value. */ - /* In case we requested one attribute only but got - * several times into that attr loop, we need to append - * the additional values. (This may happen if you derive - * attributeTypes B and C from A and then query for A.) - * In all other cases we detect the different attribute - * and append only every non first value. */ - if ((attr_count == 1 && data) || (values != firstval)) + if (data && valuecount > 1) data = string_cat(data, &size, &ptr, US",", 1); /* For multiple attributes, the data is in quotes. We must escape internal quotes, backslashes, newlines, and must double commas. */ - if (attr_count != 1) + if (attrs_requested != 1) { int j; for (j = 0; j < len; j++) @@ -850,7 +864,7 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) == /* Closing quote at the end of the data for a named attribute. */ - if (attr_count != 1) + if (attrs_requested != 1) data = string_cat(data, &size, &ptr, US"\"", 1); /* Free the values */ @@ -1125,7 +1139,7 @@ Returns: OK or FAIL or DEFER */ static int -control_ldap_search(uschar *ldap_url, int search_type, uschar **res, +control_ldap_search(const uschar *ldap_url, int search_type, uschar **res, uschar **errmsg) { BOOL defer_break = FALSE; @@ -1135,12 +1149,13 @@ int tcplimit = 0; int sep = 0; int dereference = LDAP_DEREF_NEVER; void* referrals = LDAP_OPT_ON; -uschar *url = ldap_url; -uschar *p; +const uschar *url = ldap_url; +const uschar *p; uschar *user = NULL; uschar *password = NULL; uschar *local_servers = NULL; -uschar *server, *list; +uschar *server; +const uschar *list; uschar buffer[512]; while (isspace(*url)) url++; @@ -1152,7 +1167,7 @@ NAME has the value "ldap". */ while (strncmpic(url, US"ldap", 4) != 0) { - uschar *name = url; + const uschar *name = url; while (*url != 0 && *url != '=') url++; if (*url == '=') { @@ -1336,8 +1351,8 @@ are handled by a common function, with a flag to differentiate between them. The handle and filename arguments are not used. */ static int -eldap_find(void *handle, uschar *filename, uschar *ldap_url, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +eldap_find(void *handle, uschar *filename, const uschar *ldap_url, int length, + uschar **result, uschar **errmsg, uint *do_cache) { /* Keep picky compilers happy */ do_cache = do_cache; @@ -1345,8 +1360,8 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_SINGLE, result, errmsg)); } static int -eldapm_find(void *handle, uschar *filename, uschar *ldap_url, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +eldapm_find(void *handle, uschar *filename, const uschar *ldap_url, int length, + uschar **result, uschar **errmsg, uint *do_cache) { /* Keep picky compilers happy */ do_cache = do_cache; @@ -1354,8 +1369,8 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_MULTIPLE, result, errmsg)); } static int -eldapdn_find(void *handle, uschar *filename, uschar *ldap_url, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +eldapdn_find(void *handle, uschar *filename, const uschar *ldap_url, int length, + uschar **result, uschar **errmsg, uint *do_cache) { /* Keep picky compilers happy */ do_cache = do_cache; @@ -1363,8 +1378,8 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_DN, result, errmsg)); } int -eldapauth_find(void *handle, uschar *filename, uschar *ldap_url, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +eldapauth_find(void *handle, uschar *filename, const uschar *ldap_url, int length, + uschar **result, uschar **errmsg, uint *do_cache) { /* Keep picky compilers happy */ do_cache = do_cache; diff --git a/src/src/lookups/ldap.h b/src/src/lookups/ldap.h index 5ce756d2c..ddfda8597 100644 --- a/src/src/lookups/ldap.h +++ b/src/src/lookups/ldap.h @@ -2,12 +2,12 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Header for eldapauth_find */ -extern int eldapauth_find(void *, uschar *, uschar *, int, uschar **, +extern int eldapauth_find(void *, uschar *, const uschar *, int, uschar **, uschar **, BOOL *); /* End of lookups/ldap.h */ diff --git a/src/src/lookups/lf_functions.h b/src/src/lookups/lf_functions.h index 9f7c7a296..d2487d362 100644 --- a/src/src/lookups/lf_functions.h +++ b/src/src/lookups/lf_functions.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Header for the functions that are shared by the lookups */ @@ -10,8 +10,9 @@ extern int lf_check_file(int, uschar *, int, int, uid_t *, gid_t *, const char *, uschar **); extern uschar *lf_quote(uschar *, uschar *, int, uschar *, int *, int *); -extern int lf_sqlperform(uschar *, uschar *, uschar *, uschar *, uschar **, - uschar **, BOOL *, int(*)(uschar *, uschar *, uschar **, - uschar **, BOOL *, BOOL *)); +extern int lf_sqlperform(const uschar *, const uschar *, const uschar *, + const uschar *, uschar **, + uschar **, uint *, int(*)(const uschar *, uschar *, uschar **, + uschar **, BOOL *, uint *)); /* End of lf_functions.h */ diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c index d430cd454..6d4f7a798 100644 --- a/src/src/lookups/lf_sqlperform.c +++ b/src/src/lookups/lf_sqlperform.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -27,20 +27,21 @@ Arguments: query the query result where to pass back the result errmsg where to pass back an error message - do_cache to be set FALSE if data is changed + do_cache to be set zero if data is changed func the lookup function to call Returns: the return from the lookup function, or DEFER */ int -lf_sqlperform(uschar *name, uschar *optionname, uschar *optserverlist, - uschar *query, uschar **result, uschar **errmsg, BOOL *do_cache, - int(*fn)(uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *)) +lf_sqlperform(const uschar *name, const uschar *optionname, + const uschar *optserverlist, const uschar *query, + uschar **result, uschar **errmsg, uint *do_cache, + int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *)) { int sep, rc; uschar *server; -uschar *serverlist; +const uschar *serverlist; uschar buffer[512]; BOOL defer_break = FALSE; @@ -68,8 +69,8 @@ if (Ustrncmp(query, "servers", 7) != 0) else { int qsep; - uschar *s, *ss; - uschar *qserverlist; + const uschar *s, *ss; + const uschar *qserverlist; uschar *qserver; uschar qbuffer[512]; diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c index 537cac769..eb70a45fa 100644 --- a/src/src/lookups/lsearch.c +++ b/src/src/lookups/lsearch.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -71,7 +71,7 @@ fit into the fixed sized buffer. Most of the time this will never be exercised, but people do occasionally do weird things. */ static int -internal_lsearch_find(void *handle, uschar *filename, uschar *keystring, +internal_lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, int type) { FILE *f = (FILE *)handle; @@ -146,7 +146,7 @@ for (last_was_eol = TRUE; uschar *t = s++; while (*s != 0 && *s != '\"') { - if (*s == '\\') *t++ = string_interpret_escape(&s); + if (*s == '\\') *t++ = string_interpret_escape(CUSS &s); else *t++ = *s; s++; } @@ -181,7 +181,7 @@ for (last_was_eol = TRUE; { int rc; int save = buffer[linekeylength]; - uschar *list = buffer; + const uschar *list = buffer; buffer[linekeylength] = 0; rc = match_isinlist(keystring, &list, @@ -322,8 +322,8 @@ return FAIL; /* See local README for interface description */ static int -lsearch_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ return internal_lsearch_find(handle, filename, keystring, length, result, @@ -339,8 +339,8 @@ return internal_lsearch_find(handle, filename, keystring, length, result, /* See local README for interface description */ static int -wildlsearch_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +wildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ return internal_lsearch_find(handle, filename, keystring, length, result, @@ -356,8 +356,8 @@ return internal_lsearch_find(handle, filename, keystring, length, result, /* See local README for interface description */ static int -nwildlsearch_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +nwildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ return internal_lsearch_find(handle, filename, keystring, length, result, @@ -374,8 +374,8 @@ return internal_lsearch_find(handle, filename, keystring, length, result, /* See local README for interface description */ static int -iplsearch_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +iplsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ if ((length == 1 && keystring[0] == '*') || diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c index 9511d2acb..68b04dcc8 100644 --- a/src/src/lookups/mysql.c +++ b/src/src/lookups/mysql.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Thanks to Paul Kelly for contributing the original code for these @@ -74,7 +74,7 @@ Arguments: resultptr where to store the result errmsg where to point an error message defer_break TRUE if no more servers are to be tried after DEFER - do_cache set false if data is changed + do_cache set zero if data is changed The server string is of the form "host/dbname/user/password". The host can be host:port. This string is in a nextinlist temporary buffer, so can be @@ -84,8 +84,8 @@ Returns: OK, FAIL, or DEFER */ static int -perform_mysql_search(uschar *query, uschar *server, uschar **resultptr, - uschar **errmsg, BOOL *defer_break, BOOL *do_cache) +perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr, + uschar **errmsg, BOOL *defer_break, uint *do_cache) { MYSQL *mysql_handle = NULL; /* Keep compilers happy */ MYSQL_RES *mysql_result = NULL; @@ -125,42 +125,50 @@ sdata[0] = server; /* What's left at the start */ /* See if we have a cached connection to the server */ -for (cn = mysql_connections; cn != NULL; cn = cn->next) - { +for (cn = mysql_connections; cn; cn = cn->next) if (Ustrcmp(cn->server, server_copy) == 0) { mysql_handle = cn->handle; break; } - } /* If no cached connection, we must set one up. Mysql allows for a host name and port to be specified. It also allows the name of a Unix socket to be used. Unfortunately, this contains slashes, but its use is expected to be rare, so the rather cumbersome syntax shouldn't inconvenience too many people. We use -this: host:port(socket) where all the parts are optional. */ +this: host:port(socket)[group] where all the parts are optional. +The "group" parameter specifies an option group from a MySQL option file. */ -if (cn == NULL) +if (!cn) { uschar *p; uschar *socket = NULL; int port = 0; + uschar *group = US"exim"; + + if ((p = Ustrchr(sdata[0], '['))) + { + *p++ = 0; + group = p; + while (*p && *p != ']') p++; + *p = 0; + } - if ((p = Ustrchr(sdata[0], '(')) != NULL) + if ((p = Ustrchr(sdata[0], '('))) { *p++ = 0; socket = p; - while (*p != 0 && *p != ')') p++; + while (*p && *p != ')') p++; *p = 0; } - if ((p = Ustrchr(sdata[0], ':')) != NULL) + if ((p = Ustrchr(sdata[0], ':'))) { *p++ = 0; port = Uatoi(p); } - if (Ustrchr(sdata[0], '/') != NULL) + if (Ustrchr(sdata[0], '/')) { *errmsg = string_sprintf("unexpected slash in MySQL server hostname: %s", sdata[0]); @@ -181,6 +189,7 @@ if (cn == NULL) mysql_handle = store_get(sizeof(MYSQL)); mysql_init(mysql_handle); + mysql_options(mysql_handle, MYSQL_READ_DEFAULT_GROUP, CS group); if (mysql_real_connect(mysql_handle, /* host user passwd database */ CS sdata[0], CS sdata[2], CS sdata[3], CS sdata[1], @@ -225,7 +234,7 @@ can be detected by calling mysql_field_count(). If its result is zero, no data was expected (this is all explained clearly in the MySQL manual). In this case, we return the number of rows affected by the command. In this event, we do NOT want to cache the result; also the whole cache for the handle must be cleaned -up. Setting do_cache FALSE requests this. */ +up. Setting do_cache zero requests this. */ if ((mysql_result = mysql_use_result(mysql_handle)) == NULL) { @@ -233,7 +242,7 @@ if ((mysql_result = mysql_use_result(mysql_handle)) == NULL) { DEBUG(D_lookup) debug_printf("MYSQL: query was not one that returns data\n"); result = string_sprintf("%d", mysql_affected_rows(mysql_handle)); - *do_cache = FALSE; + *do_cache = 0; goto MYSQL_EXIT; } *errmsg = string_sprintf("MYSQL: lookup result failed: %s\n", @@ -340,8 +349,8 @@ query is deferred with a retryable error is now in a separate function that is shared with other SQL lookups. */ static int -mysql_find(void *handle, uschar *filename, uschar *query, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +mysql_find(void *handle, uschar *filename, const uschar *query, int length, + uschar **result, uschar **errmsg, uint *do_cache) { return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query, result, errmsg, do_cache, perform_mysql_search); diff --git a/src/src/lookups/nis.c b/src/src/lookups/nis.c index 7b012b14c..1faa884a1 100644 --- a/src/src/lookups/nis.c +++ b/src/src/lookups/nis.c @@ -42,7 +42,7 @@ code. */ static int nis_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) + uschar **result, uschar **errmsg, uint *do_cache) { int rc; uschar *nis_data; @@ -68,7 +68,7 @@ return (rc == YPERR_KEY || rc == YPERR_MAP)? FAIL : DEFER; static int nis0_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) + uschar **result, uschar **errmsg, uint *do_cache) { int rc; uschar *nis_data; diff --git a/src/src/lookups/nisplus.c b/src/src/lookups/nisplus.c index 8895ceeec..a4a7a2d5b 100644 --- a/src/src/lookups/nisplus.c +++ b/src/src/lookups/nisplus.c @@ -43,7 +43,7 @@ equals sign. */ static int nisplus_find(void *handle, uschar *filename, uschar *query, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) + uschar **result, uschar **errmsg, uint *do_cache) { int i; int ssize = 0; diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c index 1f2520ac0..adb17b4da 100644 --- a/src/src/lookups/oracle.c +++ b/src/src/lookups/oracle.c @@ -517,7 +517,7 @@ deferred with a retryable error. */ static int oracle_find(void *handle, uschar *filename, uschar *query, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) + uschar **result, uschar **errmsg, uint *do_cache) { int sep = 0; uschar *server; diff --git a/src/src/lookups/passwd.c b/src/src/lookups/passwd.c index 4690cbf4e..315677ffa 100644 --- a/src/src/lookups/passwd.c +++ b/src/src/lookups/passwd.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -33,8 +33,8 @@ return (void *)(-1); /* Just return something non-null */ /* See local README for interface description */ static int -passwd_find(void *handle, uschar *filename, uschar *keystring, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +passwd_find(void *handle, uschar *filename, const uschar *keystring, int length, + uschar **result, uschar **errmsg, uint *do_cache) { struct passwd *pw; diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c index 95b1b8c33..01c5375bc 100644 --- a/src/src/lookups/pgsql.c +++ b/src/src/lookups/pgsql.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Thanks to Petr Cech for contributing the original code for these @@ -118,8 +118,8 @@ Returns: OK, FAIL, or DEFER */ static int -perform_pgsql_search(uschar *query, uschar *server, uschar **resultptr, - uschar **errmsg, BOOL *defer_break, BOOL *do_cache) +perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr, + uschar **errmsg, BOOL *defer_break, uint *do_cache) { PGconn *pg_conn = NULL; PGresult *pg_result = NULL; @@ -290,10 +290,10 @@ else /* The command was successful but did not return any data since it was * not SELECT but either an INSERT, UPDATE or DELETE statement. Tell the * high level code to not cache this query, and clean the current cache for - * this handle by setting *do_cache FALSE. */ + * this handle by setting *do_cache zero. */ result = string_copy(US PQcmdTuples(pg_result)); offset = Ustrlen(result); - *do_cache = FALSE; + *do_cache = 0; DEBUG(D_lookup) debug_printf("PGSQL: command does not return any data " "but was successful. Rows affected: %s\n", result); @@ -398,8 +398,8 @@ query is deferred with a retryable error is now in a separate function that is shared with other SQL lookups. */ static int -pgsql_find(void *handle, uschar *filename, uschar *query, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +pgsql_find(void *handle, uschar *filename, const uschar *query, int length, + uschar **result, uschar **errmsg, uint *do_cache) { return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query, result, errmsg, do_cache, perform_pgsql_search); @@ -413,12 +413,6 @@ return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query, /* The characters that always need to be quoted (with backslash) are newline, tab, carriage return, backspace, backslash itself, and the quote characters. -Percent and underscore are only special in contexts where they can be wild -cards, and this isn't usually the case for data inserted from messages, since -that isn't likely to be treated as a pattern of any kind. However, pgsql seems -to allow escaping "on spec". If you use something like "where id="ab\%cd" it -does treat the string as "ab%cd". So we can safely quote percent and -underscore. [This is different to MySQL, where you can't do this.] The original code quoted single quotes as \' which is documented as valid in the O'Reilly book "Practical PostgreSQL" (first edition) as an alternative to @@ -448,7 +442,7 @@ uschar *quoted; if (opt != NULL) return NULL; /* No options recognized */ while ((c = *t++) != 0) - if (Ustrchr("\n\t\r\b\'\"\\%_", c) != NULL) count++; + if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL) count++; if (count == 0) return s; t = quoted = store_get(Ustrlen(s) + count + 1); @@ -460,7 +454,7 @@ while ((c = *s++) != 0) *t++ = '\''; *t++ = '\''; } - else if (Ustrchr("\n\t\r\b\"\\%_", c) != NULL) + else if (Ustrchr("\n\t\r\b\"\\", c) != NULL) { *t++ = '\\'; switch(c) diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index ac4d0ec30..7bab34971 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -7,291 +7,361 @@ #include "../exim.h" -#ifdef EXPERIMENTAL_REDIS +#ifdef LOOKUP_REDIS #include "lf_functions.h" #include <hiredis/hiredis.h> +#ifndef nele +# define nele(arr) (sizeof(arr) / sizeof(*arr)) +#endif + /* Structure and anchor for caching connections. */ typedef struct redis_connection { - struct redis_connection *next; - uschar *server; - redisContext *handle; + struct redis_connection *next; + uschar *server; + redisContext *handle; } redis_connection; static redis_connection *redis_connections = NULL; + static void * redis_open(uschar *filename, uschar **errmsg) { - return (void *)(1); +return (void *)(1); } + void redis_tidy(void) { - redis_connection *cn; - - /* - * XXX: Not sure how often this is called! - * Guess its called after every lookup which probably would mean to just - * not use the _tidy() function at all and leave with exim exiting to - * GC connections! - */ - while ((cn = redis_connections) != NULL) { - redis_connections = cn->next; - DEBUG(D_lookup) debug_printf("close REDIS connection: %s\n", cn->server); - redisFree(cn->handle); - } +redis_connection *cn; + +/* XXX: Not sure how often this is called! + Guess its called after every lookup which probably would mean to just + not use the _tidy() function at all and leave with exim exiting to + GC connections! */ + +while ((cn = redis_connections)) + { + redis_connections = cn->next; + DEBUG(D_lookup) debug_printf("close REDIS connection: %s\n", cn->server); + redisFree(cn->handle); + } } + /* This function is called from the find entry point to do the search for a - * single server. - * - * Arguments: - * query the query string - * server the server string - * resultptr where to store the result - * errmsg where to point an error message - * defer_break TRUE if no more servers are to be tried after DEFER - * do_cache set false if data is changed - * - * The server string is of the form "host/dbnumber/password". The host can be - * host:port. This string is in a nextinlist temporary buffer, so can be - * overwritten. - * - * Returns: OK, FAIL, or DEFER - */ +single server. + + Arguments: + query the query string + server the server string + resultptr where to store the result + errmsg where to point an error message + defer_break TRUE if no more servers are to be tried after DEFER + do_cache set false if data is changed + + The server string is of the form "host/dbnumber/password". The host can be + host:port. This string is in a nextinlist temporary buffer, so can be + overwritten. + + Returns: OK, FAIL, or DEFER +*/ + static int -perform_redis_search(uschar *command, uschar *server, uschar **resultptr, - uschar **errmsg, BOOL *defer_break, BOOL *do_cache) +perform_redis_search(const uschar *command, uschar *server, uschar **resultptr, + uschar **errmsg, BOOL *defer_break, uint *do_cache) { - redisContext *redis_handle = NULL; /* Keep compilers happy */ - redisReply *redis_reply = NULL; - redisReply *entry = NULL; - redisReply *tentry = NULL; - redis_connection *cn; - int ssize = 0; - int offset = 0; - int yield = DEFER; - int i, j; - uschar *result = NULL; - uschar *server_copy = NULL; - uschar *tmp, *ttmp; - uschar *sdata[3]; - - /* - * Disaggregate the parameters from the server argument. - * The order is host:port(socket) - * We can write to the string, since it is in a nextinlist temporary buffer. - * This copy is also used for debugging output. - */ - memset(sdata, 0, sizeof(sdata)) /* Set all to NULL */; - for (i = 2; i > 0; i--) { - uschar *pp = Ustrrchr(server, '/'); - if (pp == NULL) { - *errmsg = string_sprintf("incomplete Redis server data: %s", (i == 2) ? server : server_copy); - *defer_break = TRUE; - return DEFER; - } - *pp++ = 0; - sdata[i] = pp; - if (i == 2) server_copy = string_copy(server); /* sans password */ - } - sdata[0] = server; /* What's left at the start */ - - /* If the database or password is an empty string, set it NULL */ - if (sdata[1][0] == 0) sdata[1] = NULL; - if (sdata[2][0] == 0) sdata[2] = NULL; - - /* See if we have a cached connection to the server */ - for (cn = redis_connections; cn != NULL; cn = cn->next) { - if (Ustrcmp(cn->server, server_copy) == 0) { - redis_handle = cn->handle; - break; - } - } - - if (cn == NULL) { - uschar *p; - uschar *socket = NULL; - int port = 0; - /* int redis_err = REDIS_OK; */ - - if ((p = Ustrchr(sdata[0], '(')) != NULL) { - *p++ = 0; - socket = p; - while (*p != 0 && *p != ')') - p++; - *p = 0; - } - - if ((p = Ustrchr(sdata[0], ':')) != NULL) { - *p++ = 0; - port = Uatoi(p); - } else { - port = Uatoi("6379"); - } - - if (Ustrchr(server, '/') != NULL) { - *errmsg = string_sprintf("unexpected slash in Redis server hostname: %s", sdata[0]); - *defer_break = TRUE; - return DEFER; - } - - DEBUG(D_lookup) - debug_printf("REDIS new connection: host=%s port=%d socket=%s database=%s\n", sdata[0], port, socket, sdata[1]); - - /* Get store for a new handle, initialize it, and connect to the server */ - /* XXX: Use timeouts ? */ - if (socket != NULL) - redis_handle = redisConnectUnix(CCS socket); - else - redis_handle = redisConnect(CCS server, port); - if (redis_handle == NULL) { - *errmsg = string_sprintf("REDIS connection failed"); - *defer_break = FALSE; - goto REDIS_EXIT; - } - - /* Add the connection to the cache */ - cn = store_get(sizeof(redis_connection)); - cn->server = server_copy; - cn->handle = redis_handle; - cn->next = redis_connections; - redis_connections = cn; - } else { - DEBUG(D_lookup) - debug_printf("REDIS using cached connection for %s\n", server_copy); - } - - /* Authenticate if there is a password */ - if(sdata[2] != NULL) { - if ((redis_reply = redisCommand(redis_handle, "AUTH %s", sdata[2])) == NULL) { - *errmsg = string_sprintf("REDIS Authentication failed: %s\n", redis_handle->errstr); - *defer_break = FALSE; - goto REDIS_EXIT; - } - } - - /* Select the database if there is a dbnumber passed */ - if(sdata[1] != NULL) { - if ((redis_reply = redisCommand(redis_handle, "SELECT %s", sdata[1])) == NULL) { - *errmsg = string_sprintf("REDIS: Selecting database=%s failed: %s\n", sdata[1], redis_handle->errstr); - *defer_break = FALSE; - goto REDIS_EXIT; - } else { - DEBUG(D_lookup) debug_printf("REDIS: Selecting database=%s\n", sdata[1]); - } - } - - /* Run the command */ - if ((redis_reply = redisCommand(redis_handle, CS command)) == NULL) { - *errmsg = string_sprintf("REDIS: query failed: %s\n", redis_handle->errstr); - *defer_break = FALSE; - goto REDIS_EXIT; - } - - switch (redis_reply->type) { - case REDIS_REPLY_ERROR: - *errmsg = string_sprintf("REDIS: lookup result failed: %s\n", redis_reply->str); - *defer_break = FALSE; - *do_cache = FALSE; - goto REDIS_EXIT; - /* NOTREACHED */ - - break; - case REDIS_REPLY_NIL: - DEBUG(D_lookup) debug_printf("REDIS: query was not one that returned any data\n"); - result = string_sprintf(""); - *do_cache = FALSE; - goto REDIS_EXIT; - /* NOTREACHED */ - - break; - case REDIS_REPLY_INTEGER: - ttmp = (redis_reply->integer != 0) ? US"true" : US"false"; - result = string_cat(result, &ssize, &offset, US ttmp, Ustrlen(ttmp)); - break; - case REDIS_REPLY_STRING: - case REDIS_REPLY_STATUS: - result = string_cat(result, &ssize, &offset, US redis_reply->str, redis_reply->len); - break; - case REDIS_REPLY_ARRAY: - - /* NOTE: For now support 1 nested array result. If needed a limitless result can be parsed */ - for (i = 0; i < redis_reply->elements; i++) { - entry = redis_reply->element[i]; - - if (result != NULL) - result = string_cat(result, &ssize, &offset, US"\n", 1); - - switch (entry->type) { - case REDIS_REPLY_INTEGER: - tmp = string_sprintf("%d", entry->integer); - result = string_cat(result, &ssize, &offset, US tmp, Ustrlen(tmp)); - break; - case REDIS_REPLY_STRING: - result = string_cat(result, &ssize, &offset, US entry->str, entry->len); - break; - case REDIS_REPLY_ARRAY: - for (j = 0; j < entry->elements; j++) { - tentry = entry->element[j]; - - if (result != NULL) - result = string_cat(result, &ssize, &offset, US"\n", 1); - - switch (tentry->type) { - case REDIS_REPLY_INTEGER: - ttmp = string_sprintf("%d", tentry->integer); - result = string_cat(result, &ssize, &offset, US ttmp, Ustrlen(ttmp)); - break; - case REDIS_REPLY_STRING: - result = string_cat(result, &ssize, &offset, US tentry->str, tentry->len); - break; - case REDIS_REPLY_ARRAY: - DEBUG(D_lookup) debug_printf("REDIS: result has nesting of arrays which is not supported. Ignoring!\n"); - break; - default: - DEBUG(D_lookup) debug_printf("REDIS: result has unsupported type. Ignoring!\n"); - break; - } - } - break; - default: - DEBUG(D_lookup) debug_printf("REDIS: query returned unsupported type\n"); - break; - } - } - break; - } - - - if (result == NULL) { - yield = FAIL; - *errmsg = US"REDIS: no data found"; - } else { - result[offset] = 0; - store_reset(result + offset + 1); - } - - REDIS_EXIT: - /* Free store for any result that was got; don't close the connection, as it is cached. */ - if (redis_reply != NULL) - freeReplyObject(redis_reply); - - /* Non-NULL result indicates a sucessful result */ - if (result != NULL) { - *resultptr = result; - return OK; - } else { - DEBUG(D_lookup) debug_printf("%s\n", *errmsg); - /* NOTE: Required to close connection since it needs to be reopened */ - return yield; /* FAIL or DEFER */ - } +redisContext *redis_handle = NULL; /* Keep compilers happy */ +redisReply *redis_reply = NULL; +redisReply *entry = NULL; +redisReply *tentry = NULL; +redis_connection *cn; +int ssize = 0; +int offset = 0; +int yield = DEFER; +int i, j; +uschar *result = NULL; +uschar *server_copy = NULL; +uschar *tmp, *ttmp; +uschar *sdata[3]; + +/* Disaggregate the parameters from the server argument. +The order is host:port(socket) +We can write to the string, since it is in a nextinlist temporary buffer. +This copy is also used for debugging output. */ + +memset(sdata, 0, sizeof(sdata)) /* Set all to NULL */; +for (i = 2; i > 0; i--) + { + uschar *pp = Ustrrchr(server, '/'); + + if (!pp) + { + *errmsg = string_sprintf("incomplete Redis server data: %s", + i == 2 ? server : server_copy); + *defer_break = TRUE; + return DEFER; + } + *pp++ = 0; + sdata[i] = pp; + if (i == 2) server_copy = string_copy(server); /* sans password */ + } +sdata[0] = server; /* What's left at the start */ + +/* If the database or password is an empty string, set it NULL */ +if (sdata[1][0] == 0) sdata[1] = NULL; +if (sdata[2][0] == 0) sdata[2] = NULL; + +/* See if we have a cached connection to the server */ + +for (cn = redis_connections; cn; cn = cn->next) + if (Ustrcmp(cn->server, server_copy) == 0) + { + redis_handle = cn->handle; + break; + } + +if (!cn) + { + uschar *p; + uschar *socket = NULL; + int port = 0; + /* int redis_err = REDIS_OK; */ + + if ((p = Ustrchr(sdata[0], '('))) + { + *p++ = 0; + socket = p; + while (*p != 0 && *p != ')') p++; + *p = 0; + } + + if ((p = Ustrchr(sdata[0], ':'))) + { + *p++ = 0; + port = Uatoi(p); + } + else + port = Uatoi("6379"); + + if (Ustrchr(server, '/')) + { + *errmsg = string_sprintf("unexpected slash in Redis server hostname: %s", + sdata[0]); + *defer_break = TRUE; + return DEFER; + } + + DEBUG(D_lookup) + debug_printf("REDIS new connection: host=%s port=%d socket=%s database=%s\n", + sdata[0], port, socket, sdata[1]); + + /* Get store for a new handle, initialize it, and connect to the server */ + /* XXX: Use timeouts ? */ + redis_handle = + socket ? redisConnectUnix(CCS socket) : redisConnect(CCS server, port); + if (!redis_handle) + { + *errmsg = string_sprintf("REDIS connection failed"); + *defer_break = FALSE; + goto REDIS_EXIT; + } + + /* Add the connection to the cache */ + cn = store_get(sizeof(redis_connection)); + cn->server = server_copy; + cn->handle = redis_handle; + cn->next = redis_connections; + redis_connections = cn; + } +else + { + DEBUG(D_lookup) + debug_printf("REDIS using cached connection for %s\n", server_copy); +} + +/* Authenticate if there is a password */ +if(sdata[2]) + if (!(redis_reply = redisCommand(redis_handle, "AUTH %s", sdata[2]))) + { + *errmsg = string_sprintf("REDIS Authentication failed: %s\n", redis_handle->errstr); + *defer_break = FALSE; + goto REDIS_EXIT; + } + +/* Select the database if there is a dbnumber passed */ +if(sdata[1]) + { + if (!(redis_reply = redisCommand(redis_handle, "SELECT %s", sdata[1]))) + { + *errmsg = string_sprintf("REDIS: Selecting database=%s failed: %s\n", sdata[1], redis_handle->errstr); + *defer_break = FALSE; + goto REDIS_EXIT; + } + DEBUG(D_lookup) debug_printf("REDIS: Selecting database=%s\n", sdata[1]); + } + +/* split string on whitespace into argv */ + { + uschar * argv[32]; + int i; + const uschar * s = command; + int siz, ptr; + uschar c; + + while (isspace(*s)) s++; + + for (i = 0; *s && i < nele(argv); i++) + { + for (argv[i] = NULL, siz = ptr = 0; (c = *s) && !isspace(c); s++) + if (c != '\\' || *++s) /* backslash protects next char */ + argv[i] = string_cat(argv[i], &siz, &ptr, s, 1); + *(argv[i]+ptr) = '\0'; + DEBUG(D_lookup) debug_printf("REDIS: argv[%d] '%s'\n", i, argv[i]); + while (isspace(*s)) s++; + } + + /* Run the command. We use the argv form rather than plain as that parses + into args by whitespace yet has no escaping mechanism. */ + + redis_reply = redisCommandArgv(redis_handle, i, (const char **) argv, NULL); + if (!redis_reply) + { + *errmsg = string_sprintf("REDIS: query failed: %s\n", redis_handle->errstr); + *defer_break = FALSE; + goto REDIS_EXIT; + } + } + +switch (redis_reply->type) + { + case REDIS_REPLY_ERROR: + *errmsg = string_sprintf("REDIS: lookup result failed: %s\n", redis_reply->str); + *defer_break = FALSE; + *do_cache = 0; + goto REDIS_EXIT; + /* NOTREACHED */ + + case REDIS_REPLY_NIL: + DEBUG(D_lookup) + debug_printf("REDIS: query was not one that returned any data\n"); + result = string_sprintf(""); + *do_cache = 0; + goto REDIS_EXIT; + /* NOTREACHED */ + + case REDIS_REPLY_INTEGER: + ttmp = (redis_reply->integer != 0) ? US"true" : US"false"; + result = string_cat(result, &ssize, &offset, US ttmp, Ustrlen(ttmp)); + break; + + case REDIS_REPLY_STRING: + case REDIS_REPLY_STATUS: + result = string_cat(result, &ssize, &offset, + US redis_reply->str, redis_reply->len); + break; + + case REDIS_REPLY_ARRAY: + + /* NOTE: For now support 1 nested array result. If needed a limitless + result can be parsed */ + + for (i = 0; i < redis_reply->elements; i++) + { + entry = redis_reply->element[i]; + + if (result) + result = string_cat(result, &ssize, &offset, US"\n", 1); + + switch (entry->type) + { + case REDIS_REPLY_INTEGER: + tmp = string_sprintf("%d", entry->integer); + result = string_cat(result, &ssize, &offset, US tmp, Ustrlen(tmp)); + break; + case REDIS_REPLY_STRING: + result = string_cat(result, &ssize, &offset, + US entry->str, entry->len); + break; + case REDIS_REPLY_ARRAY: + for (j = 0; j < entry->elements; j++) + { + tentry = entry->element[j]; + + if (result) + result = string_cat(result, &ssize, &offset, US"\n", 1); + + switch (tentry->type) + { + case REDIS_REPLY_INTEGER: + ttmp = string_sprintf("%d", tentry->integer); + result = string_cat(result, &ssize, &offset, + US ttmp, Ustrlen(ttmp)); + break; + case REDIS_REPLY_STRING: + result = string_cat(result, &ssize, &offset, + US tentry->str, tentry->len); + break; + case REDIS_REPLY_ARRAY: + DEBUG(D_lookup) + debug_printf("REDIS: result has nesting of arrays which" + " is not supported. Ignoring!\n"); + break; + default: + DEBUG(D_lookup) debug_printf( + "REDIS: result has unsupported type. Ignoring!\n"); + break; + } + } + break; + default: + DEBUG(D_lookup) debug_printf("REDIS: query returned unsupported type\n"); + break; + } + } + break; + } + + +if (result) + { + result[offset] = 0; + store_reset(result + offset + 1); + } +else + { + yield = FAIL; + *errmsg = US"REDIS: no data found"; + } + +REDIS_EXIT: + +/* Free store for any result that was got; don't close the connection, +as it is cached. */ + +if (redis_reply) freeReplyObject(redis_reply); + +/* Non-NULL result indicates a sucessful result */ + +if (result) + { + *resultptr = result; + return OK; + } +else + { + DEBUG(D_lookup) debug_printf("%s\n", *errmsg); + /* NOTE: Required to close connection since it needs to be reopened */ + return yield; /* FAIL or DEFER */ + } } + + /************************************************* * Find entry point * *************************************************/ @@ -303,13 +373,60 @@ perform_redis_search(uschar *command, uschar *server, uschar **resultptr, */ static int -redis_find(void *handle __attribute__((unused)), uschar *filename __attribute__((unused)), - uschar *command, int length, uschar **result, uschar **errmsg, BOOL *do_cache) +redis_find(void *handle __attribute__((unused)), + uschar *filename __attribute__((unused)), + const uschar *command, int length, uschar **result, uschar **errmsg, + uint *do_cache) { - return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command, - result, errmsg, do_cache, perform_redis_search); +return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command, + result, errmsg, do_cache, perform_redis_search); } + + +/************************************************* +* Quote entry point * +*************************************************/ + +/* Prefix any whitespace, or backslash, with a backslash. +This is not a Redis thing but instead to let the argv splitting +we do to split on whitespace, yet provide means for getting +whitespace into an argument. + +Arguments: + s the string to be quoted + opt additional option text or NULL if none + +Returns: the processed string or NULL for a bad option +*/ + +static uschar * +redis_quote(uschar *s, uschar *opt) +{ +register int c; +int count = 0; +uschar *t = s; +uschar *quoted; + +if (opt) return NULL; /* No options recognized */ + +while ((c = *t++) != 0) + if (isspace(c) || c == '\\') count++; + +if (count == 0) return s; +t = quoted = store_get(Ustrlen(s) + count + 1); + +while ((c = *s++) != 0) + { + if (isspace(c) || c == '\\') *t++ = '\\'; + *t++ = c; + } + +*t = 0; +return quoted; +} + + /************************************************* * Version reporting entry point * *************************************************/ @@ -318,13 +435,15 @@ redis_find(void *handle __attribute__((unused)), uschar *filename __attribute__( void redis_version_report(FILE *f) { - fprintf(f, "Library version: REDIS: Compile: %d [%d]\n", +fprintf(f, "Library version: REDIS: Compile: %d [%d]\n", HIREDIS_MAJOR, HIREDIS_MINOR); #ifdef DYNLOOKUP - fprintf(f, " Exim version %s\n", EXIM_VERSION_STR); +fprintf(f, " Exim version %s\n", EXIM_VERSION_STR); #endif } + + /* These are the lookup_info blocks for this driver */ static lookup_info redis_lookup_info = { US"redis", /* lookup name */ @@ -334,16 +453,16 @@ static lookup_info redis_lookup_info = { redis_find, /* find function */ NULL, /* no close function */ redis_tidy, /* tidy function */ - NULL, /* quoting function */ + redis_quote, /* quoting function */ redis_version_report /* version reporting */ }; #ifdef DYNLOOKUP -#define redis_lookup_module_info _lookup_module_info +# define redis_lookup_module_info _lookup_module_info #endif /* DYNLOOKUP */ static lookup_info *_lookup_list[] = { &redis_lookup_info }; lookup_module_info redis_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 }; -#endif /* EXPERIMENTAL_REDIS */ +#endif /* LOOKUP_REDIS */ /* End of lookups/redis.c */ diff --git a/src/src/lookups/spf.c b/src/src/lookups/spf.c index 23ad2addd..2671fc9c4 100644 --- a/src/src/lookups/spf.c +++ b/src/src/lookups/spf.c @@ -31,7 +31,9 @@ static void dummy(int x) { dummy2(x-1); } #include <spf2/spf_dns_resolv.h> #include <spf2/spf_dns_cache.h> -static void *spf_open(uschar *filename, uschar **errmsg) { +static void * +spf_open(uschar *filename, uschar **errmsg) +{ SPF_server_t *spf_server = NULL; spf_server = SPF_server_new(SPF_DNS_CACHE, 0); if (spf_server == NULL) { @@ -41,13 +43,17 @@ static void *spf_open(uschar *filename, uschar **errmsg) { return (void *) spf_server; } -static void spf_close(void *handle) { +static void +spf_close(void *handle) +{ SPF_server_t *spf_server = handle; if (spf_server) SPF_server_free(spf_server); } -static int spf_find(void *handle, uschar *filename, uschar *keystring, int key_len, - uschar **result, uschar **errmsg, BOOL *do_cache) { +static int +spf_find(void *handle, uschar *filename, uschar *keystring, int key_len, + uschar **result, uschar **errmsg, uint *do_cache) +{ SPF_server_t *spf_server = handle; SPF_request_t *spf_request = NULL; SPF_response_t *spf_response = NULL; diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index c0fc891fb..e2330f920 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -80,8 +80,8 @@ return 0; static int -sqlite_find(void *handle, uschar *filename, uschar *query, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +sqlite_find(void *handle, uschar *filename, const uschar *query, int length, + uschar **result, uschar **errmsg, uint *do_cache) { int ret; struct strbuf res = { NULL, 0, 0 }; @@ -93,7 +93,7 @@ if (ret != SQLITE_OK) return FAIL; } -if (res.string == NULL) *do_cache = FALSE; +if (res.string == NULL) *do_cache = 0; *result = res.string; return OK; diff --git a/src/src/lookups/testdb.c b/src/src/lookups/testdb.c index 7a81795cc..401f7c8bf 100644 --- a/src/src/lookups/testdb.c +++ b/src/src/lookups/testdb.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -37,8 +37,8 @@ return (void *)(1); /* Just return something non-null */ /* See local README for interface description. */ static int -testdb_find(void *handle, uschar *filename, uschar *query, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) +testdb_find(void *handle, uschar *filename, const uschar *query, int length, + uschar **result, uschar **errmsg, uint *do_cache) { handle = handle; /* Keep picky compilers happy */ filename = filename; @@ -57,7 +57,7 @@ if (Ustrcmp(query, "defer") == 0) return DEFER; } -if (Ustrcmp(query, "nocache") == 0) *do_cache = FALSE; +if (Ustrcmp(query, "nocache") == 0) *do_cache = 0; *result = string_copy(query); return OK; diff --git a/src/src/lookups/whoson.c b/src/src/lookups/whoson.c index 4166089bd..9ac5a3a43 100644 --- a/src/src/lookups/whoson.c +++ b/src/src/lookups/whoson.c @@ -36,7 +36,7 @@ return (void *)(1); /* Just return something non-null */ static int whoson_find(void *handle, uschar *filename, uschar *query, int length, - uschar **result, uschar **errmsg, BOOL *do_cache) + uschar **result, uschar **errmsg, uint *do_cache) { uschar buffer[80]; handle = handle; /* Keep picky compilers happy */ diff --git a/src/src/lss.c b/src/src/lss.c index ab358d994..62b0a35f1 100644 --- a/src/src/lss.c +++ b/src/src/lss.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Support functions for calling from local_scan(). These are mostly just @@ -27,7 +27,7 @@ Returns: OK/FAIL/DEFER int lss_match_domain(uschar *domain, uschar *list) { -return match_isinlist(domain, &list, 0, &domainlist_anchor, NULL, MCL_DOMAIN, +return match_isinlist(CUS domain, CUSS &list, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); } @@ -49,7 +49,7 @@ Returns: OK/FAIL/DEFER int lss_match_local_part(uschar *local_part, uschar *list, BOOL caseless) { -return match_isinlist(local_part, &list, 0, &localpartlist_anchor, NULL, +return match_isinlist(CUS local_part, CUSS &list, 0, &localpartlist_anchor, NULL, MCL_LOCALPART, caseless, NULL); } @@ -71,7 +71,7 @@ Returns: OK/FAIL/DEFER int lss_match_address(uschar *address, uschar *list, BOOL caseless) { -return match_address_list(address, caseless, TRUE, &list, NULL, -1, 0, NULL); +return match_address_list(CUS address, caseless, TRUE, CUSS &list, NULL, -1, 0, NULL); } @@ -95,7 +95,7 @@ Returns: OK/FAIL/DEFER int lss_match_host(uschar *host_name, uschar *host_address, uschar *list) { -return verify_check_this_host(&list, NULL, host_name, host_address, NULL); +return verify_check_this_host(CUSS &list, NULL, host_name, host_address, NULL); } diff --git a/src/src/macros.h b/src/src/macros.h index 43179a531..66abffa37 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -12,6 +12,9 @@ a string as a text string. This is sometimes useful for debugging output. */ #define mac_string(s) # s #define mac_expanded_string(s) mac_string(s) +/* Number of elements of an array */ +#define nelem(arr) (sizeof(arr) / sizeof(*arr)) + /* When running in the test harness, the load average is fudged. */ @@ -106,6 +109,13 @@ don't make the file descriptors two-way. */ #define DEBUG(x) if ((debug_selector & (x)) != 0) #define HDEBUG(x) if (host_checking || (debug_selector & (x)) != 0) +#define PTR_CHK(ptr) \ +do { \ +if ((void *)ptr > (void *)store_get(0)) \ + debug_printf("BUG: ptr '%s' beyond arena at %s:%d\n", \ + mac_expanded_string(ptr), __FUNCTION__, __LINE__); \ +} while(0) + /* The default From: text for DSNs */ #define DEFAULT_DSN_FROM "Mail Delivery System <Mailer-Daemon@$qualify_domain>" @@ -149,14 +159,14 @@ into big_buffer_size and in some circumstances increased. It should be at least as long as the maximum path length. */ #if defined PATH_MAX && PATH_MAX > 16384 -#define BIG_BUFFER_SIZE PATH_MAX +# define BIG_BUFFER_SIZE PATH_MAX #elif defined MAXPATHLEN && MAXPATHLEN > 16384 -#define BIG_BUFFER_SIZE MAXPATHLEN +# define BIG_BUFFER_SIZE MAXPATHLEN #else -#define BIG_BUFFER_SIZE 16384 +# define BIG_BUFFER_SIZE 16384 #endif -/* header size of pipe content +/* header size of pipe content currently: char id, char subid, char[5] length */ #define PIPE_HEADER_SIZE 7 @@ -184,7 +194,7 @@ record. */ /* Wait this long before determining that a Proxy Protocol configured host isn't speaking the protocol, and so is disallowed. Can be moved to runtime configuration if per site settings become needed. */ -#ifdef EXPERIMENTAL_PROXY +#ifdef SUPPORT_PROXY #define PROXY_NEGOTIATION_TIMEOUT_SEC 3 #define PROXY_NEGOTIATION_TIMEOUT_USEC 0 #endif @@ -311,46 +321,84 @@ for having to swallow the rest of an SMTP message is whether the value is #define END_SIZE 4 /* Reading ended because message too big */ #define END_WERROR 5 /* Write error while reading the message */ -/* Options bits for debugging; D_v and D_local_scan are also in local_scan.h */ - -#define D_v 0x00000001 -#define D_local_scan 0x00000002 - -#define D_acl 0x00000004 -#define D_auth 0x00000008 -#define D_deliver 0x00000010 -#define D_dns 0x00000020 -#define D_dnsbl 0x00000040 -#define D_exec 0x00000080 -#define D_expand 0x00000100 -#define D_filter 0x00000200 -#define D_hints_lookup 0x00000400 -#define D_host_lookup 0x00000800 -#define D_ident 0x00001000 -#define D_interface 0x00002000 -#define D_lists 0x00004000 -#define D_load 0x00008000 -#define D_lookup 0x00010000 -#define D_memory 0x00020000 -#define D_pid 0x00040000 -#define D_process_info 0x00080000 -#define D_queue_run 0x00100000 -#define D_receive 0x00200000 -#define D_resolver 0x00400000 -#define D_retry 0x00800000 -#define D_rewrite 0x01000000 -#define D_route 0x02000000 -#define D_timestamp 0x04000000 -#define D_tls 0x08000000 -#define D_transport 0x10000000 -#define D_uid 0x20000000 -#define D_verify 0x40000000 - -/* The D_all value must always have all bits set, as it is recognized specially -by the function that decodes debug and log selectors. This is to enable it to -set all the bits in a multi-word selector. Debug doesn't use this yet, but we -are getting close. In fact, we want to omit "memory" for -d+all, but can't -handle this here. It is fudged externally. */ +/* Bit masks for debug and log selectors */ + +/* Assume words are 32 bits wide. Tiny waste of space on 64 bit +platforms, but this ensures bit vectors always work the same way. */ +#define BITWORDSIZE 32 + +/* This macro is for single-word bit vectors: the debug selector, +and the first word of the log selector. */ +#define BIT(n) (1 << (n)) + +/* And these are for multi-word vectors. */ +#define BITWORD(n) ( (n) / BITWORDSIZE) +#define BITMASK(n) (1 << (n) % BITWORDSIZE) + +#define BIT_CLEAR(s,z,n) ((s)[BITWORD(n)] &= ~BITMASK(n)) +#define BIT_SET(s,z,n) ((s)[BITWORD(n)] |= BITMASK(n)) +#define BIT_TEST(s,z,n) (((s)[BITWORD(n)] & BITMASK(n)) != 0) + +/* Used in globals.c for initializing bit_table structures. T will be either +D or L correspondong to the debug and log selector bits declared below. */ + +#define BIT_TABLE(T,name) { US #name, T##i_##name } + +/* IOTA allows us to keep an implicit sequential count, like a simple enum, +but we can have sequentially numbered identifiers which are not declared +sequentially. We use this for more compact declarations of bit indexes and +masks, alternating between sequential bit index and corresponding mask. */ + +#define IOTA(iota) (__LINE__ - iota) +#define IOTA_INIT(zero) (__LINE__ - zero + 1) + +/* Options bits for debugging. DEBUG_BIT() declares both a bit index and the +corresponding mask. Di_all is a special value recognized by decode_bits(). + +Exim's code assumes in a number of places that the debug_selector is one +word, and this is exposed in the local_scan ABI. The D_v and D_local_scan bit +masks are part of the local_scan API so are #defined in local_scan.h */ + +#define DEBUG_BIT(name) Di_##name = IOTA(Di_iota), D_##name = BIT(Di_##name) + +enum { + Di_all = -1, + Di_v = 0, + Di_local_scan = 1, + + Di_iota = IOTA_INIT(2), + DEBUG_BIT(acl), + DEBUG_BIT(auth), + DEBUG_BIT(deliver), + DEBUG_BIT(dns), + DEBUG_BIT(dnsbl), + DEBUG_BIT(exec), + DEBUG_BIT(expand), + DEBUG_BIT(filter), + DEBUG_BIT(hints_lookup), + DEBUG_BIT(host_lookup), + DEBUG_BIT(ident), + DEBUG_BIT(interface), + DEBUG_BIT(lists), + DEBUG_BIT(load), + DEBUG_BIT(lookup), + DEBUG_BIT(memory), + DEBUG_BIT(pid), + DEBUG_BIT(process_info), + DEBUG_BIT(queue_run), + DEBUG_BIT(receive), + DEBUG_BIT(resolver), + DEBUG_BIT(retry), + DEBUG_BIT(rewrite), + DEBUG_BIT(route), + DEBUG_BIT(timestamp), + DEBUG_BIT(tls), + DEBUG_BIT(transport), + DEBUG_BIT(uid), + DEBUG_BIT(verify), +}; + +/* Multi-bit debug masks */ #define D_all 0xffffffff @@ -370,81 +418,68 @@ handle this here. It is fudged externally. */ D_timestamp | \ D_resolver)) -/* Options bits for logging. Those that will end up in log_write_selector have -values < 0x80000000. They can be used in calls to log_write(). The others have -values > 0x80000000 and are put into log_extra_selector (without the top bit). -These are only ever tested independently. "All" is a magic value that is used -only in the name table to set all options in both bit maps. */ - -/* The L_all value must always have all bits set, as it is recognized specially -by the function that decodes debug and log selectors. This is to enable it to -set all the bits in a multi-word selector. */ - -#define L_all 0xffffffff - -#define L_address_rewrite 0x00000001 -#define L_all_parents 0x00000002 -#define L_connection_reject 0x00000004 -#define L_delay_delivery 0x00000008 -#define L_dnslist_defer 0x00000010 -#define L_etrn 0x00000020 -#define L_host_lookup_failed 0x00000040 -#define L_lost_incoming_connection 0x00000080 -#define L_queue_run 0x00000100 -#define L_retry_defer 0x00000200 -#define L_size_reject 0x00000400 -#define L_skip_delivery 0x00000800 -#define L_smtp_connection 0x00001000 -#define L_smtp_incomplete_transaction 0x00002000 -#define L_smtp_protocol_error 0x00004000 -#define L_smtp_syntax_error 0x00008000 - -#define LX_acl_warn_skipped 0x80000001 -#define LX_arguments 0x80000002 -#define LX_deliver_time 0x80000004 -#define LX_delivery_size 0x80000008 -#define LX_ident_timeout 0x80000010 -#define LX_incoming_interface 0x80000020 -#define LX_incoming_port 0x80000040 -#define LX_outgoing_port 0x80000080 -#define LX_pid 0x80000100 -#define LX_queue_time 0x80000200 -#define LX_queue_time_overall 0x80000400 -#define LX_received_sender 0x80000800 -#define LX_received_recipients 0x80001000 -#define LX_rejected_header 0x80002000 -#define LX_return_path_on_delivery 0x80004000 -#define LX_sender_on_delivery 0x80008000 -#define LX_sender_verify_fail 0x80010000 -#define LX_smtp_confirmation 0x80020000 -#define LX_smtp_no_mail 0x80040000 -#define LX_subject 0x80080000 -#define LX_tls_certificate_verified 0x80100000 -#define LX_tls_cipher 0x80200000 -#define LX_tls_peerdn 0x80400000 -#define LX_tls_sni 0x80800000 -#define LX_unknown_in_list 0x81000000 -#define LX_8bitmime 0x82000000 -#define LX_smtp_mailauth 0x84000000 -#define LX_proxy 0x88000000 - -#define L_default (L_connection_reject | \ - L_delay_delivery | \ - L_dnslist_defer | \ - L_etrn | \ - L_host_lookup_failed | \ - L_lost_incoming_connection | \ - L_queue_run | \ - L_retry_defer | \ - L_size_reject | \ - L_skip_delivery) - -#define LX_default ((LX_acl_warn_skipped | \ - LX_rejected_header | \ - LX_sender_verify_fail | \ - LX_smtp_confirmation | \ - LX_tls_certificate_verified| \ - LX_tls_cipher) & 0x7fffffff) +/* Options bits for logging. Those that have values < BITWORDSIZE can be used +in calls to log_write(). The others are put into later words in log_selector +and are only ever tested independently, so they do not need bit mask +declarations. The Li_all value is recognized specially by decode_bits(). */ + +#define LOG_BIT(name) Li_##name = IOTA(Li_iota), L_##name = BIT(Li_##name) + +enum { + Li_all = -1, + + Li_iota = IOTA_INIT(0), + LOG_BIT(address_rewrite), + LOG_BIT(all_parents), + LOG_BIT(connection_reject), + LOG_BIT(delay_delivery), + LOG_BIT(dnslist_defer), + LOG_BIT(etrn), + LOG_BIT(host_lookup_failed), + LOG_BIT(lost_incoming_connection), + LOG_BIT(queue_run), + LOG_BIT(retry_defer), + LOG_BIT(size_reject), + LOG_BIT(skip_delivery), + LOG_BIT(smtp_connection), + LOG_BIT(smtp_incomplete_transaction), + LOG_BIT(smtp_protocol_error), + LOG_BIT(smtp_syntax_error), + + Li_acl_warn_skipped = BITWORDSIZE, + Li_arguments, + Li_deliver_time, + Li_delivery_size, + Li_ident_timeout, + Li_incoming_interface, + Li_incoming_port, + Li_outgoing_port, + Li_pid, + Li_queue_time, + Li_queue_time_overall, + Li_received_sender, + Li_received_recipients, + Li_rejected_header, + Li_return_path_on_delivery, + Li_sender_on_delivery, + Li_sender_verify_fail, + Li_smtp_confirmation, + Li_smtp_no_mail, + Li_subject, + Li_tls_certificate_verified, + Li_tls_cipher, + Li_tls_peerdn, + Li_tls_sni, + Li_unknown_in_list, + Li_8bitmime, + Li_smtp_mailauth, + Li_proxy, + Li_outgoing_interface, + + log_selector_size = BITWORD(Li_outgoing_interface) + 1 +}; + +#define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt) /* Private error numbers for delivery failures, set negative so as not to conflict with system errno values. */ @@ -480,7 +515,7 @@ to conflict with system errno values. */ #define ERRNO_UIDFAIL (-29) /* Failed to get uid */ #define ERRNO_BADTRANSPORT (-30) /* Unset or non-existent transport */ #define ERRNO_MBXLENGTH (-31) /* MBX length mismatch */ -#define ERRNO_UNKNOWNHOST (-32) /* Lookup failed in smtp transport */ +#define ERRNO_UNKNOWNHOST (-32) /* Lookup failed routing or in smtp tpt */ #define ERRNO_FORMATUNKNOWN (-33) /* Can't match format in appendfile */ #define ERRNO_BADCREATE (-34) /* Creation outside home in appendfile */ #define ERRNO_LISTDEFER (-35) /* Can't check a list; lookup defer */ @@ -496,7 +531,11 @@ to conflict with system errno values. */ #define ERRNO_MAIL4XX (-45) /* MAIL gave 4xx error */ #define ERRNO_DATA4XX (-46) /* DATA gave 4xx error */ #define ERRNO_PROXYFAIL (-47) /* Negotiation failed for proxy configured host */ -#define ERRNO_AUTHPROB (-48) /* Autheticator "other" failure */ +#define ERRNO_AUTHPROB (-48) /* Authenticator "other" failure */ + +#ifdef SUPPORT_I18N +# define ERRNO_UTF8_FWD (-49) /* target not supporting SMTPUTF8 */ +#endif /* These must be last, so all retry deferments can easily be identified */ @@ -506,6 +545,7 @@ to conflict with system errno values. */ #define ERRNO_HRETRY (-53) /* Not time for any remote host */ #define ERRNO_LOCAL_ONLY (-54) /* Local-only delivery */ #define ERRNO_QUEUE_DOMAIN (-55) /* Domain in queue_domains */ +#define ERRNO_TRETRY (-56) /* Transport concurrency limit */ /* Special actions to take after failure or deferment. */ diff --git a/src/src/malware.c b/src/src/malware.c index 365ef0350..9dd241b8c 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2014 */ +/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015 */ /* License: GPL */ /* Code for calling virus (malware) scanners. Called from acl.c. */ @@ -38,14 +38,12 @@ static struct scan /* The maximum number of clamd servers that are supported in the configuration */ #define MAX_CLAMD_SERVERS 32 #define MAX_CLAMD_SERVERS_S "32" -/* Maximum length of the hostname that can be specified in the clamd address list */ -#define MAX_CLAMD_ADDRESS_LENGTH 64 -#define MAX_CLAMD_ADDRESS_LENGTH_S "64" -typedef struct clamd_address_container { - uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH+1]; - unsigned int tcp_port; -} clamd_address_container; +typedef struct clamd_address { + uschar * hostspec; + unsigned tcp_port; + unsigned retry; +} clamd_address; #ifndef nelements # define nelements(arr) (sizeof(arr) / sizeof(arr[0])) @@ -115,21 +113,23 @@ extern uschar spooled_message_id[17]; static inline int malware_errlog_defer(const uschar * str) { - log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str); - return DEFER; +log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str); +return DEFER; } static int -m_errlog_defer(struct scan * scanent, const uschar * str) +m_errlog_defer(struct scan * scanent, const uschar * hostport, + const uschar * str) { - return malware_errlog_defer(string_sprintf("%s: %s", scanent->name, str)); +return malware_errlog_defer(string_sprintf("%s %s : %s", + scanent->name, hostport ? hostport : CUS"", str)); } static int -m_errlog_defer_3(struct scan * scanent, const uschar * str, - int fd_to_close) +m_errlog_defer_3(struct scan * scanent, const uschar * hostport, + const uschar * str, int fd_to_close) { - (void) close(fd_to_close); - return m_errlog_defer(scanent, str); +(void) close(fd_to_close); +return m_errlog_defer(scanent, hostport, str); } /*************************************************/ @@ -141,111 +141,61 @@ static inline int m_tcpsocket(const uschar * hostname, unsigned int port, host_item * host, uschar ** errstr) { - return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr); -} - -static int -m_tcpsocket_fromdef(const uschar * hostport, uschar ** errstr) -{ - int scan; - uschar hostname[256]; - unsigned int portlow, porthigh; - - /* extract host and port part */ - scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh); - if ( scan != 3 ) { - if ( scan != 2 ) { - *errstr = string_sprintf("invalid socket '%s'", hostport); - return -1; - } - porthigh = portlow; - } - - return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh, - 5, NULL, errstr); -} - -static int -m_unixsocket(const uschar * path, uschar ** errstr) -{ - int sock; - struct sockaddr_un server; - - if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - *errstr = US"can't open UNIX socket."; - return -1; - } - - server.sun_family = AF_UNIX; - Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1); - server.sun_path[sizeof(server.sun_path)-1] = '\0'; - if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) { - int err = errno; - (void)close(sock); - *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s", - path, strerror(err)); - return -1; - } - return sock; -} - -static inline int -m_streamsocket(const uschar * spec, uschar ** errstr) -{ - return *spec == '/' - ? m_unixsocket(spec, errstr) : m_tcpsocket_fromdef(spec, errstr); +return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr); } static int m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr) { - if (send(sock, buf, cnt, 0) < 0) { - int err = errno; - (void)close(sock); - *errstr = string_sprintf("unable to send to socket (%s): %s", - buf, strerror(err)); - return -1; - } - return sock; +if (send(sock, buf, cnt, 0) < 0) + { + int err = errno; + (void)close(sock); + *errstr = string_sprintf("unable to send to socket (%s): %s", + buf, strerror(err)); + return -1; + } +return sock; } static const pcre * m_pcre_compile(const uschar * re, uschar ** errstr) { - const uschar * rerror; - int roffset; - const pcre * cre; - - cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL); - if (!cre) - *errstr= string_sprintf("regular expression error in '%s': %s at offset %d", - re, rerror, roffset); - return cre; +const uschar * rerror; +int roffset; +const pcre * cre; + +cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL); +if (!cre) + *errstr= string_sprintf("regular expression error in '%s': %s at offset %d", + re, rerror, roffset); +return cre; } uschar * m_pcre_exec(const pcre * cre, uschar * text) { - int ovector[10*3]; - int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0, - ovector, nelements(ovector)); - uschar * substr = NULL; - if (i >= 2) /* Got it */ - pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr); - return substr; +int ovector[10*3]; +int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0, + ovector, nelements(ovector)); +uschar * substr = NULL; +if (i >= 2) /* Got it */ + pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr); +return substr; } static const pcre * -m_pcre_nextinlist(uschar ** list, int * sep, char * listerr, uschar ** errstr) +m_pcre_nextinlist(const uschar ** list, int * sep, + char * listerr, uschar ** errstr) { - const uschar * list_ele; - const pcre * cre = NULL; +const uschar * list_ele; +const pcre * cre = NULL; - if (!(list_ele = string_nextinlist(list, sep, NULL, 0))) - *errstr = US listerr; - else - cre = m_pcre_compile(CUS list_ele, errstr); - return cre; +if (!(list_ele = string_nextinlist(list, sep, NULL, 0))) + *errstr = US listerr; +else + cre = m_pcre_compile(CUS list_ele, errstr); +return cre; } /* @@ -379,7 +329,7 @@ switch (*line) case 'A': /* ERR */ if ((p = strchr (line, '\n')) != NULL) *p = '\0'; - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, NULL, string_sprintf("scanner failed: %s", line)); default: /* VIR */ @@ -397,7 +347,7 @@ switch (*line) return OK; } } - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, NULL, string_sprintf("malformed reply received: %s", line)); } } @@ -426,6 +376,26 @@ if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0) return mksd_parse_line (scanent, CS av_buffer); } + +static int +clamd_option(clamd_address * cd, const uschar * optstr, int * subsep) +{ +uschar * s; + +cd->retry = 0; +while ((s = string_nextinlist(&optstr, subsep, NULL, 0))) + if (Ustrncmp(s, "retry=", 6) == 0) + { + int sec = readconf_readtime((s += 6), '\0', FALSE); + if (sec < 0) + return FAIL; + cd->retry = sec; + } + else + return FAIL; +return OK; +} + /************************************************* * Scan content for malware * *************************************************/ @@ -447,7 +417,7 @@ malware_internal(const uschar * malware_re, const uschar * eml_filename, int timeout, BOOL faking) { int sep = 0; -uschar *av_scanner_work = av_scanner; +const uschar *av_scanner_work = av_scanner; uschar *scanner_name; unsigned long mbox_size; FILE *mbox_file; @@ -518,7 +488,8 @@ if (!malware_ok) if (!timeout) timeout = MALWARE_TIMEOUT; tmo = time(NULL) + timeout; - for (scanent = m_scans; ; scanent++) { + for (scanent = m_scans; ; scanent++) + { if (!scanent->name) return malware_errlog_defer(string_sprintf("unknown scanner type '%s'", scanner_name)); @@ -530,13 +501,13 @@ if (!malware_ok) break; switch(scanent->conn) { - case MC_TCP: sock = m_tcpsocket_fromdef(scanner_options, &errstr); break; - case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr); break; - case MC_STRM: sock = m_streamsocket(scanner_options, &errstr); break; + case MC_TCP: sock = ip_tcpsocket(scanner_options, &errstr, 5); break; + case MC_UNIX: sock = ip_unixsocket(scanner_options, &errstr); break; + case MC_STRM: sock = ip_streamsocket(scanner_options, &errstr, 5); break; default: /* compiler quietening */ break; } if (sock < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); break; } DEBUG(D_acl) debug_printf("Malware scan: %s tmo %s\n", scanner_name, readconf_printtime(timeout)); @@ -567,7 +538,7 @@ if (!malware_ok) /* send scan request */ if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); while ((len = recv_line(sock, buf, sizeof(buf), tmo)) >= 0) if (len > 0) @@ -616,7 +587,7 @@ if (!malware_ok) { /* calc file size */ if ((drweb_fd = open(CCS eml_filename, O_RDONLY)) == -1) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("can't open spool file %s: %s", eml_filename, strerror(errno)), sock); @@ -625,7 +596,7 @@ if (!malware_ok) { int err = errno; (void)close(drweb_fd); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("can't seek spool file %s: %s", eml_filename, strerror(err)), sock); @@ -634,7 +605,7 @@ if (!malware_ok) if ((off_t)fsize_uint != fsize) { (void)close(drweb_fd); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("seeking spool file %s, size overflow", eml_filename), sock); @@ -652,7 +623,7 @@ if (!malware_ok) (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) { (void)close(drweb_fd); - return m_errlog_defer_3(scanent, string_sprintf( + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send commands to socket (%s)", scanner_options), sock); } @@ -660,7 +631,7 @@ if (!malware_ok) if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) { (void)close(drweb_fd); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("unable to allocate memory %u for file (%s)", fsize_uint, eml_filename), sock); @@ -671,7 +642,7 @@ if (!malware_ok) int err = errno; (void)close(drweb_fd); free(drweb_fbuf); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), sock); @@ -682,7 +653,7 @@ if (!malware_ok) if (send(sock, drweb_fbuf, fsize, 0) < 0) { free(drweb_fbuf); - return m_errlog_defer_3(scanent, string_sprintf( + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send file body to socket (%s)", scanner_options), sock); } @@ -701,19 +672,19 @@ if (!malware_ok) (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) || (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) || (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) - return m_errlog_defer_3(scanent, string_sprintf( + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send commands to socket (%s)", scanner_options), sock); } /* wait for result */ if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo)) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"unable to read return code", sock); drweb_rc = ntohl(drweb_rc); if (!recv_len(sock, &drweb_vnum, sizeof(drweb_vnum), tmo)) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"unable to read the number of viruses", sock); drweb_vnum = ntohl(drweb_vnum); @@ -735,14 +706,14 @@ if (!malware_ok) int size = 0, off = 0, ovector[10*3]; /* read the size of report */ if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo)) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"cannot read report size", sock); drweb_slen = ntohl(drweb_slen); tmpbuf = store_get(drweb_slen); /* read report body */ if (!recv_len(sock, tmpbuf, drweb_slen, tmo)) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"cannot read report string", sock); tmpbuf[drweb_slen] = '\0'; @@ -780,7 +751,7 @@ if (!malware_ok) * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR * and others are ignored */ if (drweb_s) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s), sock); @@ -800,7 +771,7 @@ if (!malware_ok) recv_line(sock, buf, sizeof(buf), tmo); if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ), sock); @@ -813,7 +784,7 @@ if (!malware_ok) DEBUG(D_acl) debug_printf("Malware scan: issuing %s %s\n", scanner_name, buf); if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); malware_name = NULL; result = 0; @@ -824,7 +795,7 @@ if (!malware_ok) break; if (buf[0] == '5') /* aveserver is having problems */ { - result = m_errlog_defer(scanent, + result = m_errlog_defer(scanent, CUS callout_address, string_sprintf("unable to scan file %s (Responded: %s).", eml_filename, buf)); break; @@ -838,14 +809,14 @@ if (!malware_ok) } if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); /* read aveserver's greeting and see if it is ready (2xx greeting) */ buf[0] = 0; recv_line(sock, buf, sizeof(buf), tmo); if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to quit dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") ), sock); @@ -878,12 +849,12 @@ if (!malware_ok) { if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); if (bread > 0) av_buffer[bread]='\0'; if (bread < 0) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to read answer %d (%s)", i, strerror(errno)), sock); for (j = 0; j < bread; j++) @@ -895,7 +866,7 @@ if (!malware_ok) file_name = string_sprintf("SCAN\t%s\n", eml_filename); if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); /* set up match */ /* todo also SUSPICION\t */ @@ -910,14 +881,14 @@ if (!malware_ok) for (;;) { - errno = ETIME; + errno = ETIMEDOUT; i = av_buffer+sizeof(av_buffer)-p; if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to read result (%s)", strerror(errno)), sock); - for (p[bread] = '\0'; q = strchr(p, '\n'); p = q+1) + for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1) { *q = '\0'; @@ -947,7 +918,8 @@ if (!malware_ok) uschar tmpbuf[1024]; uschar * scanrequest; int kav_rc; - unsigned long kav_reportlen, bread; + unsigned long kav_reportlen; + int bread; const pcre *kav_re; uschar *p; @@ -970,11 +942,11 @@ if (!malware_ok) /* send scan request */ if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); /* wait for result */ if (!recv_len(sock, tmpbuf, 2, tmo)) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"unable to read 2 bytes from socket.", sock); /* get errorcode from one nibble */ @@ -982,14 +954,14 @@ if (!malware_ok) switch(kav_rc) { case 5: case 6: /* improper kavdaemon configuration */ - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"please reconfigure kavdaemon to NOT disinfect or remove infected files.", sock); case 1: - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"reported 'scanning not completed' (code 1).", sock); case 7: - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"reported 'kavdaemon damaged' (code 7).", sock); } @@ -1011,7 +983,7 @@ if (!malware_ok) { /* read report size */ if (!recv_len(sock, &kav_reportlen, 4, tmo)) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, US"cannot read report size", sock); /* it's possible that avp returns av_buffer[1] == 1 but the @@ -1068,19 +1040,19 @@ if (!malware_ok) uschar *p; if (!cmdline_scanner) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, NULL, errstr); /* find scanner output trigger */ cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing trigger specification", &errstr); if (!cmdline_trigger_re) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, NULL, errstr); /* find scanner name regex */ cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing virus name regex specification", &errstr); if (!cmdline_regex_re) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, NULL, errstr); /* prepare scanner call; despite the naming, file_name holds a directory name which is documented as the value given to %s. */ @@ -1105,7 +1077,7 @@ if (!malware_ok) { int err = errno; signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, NULL, string_sprintf("call (%s) failed: %s.", commandline, strerror(err))); } scanner_fd = fileno(scanner_out); @@ -1118,7 +1090,7 @@ if (!malware_ok) int err = errno; (void) pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, string_sprintf( + return m_errlog_defer(scanent, NULL, string_sprintf( "opening scanner output file (%s) failed: %s.", file_name, strerror(err))); } @@ -1128,24 +1100,23 @@ if (!malware_ok) sizeof(linebuffer), tmo))) { if (rcnt < 0) + { + int err = errno; if (rcnt == -1) break; - else - { - int err = errno; - (void) pclose(scanner_out); - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, string_sprintf( - "unable to read from scanner (%s): %s", - commandline, strerror(err))); - } + (void) pclose(scanner_out); + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + return m_errlog_defer(scanent, NULL, string_sprintf( + "unable to read from scanner (%s): %s", + commandline, strerror(err))); + } if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record)) { /* short write */ (void) pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, string_sprintf( + return m_errlog_defer(scanent, NULL, string_sprintf( "short write on scanner output file (%s).", file_name)); } putc('\n', scanner_record); @@ -1160,7 +1131,7 @@ if (!malware_ok) sep = pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); if (sep != 0) - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, NULL, sep == -1 ? string_sprintf("running scanner failed: %s", strerror(sep)) : string_sprintf("scanner returned error code: %d", sep)); @@ -1204,14 +1175,14 @@ if (!malware_ok) if ( write(sock, file_name, Ustrlen(file_name)) < 0 || write(sock, "\n", 1) != 1 ) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to write to UNIX socket (%s)", scanner_options), sock); /* wait for result */ memset(av_buffer, 0, sizeof(av_buffer)); if ((bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to read from UNIX socket (%s)", scanner_options), sock); @@ -1223,7 +1194,8 @@ if (!malware_ok) malware_name = string_copy(&av_buffer[2]); } else if (!strncmp(CS av_buffer, "-1", 2)) - return m_errlog_defer_3(scanent, US"scanner reported error", sock); + return m_errlog_defer_3(scanent, CUS callout_address, + US"scanner reported error", sock); else /* all ok, no virus */ malware_name = NULL; @@ -1244,7 +1216,7 @@ if (!malware_ok) * WITH_OLD_CLAMAV_STREAM is defined. * See Exim bug 926 for details. */ - uschar *p, *vname, *result_tag, *response_end; + uschar *p, *vname, *result_tag; int bread=0; uschar * file_name; uschar av_buffer[1024]; @@ -1255,8 +1227,7 @@ if (!malware_ok) off_t fsize; unsigned int fsize_uint; BOOL use_scan_command = FALSE; - clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS]; - int current_server; + clamd_address * cv[MAX_CLAMD_SERVERS]; int num_servers = 0; #ifdef WITH_OLD_CLAMAV_STREAM unsigned int port; @@ -1266,67 +1237,100 @@ if (!malware_ok) uint32_t send_size, send_final_zeroblock; #endif + /*XXX if unixdomain socket, only one server supported. Needs fixing; + there's no reason we should not mix local and remote servers */ + if (*scanner_options == '/') + { + clamd_address * cd; + const uschar * sublist; + int subsep = ' '; + /* Local file; so we def want to use_scan_command and don't want to try * passing IP/port combinations */ use_scan_command = TRUE; + cd = (clamd_address *) store_get(sizeof(clamd_address)); + + /* extract socket-path part */ + sublist = scanner_options; + cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0); + + /* parse options */ + if (clamd_option(cd, sublist, &subsep) != OK) + return m_errlog_defer(scanent, NULL, + string_sprintf("bad option '%s'", scanner_options)); + cv[0] = cd; + } else { - const uschar *address = scanner_options; - uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20]; - /* Go through the rest of the list of host/port and construct an array * of servers to try. The first one is the bit we just passed from * scanner_options so process that first and then scan the remainder of * the address buffer */ do { - clamd_address_container *this_clamd; + clamd_address * cd; + const uschar * sublist; + int subsep = ' '; + uschar * s; /* The 'local' option means use the SCAN command over the network * socket (ie common file storage in use) */ - if (strcmpic(address,US"local") == 0) + /*XXX we could accept this also as a local option? */ + if (strcmpic(scanner_options, US"local") == 0) { use_scan_command = TRUE; continue; } - /* XXX: If unsuccessful we should free this memory */ - this_clamd = - (clamd_address_container *)store_get(sizeof(clamd_address_container)); + cd = (clamd_address *) store_get(sizeof(clamd_address)); /* extract host and port part */ - if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", - this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) + sublist = scanner_options; + if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0))) + { + (void) m_errlog_defer(scanent, NULL, + string_sprintf("missing address: '%s'", scanner_options)); + continue; + } + if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0))) + { + (void) m_errlog_defer(scanent, NULL, + string_sprintf("missing port: '%s'", scanner_options)); + continue; + } + cd->tcp_port = atoi(CS s); + + /* parse options */ + /*XXX should these options be common over scanner types? */ + if (clamd_option(cd, sublist, &subsep) != OK) { - (void) m_errlog_defer(scanent, - string_sprintf("invalid address '%s'", address)); + return m_errlog_defer(scanent, NULL, + string_sprintf("bad option '%s'", scanner_options)); continue; } - clamd_address_vector[num_servers] = this_clamd; - num_servers++; + cv[num_servers++] = cd; if (num_servers >= MAX_CLAMD_SERVERS) { - (void) m_errlog_defer(scanent, + (void) m_errlog_defer(scanent, NULL, US"More than " MAX_CLAMD_SERVERS_S " clamd servers " "specified; only using the first " MAX_CLAMD_SERVERS_S ); break; } - } while ((address = string_nextinlist(&av_scanner_work, &sep, - address_buffer, - sizeof(address_buffer))) != NULL); + } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep, + NULL, 0))); /* check if we have at least one server */ if (!num_servers) - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, NULL, US"no useable server addresses in malware configuration option."); } /* See the discussion of response formats below to see why we really don't like colons in filenames when passing filenames to ClamAV. */ if (use_scan_command && Ustrchr(eml_filename, ':')) - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, NULL, string_sprintf("local/SCAN mode incompatible with" \ " : in path to email filename [%s]", eml_filename)); @@ -1339,43 +1343,52 @@ if (!malware_ok) while (num_servers > 0) { - int i; - /* Randomly pick a server to start with */ - current_server = random_number( num_servers ); + int i = random_number( num_servers ); + clamd_address * cd = cv[i]; - DEBUG(D_acl) - debug_printf("trying server name %s, port %u\n", - clamd_address_vector[current_server]->tcp_addr, - clamd_address_vector[current_server]->tcp_port); + DEBUG(D_acl) debug_printf("trying server name %s, port %u\n", + cd->hostspec, cd->tcp_port); /* Lookup the host. This is to ensure that we connect to the same IP * on both connections (as one host could resolve to multiple ips) */ - sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr, - clamd_address_vector[current_server]->tcp_port, - &connhost, &errstr); - if (sock >= 0) + for (;;) { - /* Connection successfully established with a server */ - hostname = clamd_address_vector[current_server]->tcp_addr; - break; + sock= m_tcpsocket(cd->hostspec, cd->tcp_port, &connhost, &errstr); + if (sock >= 0) + { + /* Connection successfully established with a server */ + hostname = cd->hostspec; + break; + } + if (cd->retry <= 0) break; + while (cd->retry > 0) cd->retry = sleep(cd->retry); } + if (sock >= 0) + break; - (void) m_errlog_defer(scanent, errstr); + (void) m_errlog_defer(scanent, CUS callout_address, errstr); /* Remove the server from the list. XXX We should free the memory */ num_servers--; - for (i = current_server; i < num_servers; i++) - clamd_address_vector[i] = clamd_address_vector[i+1]; + for (; i < num_servers; i++) + cv[i] = cv[i+1]; } if (num_servers == 0) - return m_errlog_defer(scanent, US"all servers failed"); + return m_errlog_defer(scanent, NULL, US"all servers failed"); } else - { - if ((sock = m_unixsocket(scanner_options, &errstr)) < 0) - return m_errlog_defer(scanent, errstr); - } + for (;;) + { + if ((sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0) + { + hostname = cv[0]->hostspec; + break; + } + if (cv[0]->retry <= 0) + return m_errlog_defer(scanent, CUS callout_address, errstr); + while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry); + } /* have socket in variable "sock"; command to use is semi-independent of * the socket protocol. We use SCAN if is local (either Unix/local @@ -1395,33 +1408,35 @@ if (!malware_ok) /* Pass the string to ClamAV (7 = "STREAM\n") */ if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); memset(av_buffer2, 0, sizeof(av_buffer2)); bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL)); if (bread < 0) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to read PORT from socket (%s)", strerror(errno)), sock); if (bread == sizeof(av_buffer2)) - return m_errlog_defer_3(scanent, "buffer too small", sock); + return m_errlog_defer_3(scanent, CUS callout_address, + "buffer too small", sock); if (!(*av_buffer2)) - return m_errlog_defer_3(scanent, "ClamAV returned null", sock); + return m_errlog_defer_3(scanent, CUS callout_address, + "ClamAV returned null", sock); av_buffer2[bread] = '\0'; if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("Expected port information from clamd, got '%s'", av_buffer2), sock); sockData = m_tcpsocket(connhost.address, port, NULL, &errstr); if (sockData < 0) - return m_errlog_defer_3(scanent, errstr, sock); + return m_errlog_defer_3(scanent, CUS callout_address, errstr, sock); # define CLOSE_SOCKDATA (void)close(sockData) #else /* WITH_OLD_CLAMAV_STREAM not defined */ @@ -1435,7 +1450,7 @@ if (!malware_ok) /* Pass the string to ClamAV (10 = "zINSTREAM\0") */ if (send(sock, "zINSTREAM", 10, 0) < 0) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS hostname, string_sprintf("unable to send zINSTREAM to socket (%s)", strerror(errno)), sock); @@ -1448,7 +1463,7 @@ if (!malware_ok) { int err = errno; CLOSE_SOCKDATA; - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("can't open spool file %s: %s", eml_filename, strerror(err)), sock); @@ -1457,7 +1472,7 @@ if (!malware_ok) { int err = errno; CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("can't seek spool file %s: %s", eml_filename, strerror(err)), sock); @@ -1466,7 +1481,7 @@ if (!malware_ok) if ((off_t)fsize_uint != fsize) { CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("seeking spool file %s, size overflow", eml_filename), sock); @@ -1476,7 +1491,7 @@ if (!malware_ok) if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) { CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("unable to allocate memory %u for file (%s)", fsize_uint, eml_filename), sock); @@ -1486,7 +1501,7 @@ if (!malware_ok) { int err = errno; free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), sock); @@ -1498,7 +1513,7 @@ if (!malware_ok) if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) { free(clamav_fbuf); CLOSE_SOCKDATA; - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("unable to send file body to socket (%s:%u)", hostname, port), sock); @@ -1511,7 +1526,7 @@ if (!malware_ok) (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) { free(clamav_fbuf); - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, string_sprintf("unable to send file body to socket (%s)", hostname), sock); } @@ -1545,7 +1560,7 @@ if (!malware_ok) scanner_name, scanner_options); if (send(sock, file_name, Ustrlen(file_name), 0) < 0) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to write to socket (%s)", strerror(errno)), sock); @@ -1562,12 +1577,13 @@ if (!malware_ok) sock = -1; if (bread <= 0) - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, CUS callout_address, string_sprintf("unable to read from socket (%s)", errno == 0 ? "EOF" : strerror(errno))); if (bread == sizeof(av_buffer)) - return m_errlog_defer(scanent, US"buffer too small"); + return m_errlog_defer(scanent, CUS callout_address, + US"buffer too small"); /* We're now assured of a NULL at the end of av_buffer */ /* Check the result. ClamAV returns one of two result formats. @@ -1591,7 +1607,8 @@ if (!malware_ok) passing a filename to clamd). */ if (!(*av_buffer)) - return m_errlog_defer(scanent, US"ClamAV returned null"); + return m_errlog_defer(scanent, CUS callout_address, + US"ClamAV returned null"); /* strip newline at the end (won't be present for zINSTREAM) (also any trailing whitespace, which shouldn't exist, but we depend upon @@ -1604,11 +1621,10 @@ if (!malware_ok) while (isspace(*--p) && (p > av_buffer)) *p = '\0'; if (*p) ++p; - response_end = p; /* colon in returned output? */ - if((p = Ustrchr(av_buffer,':')) == NULL) - return m_errlog_defer(scanent, string_sprintf( + if(!(p = Ustrchr(av_buffer,':'))) + return m_errlog_defer(scanent, CUS callout_address, string_sprintf( "ClamAV returned malformed result (missing colon): %s", av_buffer)); @@ -1641,7 +1657,7 @@ if (!malware_ok) } else if (Ustrcmp(result_tag, "ERROR") == 0) - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, CUS callout_address, string_sprintf("ClamAV returned: %s", av_buffer)); else if (Ustrcmp(result_tag, "OK") == 0) @@ -1652,7 +1668,7 @@ if (!malware_ok) } else - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, CUS callout_address, string_sprintf("unparseable response from ClamAV: {%s}", av_buffer)); break; @@ -1679,7 +1695,7 @@ if (!malware_ok) uschar * s = Ustrchr(sockline_scanner, '%'); if (s++) if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%')) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, NULL, US"unsafe sock scanner call spec", sock); } else @@ -1689,13 +1705,13 @@ if (!malware_ok) sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing trigger specification", &errstr); if (!sockline_trig_re) - return m_errlog_defer_3(scanent, errstr, sock); + return m_errlog_defer_3(scanent, NULL, errstr, sock); /* find virus name regex */ sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing virus name regex specification", &errstr); if (!sockline_name_re) - return m_errlog_defer_3(scanent, errstr, sock); + return m_errlog_defer_3(scanent, NULL, errstr, sock); /* prepare scanner call - security depends on expansions check above */ commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id); @@ -1704,18 +1720,19 @@ if (!malware_ok) /* Pass the command string to the socket */ if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + return m_errlog_defer(scanent, CUS callout_address, errstr); /* Read the result */ bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); if (bread <= 0) - return m_errlog_defer_3(scanent, + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf("unable to read from socket (%s)", strerror(errno)), sock); if (bread == sizeof(av_buffer)) - return m_errlog_defer_3(scanent, US"buffer too small", sock); + return m_errlog_defer_3(scanent, CUS callout_address, + US"buffer too small", sock); av_buffer[bread] = '\0'; linebuffer = string_copy(av_buffer); @@ -1744,12 +1761,12 @@ if (!malware_ok) || mksd_maxproc < 1 || mksd_maxproc > 32 ) - return m_errlog_defer(scanent, + return m_errlog_defer(scanent, CUS callout_address, string_sprintf("invalid option '%s'", scanner_options)); } - if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0) - return m_errlog_defer(scanent, errstr); + if((sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0) + return m_errlog_defer(scanent, CUS callout_address, errstr); malware_name = NULL; @@ -1793,7 +1810,7 @@ if (!malware_ok) ) { int slen = Ustrlen(buf); - if (slen >= 1) + if (slen >= 1) { DEBUG(D_acl) debug_printf("got from avast: %s\n", buf); switch (avast_stage) @@ -1831,7 +1848,7 @@ if (!malware_ok) if (send(sock, scanrequest, len, 0) < 0) { scanrequest[len-1] = '\0'; - return m_errlog_defer_3(scanent, string_sprintf( + return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send request '%s' to socket (%s): %s", scanrequest, scanner_options, strerror(errno)), sock); } @@ -1849,18 +1866,19 @@ if (!malware_ok) if ((malware_name = m_pcre_exec(ava_re_virus, buf))) { /* remove backslash in front of [whitespace|backslash] */ uschar * p, * p0; - for (p = malware_name; *p; ++p) + for (p = malware_name; *p; ++p) if (*p == '\\' && (isspace(p[1]) || p[1] == '\\')) for (p0 = p; *p0; ++p0) *p0 = p0[1]; - + avast_stage = AVA_DONE; goto endloop; } - if (Ustrncmp(buf, "200 SCAN OK", 11) == 0) + if (Ustrncmp(buf, "200 SCAN OK", 11) == 0) { /* we're done finally */ if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */ - return m_errlog_defer_3(scanent, string_sprintf( + return m_errlog_defer_3(scanent, CUS callout_address, + string_sprintf( "unable to send quit request to socket (%s): %s", scanner_options, strerror(errno)), sock); @@ -1871,6 +1889,9 @@ if (!malware_ok) /* here for any unexpected response from the scanner */ goto endloop; + + case AVA_DONE: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen", + __FILE__, __LINE__, __FUNCTION__); } } } @@ -1878,9 +1899,9 @@ if (!malware_ok) switch(avast_stage) { - case AVA_HELO: + case AVA_HELO: case AVA_OPT: - case AVA_RSP: return m_errlog_defer_3(scanent, + case AVA_RSP: return m_errlog_defer_3(scanent, CUS callout_address, nread >= 0 ? string_sprintf( "invalid response from scanner: '%s'", buf) diff --git a/src/src/match.c b/src/src/match.c index 97a098205..fa42187dd 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for matching strings */ @@ -15,8 +15,8 @@ strings, domains, and local parts. */ typedef struct check_string_block { - uschar *origsubject; /* caseful; keep these two first, in */ - uschar *subject; /* step with the block below */ + const uschar *origsubject; /* caseful; keep these two first, in */ + const uschar *subject; /* step with the block below */ int expand_setup; BOOL use_partial; BOOL caseless; @@ -28,7 +28,7 @@ typedef struct check_string_block { addresses. */ typedef struct check_address_block { - uschar *origaddress; /* caseful; keep these two first, in */ + const uschar *origaddress; /* caseful; keep these two first, in */ uschar *address; /* step with the block above */ int expand_setup; BOOL caseless; @@ -92,12 +92,12 @@ Returns: OK if matched */ static int -check_string(void *arg, uschar *pattern, uschar **valueptr, uschar **error) +check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) { -check_string_block *cb = (check_string_block *)arg; +const check_string_block *cb = arg; int search_type, partial, affixlen, starflags; int expand_setup = cb->expand_setup; -uschar *affix; +const uschar *affix; uschar *s; uschar *filename = NULL; uschar *keyquery, *result, *semicolon; @@ -111,7 +111,7 @@ if (valueptr != NULL) *valueptr = NULL; /* For non-lookup matches */ it works if the pattern uses (?-i) to turn off case-independence, overriding "caseless". */ -s = (pattern[0] == '^')? cb->origsubject : cb->subject; +s = string_copy(pattern[0] == '^' ? cb->origsubject : cb->subject); /* If required to set up $0, initialize the data but don't turn on by setting expand_nmax until the match is assured. */ @@ -131,7 +131,7 @@ if (pattern[0] == '^') { const pcre *re = regex_must_compile(pattern, cb->caseless, FALSE); return ((expand_setup < 0)? - pcre_exec(re, NULL, CS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) >= 0 + pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) >= 0 : regex_match_and_setup(re, s, 0, expand_setup) )? @@ -192,8 +192,8 @@ if (cb->at_is_special && pattern[0] == '@') BOOL prim = FALSE; BOOL secy = FALSE; BOOL removed = FALSE; - uschar *ss = pattern + 4; - uschar *ignore_target_hosts = NULL; + const uschar *ss = pattern + 4; + const uschar *ignore_target_hosts = NULL; if (strncmpic(ss, US"any", 3) == 0) ss += 3; else if (strncmpic(ss, US"primary", 7) == 0) @@ -221,8 +221,7 @@ if (cb->at_is_special && pattern[0] == '@') NULL, /* service name not relevant */ NULL, /* srv_fail_domains not relevant */ NULL, /* mx_fail_domains not relevant */ - NULL, /* no dnssec request XXX ? */ - NULL, /* no dnssec require XXX ? */ + NULL, /* no dnssec request/require XXX ? */ NULL, /* no feedback FQDN */ &removed); /* feedback if local removed */ @@ -337,8 +336,8 @@ Returns: OK if matched */ int -match_check_string(uschar *s, uschar *pattern, int expand_setup, - BOOL use_partial, BOOL caseless, BOOL at_is_special, uschar **valueptr) +match_check_string(const uschar *s, const uschar *pattern, int expand_setup, + BOOL use_partial, BOOL caseless, BOOL at_is_special, const uschar **valueptr) { check_string_block cb; cb.origsubject = s; @@ -366,7 +365,7 @@ Arguments: type MCL_STRING, MCL_DOMAIN, MCL_HOST, MCL_ADDRESS, or MCL_LOCALPART */ -static uschar * +static const uschar * get_check_key(void *arg, int type) { switch(type) @@ -436,9 +435,9 @@ Returns: OK if matched a non-negated item */ int -match_check_list(uschar **listptr, int sep, tree_node **anchorptr, - unsigned int **cache_ptr, int (*func)(void *,uschar *,uschar **,uschar **), - void *arg, int type, uschar *name, uschar **valueptr) +match_check_list(const uschar **listptr, int sep, tree_node **anchorptr, + unsigned int **cache_ptr, int (*func)(void *,const uschar *,const uschar **,uschar **), + void *arg, int type, const uschar *name, const uschar **valueptr) { int yield = OK; unsigned int *original_cache_bits = *cache_ptr; @@ -446,7 +445,7 @@ BOOL include_unknown = FALSE; BOOL ignore_unknown = FALSE; BOOL include_defer = FALSE; BOOL ignore_defer = FALSE; -uschar *list; +const uschar *list; uschar *sss; uschar *ot = NULL; uschar buffer[1024]; @@ -489,12 +488,12 @@ else if (type == MCL_DOMAIN && deliver_domain == NULL) { check_string_block *cb = (check_string_block *)arg; - deliver_domain = cb->subject; - list = expand_string(*listptr); + deliver_domain = string_copy(cb->subject); + list = expand_cstring(*listptr); deliver_domain = NULL; } - else list = expand_string(*listptr); + else list = expand_cstring(*listptr); if (list == NULL) { @@ -701,7 +700,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) cached = US" - cached"; if (valueptr != NULL) { - uschar *key = get_check_key(arg, type); + const uschar *key = get_check_key(arg, type); namedlist_cacheblock *p; for (p = nb->cache_data; p != NULL; p = p->next) { @@ -740,7 +739,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) case DEFER: if (error == NULL) - error = string_sprintf("DNS lookup of %s deferred", ss); + error = string_sprintf("DNS lookup of \"%s\" deferred", ss); if (ignore_defer) { HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", @@ -752,6 +751,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); return OK; } + if (!search_error_message) search_error_message = error; goto DEFER_RETURN; /* The ERROR return occurs when checking hosts, when either a forward @@ -771,7 +771,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) include_unknown? "yes":"no", error); if (!include_unknown) { - if ((log_extra_selector & LX_unknown_in_list) != 0) + if (LOGGING(unknown_in_list)) log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); return FAIL; } @@ -880,7 +880,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) (void)fclose(f); if (!include_unknown) { - if ((log_extra_selector & LX_unknown_in_list) != 0) + if (LOGGING(unknown_in_list)) log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); return FAIL; } @@ -952,8 +952,9 @@ Returns: OK if matched a non-negated item */ int -match_isinlist(uschar *s, uschar **listptr, int sep, tree_node **anchorptr, - unsigned int *cache_bits, int type, BOOL caseless, uschar **valueptr) +match_isinlist(const uschar *s, const uschar **listptr, int sep, + tree_node **anchorptr, + unsigned int *cache_bits, int type, BOOL caseless, const uschar **valueptr) { unsigned int *local_cache_bits = cache_bits; check_string_block cb; @@ -999,16 +1000,17 @@ Returns: OK for a match */ static int -check_address(void *arg, uschar *pattern, uschar **valueptr, uschar **error) +check_address(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) { check_address_block *cb = (check_address_block *)arg; check_string_block csb; int rc; int expand_inc = 0; unsigned int *null = NULL; -uschar *listptr; +const uschar *listptr; uschar *subject = cb->address; -uschar *s, *pdomain, *sdomain; +const uschar *s; +uschar *pdomain, *sdomain; error = error; /* Keep clever compilers from complaining */ @@ -1070,7 +1072,8 @@ looked up to obtain a list of local parts. If the subject's local part is just if (pattern[0] == '@' && pattern[1] == '@') { int watchdog = 50; - uschar *list, *key, *ss; + const uschar *key; + uschar *list, *ss; uschar buffer[1024]; if (sdomain == subject + 1 && *subject == '*') return FAIL; @@ -1083,7 +1086,7 @@ if (pattern[0] == '@' && pattern[1] == '@') int sep = 0; if ((rc = match_check_string(key, pattern + 2, -1, TRUE, FALSE, FALSE, - &list)) != OK) return rc; + CUSS &list)) != OK) return rc; /* Check for chaining from the last item; set up the next key if one is found. */ @@ -1102,8 +1105,7 @@ if (pattern[0] == '@' && pattern[1] == '@') /* Look up the local parts provided by the list; negation is permitted. If a local part has to begin with !, a regex can be used. */ - while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) - != NULL) + while ((ss = string_nextinlist(CUSS &list, &sep, buffer, sizeof(buffer)))) { int local_yield; @@ -1278,9 +1280,9 @@ Returns: OK for a positive match, or end list after a negation; */ int -match_address_list(uschar *address, BOOL caseless, BOOL expand, - uschar **listptr, unsigned int *cache_bits, int expand_setup, int sep, - uschar **valueptr) +match_address_list(const uschar *address, BOOL caseless, BOOL expand, + const uschar **listptr, unsigned int *cache_bits, int expand_setup, int sep, + const uschar **valueptr) { uschar *p; check_address_block ab; diff --git a/src/src/memcheck.h b/src/src/memcheck.h index bf95491b9..6f97b09af 100644 --- a/src/src/memcheck.h +++ b/src/src/memcheck.h @@ -22,16 +22,16 @@ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. The origin of this software must not be misrepresented; you must - not claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 4. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS @@ -53,7 +53,7 @@ the terms of the GNU General Public License, version 2. See the COPYING file in the source distribution for details. - ---------------------------------------------------------------- + ---------------------------------------------------------------- */ @@ -71,12 +71,12 @@ #include "valgrind.h" -/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! This enum comprises an ABI exported by Valgrind to programs which use client requests. DO NOT CHANGE THE ORDER OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end. */ typedef - enum { + enum { VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'), VG_USERREQ__MAKE_MEM_UNDEFINED, VG_USERREQ__MAKE_MEM_DEFINED, @@ -97,7 +97,7 @@ typedef VG_USERREQ__COUNT_LEAK_BLOCKS, /* This is just for memcheck's internal use - don't use it */ - _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR + _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR = VG_USERREQ_TOOL_BASE('M','C') + 256 } Vg_MemCheckClientRequest; @@ -110,7 +110,7 @@ typedef VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__MAKE_MEM_NOACCESS, \ (_qzz_addr), (_qzz_len), 0, 0, 0) - + /* Similarly, mark memory at _qzz_addr as addressable but undefined for _qzz_len bytes. */ #define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \ diff --git a/src/src/mime.c b/src/src/mime.c index ab701f2a6..cc9ffb7c6 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 */ +/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004, 2015 */ /* License: GPL */ #include "exim.h" @@ -21,7 +21,7 @@ uschar *mime_current_boundary = NULL; give info on detected "problems" in MIME encodings. Those are defined in mime.h. */ -void +static void mime_set_anomaly(int level, const char *text) { mime_anomaly_level = level; @@ -41,7 +41,7 @@ mime_set_anomaly(int level, const char *text) 0-255 - char to write */ -uschar * +static uschar * mime_decode_qp_char(uschar *qp_p, int *c) { uschar *initial_pos = qp_p; @@ -240,7 +240,7 @@ return size; } -FILE * +static FILE * mime_get_decode_file(uschar *pname, uschar *fname) { FILE *f = NULL; @@ -283,10 +283,10 @@ return f; int -mime_decode(uschar **listptr) +mime_decode(const uschar **listptr) { int sep = 0; -uschar *list = *listptr; +const uschar *list = *listptr; uschar *option; uschar option_buffer[1024]; uschar decode_path[1024]; @@ -369,7 +369,8 @@ mime_content_size = (size_counter + 1023) / 1024; return OK; } -int + +static int mime_get_header(FILE *f, uschar *header) { int c = EOF; @@ -474,21 +475,111 @@ else } +static void +mime_vars_reset(void) +{ +mime_anomaly_level = 0; +mime_anomaly_text = NULL; +mime_boundary = NULL; +mime_charset = NULL; +mime_decoded_filename = NULL; +mime_filename = NULL; +mime_content_description = NULL; +mime_content_disposition = NULL; +mime_content_id = NULL; +mime_content_transfer_encoding = NULL; +mime_content_type = NULL; +mime_is_multipart = 0; +mime_content_size = 0; +} + + +/* Grab a parameter value, dealing with quoting. + +Arguments: + str Input string. Updated on return to point to terminating ; or NUL + +Return: + Allocated string with parameter value +*/ +static uschar * +mime_param_val(uschar ** sp) +{ +uschar * s = *sp; +uschar * val = NULL; +int size = 0, ptr = 0; + +/* debug_printf(" considering paramval '%s'\n", s); */ + +while (*s && *s != ';') /* ; terminates */ + if (*s == '"') + { + s++; /* skip opening " */ + while (*s && *s != '"') /* " protects ; */ + val = string_cat(val, &size, &ptr, s++, 1); + if (*s) s++; /* skip closing " */ + } + else + val = string_cat(val, &size, &ptr, s++, 1); +if (val) val[ptr] = '\0'; +*sp = s; +return val; +} + +static uschar * +mime_next_semicolon(uschar * s) +{ +while (*s && *s != ';') /* ; terminates */ + if (*s == '"') + { + s++; /* skip opening " */ + while (*s && *s != '"') /* " protects ; */ + s++; + if (*s) s++; /* skip closing " */ + } + else + s++; +return s; +} + + +static uschar * +rfc2231_to_2047(const uschar * fname, const uschar * charset, int * len) +{ +int size = 0, ptr = 0; +uschar * val = string_cat(NULL, &size, &ptr, US"=?", 2); +uschar c; + +if (charset) + val = string_cat(val, &size, &ptr, charset, Ustrlen(charset)); +val = string_cat(val, &size, &ptr, US"?Q?", 3); + +while ((c = *fname)) + if (c == '%' && isxdigit(fname[1]) && isxdigit(fname[2])) + { + val = string_cat(val, &size, &ptr, US"=", 1); + val = string_cat(val, &size, &ptr, ++fname, 2); + fname += 2; + } + else + val = string_cat(val, &size, &ptr, fname++, 1); + +val = string_cat(val, &size, &ptr, US"?=", 2); +val[*len = ptr] = '\0'; +return val; +} + + int mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *context, - uschar **user_msgptr, uschar **log_msgptr) + uschar **user_msgptr, uschar **log_msgptr) { int rc = OK; -uschar *header = NULL; +uschar * header = NULL; struct mime_boundary_context nested_context; /* reserve a line buffer to work in */ -if (!(header = (uschar *)malloc(MIME_MAX_HEADER_SIZE+1))) - { - log_write(0, LOG_PANIC, - "MIME ACL: can't allocate %d bytes of memory.", MIME_MAX_HEADER_SIZE+1); - return DEFER; - } +header = store_get(MIME_MAX_HEADER_SIZE+1); /* Not actually used at the moment, but will be vital to fixing * some RFC 2046 nonconformance later... */ @@ -498,26 +589,12 @@ nested_context.parent = context; while(1) { /* reset all per-part mime variables */ - mime_anomaly_level = 0; - mime_anomaly_text = NULL; - mime_boundary = NULL; - mime_charset = NULL; - mime_decoded_filename = NULL; - mime_filename = NULL; - mime_content_description = NULL; - mime_content_disposition = NULL; - mime_content_id = NULL; - mime_content_transfer_encoding = NULL; - mime_content_type = NULL; - mime_is_multipart = 0; - mime_content_size = 0; - - /* - If boundary is null, we assume that *f is positioned on the start of headers (for example, - at the very beginning of a message. - If a boundary is given, we must first advance to it to reach the start of the next header - block. - */ + mime_vars_reset(); + + /* If boundary is null, we assume that *f is positioned on the start of + headers (for example, at the very beginning of a message. If a boundary is + given, we must first advance to it to reach the start of the next header + block. */ /* NOTE -- there's an error here -- RFC2046 specifically says to * check for outer boundaries. This code doesn't do that, and @@ -526,128 +603,188 @@ while(1) * (I have moved partway towards adding support, however, by adding * a "parent" field to my new boundary-context structure.) */ - if (context != NULL) + if (context) for (;;) { - while(fgets(CS header, MIME_MAX_HEADER_SIZE, f) != NULL) + if (!fgets(CS header, MIME_MAX_HEADER_SIZE, f)) { - /* boundary line must start with 2 dashes */ - if (Ustrncmp(header,"--",2) == 0) - { - if (Ustrncmp((header+2),context->boundary,Ustrlen(context->boundary)) == 0) - { - /* found boundary */ - if (Ustrncmp((header+2+Ustrlen(context->boundary)),"--",2) == 0) - { - /* END boundary found */ - debug_printf("End boundary found %s\n", context->boundary); - return rc; - } - else - debug_printf("Next part with boundary %s\n", context->boundary); + /* Hit EOF or read error. Ugh. */ + DEBUG(D_acl) debug_printf("MIME: Hit EOF ...\n"); + return rc; + } - /* can't use break here */ - goto DECODE_HEADERS; - } + /* boundary line must start with 2 dashes */ + if ( Ustrncmp(header, "--", 2) == 0 + && Ustrncmp(header+2, context->boundary, Ustrlen(context->boundary)) == 0 + ) + { /* found boundary */ + if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0) + { + /* END boundary found */ + DEBUG(D_acl) debug_printf("MIME: End boundary found %s\n", + context->boundary); + return rc; } + + DEBUG(D_acl) debug_printf("MIME: Next part with boundary %s\n", + context->boundary); + break; } - /* Hit EOF or read error. Ugh. */ - debug_printf("Hit EOF ...\n"); - return rc; } -DECODE_HEADERS: /* parse headers, set up expansion variables */ - while (mime_get_header(f,header)) + while (mime_get_header(f, header)) { - int i; - /* loop through header list */ - for (i = 0; i < mime_header_list_size; i++) + struct mime_header * mh; + + /* look for interesting headers */ + for (mh = mime_header_list; + mh < mime_header_list + mime_header_list_size; + mh++) if (strncmpic(mh->name, header, mh->namelen) == 0) { - uschar *header_value = NULL; - int header_value_len = 0; + uschar * p = header + mh->namelen; + uschar * q; + + /* grab the value (normalize to lower case) + and copy to its corresponding expansion variable */ + + for (q = p; *q != ';' && *q; q++) ; + *mh->value = string_copynlc(p, q-p); + DEBUG(D_acl) debug_printf("MIME: found %s header, value is '%s'\n", + mh->name, *mh->value); + + if (*(p = q)) p++; /* jump past the ; */ - /* found an interesting header? */ - if (strncmpic(mime_header_list[i].name,header,mime_header_list[i].namelen) == 0) { - uschar *p = header + mime_header_list[i].namelen; - /* yes, grab the value (normalize to lower case) - and copy to its corresponding expansion variable */ - while(*p != ';') - { - *p = tolower(*p); - p++; - } - header_value_len = (p - (header + mime_header_list[i].namelen)); - header_value = (uschar *)malloc(header_value_len+1); - memset(header_value,0,header_value_len+1); - p = header + mime_header_list[i].namelen; - Ustrncpy(header_value, p, header_value_len); - debug_printf("Found %s MIME header, value is '%s'\n", mime_header_list[i].name, header_value); - *((uschar **)(mime_header_list[i].value)) = header_value; - - /* make p point to the next character after the closing ';' */ - p += (header_value_len+1); - - /* grab all param=value tags on the remaining line, check if they are interesting */ -NEXT_PARAM_SEARCH: - while (*p != 0) + uschar * mime_fname = NULL; + uschar * mime_fname_rfc2231 = NULL; + uschar * mime_filename_charset = NULL; + BOOL decoding_failed = FALSE; + + /* grab all param=value tags on the remaining line, + check if they are interesting */ + + while (*p) { mime_parameter * mp; - for (mp = mime_parameter_list; - mp < &mime_parameter_list[mime_parameter_list_size]; - mp++) - { - uschar *param_value = NULL; - int param_value_len = 0; - - /* found an interesting parameter? */ - if (strncmpic(mp->name, p, mp->namelen) == 0) + + DEBUG(D_acl) debug_printf("MIME: considering paramlist '%s'\n", p); + + if ( !mime_filename + && strncmpic(CUS"content-disposition:", header, 20) == 0 + && strncmpic(CUS"filename*", p, 9) == 0 + ) + { /* RFC 2231 filename */ + uschar * q; + + /* find value of the filename */ + p += 9; + while(*p != '=' && *p) p++; + if (*p) p++; /* p is filename or NUL */ + q = mime_param_val(&p); /* p now trailing ; or NUL */ + + if (q && *q) { - uschar *q = p + mp->namelen; - int size = 0; - int ptr = 0; + uschar * temp_string, * err_msg; + int slen; + + /* build up an un-decoded filename over successive + filename*= parameters (for use when 2047 decode fails) */ - /* yes, grab the value and copy to its corresponding expansion variable */ - while(*q && *q != ';') /* ; terminates */ + mime_fname_rfc2231 = string_sprintf("%#s%s", + mime_fname_rfc2231, q); + + if (!decoding_failed) { - if (*q == '"') + int size; + if (!mime_filename_charset) { - q++; /* skip leading " */ - while(*q && *q != '"') /* which protects ; */ - param_value = string_cat(param_value, &size, &ptr, q++, 1); - if (*q) q++; /* skip trailing " */ + uschar * s = q; + + /* look for a ' in the "filename" */ + while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ + + if ((size = s-q) > 0) + mime_filename_charset = string_copyn(q, size); + + if (*(p = s)) p++; + while(*p == '\'') p++; /* p is after 2nd ' */ } else - param_value = string_cat(param_value, &size, &ptr, q++, 1); - } - if (param_value) - { - param_value[ptr++] = '\0'; - param_value_len = ptr; - - param_value = rfc2047_decode(param_value, - check_rfc2047_length, NULL, 32, ¶m_value_len, &q); - debug_printf("Found %s MIME parameter in %s header, " - "value is '%s'\n", mp->name, mime_header_list[i].name, - param_value); + p = q; + + DEBUG(D_acl) debug_printf("MIME: charset %s fname '%s'\n", + mime_filename_charset ? mime_filename_charset : US"<NULL>", p); + + temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen); + DEBUG(D_acl) debug_printf("MIME: 2047-name %s\n", temp_string); + + temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ', + NULL, &err_msg); + DEBUG(D_acl) debug_printf("MIME: plain-name %s\n", temp_string); + + size = Ustrlen(temp_string); + + if (size == slen) + decoding_failed = TRUE; + else + /* build up a decoded filename over successive + filename*= parameters */ + + mime_filename = mime_fname = mime_fname + ? string_sprintf("%s%s", mime_fname, temp_string) + : temp_string; } - *mp->value = param_value; - p += (mp->namelen + param_value_len + 1); - goto NEXT_PARAM_SEARCH; + } } - } + + else + /* look for interesting parameters */ + for (mp = mime_parameter_list; + mp < mime_parameter_list + nelem(mime_parameter_list); + mp++ + ) if (strncmpic(mp->name, p, mp->namelen) == 0) + { + uschar * q; + uschar * dummy_errstr; + + /* grab the value and copy to its expansion variable */ + p += mp->namelen; + q = mime_param_val(&p); /* p now trailing ; or NUL */ + + *mp->value = q && *q + ? rfc2047_decode(q, check_rfc2047_length, NULL, 32, NULL, + &dummy_errstr) + : NULL; + DEBUG(D_acl) debug_printf( + "MIME: found %s parameter in %s header, value '%s'\n", + mp->name, mh->name, *mp->value); + + break; /* done matching param names */ + } + + /* There is something, but not one of our interesting parameters. - Advance to the next semicolon */ - while(*p != ';') p++; - p++; + Advance past the next semicolon */ + p = mime_next_semicolon(p); + if (*p) p++; + } /* param scan on line */ + + if (strncmpic(CUS"content-disposition:", header, 20) == 0) + { + if (decoding_failed) mime_filename = mime_fname_rfc2231; + + DEBUG(D_acl) debug_printf( + "MIME: found %s parameter in %s header, value is '%s'\n", + "filename", mh->name, mime_filename); + } } } } - } /* set additional flag variables (easier access) */ - if ( (mime_content_type != NULL) && - (Ustrncmp(mime_content_type,"multipart",9) == 0) ) + if ( mime_content_type + && Ustrncmp(mime_content_type,"multipart",9) == 0 + ) mime_is_multipart = 1; /* Make a copy of the boundary pointer. @@ -678,7 +815,9 @@ NEXT_PARAM_SEARCH: (nested_context.boundary != NULL) && (Ustrncmp(mime_content_type,"multipart",9) == 0) ) { - debug_printf("Entering multipart recursion, boundary '%s'\n", nested_context.boundary); + DEBUG(D_acl) + debug_printf("MIME: Entering multipart recursion, boundary '%s'\n", + nested_context.boundary); nested_context.context = context && context->context == MBC_ATTACHMENT @@ -694,7 +833,7 @@ NEXT_PARAM_SEARCH: else if ( (mime_content_type != NULL) && (Ustrncmp(mime_content_type,"message/rfc822",14) == 0) ) { - uschar *rfc822name = NULL; + const uschar *rfc822name = NULL; uschar filename[2048]; int file_nr = 0; int result = 0; @@ -724,19 +863,22 @@ NEXT_PARAM_SEARCH: { log_write(0, LOG_MAIN, "mime_regex acl condition warning - could not decode RFC822 MIME part to file."); - return DEFER; + rc = DEFER; + goto out; } mime_decoded_filename = NULL; } NO_RFC822: /* If the boundary of this instance is NULL, we are finished here */ - if (context == NULL) break; + if (!context) break; if (context->context == MBC_COVERLETTER_ONESHOT) context->context = MBC_ATTACHMENT; } +out: +mime_vars_reset(); return rc; } diff --git a/src/src/mime.h b/src/src/mime.h index af09f677d..f2b9cf65d 100644 --- a/src/src/mime.h +++ b/src/src/mime.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 */ +/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004, 2015 */ /* License: GPL */ #ifdef WITH_CONTENT_SCAN @@ -22,17 +22,17 @@ struct mime_boundary_context }; typedef struct mime_header { - uschar *name; - int namelen; - void *value; + uschar * name; + int namelen; + uschar ** value; } mime_header; static mime_header mime_header_list[] = { - { US"content-type:", 13, &mime_content_type }, - { US"content-disposition:", 20, &mime_content_disposition }, + { US"content-type:", 13, &mime_content_type }, + { US"content-disposition:", 20, &mime_content_disposition }, { US"content-transfer-encoding:", 26, &mime_content_transfer_encoding }, - { US"content-id:", 11, &mime_content_id }, - { US"content-description:", 20 , &mime_content_description } + { US"content-id:", 11, &mime_content_id }, + { US"content-description:", 20, &mime_content_description } }; static int mime_header_list_size = sizeof(mime_header_list)/sizeof(mime_header); @@ -48,12 +48,10 @@ typedef struct mime_parameter { static mime_parameter mime_parameter_list[] = { { US"name=", 5, &mime_filename }, { US"filename=", 9, &mime_filename }, - { US"charset=", 8, &mime_charset }, + { US"charset=", 8, &mime_charset }, { US"boundary=", 9, &mime_boundary } }; -static int mime_parameter_list_size = sizeof(mime_parameter_list)/sizeof(mime_parameter); - /* MIME Anomaly list */ #define MIME_ANOMALY_BROKEN_BASE64 2, "Broken BASE64 encoding detected" diff --git a/src/src/moan.c b/src/src/moan.c index 4d7b51b9a..e54117c34 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for sending messages to sender or to mailmaster. */ @@ -598,7 +598,7 @@ uschar * moan_check_errorcopy(uschar *recipient) { uschar *item, *localpart, *domain; -uschar *listptr = errors_copy; +const uschar *listptr = errors_copy; uschar *yield = NULL; uschar buffer[256]; int sep = 0; @@ -619,8 +619,8 @@ llen = domain++ - recipient; while ((item = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) != NULL) { - uschar *newaddress = item; - uschar *pattern = string_dequote(&newaddress); + const uschar *newaddress = item; + const uschar *pattern = string_dequote(&newaddress); /* If no new address found, just skip this item. */ diff --git a/src/src/mytypes.h b/src/src/mytypes.h index 964abf820..4d367a95b 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -66,6 +66,7 @@ almost always literal strings. */ #define US (unsigned char *) #define CUS (const unsigned char *) #define USS (unsigned char **) +#define CUSS (const unsigned char **) /* The C library string functions expect "char *" arguments. Use macros to avoid having to write a cast each time. We do this for string and file diff --git a/src/src/parse.c b/src/src/parse.c index 94e42c182..d3f382b96 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for parsing addresses */ @@ -550,9 +550,7 @@ read_addr_spec(uschar *s, uschar *t, int term, uschar **errorptr, { s = read_local_part(s, t, errorptr, FALSE); if (*errorptr == NULL) - { if (*s != term) - { if (*s != '@') *errorptr = string_sprintf("\"@\" or \".\" expected after \"%s\"", t); else @@ -562,8 +560,6 @@ if (*errorptr == NULL) *domainptr = t; s = read_domain(s, t, errorptr); } - } - } return s; } @@ -817,7 +813,7 @@ if (*end - *start > ADDRESS_MAXLENGTH) return NULL; } -return (uschar *)yield; +return yield; /* Use goto (via the macro FAILED) to get to here from a variety of places. We might have an empty address in a group - the caller can choose to ignore @@ -866,11 +862,11 @@ Returns: pointer to the original string, if no quoting needed, or the introduction */ -uschar * -parse_quote_2047(uschar *string, int len, uschar *charset, uschar *buffer, +const uschar * +parse_quote_2047(const uschar *string, int len, uschar *charset, uschar *buffer, int buffer_size, BOOL fold) { -uschar *s = string; +const uschar *s = string; uschar *p, *t; int hlen; BOOL coded = FALSE; @@ -910,7 +906,7 @@ for (; len > 0; len--) { *t++ = '_'; first_byte = FALSE; - } + } else { sprintf(CS t, "=%02X", ch); @@ -985,12 +981,13 @@ Arguments: Returns: the fixed RFC822 phrase */ -uschar * -parse_fix_phrase(uschar *phrase, int len, uschar *buffer, int buffer_size) +const uschar * +parse_fix_phrase(const uschar *phrase, int len, uschar *buffer, int buffer_size) { int ch, i; BOOL quoted = FALSE; -uschar *s, *t, *end, *yield; +const uschar *s, *end; +uschar *t, *yield; while (len > 0 && isspace(*phrase)) { phrase++; len--; } if (len > buffer_size/4) return US"Name too long"; @@ -1119,7 +1116,7 @@ while (s < end) else if (ch == '(') { - uschar *ss = s; /* uschar after '(' */ + const uschar *ss = s; /* uschar after '(' */ int level = 1; while(ss < end) { @@ -1245,7 +1242,7 @@ Returns: FF_DELIVERED addresses extracted int parse_forward_list(uschar *s, int options, address_item **anchor, - uschar **error, uschar *incoming_domain, uschar *directory, + uschar **error, const uschar *incoming_domain, uschar *directory, error_block **syntax_errors) { int count = 0; diff --git a/src/src/pdkim/Makefile b/src/src/pdkim/Makefile index 610c011d9..9f3dc7945 100644 --- a/src/src/pdkim/Makefile +++ b/src/src/pdkim/Makefile @@ -1,6 +1,6 @@ # Make file for building the pdkim library. -OBJ = base64.o bignum.o pdkim.o rsa.o sha1.o sha2.o +OBJ = base64.o bignum.o pdkim.o rsa.o sha1.o sha2.o part-x509parse.o pdkim-rsa.o pdkim.a: $(OBJ) @$(RM_COMMAND) -f pdkim.a @@ -10,11 +10,14 @@ pdkim.a: $(OBJ) .SUFFIXES: .o .c .c.o:; @echo "$(CC) $*.c" - $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) $*.c + $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -I. $*.c base64.o: $(HDRS) base64.c bignum.o: $(HDRS) bignum.c -pdkim.o: $(HDRS) pdkim.c +part-x509parse.o: $(HDRS) part-x509parse.c +pdkim.o: $(HDRS) pdkim.h pdkim-rsa.h pdkim.c +pdkim-rsa.o: $(HDRS) polarssl/private-x509parse_c.h polarssl/base64.h \ + pdkim-rsa.h pdkim-rsa.c rsa.o: $(HDRS) rsa.c sha1.o: $(HDRS) sha1.c sha2.o: $(HDRS) sha2.c diff --git a/src/src/pdkim/base64.c b/src/src/pdkim/base64.c index a82fc2d75..ae3c6b9e5 100644 --- a/src/src/pdkim/base64.c +++ b/src/src/pdkim/base64.c @@ -23,7 +23,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "base64.h" +#include "polarssl/config.h" + +#if defined(POLARSSL_BASE64_C) + +#include "polarssl/base64.h" static const unsigned char base64_enc_map[64] = { @@ -128,20 +132,22 @@ int base64_decode( unsigned char *dst, int *dlen, for( i = j = n = 0; i < slen; i++ ) { + unsigned char c = src[i]; + if( ( slen - i ) >= 2 && - src[i] == '\r' && src[i + 1] == '\n' ) + c == '\r' && src[i + 1] == '\n' ) continue; - if( src[i] == '\n' ) + if( c == '\n' || c == ' ' || c == '\t' ) continue; - if( src[i] == '=' && ++j > 2 ) + if( c == '=' && ++j > 2 ) return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); - if( src[i] > 127 || base64_dec_map[src[i]] == 127 ) + if( c > 127 || base64_dec_map[src[i]] == 127 ) return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); - if( base64_dec_map[src[i]] < 64 && j != 0 ) + if( base64_dec_map[c] < 64 && j != 0 ) return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); n++; @@ -160,11 +166,13 @@ int base64_decode( unsigned char *dst, int *dlen, for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ) { - if( *src == '\r' || *src == '\n' ) + unsigned char c = *src; + + if( c == '\r' || c == '\n' || c == ' ' || c == '\t' ) continue; - j -= ( base64_dec_map[*src] == 64 ); - x = (x << 6) | ( base64_dec_map[*src] & 0x3F ); + j -= ( base64_dec_map[c] == 64 ); + x = (x << 6) | ( base64_dec_map[c] & 0x3F ); if( ++n == 4 ) { @@ -179,3 +187,72 @@ int base64_decode( unsigned char *dst, int *dlen, return( 0 ); } + +#if defined(POLARSSL_SELF_TEST) + +#include <string.h> +#include <stdio.h> + +static const unsigned char base64_test_dec[64] = +{ + 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, + 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, + 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, + 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, + 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, + 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, + 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, + 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 +}; + +static const unsigned char base64_test_enc[] = + "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" + "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; + +/* + * Checkup routine + */ +int base64_self_test( int verbose ) +{ + int len; + unsigned char *src, buffer[128]; + + if( verbose != 0 ) + printf( " Base64 encoding test: " ); + + len = sizeof( buffer ); + src = (unsigned char *) base64_test_dec; + + if( base64_encode( buffer, &len, src, 64 ) != 0 || + memcmp( base64_test_enc, buffer, 88 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n Base64 decoding test: " ); + + len = sizeof( buffer ); + src = (unsigned char *) base64_test_enc; + + if( base64_decode( buffer, &len, src, 88 ) != 0 || + memcmp( base64_test_dec, buffer, 64 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n\n" ); + + return( 0 ); +} + +#endif + +#endif diff --git a/src/src/pdkim/bignum.c b/src/src/pdkim/bignum.c index 692fe822a..b55c2b8ff 100644 --- a/src/src/pdkim/bignum.c +++ b/src/src/pdkim/bignum.c @@ -30,8 +30,12 @@ * http://math.libtomcrypt.com/files/tommath.pdf */ -#include "bignum.h" -#include "bn_mul.h" +#include "polarssl/config.h" + +#if defined(POLARSSL_BIGNUM_C) + +#include "polarssl/bignum.h" +#include "polarssl/bn_mul.h" #include <string.h> #include <stdlib.h> @@ -1863,3 +1867,172 @@ cleanup: } #endif + +#if defined(POLARSSL_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mpi_self_test( int verbose ) +{ + int ret, i; + mpi A, E, N, X, Y, U, V; + + mpi_init( &A, &E, &N, &X, &Y, &U, &V, NULL ); + + MPI_CHK( mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MPI_CHK( mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MPI_CHK( mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MPI_CHK( mpi_mul_mpi( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if( verbose != 0 ) + printf( " MPI test #1 (mul_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_div_mpi( &X, &Y, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MPI_CHK( mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if( verbose != 0 ) + printf( " MPI test #2 (div_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 || + mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if( verbose != 0 ) + printf( " MPI test #3 (exp_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_inv_mod( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if( verbose != 0 ) + printf( " MPI test #4 (inv_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + if( verbose != 0 ) + printf( " MPI test #5 (simple gcd): " ); + + for ( i = 0; i < GCD_PAIR_COUNT; i++) + { + MPI_CHK( mpi_lset( &X, gcd_pairs[i][0] ) ); + MPI_CHK( mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MPI_CHK( mpi_gcd( &A, &X, &Y ) ); + + if( mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if( verbose != 0 ) + printf( "failed at %d\n", i ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + +cleanup: + + if( ret != 0 && verbose != 0 ) + printf( "Unexpected error, return code = %08X\n", ret ); + + mpi_free( &V, &U, &Y, &X, &N, &E, &A, NULL ); + + if( verbose != 0 ) + printf( "\n" ); + + return( ret ); +} + +#endif + +#endif diff --git a/src/src/pdkim/bn_mul.h b/src/src/pdkim/bn_mul.h deleted file mode 100644 index b3c09f059..000000000 --- a/src/src/pdkim/bn_mul.h +++ /dev/null @@ -1,735 +0,0 @@ -/** - * \file bn_mul.h - * - * Copyright (C) 2006-2010, Brainspark B.V. - * - * This file is part of PolarSSL (http://www.polarssl.org) - * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org> - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/* - * Multiply source vector [s] with b, add result - * to destination vector [d] and set carry c. - * - * Currently supports: - * - * . IA-32 (386+) . AMD64 / EM64T - * . IA-32 (SSE2) . Motorola 68000 - * . PowerPC, 32-bit . MicroBlaze - * . PowerPC, 64-bit . TriCore - * . SPARC v8 . ARM v3+ - * . Alpha . MIPS32 - * . C, longlong . C, generic - */ - -#ifndef POLARSSL_BN_MUL_H -#define POLARSSL_BN_MUL_H - -#if defined(POLARSSL_HAVE_ASM) - -#if defined(__GNUC__) -#if defined(__i386__) - -#define MULADDC_INIT \ - asm( " \ - movl %%ebx, %0; \ - movl %5, %%esi; \ - movl %6, %%edi; \ - movl %7, %%ecx; \ - movl %8, %%ebx; \ - " - -#define MULADDC_CORE \ - " \ - lodsl; \ - mull %%ebx; \ - addl %%ecx, %%eax; \ - adcl $0, %%edx; \ - addl (%%edi), %%eax; \ - adcl $0, %%edx; \ - movl %%edx, %%ecx; \ - stosl; \ - " - -#if defined(POLARSSL_HAVE_SSE2) - -#define MULADDC_HUIT \ - " \ - movd %%ecx, %%mm1; \ - movd %%ebx, %%mm0; \ - movd (%%edi), %%mm3; \ - paddq %%mm3, %%mm1; \ - movd (%%esi), %%mm2; \ - pmuludq %%mm0, %%mm2; \ - movd 4(%%esi), %%mm4; \ - pmuludq %%mm0, %%mm4; \ - movd 8(%%esi), %%mm6; \ - pmuludq %%mm0, %%mm6; \ - movd 12(%%esi), %%mm7; \ - pmuludq %%mm0, %%mm7; \ - paddq %%mm2, %%mm1; \ - movd 4(%%edi), %%mm3; \ - paddq %%mm4, %%mm3; \ - movd 8(%%edi), %%mm5; \ - paddq %%mm6, %%mm5; \ - movd 12(%%edi), %%mm4; \ - paddq %%mm4, %%mm7; \ - movd %%mm1, (%%edi); \ - movd 16(%%esi), %%mm2; \ - pmuludq %%mm0, %%mm2; \ - psrlq $32, %%mm1; \ - movd 20(%%esi), %%mm4; \ - pmuludq %%mm0, %%mm4; \ - paddq %%mm3, %%mm1; \ - movd 24(%%esi), %%mm6; \ - pmuludq %%mm0, %%mm6; \ - movd %%mm1, 4(%%edi); \ - psrlq $32, %%mm1; \ - movd 28(%%esi), %%mm3; \ - pmuludq %%mm0, %%mm3; \ - paddq %%mm5, %%mm1; \ - movd 16(%%edi), %%mm5; \ - paddq %%mm5, %%mm2; \ - movd %%mm1, 8(%%edi); \ - psrlq $32, %%mm1; \ - paddq %%mm7, %%mm1; \ - movd 20(%%edi), %%mm5; \ - paddq %%mm5, %%mm4; \ - movd %%mm1, 12(%%edi); \ - psrlq $32, %%mm1; \ - paddq %%mm2, %%mm1; \ - movd 24(%%edi), %%mm5; \ - paddq %%mm5, %%mm6; \ - movd %%mm1, 16(%%edi); \ - psrlq $32, %%mm1; \ - paddq %%mm4, %%mm1; \ - movd 28(%%edi), %%mm5; \ - paddq %%mm5, %%mm3; \ - movd %%mm1, 20(%%edi); \ - psrlq $32, %%mm1; \ - paddq %%mm6, %%mm1; \ - movd %%mm1, 24(%%edi); \ - psrlq $32, %%mm1; \ - paddq %%mm3, %%mm1; \ - movd %%mm1, 28(%%edi); \ - addl $32, %%edi; \ - addl $32, %%esi; \ - psrlq $32, %%mm1; \ - movd %%mm1, %%ecx; \ - " - -#define MULADDC_STOP \ - " \ - emms; \ - movl %4, %%ebx; \ - movl %%ecx, %1; \ - movl %%edi, %2; \ - movl %%esi, %3; \ - " \ - : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ - : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ - : "eax", "ecx", "edx", "esi", "edi" \ - ); - -#else - -#define MULADDC_STOP \ - " \ - movl %4, %%ebx; \ - movl %%ecx, %1; \ - movl %%edi, %2; \ - movl %%esi, %3; \ - " \ - : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ - : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ - : "eax", "ecx", "edx", "esi", "edi" \ - ); -#endif /* SSE2 */ -#endif /* i386 */ - -#if defined(__amd64__) || defined (__x86_64__) - -#define MULADDC_INIT \ - asm( "movq %0, %%rsi " :: "m" (s)); \ - asm( "movq %0, %%rdi " :: "m" (d)); \ - asm( "movq %0, %%rcx " :: "m" (c)); \ - asm( "movq %0, %%rbx " :: "m" (b)); \ - asm( "xorq %r8, %r8 " ); - -#define MULADDC_CORE \ - asm( "movq (%rsi),%rax " ); \ - asm( "mulq %rbx " ); \ - asm( "addq $8, %rsi " ); \ - asm( "addq %rcx, %rax " ); \ - asm( "movq %r8, %rcx " ); \ - asm( "adcq $0, %rdx " ); \ - asm( "nop " ); \ - asm( "addq %rax, (%rdi) " ); \ - asm( "adcq %rdx, %rcx " ); \ - asm( "addq $8, %rdi " ); - -#define MULADDC_STOP \ - asm( "movq %%rcx, %0 " : "=m" (c)); \ - asm( "movq %%rdi, %0 " : "=m" (d)); \ - asm( "movq %%rsi, %0 " : "=m" (s) :: \ - "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); - -#endif /* AMD64 */ - -#if defined(__mc68020__) || defined(__mcpu32__) - -#define MULADDC_INIT \ - asm( "movl %0, %%a2 " :: "m" (s)); \ - asm( "movl %0, %%a3 " :: "m" (d)); \ - asm( "movl %0, %%d3 " :: "m" (c)); \ - asm( "movl %0, %%d2 " :: "m" (b)); \ - asm( "moveq #0, %d0 " ); - -#define MULADDC_CORE \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d4:%d1 " ); \ - asm( "addl %d3, %d1 " ); \ - asm( "addxl %d0, %d4 " ); \ - asm( "moveq #0, %d3 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "addxl %d4, %d3 " ); - -#define MULADDC_STOP \ - asm( "movl %%d3, %0 " : "=m" (c)); \ - asm( "movl %%a3, %0 " : "=m" (d)); \ - asm( "movl %%a2, %0 " : "=m" (s) :: \ - "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); - -#define MULADDC_HUIT \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d4:%d1 " ); \ - asm( "addxl %d3, %d1 " ); \ - asm( "addxl %d0, %d4 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d3:%d1 " ); \ - asm( "addxl %d4, %d1 " ); \ - asm( "addxl %d0, %d3 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d4:%d1 " ); \ - asm( "addxl %d3, %d1 " ); \ - asm( "addxl %d0, %d4 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d3:%d1 " ); \ - asm( "addxl %d4, %d1 " ); \ - asm( "addxl %d0, %d3 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d4:%d1 " ); \ - asm( "addxl %d3, %d1 " ); \ - asm( "addxl %d0, %d4 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d3:%d1 " ); \ - asm( "addxl %d4, %d1 " ); \ - asm( "addxl %d0, %d3 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d4:%d1 " ); \ - asm( "addxl %d3, %d1 " ); \ - asm( "addxl %d0, %d4 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "movel %a2@+, %d1 " ); \ - asm( "mulul %d2, %d3:%d1 " ); \ - asm( "addxl %d4, %d1 " ); \ - asm( "addxl %d0, %d3 " ); \ - asm( "addl %d1, %a3@+ " ); \ - asm( "addxl %d0, %d3 " ); - -#endif /* MC68000 */ - -#if defined(__powerpc__) || defined(__ppc__) -#if defined(__powerpc64__) || defined(__ppc64__) - -#if defined(__MACH__) && defined(__APPLE__) - -#define MULADDC_INIT \ - asm( "ld r3, %0 " :: "m" (s)); \ - asm( "ld r4, %0 " :: "m" (d)); \ - asm( "ld r5, %0 " :: "m" (c)); \ - asm( "ld r6, %0 " :: "m" (b)); \ - asm( "addi r3, r3, -8 " ); \ - asm( "addi r4, r4, -8 " ); \ - asm( "addic r5, r5, 0 " ); - -#define MULADDC_CORE \ - asm( "ldu r7, 8(r3) " ); \ - asm( "mulld r8, r7, r6 " ); \ - asm( "mulhdu r9, r7, r6 " ); \ - asm( "adde r8, r8, r5 " ); \ - asm( "ld r7, 8(r4) " ); \ - asm( "addze r5, r9 " ); \ - asm( "addc r8, r8, r7 " ); \ - asm( "stdu r8, 8(r4) " ); - -#define MULADDC_STOP \ - asm( "addze r5, r5 " ); \ - asm( "addi r4, r4, 8 " ); \ - asm( "addi r3, r3, 8 " ); \ - asm( "std r5, %0 " : "=m" (c)); \ - asm( "std r4, %0 " : "=m" (d)); \ - asm( "std r3, %0 " : "=m" (s) :: \ - "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); - -#else - -#define MULADDC_INIT \ - asm( "ld %%r3, %0 " :: "m" (s)); \ - asm( "ld %%r4, %0 " :: "m" (d)); \ - asm( "ld %%r5, %0 " :: "m" (c)); \ - asm( "ld %%r6, %0 " :: "m" (b)); \ - asm( "addi %r3, %r3, -8 " ); \ - asm( "addi %r4, %r4, -8 " ); \ - asm( "addic %r5, %r5, 0 " ); - -#define MULADDC_CORE \ - asm( "ldu %r7, 8(%r3) " ); \ - asm( "mulld %r8, %r7, %r6 " ); \ - asm( "mulhdu %r9, %r7, %r6 " ); \ - asm( "adde %r8, %r8, %r5 " ); \ - asm( "ld %r7, 8(%r4) " ); \ - asm( "addze %r5, %r9 " ); \ - asm( "addc %r8, %r8, %r7 " ); \ - asm( "stdu %r8, 8(%r4) " ); - -#define MULADDC_STOP \ - asm( "addze %r5, %r5 " ); \ - asm( "addi %r4, %r4, 8 " ); \ - asm( "addi %r3, %r3, 8 " ); \ - asm( "std %%r5, %0 " : "=m" (c)); \ - asm( "std %%r4, %0 " : "=m" (d)); \ - asm( "std %%r3, %0 " : "=m" (s) :: \ - "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); - -#endif - -#else /* PPC32 */ - -#if defined(__MACH__) && defined(__APPLE__) - -#define MULADDC_INIT \ - asm( "lwz r3, %0 " :: "m" (s)); \ - asm( "lwz r4, %0 " :: "m" (d)); \ - asm( "lwz r5, %0 " :: "m" (c)); \ - asm( "lwz r6, %0 " :: "m" (b)); \ - asm( "addi r3, r3, -4 " ); \ - asm( "addi r4, r4, -4 " ); \ - asm( "addic r5, r5, 0 " ); - -#define MULADDC_CORE \ - asm( "lwzu r7, 4(r3) " ); \ - asm( "mullw r8, r7, r6 " ); \ - asm( "mulhwu r9, r7, r6 " ); \ - asm( "adde r8, r8, r5 " ); \ - asm( "lwz r7, 4(r4) " ); \ - asm( "addze r5, r9 " ); \ - asm( "addc r8, r8, r7 " ); \ - asm( "stwu r8, 4(r4) " ); - -#define MULADDC_STOP \ - asm( "addze r5, r5 " ); \ - asm( "addi r4, r4, 4 " ); \ - asm( "addi r3, r3, 4 " ); \ - asm( "stw r5, %0 " : "=m" (c)); \ - asm( "stw r4, %0 " : "=m" (d)); \ - asm( "stw r3, %0 " : "=m" (s) :: \ - "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); - -#else - -#define MULADDC_INIT \ - asm( "lwz %%r3, %0 " :: "m" (s)); \ - asm( "lwz %%r4, %0 " :: "m" (d)); \ - asm( "lwz %%r5, %0 " :: "m" (c)); \ - asm( "lwz %%r6, %0 " :: "m" (b)); \ - asm( "addi %r3, %r3, -4 " ); \ - asm( "addi %r4, %r4, -4 " ); \ - asm( "addic %r5, %r5, 0 " ); - -#define MULADDC_CORE \ - asm( "lwzu %r7, 4(%r3) " ); \ - asm( "mullw %r8, %r7, %r6 " ); \ - asm( "mulhwu %r9, %r7, %r6 " ); \ - asm( "adde %r8, %r8, %r5 " ); \ - asm( "lwz %r7, 4(%r4) " ); \ - asm( "addze %r5, %r9 " ); \ - asm( "addc %r8, %r8, %r7 " ); \ - asm( "stwu %r8, 4(%r4) " ); - -#define MULADDC_STOP \ - asm( "addze %r5, %r5 " ); \ - asm( "addi %r4, %r4, 4 " ); \ - asm( "addi %r3, %r3, 4 " ); \ - asm( "stw %%r5, %0 " : "=m" (c)); \ - asm( "stw %%r4, %0 " : "=m" (d)); \ - asm( "stw %%r3, %0 " : "=m" (s) :: \ - "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); - -#endif - -#endif /* PPC32 */ -#endif /* PPC64 */ - -#if defined(__sparc__) - -#define MULADDC_INIT \ - asm( "ld %0, %%o0 " :: "m" (s)); \ - asm( "ld %0, %%o1 " :: "m" (d)); \ - asm( "ld %0, %%o2 " :: "m" (c)); \ - asm( "ld %0, %%o3 " :: "m" (b)); - -#define MULADDC_CORE \ - asm( "ld [%o0], %o4 " ); \ - asm( "inc 4, %o0 " ); \ - asm( "ld [%o1], %o5 " ); \ - asm( "umul %o3, %o4, %o4 " ); \ - asm( "addcc %o4, %o2, %o4 " ); \ - asm( "rd %y, %g1 " ); \ - asm( "addx %g1, 0, %g1 " ); \ - asm( "addcc %o4, %o5, %o4 " ); \ - asm( "st %o4, [%o1] " ); \ - asm( "addx %g1, 0, %o2 " ); \ - asm( "inc 4, %o1 " ); - -#define MULADDC_STOP \ - asm( "st %%o2, %0 " : "=m" (c)); \ - asm( "st %%o1, %0 " : "=m" (d)); \ - asm( "st %%o0, %0 " : "=m" (s) :: \ - "g1", "o0", "o1", "o2", "o3", "o4", "o5" ); - -#endif /* SPARCv8 */ - -#if defined(__microblaze__) || defined(microblaze) - -#define MULADDC_INIT \ - asm( "lwi r3, %0 " :: "m" (s)); \ - asm( "lwi r4, %0 " :: "m" (d)); \ - asm( "lwi r5, %0 " :: "m" (c)); \ - asm( "lwi r6, %0 " :: "m" (b)); \ - asm( "andi r7, r6, 0xffff" ); \ - asm( "bsrli r6, r6, 16 " ); - -#define MULADDC_CORE \ - asm( "lhui r8, r3, 0 " ); \ - asm( "addi r3, r3, 2 " ); \ - asm( "lhui r9, r3, 0 " ); \ - asm( "addi r3, r3, 2 " ); \ - asm( "mul r10, r9, r6 " ); \ - asm( "mul r11, r8, r7 " ); \ - asm( "mul r12, r9, r7 " ); \ - asm( "mul r13, r8, r6 " ); \ - asm( "bsrli r8, r10, 16 " ); \ - asm( "bsrli r9, r11, 16 " ); \ - asm( "add r13, r13, r8 " ); \ - asm( "add r13, r13, r9 " ); \ - asm( "bslli r10, r10, 16 " ); \ - asm( "bslli r11, r11, 16 " ); \ - asm( "add r12, r12, r10 " ); \ - asm( "addc r13, r13, r0 " ); \ - asm( "add r12, r12, r11 " ); \ - asm( "addc r13, r13, r0 " ); \ - asm( "lwi r10, r4, 0 " ); \ - asm( "add r12, r12, r10 " ); \ - asm( "addc r13, r13, r0 " ); \ - asm( "add r12, r12, r5 " ); \ - asm( "addc r5, r13, r0 " ); \ - asm( "swi r12, r4, 0 " ); \ - asm( "addi r4, r4, 4 " ); - -#define MULADDC_STOP \ - asm( "swi r5, %0 " : "=m" (c)); \ - asm( "swi r4, %0 " : "=m" (d)); \ - asm( "swi r3, %0 " : "=m" (s) :: \ - "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ - "r9", "r10", "r11", "r12", "r13" ); - -#endif /* MicroBlaze */ - -#if defined(__tricore__) - -#define MULADDC_INIT \ - asm( "ld.a %%a2, %0 " :: "m" (s)); \ - asm( "ld.a %%a3, %0 " :: "m" (d)); \ - asm( "ld.w %%d4, %0 " :: "m" (c)); \ - asm( "ld.w %%d1, %0 " :: "m" (b)); \ - asm( "xor %d5, %d5 " ); - -#define MULADDC_CORE \ - asm( "ld.w %d0, [%a2+] " ); \ - asm( "madd.u %e2, %e4, %d0, %d1 " ); \ - asm( "ld.w %d0, [%a3] " ); \ - asm( "addx %d2, %d2, %d0 " ); \ - asm( "addc %d3, %d3, 0 " ); \ - asm( "mov %d4, %d3 " ); \ - asm( "st.w [%a3+], %d2 " ); - -#define MULADDC_STOP \ - asm( "st.w %0, %%d4 " : "=m" (c)); \ - asm( "st.a %0, %%a3 " : "=m" (d)); \ - asm( "st.a %0, %%a2 " : "=m" (s) :: \ - "d0", "d1", "e2", "d4", "a2", "a3" ); - -#endif /* TriCore */ - -#if defined(__arm__) - -#define MULADDC_INIT \ - asm( "ldr r0, %0 " :: "m" (s)); \ - asm( "ldr r1, %0 " :: "m" (d)); \ - asm( "ldr r2, %0 " :: "m" (c)); \ - asm( "ldr r3, %0 " :: "m" (b)); - -#define MULADDC_CORE \ - asm( "ldr r4, [r0], #4 " ); \ - asm( "mov r5, #0 " ); \ - asm( "ldr r6, [r1] " ); \ - asm( "umlal r2, r5, r3, r4 " ); \ - asm( "adds r7, r6, r2 " ); \ - asm( "adc r2, r5, #0 " ); \ - asm( "str r7, [r1], #4 " ); - -#define MULADDC_STOP \ - asm( "str r2, %0 " : "=m" (c)); \ - asm( "str r1, %0 " : "=m" (d)); \ - asm( "str r0, %0 " : "=m" (s) :: \ - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" ); - -#endif /* ARMv3 */ - -#if defined(__alpha__) - -#define MULADDC_INIT \ - asm( "ldq $1, %0 " :: "m" (s)); \ - asm( "ldq $2, %0 " :: "m" (d)); \ - asm( "ldq $3, %0 " :: "m" (c)); \ - asm( "ldq $4, %0 " :: "m" (b)); - -#define MULADDC_CORE \ - asm( "ldq $6, 0($1) " ); \ - asm( "addq $1, 8, $1 " ); \ - asm( "mulq $6, $4, $7 " ); \ - asm( "umulh $6, $4, $6 " ); \ - asm( "addq $7, $3, $7 " ); \ - asm( "cmpult $7, $3, $3 " ); \ - asm( "ldq $5, 0($2) " ); \ - asm( "addq $7, $5, $7 " ); \ - asm( "cmpult $7, $5, $5 " ); \ - asm( "stq $7, 0($2) " ); \ - asm( "addq $2, 8, $2 " ); \ - asm( "addq $6, $3, $3 " ); \ - asm( "addq $5, $3, $3 " ); - -#define MULADDC_STOP \ - asm( "stq $3, %0 " : "=m" (c)); \ - asm( "stq $2, %0 " : "=m" (d)); \ - asm( "stq $1, %0 " : "=m" (s) :: \ - "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); - -#endif /* Alpha */ - -#if defined(__mips__) - -#define MULADDC_INIT \ - asm( "lw $10, %0 " :: "m" (s)); \ - asm( "lw $11, %0 " :: "m" (d)); \ - asm( "lw $12, %0 " :: "m" (c)); \ - asm( "lw $13, %0 " :: "m" (b)); - -#define MULADDC_CORE \ - asm( "lw $14, 0($10) " ); \ - asm( "multu $13, $14 " ); \ - asm( "addi $10, $10, 4 " ); \ - asm( "mflo $14 " ); \ - asm( "mfhi $9 " ); \ - asm( "addu $14, $12, $14 " ); \ - asm( "lw $15, 0($11) " ); \ - asm( "sltu $12, $14, $12 " ); \ - asm( "addu $15, $14, $15 " ); \ - asm( "sltu $14, $15, $14 " ); \ - asm( "addu $12, $12, $9 " ); \ - asm( "sw $15, 0($11) " ); \ - asm( "addu $12, $12, $14 " ); \ - asm( "addi $11, $11, 4 " ); - -#define MULADDC_STOP \ - asm( "sw $12, %0 " : "=m" (c)); \ - asm( "sw $11, %0 " : "=m" (d)); \ - asm( "sw $10, %0 " : "=m" (s) :: \ - "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); - -#endif /* MIPS */ -#endif /* GNUC */ - -#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) - -#define MULADDC_INIT \ - __asm mov esi, s \ - __asm mov edi, d \ - __asm mov ecx, c \ - __asm mov ebx, b - -#define MULADDC_CORE \ - __asm lodsd \ - __asm mul ebx \ - __asm add eax, ecx \ - __asm adc edx, 0 \ - __asm add eax, [edi] \ - __asm adc edx, 0 \ - __asm mov ecx, edx \ - __asm stosd - -#if defined(POLARSSL_HAVE_SSE2) - -#define EMIT __asm _emit - -#define MULADDC_HUIT \ - EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ - EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ - EMIT 0x0F EMIT 0x6E EMIT 0x1F \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ - EMIT 0x0F EMIT 0x6E EMIT 0x16 \ - EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ - EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ - EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ - EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ - EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ - EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ - EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ - EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ - EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ - EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ - EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ - EMIT 0x0F EMIT 0x7E EMIT 0x0F \ - EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ - EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ - EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ - EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ - EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ - EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ - EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ - EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ - EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ - EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ - EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ - EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ - EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ - EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ - EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ - EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ - EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ - EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ - EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ - EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ - EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ - EMIT 0x0F EMIT 0x7E EMIT 0xC9 - -#define MULADDC_STOP \ - EMIT 0x0F EMIT 0x77 \ - __asm mov c, ecx \ - __asm mov d, edi \ - __asm mov s, esi \ - -#else - -#define MULADDC_STOP \ - __asm mov c, ecx \ - __asm mov d, edi \ - __asm mov s, esi \ - -#endif /* SSE2 */ -#endif /* MSVC */ - -#endif /* POLARSSL_HAVE_ASM */ - -#if !defined(MULADDC_CORE) -#if defined(POLARSSL_HAVE_LONGLONG) - -#define MULADDC_INIT \ -{ \ - t_dbl r; \ - t_int r0, r1; - -#define MULADDC_CORE \ - r = *(s++) * (t_dbl) b; \ - r0 = r; \ - r1 = r >> biL; \ - r0 += c; r1 += (r0 < c); \ - r0 += *d; r1 += (r0 < *d); \ - c = r1; *(d++) = r0; - -#define MULADDC_STOP \ -} - -#else -#define MULADDC_INIT \ -{ \ - t_int s0, s1, b0, b1; \ - t_int r0, r1, rx, ry; \ - b0 = ( b << biH ) >> biH; \ - b1 = ( b >> biH ); - -#define MULADDC_CORE \ - s0 = ( *s << biH ) >> biH; \ - s1 = ( *s >> biH ); s++; \ - rx = s0 * b1; r0 = s0 * b0; \ - ry = s1 * b0; r1 = s1 * b1; \ - r1 += ( rx >> biH ); \ - r1 += ( ry >> biH ); \ - rx <<= biH; ry <<= biH; \ - r0 += rx; r1 += (r0 < rx); \ - r0 += ry; r1 += (r0 < ry); \ - r0 += c; r1 += (r0 < c); \ - r0 += *d; r1 += (r0 < *d); \ - c = r1; *(d++) = r0; - -#define MULADDC_STOP \ -} - -#endif /* C (generic) */ -#endif /* C (longlong) */ - -#endif /* bn_mul.h */ diff --git a/src/src/pdkim/config.h b/src/src/pdkim/config.h new file mode 100644 index 000000000..fdd4cfe4f --- /dev/null +++ b/src/src/pdkim/config.h @@ -0,0 +1,4 @@ +#define POLARSSL_BASE64_C + + + diff --git a/src/src/pdkim/part-x509parse.c b/src/src/pdkim/part-x509parse.c new file mode 100644 index 000000000..935d25ad2 --- /dev/null +++ b/src/src/pdkim/part-x509parse.c @@ -0,0 +1,149 @@ +#include "polarssl/bignum.h" +#include "polarssl/part-x509.h" +#include "polarssl/private-x509parse_c.h" + +#ifndef POLARSSL_PRIVATE_X509_PARSE_C_H +#define POLARSSL_PRIVATE_X509_PARSE_C_H +/* *************** begin copy from x509parse.c ********************/ +/* + * X.509 certificate and private key decoding + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org> + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ITU-T X.509 standard defines a certificat format for PKI. + * + * http://www.ietf.org/rfc/rfc2459.txt + * http://www.ietf.org/rfc/rfc3279.txt + * + * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + + +/* + * ASN.1 DER decoding routines + */ +static int asn1_get_len( unsigned char **p, + const unsigned char *end, + int *len ) +{ + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( ( **p & 0x80 ) == 0 ) + *len = *(*p)++; + else + { + switch( **p & 0x7F ) + { + case 1: + if( ( end - *p ) < 2 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = (*p)[1]; + (*p) += 2; + break; + + case 2: + if( ( end - *p ) < 3 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (*p)[1] << 8 ) | (*p)[2]; + (*p) += 3; + break; + + default: + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + break; + } + } + + if( *len > (int) ( end - *p ) ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + return( 0 ); +} + +/* This function is not exported by PolarSSL 0.14.2 + * static */ +int asn1_get_tag( unsigned char **p, + const unsigned char *end, + int *len, int tag ) +{ + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( **p != tag ) + return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + (*p)++; + + return( asn1_get_len( p, end, len ) ); +} + +/* This function is not exported by PolarSSL 0.14.2 + * static */ +int asn1_get_int( unsigned char **p, + const unsigned char *end, + int *val ) +{ + int ret, len; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) + return( ret ); + + if( len > (int) sizeof( int ) || ( **p & 0x80 ) != 0 ) + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + *val = 0; + + while( len-- > 0 ) + { + *val = ( *val << 8 ) | **p; + (*p)++; + } + + return( 0 ); +} + +/* This function is not exported by PolarSSL 0.14.2 + * static */ +int asn1_get_mpi( unsigned char **p, + const unsigned char *end, + mpi *X ) +{ + int ret, len; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) + return( ret ); + + ret = mpi_read_binary( X, *p, len ); + + *p += len; + + return( ret ); +} +/* *************** end copy from x509parse.c ********************/ +#endif /* private-x509parse_c.h */ diff --git a/src/src/pdkim/pdkim-rsa.c b/src/src/pdkim/pdkim-rsa.c new file mode 100644 index 000000000..9bd229ac9 --- /dev/null +++ b/src/src/pdkim/pdkim-rsa.c @@ -0,0 +1,213 @@ +#include "pdkim-rsa.h" +#include "polarssl/base64.h" +#include <stdlib.h> +#include <string.h> +#include "polarssl/private-x509parse_c.h" + +/* PDKIM code (not copied from polarssl) */ +/* + * Parse a public RSA key + +OpenSSL RSA public key ASN1 container + 0:d=0 hl=3 l= 159 cons: SEQUENCE + 3:d=1 hl=2 l= 13 cons: SEQUENCE + 5:d=2 hl=2 l= 9 prim: OBJECT:rsaEncryption + 16:d=2 hl=2 l= 0 prim: NULL + 18:d=1 hl=3 l= 141 prim: BIT STRING:RSAPublicKey (below) + +RSAPublicKey ASN1 container + 0:d=0 hl=3 l= 137 cons: SEQUENCE + 3:d=1 hl=3 l= 129 prim: INTEGER:Public modulus +135:d=1 hl=2 l= 3 prim: INTEGER:Public exponent +*/ + +int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen ) +{ + unsigned char *p, *end; + int ret, len; + + p = buf; + end = buf+buflen; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) { + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) == 0 ) { + /* Skip over embedded rsaEncryption Object */ + p+=len; + + /* The RSAPublicKey ASN1 container is wrapped in a BIT STRING */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_BIT_STRING ) ) != 0 ) { + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + /* Limit range to that BIT STRING */ + end = p + len; + p++; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) { + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + } + + if ( ( ( ret = asn1_get_mpi( &p, end, &(rsa->N) ) ) == 0 ) && + ( ( ret = asn1_get_mpi( &p, end, &(rsa->E) ) ) == 0 ) ) { + rsa->len = mpi_size( &rsa->N ); + return 0; + } + + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); +} + +/* + * Parse a private RSA key + */ +int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, + unsigned char *pwd, int pwdlen ) +{ + int ret, len, enc; + unsigned char *s1, *s2; + unsigned char *p, *end; + + s1 = (unsigned char *) strstr( (char *) buf, + "-----BEGIN RSA PRIVATE KEY-----" ); + + if( s1 != NULL ) + { + s2 = (unsigned char *) strstr( (char *) buf, + "-----END RSA PRIVATE KEY-----" ); + + if( s2 == NULL || s2 <= s1 ) + return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); + + s1 += 31; + if( *s1 == '\r' ) s1++; + if( *s1 == '\n' ) s1++; + else return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); + + enc = 0; + + if( memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 ) + { + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); + } + + len = 0; + ret = base64_decode( NULL, &len, s1, s2 - s1 ); + + if( ret == POLARSSL_ERR_BASE64_INVALID_CHARACTER ) + return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM ); + + if( ( buf = (unsigned char *) malloc( len ) ) == NULL ) + return( 1 ); + + if( ( ret = base64_decode( buf, &len, s1, s2 - s1 ) ) != 0 ) + { + free( buf ); + return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM ); + } + + buflen = len; + + if( enc != 0 ) + { + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); + } + } + + memset( rsa, 0, sizeof( rsa_context ) ); + + p = buf; + end = buf + buflen; + + /* + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + end = p + len; + + if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + } + + if( rsa->ver != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( ret | POLARSSL_ERR_X509_KEY_INVALID_VERSION ); + } + + if( ( ret = asn1_get_mpi( &p, end, &rsa->N ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->E ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->D ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->P ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->Q ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DP ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DQ ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( ret | POLARSSL_ERR_X509_KEY_INVALID_FORMAT ); + } + + rsa->len = mpi_size( &rsa->N ); + + if( p != end ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + if( ( ret = rsa_check_privkey( rsa ) ) != 0 ) + { + if( s1 != NULL ) + free( buf ); + + rsa_free( rsa ); + return( ret ); + } + + if( s1 != NULL ) + free( buf ); + + return( 0 ); +} diff --git a/src/src/pdkim/pdkim-rsa.h b/src/src/pdkim/pdkim-rsa.h new file mode 100644 index 000000000..1ef8787c3 --- /dev/null +++ b/src/src/pdkim/pdkim-rsa.h @@ -0,0 +1,8 @@ +#include "polarssl/part-x509.h" +#include "polarssl/rsa.h" + +/* PDKIM declarations (not part of polarssl) */ +int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen ); +int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, + unsigned char *pwd, int pwdlen ); + diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 4f0da3f71..d9fbb8e8b 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1,7 +1,7 @@ /* * PDKIM - a RFC4871 (DKIM) implementation * - * Copyright (C) 2009 - 2012 Tom Kistner <tom@duncanthrax.net> + * Copyright (C) 2009 - 2015 Tom Kistner <tom@duncanthrax.net> * * http://duncanthrax.net/pdkim/ * @@ -20,17 +20,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> - +#include "../exim.h" #include "pdkim.h" +#include "pdkim-rsa.h" -#include "sha1.h" -#include "sha2.h" -#include "rsa.h" -#include "base64.h" +#include "polarssl/sha1.h" +#include "polarssl/sha2.h" +#include "polarssl/rsa.h" +#include "polarssl/base64.h" #define PDKIM_SIGNATURE_VERSION "1" #define PDKIM_PUB_RECORD_VERSION "DKIM1" @@ -127,203 +124,252 @@ const char *pdkim_verify_ext_status_str(int ext_status) { /* -------------------------------------------------------------------------- */ /* Print debugging functions */ -#ifdef PDKIM_DEBUG -void pdkim_quoteprint(FILE *stream, const char *data, int len, int lf) { - int i; - const unsigned char *p = (const unsigned char *)data; - - for (i=0;i<len;i++) { - const int c = p[i]; - switch (c) { - case ' ' : fprintf(stream,"{SP}"); break; - case '\t': fprintf(stream,"{TB}"); break; - case '\r': fprintf(stream,"{CR}"); break; - case '\n': fprintf(stream,"{LF}"); break; - case '{' : fprintf(stream,"{BO}"); break; - case '}' : fprintf(stream,"{BC}"); break; - default: - if ( (c < 32) || (c > 127) ) - fprintf(stream,"{%02x}",c); - else - fputc(c,stream); +void +pdkim_quoteprint(const char *data, int len, int lf) +{ +int i; +const unsigned char *p = (const unsigned char *)data; + +for (i = 0; i < len; i++) + { + const int c = p[i]; + switch (c) + { + case ' ' : debug_printf("{SP}"); break; + case '\t': debug_printf("{TB}"); break; + case '\r': debug_printf("{CR}"); break; + case '\n': debug_printf("{LF}"); break; + case '{' : debug_printf("{BO}"); break; + case '}' : debug_printf("{BC}"); break; + default: + if ( (c < 32) || (c > 127) ) + debug_printf("{%02x}", c); + else + debug_printf("%c", c); break; } } - if (lf) - fputc('\n',stream); +if (lf) + debug_printf("\n"); } -void pdkim_hexprint(FILE *stream, const char *data, int len, int lf) { - int i; - const unsigned char *p = (const unsigned char *)data; - for (i=0;i<len;i++) { - const int c = p[i]; - fprintf(stream,"%02x",c); - } - if (lf) - fputc('\n',stream); +void +pdkim_hexprint(const char *data, int len, int lf) +{ +int i; +const unsigned char *p = (const unsigned char *)data; + +for (i = 0 ; i < len; i++) + debug_printf("%02x", p[i]); +if (lf) + debug_printf("\n"); } -#endif /* -------------------------------------------------------------------------- */ /* Simple string list implementation for convinience */ -pdkim_stringlist *pdkim_append_stringlist(pdkim_stringlist *base, char *str) { - pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist)); - if (new_entry == NULL) return NULL; - memset(new_entry,0,sizeof(pdkim_stringlist)); - new_entry->value = strdup(str); - if (new_entry->value == NULL) return NULL; - if (base != NULL) { - pdkim_stringlist *last = base; - while (last->next != NULL) { last = last->next; } - last->next = new_entry; - return base; - } - else return new_entry; -} -pdkim_stringlist *pdkim_prepend_stringlist(pdkim_stringlist *base, char *str) { - pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist)); - if (new_entry == NULL) return NULL; - memset(new_entry,0,sizeof(pdkim_stringlist)); - new_entry->value = strdup(str); - if (new_entry->value == NULL) return NULL; - if (base != NULL) { - new_entry->next = base; +pdkim_stringlist * +pdkim_append_stringlist(pdkim_stringlist *base, char *str) +{ +pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist)); + +if (!new_entry) return NULL; +memset(new_entry, 0, sizeof(pdkim_stringlist)); +if (!(new_entry->value = strdup(str))) return NULL; +if (base) + { + pdkim_stringlist *last = base; + while (last->next != NULL) { last = last->next; } + last->next = new_entry; + return base; } +else return new_entry; } +pdkim_stringlist * +pdkim_prepend_stringlist(pdkim_stringlist *base, char *str) +{ +pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist)); + +if (!new_entry) return NULL; +memset(new_entry, 0, sizeof(pdkim_stringlist)); +if (!(new_entry->value = strdup(str))) return NULL; +if (base) + new_entry->next = base; +return new_entry; +} + /* -------------------------------------------------------------------------- */ /* A small "growing string" implementation to escape malloc/realloc hell */ -pdkim_str *pdkim_strnew (const char *cstr) { - unsigned int len = cstr?strlen(cstr):0; - pdkim_str *p = malloc(sizeof(pdkim_str)); - if (p == NULL) return NULL; - memset(p,0,sizeof(pdkim_str)); - p->str = malloc(len+1); - if (p->str == NULL) { - free(p); - return NULL; + +pdkim_str * +pdkim_strnew (const char *cstr) +{ +unsigned int len = cstr ? strlen(cstr) : 0; +pdkim_str *p = malloc(sizeof(pdkim_str)); + +if (!p) return NULL; +memset(p, 0, sizeof(pdkim_str)); +if (!(p->str = malloc(len+1))) + { + free(p); + return NULL; } - p->allocated=(len+1); - p->len=len; - if (cstr) strcpy(p->str,cstr); - else p->str[p->len] = '\0'; - return p; +p->allocated = len+1; +p->len = len; +if (cstr) + strcpy(p->str, cstr); +else + p->str[p->len] = '\0'; +return p; } -char *pdkim_strncat(pdkim_str *str, const char *data, int len) { - if ((str->allocated - str->len) < (len+1)) { - /* Extend the buffer */ - int num_frags = ((len+1)/PDKIM_STR_ALLOC_FRAG)+1; - char *n = realloc(str->str, - (str->allocated+(num_frags*PDKIM_STR_ALLOC_FRAG))); - if (n == NULL) return NULL; - str->str = n; - str->allocated += (num_frags*PDKIM_STR_ALLOC_FRAG); + +char * +pdkim_strncat(pdkim_str *str, const char *data, int len) +{ +if ((str->allocated - str->len) < (len+1)) + { + /* Extend the buffer */ + int num_frags = ((len+1)/PDKIM_STR_ALLOC_FRAG)+1; + char *n = realloc(str->str, + (str->allocated+(num_frags*PDKIM_STR_ALLOC_FRAG))); + if (n == NULL) return NULL; + str->str = n; + str->allocated += (num_frags*PDKIM_STR_ALLOC_FRAG); } - strncpy(&(str->str[str->len]),data,len); - str->len+=len; - str->str[str->len] = '\0'; - return str->str; +strncpy(&(str->str[str->len]), data, len); +str->len += len; +str->str[str->len] = '\0'; +return str->str; } -char *pdkim_strcat(pdkim_str *str, const char *cstr) { - return pdkim_strncat(str, cstr, strlen(cstr)); + +char * +pdkim_strcat(pdkim_str *str, const char *cstr) +{ +return pdkim_strncat(str, cstr, strlen(cstr)); } -char *pdkim_numcat(pdkim_str *str, unsigned long num) { - char minibuf[20]; - snprintf(minibuf,20,"%lu",num); - return pdkim_strcat(str,minibuf); + +char * +pdkim_numcat(pdkim_str *str, unsigned long num) +{ +char minibuf[20]; +snprintf(minibuf, 20, "%lu", num); +return pdkim_strcat(str, minibuf); } -char *pdkim_strtrim(pdkim_str *str) { - char *p = str->str; - char *q = str->str; - while ( (*p != '\0') && ((*p == '\t') || (*p == ' ')) ) p++; - while (*p != '\0') {*q = *p; q++; p++;} + +char * +pdkim_strtrim(pdkim_str *str) +{ +char *p = str->str; +char *q = str->str; +while ( (*p != '\0') && ((*p == '\t') || (*p == ' ')) ) p++; +while (*p != '\0') {*q = *p; q++; p++;} +*q = '\0'; +while ( (q != str->str) && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) ) + { *q = '\0'; - while ( (q != str->str) && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) ) { - *q = '\0'; - q--; + q--; } - str->len = strlen(str->str); - return str->str; +str->len = strlen(str->str); +return str->str; } -char *pdkim_strclear(pdkim_str *str) { - str->str[0] = '\0'; - str->len = 0; - return str->str; + +char * +pdkim_strclear(pdkim_str *str) +{ +str->str[0] = '\0'; +str->len = 0; +return str->str; } -void pdkim_strfree(pdkim_str *str) { - if (str == NULL) return; - if (str->str != NULL) free(str->str); - free(str); + +void +pdkim_strfree(pdkim_str *str) +{ +if (!str) return; +if (str->str) free(str->str); +free(str); } /* -------------------------------------------------------------------------- */ -void pdkim_free_pubkey(pdkim_pubkey *pub) { - if (pub) { - if (pub->version != NULL) free(pub->version); - if (pub->granularity != NULL) free(pub->granularity); - if (pub->hashes != NULL) free(pub->hashes); - if (pub->keytype != NULL) free(pub->keytype); - if (pub->srvtype != NULL) free(pub->srvtype); - if (pub->notes != NULL) free(pub->notes); - if (pub->key != NULL) free(pub->key); - free(pub); + +void +pdkim_free_pubkey(pdkim_pubkey *pub) +{ +if (pub) + { + if (pub->version ) free(pub->version); + if (pub->granularity) free(pub->granularity); + if (pub->hashes ) free(pub->hashes); + if (pub->keytype ) free(pub->keytype); + if (pub->srvtype ) free(pub->srvtype); + if (pub->notes ) free(pub->notes); + if (pub->key ) free(pub->key); + free(pub); } } /* -------------------------------------------------------------------------- */ -void pdkim_free_sig(pdkim_signature *sig) { - if (sig) { - pdkim_signature *next = (pdkim_signature *)sig->next; - - pdkim_stringlist *e = sig->headers; - while(e != NULL) { - pdkim_stringlist *c = e; - if (e->value != NULL) free(e->value); - e = e->next; - free(c); + +void +pdkim_free_sig(pdkim_signature *sig) +{ +if (sig) + { + pdkim_signature *next = (pdkim_signature *)sig->next; + + pdkim_stringlist *e = sig->headers; + while(e) + { + pdkim_stringlist *c = e; + if (e->value) free(e->value); + e = e->next; + free(c); } - if (sig->sigdata != NULL) free(sig->sigdata); - if (sig->bodyhash != NULL) free(sig->bodyhash); - if (sig->selector != NULL) free(sig->selector); - if (sig->domain != NULL) free(sig->domain); - if (sig->identity != NULL) free(sig->identity); - if (sig->headernames != NULL) free(sig->headernames); - if (sig->copiedheaders != NULL) free(sig->copiedheaders); - if (sig->rsa_privkey != NULL) free(sig->rsa_privkey); - if (sig->sign_headers != NULL) free(sig->sign_headers); - if (sig->signature_header != NULL) free(sig->signature_header); - if (sig->sha1_body != NULL) free(sig->sha1_body); - if (sig->sha2_body != NULL) free(sig->sha2_body); - - if (sig->pubkey != NULL) pdkim_free_pubkey(sig->pubkey); - - free(sig); - if (next != NULL) pdkim_free_sig(next); + if (sig->sigdata ) free(sig->sigdata); + if (sig->bodyhash ) free(sig->bodyhash); + if (sig->selector ) free(sig->selector); + if (sig->domain ) free(sig->domain); + if (sig->identity ) free(sig->identity); + if (sig->headernames ) free(sig->headernames); + if (sig->copiedheaders ) free(sig->copiedheaders); + if (sig->rsa_privkey ) free(sig->rsa_privkey); + if (sig->sign_headers ) free(sig->sign_headers); + if (sig->signature_header) free(sig->signature_header); + if (sig->sha1_body ) free(sig->sha1_body); + if (sig->sha2_body ) free(sig->sha2_body); + + if (sig->pubkey) pdkim_free_pubkey(sig->pubkey); + + free(sig); + if (next) pdkim_free_sig(next); } } /* -------------------------------------------------------------------------- */ -DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) { - if (ctx) { - pdkim_stringlist *e = ctx->headers; - while(e != NULL) { - pdkim_stringlist *c = e; - if (e->value != NULL) free(e->value); - e = e->next; - free(c); + +DLLEXPORT void +pdkim_free_ctx(pdkim_ctx *ctx) +{ +if (ctx) + { + pdkim_stringlist *e = ctx->headers; + while(e) + { + pdkim_stringlist *c = e; + if (e->value) free(e->value); + e = e->next; + free(c); } - pdkim_free_sig(ctx->sig); - pdkim_strfree(ctx->cur_header); - free(ctx); + pdkim_free_sig(ctx->sig); + pdkim_strfree(ctx->cur_header); + free(ctx); } } @@ -333,188 +379,219 @@ DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) { the passed colon-separated "list", starting at entry "start". Returns the position of the header name in the list. */ -int header_name_match(const char *header, - char *tick, - int do_tick) { - char *hname; - char *lcopy; - char *p; - char *q; - int rc = PDKIM_FAIL; - - /* Get header name */ - char *hcolon = strchr(header,':'); - if (hcolon == NULL) return rc; /* This isn't a header */ - hname = malloc((hcolon-header)+1); - if (hname == NULL) return PDKIM_ERR_OOM; - memset(hname,0,(hcolon-header)+1); - strncpy(hname,header,(hcolon-header)); - - /* Copy tick-off list locally, so we can punch zeroes into it */ - lcopy = strdup(tick); - if (lcopy == NULL) { - free(hname); - return PDKIM_ERR_OOM; - } - p = lcopy; - q = strchr(p,':'); - while (q != NULL) { - *q = '\0'; - - if (strcasecmp(p,hname) == 0) { - rc = PDKIM_OK; - /* Invalidate header name instance in tick-off list */ - if (do_tick) tick[p-lcopy] = '_'; - goto BAIL; - } - p = q+1; - q = strchr(p,':'); +int +header_name_match(const char *header, + char *tick, + int do_tick) +{ +char *hname; +char *lcopy; +char *p; +char *q; +int rc = PDKIM_FAIL; + +/* Get header name */ +char *hcolon = strchr(header, ':'); + +if (!hcolon) return rc; /* This isn't a header */ + +if (!(hname = malloc((hcolon-header)+1))) + return PDKIM_ERR_OOM; +memset(hname, 0, (hcolon-header)+1); +strncpy(hname, header, (hcolon-header)); + +/* Copy tick-off list locally, so we can punch zeroes into it */ +if (!(lcopy = strdup(tick))) + { + free(hname); + return PDKIM_ERR_OOM; } +p = lcopy; +q = strchr(p, ':'); +while (q) + { + *q = '\0'; - if (strcasecmp(p,hname) == 0) { + if (strcasecmp(p, hname) == 0) + { rc = PDKIM_OK; /* Invalidate header name instance in tick-off list */ if (do_tick) tick[p-lcopy] = '_'; + goto BAIL; + } + + p = q+1; + q = strchr(p, ':'); } - BAIL: - free(hname); - free(lcopy); - return rc; +if (strcasecmp(p, hname) == 0) + { + rc = PDKIM_OK; + /* Invalidate header name instance in tick-off list */ + if (do_tick) tick[p-lcopy] = '_'; + } + +BAIL: +free(hname); +free(lcopy); +return rc; } /* -------------------------------------------------------------------------- */ /* Performs "relaxed" canonicalization of a header. The returned pointer needs to be free()d. */ -char *pdkim_relax_header (char *header, int crlf) { - int past_field_name = 0; - int seen_wsp = 0; - char *p = header; - char *q; - char *relaxed = malloc(strlen(header)+3); - if (relaxed == NULL) return NULL; - q = relaxed; - while (*p != '\0') { - int c = *p; - /* Ignore CR & LF */ - if ( (c == '\r') || (c == '\n') ) { - p++; + +char * +pdkim_relax_header (char *header, int crlf) +{ +BOOL past_field_name = FALSE; +BOOL seen_wsp = FALSE; +char *p; +char *q; +char *relaxed = malloc(strlen(header)+3); + +if (!relaxed) return NULL; + +q = relaxed; +for (p = header; *p != '\0'; p++) + { + int c = *p; + /* Ignore CR & LF */ + if (c == '\r' || c == '\n') + continue; + if (c == '\t' || c == ' ') + { + if (seen_wsp) continue; + c = ' '; /* Turns WSP into SP */ + seen_wsp = TRUE; } - if ( (c == '\t') || (c == ' ') ) { - c = ' '; /* Turns WSP into SP */ - if (seen_wsp) { - p++; - continue; - } - else seen_wsp = 1; - } - else { - if ( (!past_field_name) && (c == ':') ) { - if (seen_wsp) q--; /* This removes WSP before the colon */ - seen_wsp = 1; /* This removes WSP after the colon */ - past_field_name = 1; + else + if (!past_field_name && c == ':') + { + if (seen_wsp) q--; /* This removes WSP before the colon */ + seen_wsp = TRUE; /* This removes WSP after the colon */ + past_field_name = TRUE; } - else seen_wsp = 0; - } - /* Lowercase header name */ - if (!past_field_name) c = tolower(c); - *q = c; - p++; - q++; + else + seen_wsp = FALSE; + + /* Lowercase header name */ + if (!past_field_name) c = tolower(c); + *q++ = c; } - if ((q>relaxed) && (*(q-1) == ' ')) q--; /* Squash eventual trailing SP */ - *q = '\0'; - if (crlf) strcat(relaxed,"\r\n"); - return relaxed; + +if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */ +*q = '\0'; + +if (crlf) strcat(relaxed, "\r\n"); +return relaxed; } /* -------------------------------------------------------------------------- */ #define PDKIM_QP_ERROR_DECODE -1 -char *pdkim_decode_qp_char(char *qp_p, int *c) { - char *initial_pos = qp_p; - - /* Advance one char */ - qp_p++; - - /* Check for two hex digits and decode them */ - if (isxdigit(*qp_p) && isxdigit(qp_p[1])) { - /* Do hex conversion */ - if (isdigit(*qp_p)) {*c = *qp_p - '0';} - else {*c = toupper(*qp_p) - 'A' + 10;} - *c <<= 4; - if (isdigit(qp_p[1])) {*c |= qp_p[1] - '0';} - else {*c |= toupper(qp_p[1]) - 'A' + 10;} - return qp_p + 2; + +static char * +pdkim_decode_qp_char(char *qp_p, int *c) +{ +char *initial_pos = qp_p; + +/* Advance one char */ +qp_p++; + +/* Check for two hex digits and decode them */ +if (isxdigit(*qp_p) && isxdigit(qp_p[1])) + { + /* Do hex conversion */ + *c = (isdigit(*qp_p) ? *qp_p - '0' : toupper(*qp_p) - 'A' + 10) << 4; + *c |= isdigit(qp_p[1]) ? qp_p[1] - '0' : toupper(qp_p[1]) - 'A' + 10; + return qp_p + 2; } - /* Illegal char here */ - *c = PDKIM_QP_ERROR_DECODE; - return initial_pos; +/* Illegal char here */ +*c = PDKIM_QP_ERROR_DECODE; +return initial_pos; } /* -------------------------------------------------------------------------- */ -char *pdkim_decode_qp(char *str) { - int nchar = 0; - char *q; - char *p = str; - char *n = malloc(strlen(p)+1); - if (n == NULL) return NULL; - *n = '\0'; - q = n; - while (*p != '\0') { - if (*p == '=') { - p = pdkim_decode_qp_char(p,&nchar); - if (nchar >= 0) { - *q = nchar; - q++; - continue; + +char * +pdkim_decode_qp(char *str) +{ +int nchar = 0; +char *q; +char *p = str; +char *n = malloc(strlen(p)+1); + +if (!n) return NULL; + +*n = '\0'; +q = n; +while (*p != '\0') + { + if (*p == '=') + { + p = pdkim_decode_qp_char(p, &nchar); + if (nchar >= 0) + { + *q++ = nchar; + continue; } } - else { - *q = *p; - q++; - } - p++; + else + *q++ = *p; + p++; } - *q = '\0'; - return n; +*q = '\0'; +return n; } /* -------------------------------------------------------------------------- */ -char *pdkim_decode_base64(char *str, int *num_decoded) { - int dlen = 0; - char *res; - - base64_decode(NULL, &dlen, (unsigned char *)str, strlen(str)); - res = malloc(dlen+1); - if (res == NULL) return NULL; - if (base64_decode((unsigned char *)res,&dlen,(unsigned char *)str,strlen(str)) != 0) { - free(res); - return NULL; + +char * +pdkim_decode_base64(char *str, int *num_decoded) +{ +int dlen = 0; +char *res; + +base64_decode(NULL, &dlen, (unsigned char *)str, strlen(str)); + +if (!(res = malloc(dlen+1))) + return NULL; +if (base64_decode((unsigned char *)res, &dlen, (unsigned char *)str, strlen(str)) != 0) + { + free(res); + return NULL; } - if (num_decoded != NULL) *num_decoded = dlen; - return res; + +if (num_decoded) *num_decoded = dlen; +return res; } + /* -------------------------------------------------------------------------- */ -char *pdkim_encode_base64(char *str, int num) { - int dlen = 0; - char *res; - - base64_encode(NULL, &dlen, (unsigned char *)str, num); - res = malloc(dlen+1); - if (res == NULL) return NULL; - if (base64_encode((unsigned char *)res,&dlen,(unsigned char *)str,num) != 0) { - free(res); - return NULL; + +char * +pdkim_encode_base64(char *str, int num) +{ +int dlen = 0; +char *res; + +base64_encode(NULL, &dlen, (unsigned char *)str, num); + +if (!(res = malloc(dlen+1))) + return NULL; +if (base64_encode((unsigned char *)res, &dlen, (unsigned char *)str, num) != 0) + { + free(res); + return NULL; } - return res; +return res; } @@ -522,1227 +599,1410 @@ char *pdkim_encode_base64(char *str, int num) { #define PDKIM_HDR_LIMBO 0 #define PDKIM_HDR_TAG 1 #define PDKIM_HDR_VALUE 2 -pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) { - pdkim_signature *sig ; - char *p,*q; - pdkim_str *cur_tag = NULL; - pdkim_str *cur_val = NULL; - int past_hname = 0; - int in_b_val = 0; - int where = PDKIM_HDR_LIMBO; - int i; - - sig = malloc(sizeof(pdkim_signature)); - if (sig == NULL) return NULL; - memset(sig,0,sizeof(pdkim_signature)); - sig->bodylength = -1; - - sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1); - if (sig->rawsig_no_b_val == NULL) { - free(sig); - return NULL; + +pdkim_signature * +pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) +{ +pdkim_signature *sig ; +char *p, *q; +pdkim_str *cur_tag = NULL; +pdkim_str *cur_val = NULL; +BOOL past_hname = FALSE; +BOOL in_b_val = FALSE; +int where = PDKIM_HDR_LIMBO; +int i; + +if (!(sig = malloc(sizeof(pdkim_signature)))) return NULL; +memset(sig, 0, sizeof(pdkim_signature)); +sig->bodylength = -1; + +if (!(sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1))) + { + free(sig); + return NULL; } - p = raw_hdr; - q = sig->rawsig_no_b_val; +q = sig->rawsig_no_b_val; - while (1) { +for (p = raw_hdr; ; p++) + { + char c = *p; - /* Ignore FWS */ - if ( (*p == '\r') || (*p == '\n') ) - goto NEXT_CHAR; + /* Ignore FWS */ + if (c == '\r' || c == '\n') + goto NEXT_CHAR; - /* Fast-forward through header name */ - if (!past_hname) { - if (*p == ':') past_hname = 1; - goto NEXT_CHAR; + /* Fast-forward through header name */ + if (!past_hname) + { + if (c == ':') past_hname = TRUE; + goto NEXT_CHAR; } - if (where == PDKIM_HDR_LIMBO) { - /* In limbo, just wait for a tag-char to appear */ - if (!((*p >= 'a') && (*p <= 'z'))) - goto NEXT_CHAR; + if (where == PDKIM_HDR_LIMBO) + { + /* In limbo, just wait for a tag-char to appear */ + if (!(c >= 'a' && c <= 'z')) + goto NEXT_CHAR; - where = PDKIM_HDR_TAG; + where = PDKIM_HDR_TAG; } - if (where == PDKIM_HDR_TAG) { - if (cur_tag == NULL) - cur_tag = pdkim_strnew(NULL); - - if ((*p >= 'a') && (*p <= 'z')) - pdkim_strncat(cur_tag,p,1); - - if (*p == '=') { - if (strcmp(cur_tag->str,"b") == 0) { - *q = '='; q++; - in_b_val = 1; - } - where = PDKIM_HDR_VALUE; - goto NEXT_CHAR; + if (where == PDKIM_HDR_TAG) + { + if (!cur_tag) + cur_tag = pdkim_strnew(NULL); + + if (c >= 'a' && c <= 'z') + pdkim_strncat(cur_tag, p, 1); + + if (c == '=') + { + if (strcmp(cur_tag->str, "b") == 0) + { + *q = '='; q++; + in_b_val = TRUE; + } + where = PDKIM_HDR_VALUE; + goto NEXT_CHAR; } } - if (where == PDKIM_HDR_VALUE) { - if (cur_val == NULL) - cur_val = pdkim_strnew(NULL); - - if ( (*p == '\r') || (*p == '\n') || (*p == ' ') || (*p == '\t') ) - goto NEXT_CHAR; - - if ( (*p == ';') || (*p == '\0') ) { - if (cur_tag->len > 0) { - pdkim_strtrim(cur_val); - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str); - #endif - switch (cur_tag->str[0]) { - case 'b': - switch (cur_tag->str[1]) { - case 'h': - sig->bodyhash = pdkim_decode_base64(cur_val->str,&(sig->bodyhash_len)); - break; - default: - sig->sigdata = pdkim_decode_base64(cur_val->str,&(sig->sigdata_len)); - break; - } - break; - case 'v': - if (strcmp(cur_val->str,PDKIM_SIGNATURE_VERSION) == 0) { - /* We only support version 1, and that is currently the - only version there is. */ - sig->version = 1; - } - break; - case 'a': - i = 0; - while (pdkim_algos[i] != NULL) { - if (strcmp(cur_val->str,pdkim_algos[i]) == 0 ) { - sig->algo = i; - break; - } - i++; - } - break; - case 'c': - i = 0; - while (pdkim_combined_canons[i].str != NULL) { - if (strcmp(cur_val->str,pdkim_combined_canons[i].str) == 0 ) { - sig->canon_headers = pdkim_combined_canons[i].canon_headers; - sig->canon_body = pdkim_combined_canons[i].canon_body; - break; - } - i++; - } - break; - case 'q': - i = 0; - while (pdkim_querymethods[i] != NULL) { - if (strcmp(cur_val->str,pdkim_querymethods[i]) == 0 ) { - sig->querymethod = i; - break; - } - i++; - } - break; - case 's': - sig->selector = strdup(cur_val->str); - break; - case 'd': - sig->domain = strdup(cur_val->str); - break; - case 'i': - sig->identity = pdkim_decode_qp(cur_val->str); - break; - case 't': - sig->created = strtoul(cur_val->str,NULL,10); - break; - case 'x': - sig->expires = strtoul(cur_val->str,NULL,10); - break; - case 'l': - sig->bodylength = strtol(cur_val->str,NULL,10); - break; - case 'h': - sig->headernames = strdup(cur_val->str); - break; - case 'z': - sig->copiedheaders = pdkim_decode_qp(cur_val->str); - break; - default: - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, "Unknown tag encountered\n"); - #endif - break; - } - } - pdkim_strclear(cur_tag); - pdkim_strclear(cur_val); - in_b_val = 0; - where = PDKIM_HDR_LIMBO; - goto NEXT_CHAR; - } - else pdkim_strncat(cur_val,p,1); - } + if (where == PDKIM_HDR_VALUE) + { + if (!cur_val) + cur_val = pdkim_strnew(NULL); - NEXT_CHAR: - if (*p == '\0') break; + if (c == '\r' || c == '\n' || c == ' ' || c == '\t') + goto NEXT_CHAR; - if (!in_b_val) { - *q = *p; - q++; + if (c == ';' || c == '\0') + { + if (cur_tag->len > 0) + { + pdkim_strtrim(cur_val); + + DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str); + + switch (cur_tag->str[0]) + { + case 'b': + if (cur_tag->str[1] == 'h') + sig->bodyhash = pdkim_decode_base64(cur_val->str, + &sig->bodyhash_len); + else + sig->sigdata = pdkim_decode_base64(cur_val->str, + &sig->sigdata_len); + break; + case 'v': + /* We only support version 1, and that is currently the + only version there is. */ + if (strcmp(cur_val->str, PDKIM_SIGNATURE_VERSION) == 0) + sig->version = 1; + break; + case 'a': + for (i = 0; pdkim_algos[i]; i++) + if (strcmp(cur_val->str, pdkim_algos[i]) == 0) + { + sig->algo = i; + break; + } + break; + case 'c': + for (i = 0; pdkim_combined_canons[i].str; i++) + if (strcmp(cur_val->str, pdkim_combined_canons[i].str) == 0) + { + sig->canon_headers = pdkim_combined_canons[i].canon_headers; + sig->canon_body = pdkim_combined_canons[i].canon_body; + break; + } + break; + case 'q': + for (i = 0; pdkim_querymethods[i]; i++) + if (strcmp(cur_val->str, pdkim_querymethods[i]) == 0) + { + sig->querymethod = i; + break; + } + break; + case 's': + sig->selector = strdup(cur_val->str); break; + case 'd': + sig->domain = strdup(cur_val->str); break; + case 'i': + sig->identity = pdkim_decode_qp(cur_val->str); break; + case 't': + sig->created = strtoul(cur_val->str, NULL, 10); break; + case 'x': + sig->expires = strtoul(cur_val->str, NULL, 10); break; + case 'l': + sig->bodylength = strtol(cur_val->str, NULL, 10); break; + case 'h': + sig->headernames = strdup(cur_val->str); break; + case 'z': + sig->copiedheaders = pdkim_decode_qp(cur_val->str); break; + default: + DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); + break; + } + } + pdkim_strclear(cur_tag); + pdkim_strclear(cur_val); + in_b_val = FALSE; + where = PDKIM_HDR_LIMBO; + } + else + pdkim_strncat(cur_val, p, 1); } - p++; - } - /* Make sure the most important bits are there. */ - if (!(sig->domain && (*(sig->domain) != '\0') && - sig->selector && (*(sig->selector) != '\0') && - sig->headernames && (*(sig->headernames) != '\0') && - sig->bodyhash && - sig->sigdata && - sig->version)) { - pdkim_free_sig(sig); - return NULL; - } +NEXT_CHAR: + if (c == '\0') + break; - *q = '\0'; - /* Chomp raw header. The final newline must not be added to the signature. */ - q--; - while( (q > sig->rawsig_no_b_val) && ((*q == '\r') || (*q == '\n')) ) { - *q = '\0'; q--; + if (!in_b_val) + *q++ = c; } - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, - "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - pdkim_quoteprint(ctx->debug_stream, - sig->rawsig_no_b_val, - strlen(sig->rawsig_no_b_val), 1); - fprintf(ctx->debug_stream, - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); +/* Make sure the most important bits are there. */ +if (!(sig->domain && (*(sig->domain) != '\0') && + sig->selector && (*(sig->selector) != '\0') && + sig->headernames && (*(sig->headernames) != '\0') && + sig->bodyhash && + sig->sigdata && + sig->version)) + { + pdkim_free_sig(sig); + return NULL; } - #endif - sig->sha1_body = malloc(sizeof(sha1_context)); - if (sig->sha1_body == NULL) { - pdkim_free_sig(sig); - return NULL; +*q = '\0'; +/* Chomp raw header. The final newline must not be added to the signature. */ +q--; +while (q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n')) + *q = '\0'; q--; /*XXX questionable code layout; possible bug */ + +DEBUG(D_acl) + { + debug_printf( + "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val), 1); + debug_printf( + "PDKIM >> Sig size: %4d bits\n", sig->sigdata_len*8); + debug_printf( + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } - sig->sha2_body = malloc(sizeof(sha2_context)); - if (sig->sha2_body == NULL) { - pdkim_free_sig(sig); - return NULL; + +if ( !(sig->sha1_body = malloc(sizeof(sha1_context))) + || !(sig->sha2_body = malloc(sizeof(sha2_context))) + ) + { + pdkim_free_sig(sig); + return NULL; } - sha1_starts(sig->sha1_body); - sha2_starts(sig->sha2_body,0); +sha1_starts(sig->sha1_body); +sha2_starts(sig->sha2_body, 0); - return sig; +return sig; } /* -------------------------------------------------------------------------- */ -pdkim_pubkey *pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) { - pdkim_pubkey *pub ; - char *p; - pdkim_str *cur_tag = NULL; - pdkim_str *cur_val = NULL; - int where = PDKIM_HDR_LIMBO; - - pub = malloc(sizeof(pdkim_pubkey)); - if (pub == NULL) return NULL; - memset(pub,0,sizeof(pdkim_pubkey)); - p = raw_record; - - while (1) { - - /* Ignore FWS */ - if ( (*p == '\r') || (*p == '\n') ) +pdkim_pubkey * +pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) +{ +pdkim_pubkey *pub; +char *p; +pdkim_str *cur_tag = NULL; +pdkim_str *cur_val = NULL; +int where = PDKIM_HDR_LIMBO; + +if (!(pub = malloc(sizeof(pdkim_pubkey)))) return NULL; +memset(pub, 0, sizeof(pdkim_pubkey)); + +for (p = raw_record; ; p++) + { + char c = *p; + + /* Ignore FWS */ + if (c == '\r' || c == '\n') + goto NEXT_CHAR; + + if (where == PDKIM_HDR_LIMBO) + { + /* In limbo, just wait for a tag-char to appear */ + if (!(c >= 'a' && c <= 'z')) goto NEXT_CHAR; - if (where == PDKIM_HDR_LIMBO) { - /* In limbo, just wait for a tag-char to appear */ - if (!((*p >= 'a') && (*p <= 'z'))) - goto NEXT_CHAR; - - where = PDKIM_HDR_TAG; + where = PDKIM_HDR_TAG; } - if (where == PDKIM_HDR_TAG) { - if (cur_tag == NULL) - cur_tag = pdkim_strnew(NULL); + if (where == PDKIM_HDR_TAG) + { + if (!cur_tag) + cur_tag = pdkim_strnew(NULL); - if ((*p >= 'a') && (*p <= 'z')) - pdkim_strncat(cur_tag,p,1); + if (c >= 'a' && c <= 'z') + pdkim_strncat(cur_tag, p, 1); - if (*p == '=') { - where = PDKIM_HDR_VALUE; - goto NEXT_CHAR; + if (c == '=') + { + where = PDKIM_HDR_VALUE; + goto NEXT_CHAR; } } - if (where == PDKIM_HDR_VALUE) { - if (cur_val == NULL) - cur_val = pdkim_strnew(NULL); - - if ( (*p == '\r') || (*p == '\n') ) - goto NEXT_CHAR; - - if ( (*p == ';') || (*p == '\0') ) { - if (cur_tag->len > 0) { - pdkim_strtrim(cur_val); - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str); - #endif - switch (cur_tag->str[0]) { - case 'v': - /* This tag isn't evaluated because: - - We only support version DKIM1. - - Which is the default for this value (set below) - - Other versions are currently not specified. */ - break; - case 'h': - pub->hashes = strdup(cur_val->str); - break; - case 'g': - pub->granularity = strdup(cur_val->str); - break; - case 'n': - pub->notes = pdkim_decode_qp(cur_val->str); - break; - case 'p': - pub->key = pdkim_decode_base64(cur_val->str,&(pub->key_len)); - break; - case 'k': - pub->hashes = strdup(cur_val->str); - break; - case 's': - pub->srvtype = strdup(cur_val->str); - break; - case 't': - if (strchr(cur_val->str,'y') != NULL) pub->testing = 1; - if (strchr(cur_val->str,'s') != NULL) pub->no_subdomaining = 1; - break; - default: - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, "Unknown tag encountered\n"); - #endif - break; - } - } - pdkim_strclear(cur_tag); - pdkim_strclear(cur_val); - where = PDKIM_HDR_LIMBO; - goto NEXT_CHAR; + if (where == PDKIM_HDR_VALUE) + { + if (!cur_val) + cur_val = pdkim_strnew(NULL); + + if (c == '\r' || c == '\n') + goto NEXT_CHAR; + + if (c == ';' || c == '\0') + { + if (cur_tag->len > 0) + { + pdkim_strtrim(cur_val); + DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str); + + switch (cur_tag->str[0]) + { + case 'v': + /* This tag isn't evaluated because: + - We only support version DKIM1. + - Which is the default for this value (set below) + - Other versions are currently not specified. */ + break; + case 'h': + pub->hashes = strdup(cur_val->str); break; + case 'g': + pub->granularity = strdup(cur_val->str); break; + case 'n': + pub->notes = pdkim_decode_qp(cur_val->str); break; + case 'p': + pub->key = pdkim_decode_base64(cur_val->str, &(pub->key_len)); break; + case 'k': + pub->hashes = strdup(cur_val->str); break; + case 's': + pub->srvtype = strdup(cur_val->str); break; + case 't': + if (strchr(cur_val->str, 'y') != NULL) pub->testing = 1; + if (strchr(cur_val->str, 's') != NULL) pub->no_subdomaining = 1; + break; + default: + DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); + break; + } + } + pdkim_strclear(cur_tag); + pdkim_strclear(cur_val); + where = PDKIM_HDR_LIMBO; } - else pdkim_strncat(cur_val,p,1); + else + pdkim_strncat(cur_val, p, 1); } - NEXT_CHAR: - if (*p == '\0') break; - p++; +NEXT_CHAR: + if (c == '\0') break; } - /* Set fallback defaults */ - if (pub->version == NULL) pub->version = strdup(PDKIM_PUB_RECORD_VERSION); - if (pub->granularity == NULL) pub->granularity = strdup("*"); - if (pub->keytype == NULL) pub->keytype = strdup("rsa"); - if (pub->srvtype == NULL) pub->srvtype = strdup("*"); - - /* p= is required */ - if (pub->key == NULL) { - pdkim_free_pubkey(pub); - return NULL; - } +/* Set fallback defaults */ +if (!pub->version ) pub->version = strdup(PDKIM_PUB_RECORD_VERSION); +if (!pub->granularity) pub->granularity = strdup("*"); +if (!pub->keytype ) pub->keytype = strdup("rsa"); +if (!pub->srvtype ) pub->srvtype = strdup("*"); +/* p= is required */ +if (pub->key) return pub; + +pdkim_free_pubkey(pub); +return NULL; } /* -------------------------------------------------------------------------- */ -int pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len) { - pdkim_signature *sig = ctx->sig; - /* Cache relaxed version of data */ - char *relaxed_data = NULL; - int relaxed_len = 0; - - /* Traverse all signatures, updating their hashes. */ - while (sig != NULL) { - /* Defaults to simple canon (no further treatment necessary) */ - const char *canon_data = data; - int canon_len = len; - - if (sig->canon_body == PDKIM_CANON_RELAXED) { - /* Relax the line if not done already */ - if (relaxed_data == NULL) { - int seen_wsp = 0; - const char *p = data; - int q = 0; - relaxed_data = malloc(len+1); - if (relaxed_data == NULL) return PDKIM_ERR_OOM; - while (*p != '\0') { - char c = *p; - if (c == '\r') { - if ( (q > 0) && (relaxed_data[q-1] == ' ') ) q--; - } - else if ( (c == '\t') || (c == ' ') ) { - c = ' '; /* Turns WSP into SP */ - if (seen_wsp) { - p++; - continue; - } - else seen_wsp = 1; - } - else seen_wsp = 0; - relaxed_data[q++] = c; - p++; - } - relaxed_data[q] = '\0'; - relaxed_len = q; + +int +pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len) +{ +pdkim_signature *sig = ctx->sig; +/* Cache relaxed version of data */ +char *relaxed_data = NULL; +int relaxed_len = 0; + +/* Traverse all signatures, updating their hashes. */ +while (sig) + { + /* Defaults to simple canon (no further treatment necessary) */ + const char *canon_data = data; + int canon_len = len; + + if (sig->canon_body == PDKIM_CANON_RELAXED) + { + /* Relax the line if not done already */ + if (!relaxed_data) + { + BOOL seen_wsp = FALSE; + const char *p; + int q = 0; + + if (!(relaxed_data = malloc(len+1))) + return PDKIM_ERR_OOM; + + for (p = data; *p; p++) + { + char c = *p; + if (c == '\r') + { + if (q > 0 && relaxed_data[q-1] == ' ') + q--; + } + else if (c == '\t' || c == ' ') + { + c = ' '; /* Turns WSP into SP */ + if (seen_wsp) + continue; + seen_wsp = TRUE; + } + else + seen_wsp = FALSE; + relaxed_data[q++] = c; + } + relaxed_data[q] = '\0'; + relaxed_len = q; } - canon_data = relaxed_data; - canon_len = relaxed_len; + canon_data = relaxed_data; + canon_len = relaxed_len; } - /* Make sure we don't exceed the to-be-signed body length */ - if ((sig->bodylength >= 0) && - ((sig->signed_body_bytes+(unsigned long)canon_len) > sig->bodylength)) - canon_len = (sig->bodylength - sig->signed_body_bytes); + /* Make sure we don't exceed the to-be-signed body length */ + if ( sig->bodylength >= 0 + && sig->signed_body_bytes + (unsigned long)canon_len > sig->bodylength + ) + canon_len = sig->bodylength - sig->signed_body_bytes; - if (canon_len > 0) { - if (sig->algo == PDKIM_ALGO_RSA_SHA1) - sha1_update(sig->sha1_body,(unsigned char *)canon_data,canon_len); - else - sha2_update(sig->sha2_body,(unsigned char *)canon_data,canon_len); - sig->signed_body_bytes += canon_len; -#ifdef PDKIM_DEBUG - if (ctx->debug_stream!=NULL) - pdkim_quoteprint(ctx->debug_stream,canon_data,canon_len,0); -#endif + if (canon_len > 0) + { + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_update(sig->sha1_body, (unsigned char *)canon_data, canon_len); + else + sha2_update(sig->sha2_body, (unsigned char *)canon_data, canon_len); + + sig->signed_body_bytes += canon_len; + DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len, 1); } - sig = sig->next; + sig = sig->next; } - if (relaxed_data != NULL) free(relaxed_data); - return PDKIM_OK; +if (relaxed_data) free(relaxed_data); +return PDKIM_OK; } /* -------------------------------------------------------------------------- */ -int pdkim_finish_bodyhash(pdkim_ctx *ctx) { - pdkim_signature *sig = ctx->sig; - /* Traverse all signatures */ - while (sig != NULL) { - - /* Finish hashes */ - unsigned char bh[32]; /* SHA-256 = 32 Bytes, SHA-1 = 20 Bytes */ - if (sig->algo == PDKIM_ALGO_RSA_SHA1) - sha1_finish(sig->sha1_body,bh); - else - sha2_finish(sig->sha2_body,bh); - - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, "PDKIM [%s] Body bytes hashed: %lu\n", - sig->domain, sig->signed_body_bytes); - fprintf(ctx->debug_stream, "PDKIM [%s] bh computed: ", sig->domain); - pdkim_hexprint(ctx->debug_stream, (char *)bh, - (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32,1); +int +pdkim_finish_bodyhash(pdkim_ctx *ctx) +{ +pdkim_signature *sig = ctx->sig; + +/* Traverse all signatures */ +while (sig) + { /* Finish hashes */ + unsigned char bh[32]; /* SHA-256 = 32 Bytes, SHA-1 = 20 Bytes */ + + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_finish(sig->sha1_body, bh); + else + sha2_finish(sig->sha2_body, bh); + + DEBUG(D_acl) + { + debug_printf("PDKIM [%s] Body bytes hashed: %lu\n" + "PDKIM [%s] bh computed: ", + sig->domain, sig->signed_body_bytes, sig->domain); + pdkim_hexprint((char *)bh, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1); } - #endif - - /* SIGNING -------------------------------------------------------------- */ - if (ctx->mode == PDKIM_MODE_SIGN) { - sig->bodyhash_len = (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32; - sig->bodyhash = malloc(sig->bodyhash_len); - if (sig->bodyhash == NULL) return PDKIM_ERR_OOM; - memcpy(sig->bodyhash,bh,sig->bodyhash_len); - - /* If bodylength limit is set, and we have received less bytes - than the requested amount, effectively remove the limit tag. */ - if (sig->signed_body_bytes < sig->bodylength) sig->bodylength = -1; + + /* SIGNING -------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) + { + sig->bodyhash_len = (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32; + + if (!(sig->bodyhash = malloc(sig->bodyhash_len))) + return PDKIM_ERR_OOM; + memcpy(sig->bodyhash, bh, sig->bodyhash_len); + + /* If bodylength limit is set, and we have received less bytes + than the requested amount, effectively remove the limit tag. */ + if (sig->signed_body_bytes < sig->bodylength) + sig->bodylength = -1; } - /* VERIFICATION --------------------------------------------------------- */ - else { - /* Compare bodyhash */ - if (memcmp(bh,sig->bodyhash, - (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0) { - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, "PDKIM [%s] Body hash verified OK\n", - sig->domain); - #endif + + /* VERIFICATION --------------------------------------------------------- */ + else + { + /* Compare bodyhash */ + if (memcmp(bh, sig->bodyhash, + (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0) + { + DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain); } - else { - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, "PDKIM [%s] Body hash did NOT verify\n", - sig->domain); - fprintf(ctx->debug_stream, "PDKIM [%s] bh signature: ", sig->domain); - pdkim_hexprint(ctx->debug_stream, sig->bodyhash, - (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32,1); - } - #endif - sig->verify_status = PDKIM_VERIFY_FAIL; - sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY; + else + { + DEBUG(D_acl) + { + debug_printf("PDKIM [%s] bh signature: ", sig->domain); + pdkim_hexprint(sig->bodyhash, + sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1); + debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain); + } + sig->verify_status = PDKIM_VERIFY_FAIL; + sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY; } } - sig = sig->next; + sig = sig->next; } - return PDKIM_OK; +return PDKIM_OK; } /* -------------------------------------------------------------------------- */ /* Callback from pdkim_feed below for processing complete body lines */ -int pdkim_bodyline_complete(pdkim_ctx *ctx) { - char *p = ctx->linebuf; - int n = ctx->linebuf_offset; - /* Ignore extra data if we've seen the end-of-data marker */ - if (ctx->seen_eod) goto BAIL; +static int +pdkim_bodyline_complete(pdkim_ctx *ctx) +{ +char *p = ctx->linebuf; +int n = ctx->linebuf_offset; +pdkim_signature *sig = ctx->sig; /*XXX assumes only one sig */ + +/* Ignore extra data if we've seen the end-of-data marker */ +if (ctx->seen_eod) goto BAIL; + +/* We've always got one extra byte to stuff a zero ... */ +ctx->linebuf[ctx->linebuf_offset] = '\0'; + +/* Terminate on EOD marker */ +if (memcmp(p, ".\r\n", 3) == 0) + { + /* In simple body mode, if any empty lines were buffered, + replace with one. rfc 4871 3.4.3 */ + /*XXX checking the signed-body-bytes is a gross hack; I think + it indicates that all linebreaks should be buffered, including + the one terminating a text line */ + if ( sig && sig->canon_body == PDKIM_CANON_SIMPLE + && sig->signed_body_bytes == 0 + && ctx->num_buffered_crlf > 0 + ) + pdkim_update_bodyhash(ctx, "\r\n", 2); + + ctx->seen_eod = TRUE; + goto BAIL; + } +/* Unstuff dots */ +if (memcmp(p, "..", 2) == 0) + { + p++; + n--; + } - /* We've always got one extra byte to stuff a zero ... */ - ctx->linebuf[(ctx->linebuf_offset)] = '\0'; +/* Empty lines need to be buffered until we find a non-empty line */ +if (memcmp(p, "\r\n", 2) == 0) + { + ctx->num_buffered_crlf++; + goto BAIL; + } - if (ctx->input_mode == PDKIM_INPUT_SMTP) { - /* Terminate on EOD marker */ - if (memcmp(p,".\r\n",3) == 0) { - ctx->seen_eod = 1; - goto BAIL; - } - /* Unstuff dots */ - if (memcmp(p,"..",2) == 0) { - p++; - n--; +if (sig && sig->canon_body == PDKIM_CANON_RELAXED) + { + /* Lines with just spaces need to be buffered too */ + char *check = p; + while (memcmp(check, "\r\n", 2) != 0) + { + char c = *check; + + if (c != '\t' && c != ' ') + goto PROCESS; + check++; } - } - /* Empty lines need to be buffered until we find a non-empty line */ - if (memcmp(p,"\r\n",2) == 0) { - ctx->num_buffered_crlf++; - goto BAIL; - } + ctx->num_buffered_crlf++; + goto BAIL; +} - /* At this point, we have a non-empty line, so release the buffered ones. */ - while (ctx->num_buffered_crlf) { - pdkim_update_bodyhash(ctx,"\r\n",2); - ctx->num_buffered_crlf--; +PROCESS: +/* At this point, we have a non-empty line, so release the buffered ones. */ +while (ctx->num_buffered_crlf) + { + pdkim_update_bodyhash(ctx, "\r\n", 2); + ctx->num_buffered_crlf--; } - pdkim_update_bodyhash(ctx,p,n); +pdkim_update_bodyhash(ctx, p, n); - BAIL: - ctx->linebuf_offset = 0; - return PDKIM_OK; +BAIL: +ctx->linebuf_offset = 0; +return PDKIM_OK; } /* -------------------------------------------------------------------------- */ /* Callback from pdkim_feed below for processing complete headers */ #define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:" -int pdkim_header_complete(pdkim_ctx *ctx) { - pdkim_signature *sig = ctx->sig; - - /* Special case: The last header can have an extra \r appended */ - if ( (ctx->cur_header->len > 1) && - (ctx->cur_header->str[(ctx->cur_header->len)-1] == '\r') ) { - ctx->cur_header->str[(ctx->cur_header->len)-1] = '\0'; - ctx->cur_header->len--; + +int +pdkim_header_complete(pdkim_ctx *ctx) +{ +/* Special case: The last header can have an extra \r appended */ +if ( (ctx->cur_header->len > 1) && + (ctx->cur_header->str[(ctx->cur_header->len)-1] == '\r') ) + { + ctx->cur_header->str[(ctx->cur_header->len)-1] = '\0'; + ctx->cur_header->len--; } - ctx->num_headers++; - if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL; +ctx->num_headers++; +if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL; - /* SIGNING -------------------------------------------------------------- */ - if (ctx->mode == PDKIM_MODE_SIGN) { - /* Traverse all signatures */ - while (sig != NULL) { - pdkim_stringlist *list; +/* SIGNING -------------------------------------------------------------- */ +if (ctx->mode == PDKIM_MODE_SIGN) + { + pdkim_signature *sig; - if (header_name_match(ctx->cur_header->str, - sig->sign_headers? - sig->sign_headers: - PDKIM_DEFAULT_SIGN_HEADERS, 0) != PDKIM_OK) goto NEXT_SIG; + for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */ + if (header_name_match(ctx->cur_header->str, + sig->sign_headers? + sig->sign_headers: + PDKIM_DEFAULT_SIGN_HEADERS, 0) == PDKIM_OK) + { + pdkim_stringlist *list; /* Add header to the signed headers list (in reverse order) */ - list = pdkim_prepend_stringlist(sig->headers, - ctx->cur_header->str); - if (list == NULL) return PDKIM_ERR_OOM; + if (!(list = pdkim_prepend_stringlist(sig->headers, + ctx->cur_header->str))) + return PDKIM_ERR_OOM; sig->headers = list; - - NEXT_SIG: - sig = sig->next; - } + } } - /* DKIM-Signature: headers are added to the verification list */ - if (ctx->mode == PDKIM_MODE_VERIFY) { - if (strncasecmp(ctx->cur_header->str, - DKIM_SIGNATURE_HEADERNAME, - strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) { - pdkim_signature *new_sig; - /* Create and chain new signature block */ - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, - "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - #endif - new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str); - if (new_sig != NULL) { - pdkim_signature *last_sig = ctx->sig; - if (last_sig == NULL) { - ctx->sig = new_sig; - } - else { - while (last_sig->next != NULL) { last_sig = last_sig->next; } - last_sig->next = new_sig; - } - } - else { - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream,"Error while parsing signature header\n"); - fprintf(ctx->debug_stream, - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - } - #endif +/* VERIFICATION ----------------------------------------------------------- */ +/* DKIM-Signature: headers are added to the verification list */ +if (ctx->mode == PDKIM_MODE_VERIFY) + { + if (strncasecmp(ctx->cur_header->str, + DKIM_SIGNATURE_HEADERNAME, + strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) + { + pdkim_signature *new_sig; + + /* Create and chain new signature block */ + DEBUG(D_acl) debug_printf( + "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + + if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str))) + { + pdkim_signature *last_sig = ctx->sig; + if (!last_sig) + ctx->sig = new_sig; + else + { + while (last_sig->next) last_sig = last_sig->next; + last_sig->next = new_sig; + } } + else + DEBUG(D_acl) debug_printf( + "Error while parsing signature header\n" + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } - /* every other header is stored for signature verification */ - else { - pdkim_stringlist *list; - list = pdkim_prepend_stringlist(ctx->headers, - ctx->cur_header->str); - if (list == NULL) return PDKIM_ERR_OOM; - ctx->headers = list; + /* every other header is stored for signature verification */ + else + { + pdkim_stringlist *list; + + if (!(list = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->str))) + return PDKIM_ERR_OOM; + ctx->headers = list; } } - BAIL: - pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */ - return PDKIM_OK; +BAIL: +pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */ +return PDKIM_OK; } /* -------------------------------------------------------------------------- */ #define HEADER_BUFFER_FRAG_SIZE 256 -DLLEXPORT int pdkim_feed (pdkim_ctx *ctx, - char *data, - int len) { - int p; - for (p=0;p<len;p++) { - char c = data[p]; - if (ctx->past_headers) { - /* Processing body byte */ - ctx->linebuf[(ctx->linebuf_offset)++] = c; - if (c == '\n') { - int rc = pdkim_bodyline_complete(ctx); /* End of line */ - if (rc != PDKIM_OK) return rc; + +DLLEXPORT int +pdkim_feed (pdkim_ctx *ctx, char *data, int len) +{ +int p; + +for (p = 0; p<len; p++) + { + char c = data[p]; + + if (ctx->past_headers) + { + /* Processing body byte */ + ctx->linebuf[ctx->linebuf_offset++] = c; + if (c == '\n') + { + int rc = pdkim_bodyline_complete(ctx); /* End of line */ + if (rc != PDKIM_OK) return rc; } - if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1)) - return PDKIM_ERR_LONG_LINE; + if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1)) + return PDKIM_ERR_LONG_LINE; } - else { - /* Processing header byte */ - if (c != '\r') { - if (c == '\n') { - if (ctx->seen_lf) { - int rc = pdkim_header_complete(ctx); /* Seen last header line */ - if (rc != PDKIM_OK) return rc; - ctx->past_headers = 1; - ctx->seen_lf = 0; -#ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, - "PDKIM >> Hashed body data, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); -#endif - continue; - } - else ctx->seen_lf = 1; - } - else if (ctx->seen_lf) { - if (! ((c == '\t') || (c == ' '))) { - int rc = pdkim_header_complete(ctx); /* End of header */ - if (rc != PDKIM_OK) return rc; - } - ctx->seen_lf = 0; - } - } - if (ctx->cur_header == NULL) { - ctx->cur_header = pdkim_strnew(NULL); - if (ctx->cur_header == NULL) return PDKIM_ERR_OOM; + else + { + /* Processing header byte */ + if (c != '\r') + { + if (c == '\n') + { + if (ctx->seen_lf) + { + int rc = pdkim_header_complete(ctx); /* Seen last header line */ + if (rc != PDKIM_OK) return rc; + + ctx->past_headers = TRUE; + ctx->seen_lf = 0; + DEBUG(D_acl) debug_printf( + "PDKIM >> Hashed body data, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + continue; + } + else + ctx->seen_lf = TRUE; + } + else if (ctx->seen_lf) + { + if (!(c == '\t' || c == ' ')) + { + int rc = pdkim_header_complete(ctx); /* End of header */ + if (rc != PDKIM_OK) return rc; + } + ctx->seen_lf = FALSE; + } } - if (ctx->cur_header->len < PDKIM_MAX_HEADER_LEN) - if (pdkim_strncat(ctx->cur_header,&data[p],1) == NULL) - return PDKIM_ERR_OOM; + + if (!ctx->cur_header) + if (!(ctx->cur_header = pdkim_strnew(NULL))) + return PDKIM_ERR_OOM; + + if (ctx->cur_header->len < PDKIM_MAX_HEADER_LEN) + if (!pdkim_strncat(ctx->cur_header, &data[p], 1)) + return PDKIM_ERR_OOM; } } - return PDKIM_OK; +return PDKIM_OK; } +/* + * RFC 5322 specifies that header line length SHOULD be no more than 78 + * lets make it so! + * pdkim_headcat + * returns char* + * + * col: this int holds and receives column number (octets since last '\n') + * str: partial string to append to + * pad: padding, split line or space after before or after eg: ";" + * intro: - must join to payload eg "h=", usually the tag name + * payload: eg base64 data - long data can be split arbitrarily. + * + * this code doesn't fold the header in some of the places that RFC4871 + * allows: As per RFC5322(2.2.3) it only folds before or after tag-value + * pairs and inside long values. it also always spaces or breaks after the + * "pad" + * + * no guarantees are made for output given out-of range input. like tag + * names loinger than 78, or bogus col. Input is assumed to be free of line breaks. + */ -/* -------------------------------------------------------------------------- */ -char *pdkim_create_header(pdkim_signature *sig, int final) { - char *rc = NULL; - char *base64_bh = NULL; - char *base64_b = NULL; - pdkim_str *hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION); - if (hdr == NULL) return NULL; - - base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len); - if (base64_bh == NULL) goto BAIL; - - /* Required and static bits */ - if ( - pdkim_strcat(hdr,"; a=") && - pdkim_strcat(hdr,pdkim_algos[sig->algo]) && - pdkim_strcat(hdr,"; q=") && - pdkim_strcat(hdr,pdkim_querymethods[sig->querymethod]) && - pdkim_strcat(hdr,"; c=") && - pdkim_strcat(hdr,pdkim_canons[sig->canon_headers]) && - pdkim_strcat(hdr,"/") && - pdkim_strcat(hdr,pdkim_canons[sig->canon_body]) && - pdkim_strcat(hdr,"; d=") && - pdkim_strcat(hdr,sig->domain) && - pdkim_strcat(hdr,"; s=") && - pdkim_strcat(hdr,sig->selector) && - pdkim_strcat(hdr,";\r\n\th=") && - pdkim_strcat(hdr,sig->headernames) && - pdkim_strcat(hdr,"; bh=") && - pdkim_strcat(hdr,base64_bh) && - pdkim_strcat(hdr,";\r\n\t") - ) { - /* Optional bits */ - if (sig->identity != NULL) { - if (!( pdkim_strcat(hdr,"i=") && - pdkim_strcat(hdr,sig->identity) && - pdkim_strcat(hdr,";") ) ) { - goto BAIL; - } - } - if (sig->created > 0) { - if (!( pdkim_strcat(hdr,"t=") && - pdkim_numcat(hdr,sig->created) && - pdkim_strcat(hdr,";") ) ) { - goto BAIL; - } - } - if (sig->expires > 0) { - if (!( pdkim_strcat(hdr,"x=") && - pdkim_numcat(hdr,sig->expires) && - pdkim_strcat(hdr,";") ) ) { - goto BAIL; - } +static char * +pdkim_headcat(int *col, pdkim_str *str, const char * pad, + const char *intro, const char *payload) +{ +size_t l; + +if (pad) + { + l = strlen(pad); + if (*col + l > 78) + { + pdkim_strcat(str, "\r\n\t"); + *col = 1; } - if (sig->bodylength >= 0) { - if (!( pdkim_strcat(hdr,"l=") && - pdkim_numcat(hdr,sig->bodylength) && - pdkim_strcat(hdr,";") ) ) { - goto BAIL; - } - } - /* Extra linebreak */ - if (hdr->str[(hdr->len)-1] == ';') { - if (!pdkim_strcat(hdr," \r\n\t")) goto BAIL; + pdkim_strncat(str, pad, l); + *col += l; + } + +l = (pad?1:0) + (intro?strlen(intro):0); + +if (*col + l > 78) + { /*can't fit intro - start a new line to make room.*/ + pdkim_strcat(str, "\r\n\t"); + *col = 1; + l = intro?strlen(intro):0; + } + +l += payload ? strlen(payload):0 ; + +while (l>77) + { /* this fragment will not fit on a single line */ + if (pad) + { + pdkim_strcat(str, " "); + *col += 1; + pad = NULL; /* only want this once */ + l--; } - /* Preliminary or final version? */ - if (final) { - base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len); - if (base64_b == NULL) goto BAIL; - if ( - pdkim_strcat(hdr,"b=") && - pdkim_strcat(hdr,base64_b) && - pdkim_strcat(hdr,";") - ) goto DONE; + + if (intro) + { + size_t sl = strlen(intro); + + pdkim_strncat(str, intro, sl); + *col += sl; + l -= sl; + intro = NULL; /* only want this once */ } - else { - if (pdkim_strcat(hdr,"b=;")) goto DONE; + + if (payload) + { + size_t sl = strlen(payload); + size_t chomp = *col+sl < 77 ? sl : 78-*col; + + pdkim_strncat(str, payload, chomp); + *col += chomp; + payload += chomp; + l -= chomp-1; } - goto BAIL; + /* the while precondition tells us it didn't fit. */ + pdkim_strcat(str, "\r\n\t"); + *col = 1; + } + +if (*col + l > 78) + { + pdkim_strcat(str, "\r\n\t"); + *col = 1; + pad = NULL; } - DONE: - rc = strdup(hdr->str); +if (pad) + { + pdkim_strcat(str, " "); + *col += 1; + pad = NULL; + } + +if (intro) + { + size_t sl = strlen(intro); + + pdkim_strncat(str, intro, sl); + *col += sl; + l -= sl; + intro = NULL; + } - BAIL: - pdkim_strfree(hdr); - if (base64_bh != NULL) free(base64_bh); - if (base64_b != NULL) free(base64_b); - return rc; +if (payload) + { + size_t sl = strlen(payload); + + pdkim_strncat(str, payload, sl); + *col += sl; + } + +return str->str; } /* -------------------------------------------------------------------------- */ -DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures) { - pdkim_signature *sig = ctx->sig; - pdkim_str *headernames = NULL; /* Collected signed header names */ - - /* Check if we must still flush a (partial) header. If that is the - case, the message has no body, and we must compute a body hash - out of '<CR><LF>' */ - if (ctx->cur_header && ctx->cur_header->len) { - int rc = pdkim_header_complete(ctx); - if (rc != PDKIM_OK) return rc; - pdkim_update_bodyhash(ctx,"\r\n",2); + +char * +pdkim_create_header(pdkim_signature *sig, int final) +{ +char *rc = NULL; +char *base64_bh = NULL; +char *base64_b = NULL; +int col = 0; +pdkim_str *hdr; +pdkim_str *canon_all; + +if (!(hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION))) + return NULL; + +if (!(canon_all = pdkim_strnew(pdkim_canons[sig->canon_headers]))) + goto BAIL; + +if (!(base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len))) + goto BAIL; + +col = strlen(hdr->str); + +/* Required and static bits */ +if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo]) + && pdkim_headcat(&col, hdr, ";", "q=", pdkim_querymethods[sig->querymethod]) + && pdkim_strcat(canon_all, "/") + && pdkim_strcat(canon_all, pdkim_canons[sig->canon_body]) + && pdkim_headcat(&col, hdr, ";", "c=", canon_all->str) + && pdkim_headcat(&col, hdr, ";", "d=", sig->domain) + && pdkim_headcat(&col, hdr, ";", "s=", sig->selector) + ) + { + /* list of eader names can be split between items. */ + { + char *n = strdup(sig->headernames); + char *f = n; + char *i = "h="; + char *s = ";"; + + if (!n) goto BAIL; + while (*n) + { + char *c = strchr(n, ':'); + + if (c) *c ='\0'; + + if (!i) + if (!pdkim_headcat(&col, hdr, NULL, NULL, ":")) + { + free(f); + goto BAIL; + } + + if (!pdkim_headcat(&col, hdr, s, i, n)) + { + free(f); + goto BAIL; + } + + if (!c) + break; + + n = c+1; + s = NULL; + i = NULL; + } + free(f); + } + + if(!pdkim_headcat(&col, hdr, ";", "bh=", base64_bh)) + goto BAIL; + + /* Optional bits */ + if (sig->identity) + if(!pdkim_headcat(&col, hdr, ";", "i=", sig->identity)) + goto BAIL; + + if (sig->created > 0) + { + char minibuf[20]; + + snprintf(minibuf, 20, "%lu", sig->created); + if(!pdkim_headcat(&col, hdr, ";", "t=", minibuf)) + goto BAIL; } - else { - /* For non-smtp input, check if there's an unfinished line in the - body line buffer. If that is the case, we must add a CRLF to the - hash to properly terminate the message. */ - if ((ctx->input_mode == PDKIM_INPUT_NORMAL) && ctx->linebuf_offset) { - pdkim_update_bodyhash(ctx, ctx->linebuf, ctx->linebuf_offset); - pdkim_update_bodyhash(ctx,"\r\n",2); + + if (sig->expires > 0) + { + char minibuf[20]; + + snprintf(minibuf, 20, "%lu", sig->expires); + if(!pdkim_headcat(&col, hdr, ";", "x=", minibuf)) + goto BAIL; } - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - #endif + + if (sig->bodylength >= 0) + { + char minibuf[20]; + + snprintf(minibuf, 20, "%lu", sig->bodylength); + if(!pdkim_headcat(&col, hdr, ";", "l=", minibuf)) + goto BAIL; + } + + /* Preliminary or final version? */ + if (final) + { + if (!(base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len))) + goto BAIL; + if (!pdkim_headcat(&col, hdr, ";", "b=", base64_b)) + goto BAIL; } + else + if(!pdkim_headcat(&col, hdr, ";", "b=", "")) + goto BAIL; - /* Build (and/or evaluate) body hash */ - if (pdkim_finish_bodyhash(ctx) != PDKIM_OK) return PDKIM_ERR_OOM; + /* add trailing semicolon: I'm not sure if this is actually needed */ + if (!pdkim_headcat(&col, hdr, NULL, ";", "")) + goto BAIL; + } - /* SIGNING -------------------------------------------------------------- */ - if (ctx->mode == PDKIM_MODE_SIGN) { - headernames = pdkim_strnew(NULL); - if (headernames == NULL) return PDKIM_ERR_OOM; +rc = strdup(hdr->str); + +BAIL: +pdkim_strfree(hdr); +if (canon_all) pdkim_strfree(canon_all); +if (base64_bh) free(base64_bh); +if (base64_b ) free(base64_b); +return rc; +} + + +/* -------------------------------------------------------------------------- */ + +DLLEXPORT int +pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures) +{ +pdkim_signature *sig = ctx->sig; +pdkim_str *headernames = NULL; /* Collected signed header names */ + +/* Check if we must still flush a (partial) header. If that is the + case, the message has no body, and we must compute a body hash + out of '<CR><LF>' */ +if (ctx->cur_header && ctx->cur_header->len) + { + int rc = pdkim_header_complete(ctx); + if (rc != PDKIM_OK) return rc; + pdkim_update_bodyhash(ctx, "\r\n", 2); } - /* ---------------------------------------------------------------------- */ +else + DEBUG(D_acl) debug_printf( + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - while (sig != NULL) { - sha1_context sha1_headers; - sha2_context sha2_headers; - char *sig_hdr; - char headerhash[32]; +/* Build (and/or evaluate) body hash */ +if (pdkim_finish_bodyhash(ctx) != PDKIM_OK) + return PDKIM_ERR_OOM; - if (sig->algo == PDKIM_ALGO_RSA_SHA1) - sha1_starts(&sha1_headers); - else - sha2_starts(&sha2_headers,0); - - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, - "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n"); - #endif - - /* SIGNING ---------------------------------------------------------------- */ - /* When signing, walk through our header list and add them to the hash. As we - go, construct a list of the header's names to use for the h= parameter. */ - if (ctx->mode == PDKIM_MODE_SIGN) { - pdkim_stringlist *p = sig->headers; - while (p != NULL) { - char *rh = NULL; - /* Collect header names (Note: colon presence is guaranteed here) */ - char *q = strchr(p->value,':'); - if (pdkim_strncat(headernames, p->value, - (q-(p->value))+((p->next==NULL)?0:1)) == NULL) - return PDKIM_ERR_OOM; - - if (sig->canon_headers == PDKIM_CANON_RELAXED) - rh = pdkim_relax_header(p->value,1); /* cook header for relaxed canon */ - else - rh = strdup(p->value); /* just copy it for simple canon */ - - if (rh == NULL) return PDKIM_ERR_OOM; - - /* Feed header to the hash algorithm */ - if (sig->algo == PDKIM_ALGO_RSA_SHA1) - sha1_update(&(sha1_headers),(unsigned char *)rh,strlen(rh)); - else - sha2_update(&(sha2_headers),(unsigned char *)rh,strlen(rh)); - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1); - #endif - free(rh); - p = p->next; +/* SIGNING -------------------------------------------------------------- */ +if (ctx->mode == PDKIM_MODE_SIGN) + if (!(headernames = pdkim_strnew(NULL))) + return PDKIM_ERR_OOM; +/* ---------------------------------------------------------------------- */ + +while (sig) + { + sha1_context sha1_headers; + sha2_context sha2_headers; + char *sig_hdr; + char headerhash[32]; + + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_starts(&sha1_headers); + else + sha2_starts(&sha2_headers, 0); + + DEBUG(D_acl) debug_printf( + "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n"); + + /* SIGNING ---------------------------------------------------------------- */ + /* When signing, walk through our header list and add them to the hash. As we + go, construct a list of the header's names to use for the h= parameter. */ + + if (ctx->mode == PDKIM_MODE_SIGN) + { + pdkim_stringlist *p; + + for (p = sig->headers; p; p = p->next) + { + char *rh = NULL; + /* Collect header names (Note: colon presence is guaranteed here) */ + char *q = strchr(p->value, ':'); + + if (!(pdkim_strncat(headernames, p->value, + (q-(p->value)) + (p->next ? 1 : 0)))) + return PDKIM_ERR_OOM; + + rh = sig->canon_headers == PDKIM_CANON_RELAXED + ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */ + : strdup(p->value); /* just copy it for simple canon */ + if (!rh) + return PDKIM_ERR_OOM; + + /* Feed header to the hash algorithm */ + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_update(&(sha1_headers), (unsigned char *)rh, strlen(rh)); + else + sha2_update(&(sha2_headers), (unsigned char *)rh, strlen(rh)); + + DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1); + free(rh); } } - /* VERIFICATION ----------------------------------------------------------- */ - /* When verifying, walk through the header name list in the h= parameter and - add the headers to the hash in that order. */ - else { - char *b = strdup(sig->headernames); - char *p = b; - char *q = NULL; - pdkim_stringlist *hdrs = ctx->headers; - - if (b == NULL) return PDKIM_ERR_OOM; - - /* clear tags */ - while (hdrs != NULL) { - hdrs->tag = 0; - hdrs = hdrs->next; - } - while(1) { - hdrs = ctx->headers; - q = strchr(p,':'); - if (q != NULL) *q = '\0'; - while (hdrs != NULL) { - if ( (hdrs->tag == 0) && - (strncasecmp(hdrs->value,p,strlen(p)) == 0) && - ((hdrs->value)[strlen(p)] == ':') ) { - char *rh = NULL; - if (sig->canon_headers == PDKIM_CANON_RELAXED) - rh = pdkim_relax_header(hdrs->value,1); /* cook header for relaxed canon */ - else - rh = strdup(hdrs->value); /* just copy it for simple canon */ - if (rh == NULL) return PDKIM_ERR_OOM; - /* Feed header to the hash algorithm */ - if (sig->algo == PDKIM_ALGO_RSA_SHA1) - sha1_update(&(sha1_headers),(unsigned char *)rh,strlen(rh)); - else - sha2_update(&(sha2_headers),(unsigned char *)rh,strlen(rh)); - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1); - #endif - free(rh); - hdrs->tag = 1; - break; - } - hdrs = hdrs->next; - } - if (q == NULL) break; - p = q+1; + /* VERIFICATION ----------------------------------------------------------- */ + /* When verifying, walk through the header name list in the h= parameter and + add the headers to the hash in that order. */ + else + { + char *b = strdup(sig->headernames); + char *p = b; + char *q = NULL; + pdkim_stringlist *hdrs; + + if (!b) return PDKIM_ERR_OOM; + + /* clear tags */ + for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next) + hdrs->tag = 0; + + while(1) + { + if ((q = strchr(p, ':'))) + *q = '\0'; + + for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next) + if ( hdrs->tag == 0 + && strncasecmp(hdrs->value, p, strlen(p)) == 0 + && (hdrs->value)[strlen(p)] == ':' + ) + { + char *rh; + + rh = sig->canon_headers == PDKIM_CANON_RELAXED + ? pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */ + : strdup(hdrs->value); /* just copy it for simple canon */ + if (!rh) + return PDKIM_ERR_OOM; + + /* Feed header to the hash algorithm */ + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + sha1_update(&sha1_headers, (unsigned char *)rh, strlen(rh)); + else + sha2_update(&sha2_headers, (unsigned char *)rh, strlen(rh)); + + DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1); + free(rh); + hdrs->tag = 1; + break; + } + + if (!q) break; + p = q+1; } - free(b); + free(b); } - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) - fprintf(ctx->debug_stream, - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - #endif + DEBUG(D_acl) debug_printf( + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - /* SIGNING ---------------------------------------------------------------- */ - if (ctx->mode == PDKIM_MODE_SIGN) { - /* Copy headernames to signature struct */ - sig->headernames = strdup(headernames->str); - pdkim_strfree(headernames); + /* SIGNING ---------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) + { + /* Copy headernames to signature struct */ + sig->headernames = strdup(headernames->str); + pdkim_strfree(headernames); - /* Create signature header with b= omitted */ - sig_hdr = pdkim_create_header(ctx->sig,0); - } - /* VERIFICATION ----------------------------------------------------------- */ - else { - sig_hdr = strdup(sig->rawsig_no_b_val); + /* Create signature header with b= omitted */ + sig_hdr = pdkim_create_header(ctx->sig, 0); } - /* ------------------------------------------------------------------------ */ - if (sig_hdr == NULL) return PDKIM_ERR_OOM; + /* VERIFICATION ----------------------------------------------------------- */ + else + sig_hdr = strdup(sig->rawsig_no_b_val); + /* ------------------------------------------------------------------------ */ - /* Relax header if necessary */ - if (sig->canon_headers == PDKIM_CANON_RELAXED) { - char *relaxed_hdr = pdkim_relax_header(sig_hdr,0); - free(sig_hdr); - if (relaxed_hdr == NULL) return PDKIM_ERR_OOM; - sig_hdr = relaxed_hdr; + if (!sig_hdr) + return PDKIM_ERR_OOM; + + /* Relax header if necessary */ + if (sig->canon_headers == PDKIM_CANON_RELAXED) + { + char *relaxed_hdr = pdkim_relax_header(sig_hdr, 0); + + free(sig_hdr); + if (!relaxed_hdr) + return PDKIM_ERR_OOM; + sig_hdr = relaxed_hdr; } - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, - "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n"); - pdkim_quoteprint(ctx->debug_stream, sig_hdr, strlen(sig_hdr), 1); - fprintf(ctx->debug_stream, - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + DEBUG(D_acl) + { + debug_printf( + "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n"); + pdkim_quoteprint(sig_hdr, strlen(sig_hdr), 1); + debug_printf( + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } - #endif - - /* Finalize header hash */ - if (sig->algo == PDKIM_ALGO_RSA_SHA1) { - sha1_update(&(sha1_headers),(unsigned char *)sig_hdr,strlen(sig_hdr)); - sha1_finish(&(sha1_headers),(unsigned char *)headerhash); - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain); - pdkim_hexprint(ctx->debug_stream, headerhash, 20, 1); + + /* Finalize header hash */ + if (sig->algo == PDKIM_ALGO_RSA_SHA1) + { + sha1_update(&sha1_headers, (unsigned char *)sig_hdr, strlen(sig_hdr)); + sha1_finish(&sha1_headers, (unsigned char *)headerhash); + + DEBUG(D_acl) + { + debug_printf( "PDKIM [%s] hh computed: ", sig->domain); + pdkim_hexprint(headerhash, 20, 1); } - #endif } - else { - sha2_update(&(sha2_headers),(unsigned char *)sig_hdr,strlen(sig_hdr)); - sha2_finish(&(sha2_headers),(unsigned char *)headerhash); - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain); - pdkim_hexprint(ctx->debug_stream, headerhash, 32, 1); + else + { + sha2_update(&sha2_headers, (unsigned char *)sig_hdr, strlen(sig_hdr)); + sha2_finish(&sha2_headers, (unsigned char *)headerhash); + + DEBUG(D_acl) + { + debug_printf("PDKIM [%s] hh computed: ", sig->domain); + pdkim_hexprint(headerhash, 32, 1); } - #endif } - free(sig_hdr); + free(sig_hdr); - /* SIGNING ---------------------------------------------------------------- */ - if (ctx->mode == PDKIM_MODE_SIGN) { - rsa_context rsa; + /* SIGNING ---------------------------------------------------------------- */ + if (ctx->mode == PDKIM_MODE_SIGN) + { + rsa_context rsa; - rsa_init(&rsa,RSA_PKCS_V15,0); + rsa_init(&rsa, RSA_PKCS_V15, 0); - /* Perform private key operation */ - if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey, - strlen(sig->rsa_privkey), NULL, 0) != 0) { - return PDKIM_ERR_RSA_PRIVKEY; - } + /* Perform private key operation */ + if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey, + strlen(sig->rsa_privkey), NULL, 0) != 0) + return PDKIM_ERR_RSA_PRIVKEY; - sig->sigdata_len = mpi_size(&(rsa.N)); - sig->sigdata = malloc(sig->sigdata_len); - if (sig->sigdata == NULL) return PDKIM_ERR_OOM; - - if (rsa_pkcs1_sign( &rsa, RSA_PRIVATE, - ((sig->algo == PDKIM_ALGO_RSA_SHA1)? - SIG_RSA_SHA1:SIG_RSA_SHA256), - 0, - (unsigned char *)headerhash, - (unsigned char *)sig->sigdata ) != 0) { - return PDKIM_ERR_RSA_SIGNING; - } + sig->sigdata_len = mpi_size(&(rsa.N)); + if (!(sig->sigdata = malloc(sig->sigdata_len))) + return PDKIM_ERR_OOM; + + if (rsa_pkcs1_sign( &rsa, RSA_PRIVATE, + ((sig->algo == PDKIM_ALGO_RSA_SHA1)? + SIG_RSA_SHA1:SIG_RSA_SHA256), + 0, + (unsigned char *)headerhash, + (unsigned char *)sig->sigdata ) != 0) + return PDKIM_ERR_RSA_SIGNING; - rsa_free(&rsa); + rsa_free(&rsa); - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, "PDKIM [%s] b computed: ", - sig->domain); - pdkim_hexprint(ctx->debug_stream, sig->sigdata, sig->sigdata_len, 1); + DEBUG(D_acl) + { + debug_printf( "PDKIM [%s] b computed: ", sig->domain); + pdkim_hexprint(sig->sigdata, sig->sigdata_len, 1); } - #endif - sig->signature_header = pdkim_create_header(ctx->sig,1); - if (sig->signature_header == NULL) return PDKIM_ERR_OOM; + if (!(sig->signature_header = pdkim_create_header(ctx->sig, 1))) + return PDKIM_ERR_OOM; } - /* VERIFICATION ----------------------------------------------------------- */ - else { - rsa_context rsa; - char *dns_txt_name, *dns_txt_reply; - - rsa_init(&rsa,RSA_PKCS_V15,0); - - dns_txt_name = malloc(PDKIM_DNS_TXT_MAX_NAMELEN); - if (dns_txt_name == NULL) return PDKIM_ERR_OOM; - dns_txt_reply = malloc(PDKIM_DNS_TXT_MAX_RECLEN); - if (dns_txt_reply == NULL) { - free(dns_txt_name); - return PDKIM_ERR_OOM; - } - memset(dns_txt_reply,0,PDKIM_DNS_TXT_MAX_RECLEN); - memset(dns_txt_name ,0,PDKIM_DNS_TXT_MAX_NAMELEN); - - if (snprintf(dns_txt_name,PDKIM_DNS_TXT_MAX_NAMELEN, - "%s._domainkey.%s.", - sig->selector,sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN) { - sig->verify_status = PDKIM_VERIFY_INVALID; - sig->verify_ext_status = PDKIM_VERIFY_INVALID_BUFFER_SIZE; - goto NEXT_VERIFY; - } - if ((ctx->dns_txt_callback(dns_txt_name, dns_txt_reply) != PDKIM_OK) || - (dns_txt_reply[0] == '\0')) { - sig->verify_status = PDKIM_VERIFY_INVALID; - sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE; - goto NEXT_VERIFY; + /* VERIFICATION ----------------------------------------------------------- */ + else + { + rsa_context rsa; + char *dns_txt_name, *dns_txt_reply; + + rsa_init(&rsa, RSA_PKCS_V15, 0); + + if (!(dns_txt_name = malloc(PDKIM_DNS_TXT_MAX_NAMELEN))) + return PDKIM_ERR_OOM; + + if (!(dns_txt_reply = malloc(PDKIM_DNS_TXT_MAX_RECLEN))) + { + free(dns_txt_name); + return PDKIM_ERR_OOM; } - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, - "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - fprintf(ctx->debug_stream,"Raw record: "); - pdkim_quoteprint(ctx->debug_stream, dns_txt_reply, strlen(dns_txt_reply), 1); + memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN); + memset(dns_txt_name , 0, PDKIM_DNS_TXT_MAX_NAMELEN); + + if (snprintf(dns_txt_name, PDKIM_DNS_TXT_MAX_NAMELEN, + "%s._domainkey.%s.", + sig->selector, sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN) + { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_BUFFER_SIZE; + goto NEXT_VERIFY; } - #endif - - sig->pubkey = pdkim_parse_pubkey_record(ctx,dns_txt_reply); - if (sig->pubkey == NULL) { - sig->verify_status = PDKIM_VERIFY_INVALID; - sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING; - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream,"Error while parsing public key record\n"); - fprintf(ctx->debug_stream, - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - } - #endif - goto NEXT_VERIFY; + + if ( ctx->dns_txt_callback(dns_txt_name, dns_txt_reply) != PDKIM_OK + || dns_txt_reply[0] == '\0') + { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE; + goto NEXT_VERIFY; } - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + DEBUG(D_acl) + { + debug_printf( + "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + " Raw record: "); + pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply), 1); } - #endif - - if (rsa_parse_public_key(&rsa, - (unsigned char *)sig->pubkey->key, - sig->pubkey->key_len) != 0) { - sig->verify_status = PDKIM_VERIFY_INVALID; - sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING; - goto NEXT_VERIFY; + + if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, dns_txt_reply))) + { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING; + + DEBUG(D_acl) debug_printf( + " Error while parsing public key record\n" + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + goto NEXT_VERIFY; } - /* Check the signature */ - if (rsa_pkcs1_verify(&rsa, - RSA_PUBLIC, - ((sig->algo == PDKIM_ALGO_RSA_SHA1)? - SIG_RSA_SHA1:SIG_RSA_SHA256), - 0, - (unsigned char *)headerhash, - (unsigned char *)sig->sigdata) != 0) { - sig->verify_status = PDKIM_VERIFY_FAIL; - sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE; - goto NEXT_VERIFY; + DEBUG(D_acl) debug_printf( + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + + if (rsa_parse_public_key(&rsa, + (unsigned char *)sig->pubkey->key, + sig->pubkey->key_len) != 0) + { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_PARSING; + goto NEXT_VERIFY; } - /* We have a winner! (if bodydhash was correct earlier) */ - if (sig->verify_status == PDKIM_VERIFY_NONE) { - sig->verify_status = PDKIM_VERIFY_PASS; + /* Check the signature */ + if (rsa_pkcs1_verify(&rsa, + RSA_PUBLIC, + ((sig->algo == PDKIM_ALGO_RSA_SHA1)? + SIG_RSA_SHA1:SIG_RSA_SHA256), + 0, + (unsigned char *)headerhash, + (unsigned char *)sig->sigdata) != 0) + { + sig->verify_status = PDKIM_VERIFY_FAIL; + sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE; + goto NEXT_VERIFY; } - NEXT_VERIFY: - - #ifdef PDKIM_DEBUG - if (ctx->debug_stream) { - fprintf(ctx->debug_stream, "PDKIM [%s] signature status: %s", - sig->domain, pdkim_verify_status_str(sig->verify_status)); - if (sig->verify_ext_status > 0) { - fprintf(ctx->debug_stream, " (%s)\n", - pdkim_verify_ext_status_str(sig->verify_ext_status)); - } - else { - fprintf(ctx->debug_stream, "\n"); - } + /* We have a winner! (if bodydhash was correct earlier) */ + if (sig->verify_status == PDKIM_VERIFY_NONE) + sig->verify_status = PDKIM_VERIFY_PASS; + +NEXT_VERIFY: + + DEBUG(D_acl) + { + debug_printf("PDKIM [%s] signature status: %s", + sig->domain, pdkim_verify_status_str(sig->verify_status)); + if (sig->verify_ext_status > 0) + debug_printf(" (%s)\n", + pdkim_verify_ext_status_str(sig->verify_ext_status)); + else + debug_printf("\n"); } - #endif - rsa_free(&rsa); - free(dns_txt_name); - free(dns_txt_reply); + rsa_free(&rsa); + free(dns_txt_name); + free(dns_txt_reply); } - sig = sig->next; + sig = sig->next; } - /* If requested, set return pointer to signature(s) */ - if (return_signatures != NULL) { - *return_signatures = ctx->sig; - } +/* If requested, set return pointer to signature(s) */ +if (return_signatures) + *return_signatures = ctx->sig; - return PDKIM_OK; +return PDKIM_OK; } /* -------------------------------------------------------------------------- */ -DLLEXPORT pdkim_ctx *pdkim_init_verify(int input_mode, - int(*dns_txt_callback)(char *, char *) - ) { - pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx)); - if (ctx == NULL) return NULL; - memset(ctx,0,sizeof(pdkim_ctx)); - - ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN); - if (ctx->linebuf == NULL) { - free(ctx); - return NULL; + +DLLEXPORT pdkim_ctx * +pdkim_init_verify(int(*dns_txt_callback)(char *, char *)) +{ +pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx)); + +if (!ctx) + return NULL; +memset(ctx, 0, sizeof(pdkim_ctx)); + +if (!(ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN))) + { + free(ctx); + return NULL; } - ctx->mode = PDKIM_MODE_VERIFY; - ctx->input_mode = input_mode; - ctx->dns_txt_callback = dns_txt_callback; +ctx->mode = PDKIM_MODE_VERIFY; +ctx->dns_txt_callback = dns_txt_callback; - return ctx; +return ctx; } /* -------------------------------------------------------------------------- */ -DLLEXPORT pdkim_ctx *pdkim_init_sign(int input_mode, - char *domain, - char *selector, - char *rsa_privkey) { - pdkim_ctx *ctx; - pdkim_signature *sig; - if (!domain || !selector || !rsa_privkey) return NULL; +DLLEXPORT pdkim_ctx * +pdkim_init_sign(char *domain, char *selector, char *rsa_privkey) +{ +pdkim_ctx *ctx; +pdkim_signature *sig; - ctx = malloc(sizeof(pdkim_ctx)); - if (ctx == NULL) return NULL; - memset(ctx,0,sizeof(pdkim_ctx)); +if (!domain || !selector || !rsa_privkey) + return NULL; - ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN); - if (ctx->linebuf == NULL) { - free(ctx); - return NULL; +if (!(ctx = malloc(sizeof(pdkim_ctx)))) + return NULL; +memset(ctx, 0, sizeof(pdkim_ctx)); + +if (!(ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN))) + { + free(ctx); + return NULL; } - sig = malloc(sizeof(pdkim_signature)); - if (sig == NULL) { - free(ctx->linebuf); - free(ctx); - return NULL; +if (!(sig = malloc(sizeof(pdkim_signature)))) + { + free(ctx->linebuf); + free(ctx); + return NULL; } - memset(sig,0,sizeof(pdkim_signature)); - sig->bodylength = -1; +memset(sig, 0, sizeof(pdkim_signature)); - ctx->mode = PDKIM_MODE_SIGN; - ctx->input_mode = input_mode; - ctx->sig = sig; +sig->bodylength = -1; - ctx->sig->domain = strdup(domain); - ctx->sig->selector = strdup(selector); - ctx->sig->rsa_privkey = strdup(rsa_privkey); +ctx->mode = PDKIM_MODE_SIGN; +ctx->sig = sig; - if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey) { - pdkim_free_ctx(ctx); - return NULL; - } +ctx->sig->domain = strdup(domain); +ctx->sig->selector = strdup(selector); +ctx->sig->rsa_privkey = strdup(rsa_privkey); - ctx->sig->sha1_body = malloc(sizeof(sha1_context)); - if (ctx->sig->sha1_body == NULL) { - pdkim_free_ctx(ctx); - return NULL; - } - sha1_starts(ctx->sig->sha1_body); +if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey) + goto BAIL; - ctx->sig->sha2_body = malloc(sizeof(sha2_context)); - if (ctx->sig->sha2_body == NULL) { - pdkim_free_ctx(ctx); - return NULL; - } - sha2_starts(ctx->sig->sha2_body,0); +if (!(ctx->sig->sha1_body = malloc(sizeof(sha1_context)))) + goto BAIL; +sha1_starts(ctx->sig->sha1_body); - return ctx; -} +if (!(ctx->sig->sha2_body = malloc(sizeof(sha2_context)))) + goto BAIL; +sha2_starts(ctx->sig->sha2_body, 0); -#ifdef PDKIM_DEBUG -/* -------------------------------------------------------------------------- */ -DLLEXPORT void pdkim_set_debug_stream(pdkim_ctx *ctx, - FILE *debug_stream) { - ctx->debug_stream = debug_stream; +return ctx; + +BAIL: + pdkim_free_ctx(ctx); + return NULL; } -#endif /* -------------------------------------------------------------------------- */ -DLLEXPORT int pdkim_set_optional(pdkim_ctx *ctx, + +DLLEXPORT int +pdkim_set_optional(pdkim_ctx *ctx, char *sign_headers, char *identity, int canon_headers, @@ -1750,24 +2010,25 @@ DLLEXPORT int pdkim_set_optional(pdkim_ctx *ctx, long bodylength, int algo, unsigned long created, - unsigned long expires) { + unsigned long expires) +{ - if (identity != NULL) { - ctx->sig->identity = strdup(identity); - if (ctx->sig->identity == NULL) return PDKIM_ERR_OOM; - } +if (identity) + if (!(ctx->sig->identity = strdup(identity))) + return PDKIM_ERR_OOM; - if (sign_headers != NULL) { - ctx->sig->sign_headers = strdup(sign_headers); - if (ctx->sig->sign_headers == NULL) return PDKIM_ERR_OOM; - } +if (sign_headers) + if (!(ctx->sig->sign_headers = strdup(sign_headers))) + return PDKIM_ERR_OOM; - ctx->sig->canon_headers = canon_headers; - ctx->sig->canon_body = canon_body; - ctx->sig->bodylength = bodylength; - ctx->sig->algo = algo; - ctx->sig->created = created; - ctx->sig->expires = expires; +ctx->sig->canon_headers = canon_headers; +ctx->sig->canon_body = canon_body; +ctx->sig->bodylength = bodylength; +ctx->sig->algo = algo; +ctx->sig->created = created; +ctx->sig->expires = expires; - return PDKIM_OK; +return PDKIM_OK; } + + diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h index 1d364a3c9..4a7498db9 100644 --- a/src/src/pdkim/pdkim.h +++ b/src/src/pdkim/pdkim.h @@ -21,11 +21,6 @@ */ /* -------------------------------------------------------------------------- */ -/* Debugging. This can also be enabled/disabled at run-time. I recommend to - leave it defined. */ -#define PDKIM_DEBUG - -/* -------------------------------------------------------------------------- */ /* Length of the preallocated buffer for the "answer" from the dns/txt callback function. This should match the maximum RDLENGTH from DNS. */ #define PDKIM_DNS_TXT_MAX_RECLEN (1 << 16) @@ -245,16 +240,11 @@ typedef struct pdkim_signature { /* Context to keep state between all operations. */ #define PDKIM_MODE_SIGN 0 #define PDKIM_MODE_VERIFY 1 -#define PDKIM_INPUT_NORMAL 0 -#define PDKIM_INPUT_SMTP 1 typedef struct pdkim_ctx { /* PDKIM_MODE_VERIFY or PDKIM_MODE_SIGN */ int mode; - /* PDKIM_INPUT_SMTP or PDKIM_INPUT_NORMAL */ - int input_mode; - /* One (signing) or several chained (verification) signatures */ pdkim_signature *sig; @@ -265,19 +255,12 @@ typedef struct pdkim_ctx { pdkim_str *cur_header; char *linebuf; int linebuf_offset; - int seen_lf; - int seen_eod; - int past_headers; + BOOL seen_lf; + BOOL seen_eod; + BOOL past_headers; int num_buffered_crlf; int num_headers; pdkim_stringlist *headers; /* Raw headers for verification */ - -#ifdef PDKIM_DEBUG - /* A FILE pointer. When not NULL, debug output will be generated - and sent to this stream */ - FILE *debug_stream; -#endif - } pdkim_ctx; @@ -291,10 +274,10 @@ extern "C" { #endif DLLEXPORT -pdkim_ctx *pdkim_init_sign (int, char *, char *, char *); +pdkim_ctx *pdkim_init_sign (char *, char *, char *); DLLEXPORT -pdkim_ctx *pdkim_init_verify (int, int(*)(char *, char *)); +pdkim_ctx *pdkim_init_verify (int(*)(char *, char *)); DLLEXPORT int pdkim_set_optional (pdkim_ctx *, char *, char *,int, int, @@ -310,11 +293,6 @@ int pdkim_feed_finish (pdkim_ctx *, pdkim_signature **); DLLEXPORT void pdkim_free_ctx (pdkim_ctx *); -#ifdef PDKIM_DEBUG -DLLEXPORT -void pdkim_set_debug_stream(pdkim_ctx *, FILE *); -#endif - #ifdef __cplusplus } #endif diff --git a/src/src/pdkim/base64.h b/src/src/pdkim/polarssl/base64.h index a3fd39547..2ae416920 100644 --- a/src/src/pdkim/base64.h +++ b/src/src/pdkim/polarssl/base64.h @@ -22,7 +22,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifndef POLARSSL_BASE64_H #define POLARSSL_BASE64_H @@ -70,6 +69,13 @@ int base64_encode( unsigned char *dst, int *dlen, int base64_decode( unsigned char *dst, int *dlen, const unsigned char *src, int slen ); +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int base64_self_test( int verbose ); + #ifdef __cplusplus } #endif diff --git a/src/src/pdkim/bignum.h b/src/src/pdkim/polarssl/bignum.h index 581f7f21c..31aa15cfe 100644 --- a/src/src/pdkim/bignum.h +++ b/src/src/pdkim/polarssl/bignum.h @@ -22,7 +22,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifndef POLARSSL_BIGNUM_H #define POLARSSL_BIGNUM_H @@ -520,6 +519,13 @@ int mpi_is_prime( mpi *X, int (*f_rng)(void *), void *p_rng ); int mpi_gen_prime( mpi *X, int nbits, int dh_flag, int (*f_rng)(void *), void *p_rng ); +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mpi_self_test( int verbose ); + #ifdef __cplusplus } #endif diff --git a/src/src/pdkim/polarssl/bn_mul.h b/src/src/pdkim/polarssl/bn_mul.h new file mode 100644 index 000000000..03558b484 --- /dev/null +++ b/src/src/pdkim/polarssl/bn_mul.h @@ -0,0 +1,1472 @@ +/** + * \file bn_mul.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org> + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "polarssl/config.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( " \ + movl %%ebx, %0; \ + movl %5, %%esi; \ + movl %6, %%edi; \ + movl %7, %%ecx; \ + movl %8, %%ebx; \ + " + +#define MULADDC_CORE \ + " \ + lodsl; \ + mull %%ebx; \ + addl %%ecx, %%eax; \ + adcl $0, %%edx; \ + addl (%%edi), %%eax; \ + adcl $0, %%edx; \ + movl %%edx, %%ecx; \ + stosl; \ + " + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + " \ + movd %%ecx, %%mm1; \ + movd %%ebx, %%mm0; \ + movd (%%edi), %%mm3; \ + paddq %%mm3, %%mm1; \ + movd (%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + movd 4(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + movd 8(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd 12(%%esi), %%mm7; \ + pmuludq %%mm0, %%mm7; \ + paddq %%mm2, %%mm1; \ + movd 4(%%edi), %%mm3; \ + paddq %%mm4, %%mm3; \ + movd 8(%%edi), %%mm5; \ + paddq %%mm6, %%mm5; \ + movd 12(%%edi), %%mm4; \ + paddq %%mm4, %%mm7; \ + movd %%mm1, (%%edi); \ + movd 16(%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + psrlq $32, %%mm1; \ + movd 20(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + paddq %%mm3, %%mm1; \ + movd 24(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd %%mm1, 4(%%edi); \ + psrlq $32, %%mm1; \ + movd 28(%%esi), %%mm3; \ + pmuludq %%mm0, %%mm3; \ + paddq %%mm5, %%mm1; \ + movd 16(%%edi), %%mm5; \ + paddq %%mm5, %%mm2; \ + movd %%mm1, 8(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm7, %%mm1; \ + movd 20(%%edi), %%mm5; \ + paddq %%mm5, %%mm4; \ + movd %%mm1, 12(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm2, %%mm1; \ + movd 24(%%edi), %%mm5; \ + paddq %%mm5, %%mm6; \ + movd %%mm1, 16(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm4, %%mm1; \ + movd 28(%%edi), %%mm5; \ + paddq %%mm5, %%mm3; \ + movd %%mm1, 20(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm6, %%mm1; \ + movd %%mm1, 24(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm3, %%mm1; \ + movd %%mm1, 28(%%edi); \ + addl $32, %%edi; \ + addl $32, %%esi; \ + psrlq $32, %%mm1; \ + movd %%mm1, %%ecx; \ + " + +#define MULADDC_STOP \ + " \ + emms; \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + " \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) + +#define MULADDC_INIT \ + asm( "ld %0, %%o0 " :: "m" (s)); \ + asm( "ld %0, %%o1 " :: "m" (d)); \ + asm( "ld %0, %%o2 " :: "m" (c)); \ + asm( "ld %0, %%o3 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ld [%o0], %o4 " ); \ + asm( "inc 4, %o0 " ); \ + asm( "ld [%o1], %o5 " ); \ + asm( "umul %o3, %o4, %o4 " ); \ + asm( "addcc %o4, %o2, %o4 " ); \ + asm( "rd %y, %g1 " ); \ + asm( "addx %g1, 0, %g1 " ); \ + asm( "addcc %o4, %o5, %o4 " ); \ + asm( "st %o4, [%o1] " ); \ + asm( "addx %g1, 0, %o2 " ); \ + asm( "inc 4, %o1 " ); + +#define MULADDC_STOP \ + asm( "st %%o2, %0 " : "=m" (c)); \ + asm( "st %%o1, %0 " : "=m" (d)); \ + asm( "st %%o0, %0 " : "=m" (s) :: \ + "g1", "o0", "o1", "o2", "o3", "o4", "o5" ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) + +#define MULADDC_INIT \ + asm( "ldr r0, %0 " :: "m" (s)); \ + asm( "ldr r1, %0 " :: "m" (d)); \ + asm( "ldr r2, %0 " :: "m" (c)); \ + asm( "ldr r3, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldr r4, [r0], #4 " ); \ + asm( "mov r5, #0 " ); \ + asm( "ldr r6, [r1] " ); \ + asm( "umlal r2, r5, r3, r4 " ); \ + asm( "adds r7, r6, r2 " ); \ + asm( "adc r2, r5, #0 " ); \ + asm( "str r7, [r1], #4 " ); + +#define MULADDC_STOP \ + asm( "str r2, %0 " : "=m" (c)); \ + asm( "str r1, %0 " : "=m" (d)); \ + asm( "str r0, %0 " : "=m" (s) :: \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" ); + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_LONGLONG) + +#define MULADDC_INIT \ +{ \ + t_dbl r; \ + t_int r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_dbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_int s0, s1, b0, b1; \ + t_int r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ +/** + * \file bn_mul.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org> + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "polarssl/config.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( " \ + movl %%ebx, %0; \ + movl %5, %%esi; \ + movl %6, %%edi; \ + movl %7, %%ecx; \ + movl %8, %%ebx; \ + " + +#define MULADDC_CORE \ + " \ + lodsl; \ + mull %%ebx; \ + addl %%ecx, %%eax; \ + adcl $0, %%edx; \ + addl (%%edi), %%eax; \ + adcl $0, %%edx; \ + movl %%edx, %%ecx; \ + stosl; \ + " + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + " \ + movd %%ecx, %%mm1; \ + movd %%ebx, %%mm0; \ + movd (%%edi), %%mm3; \ + paddq %%mm3, %%mm1; \ + movd (%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + movd 4(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + movd 8(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd 12(%%esi), %%mm7; \ + pmuludq %%mm0, %%mm7; \ + paddq %%mm2, %%mm1; \ + movd 4(%%edi), %%mm3; \ + paddq %%mm4, %%mm3; \ + movd 8(%%edi), %%mm5; \ + paddq %%mm6, %%mm5; \ + movd 12(%%edi), %%mm4; \ + paddq %%mm4, %%mm7; \ + movd %%mm1, (%%edi); \ + movd 16(%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + psrlq $32, %%mm1; \ + movd 20(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + paddq %%mm3, %%mm1; \ + movd 24(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd %%mm1, 4(%%edi); \ + psrlq $32, %%mm1; \ + movd 28(%%esi), %%mm3; \ + pmuludq %%mm0, %%mm3; \ + paddq %%mm5, %%mm1; \ + movd 16(%%edi), %%mm5; \ + paddq %%mm5, %%mm2; \ + movd %%mm1, 8(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm7, %%mm1; \ + movd 20(%%edi), %%mm5; \ + paddq %%mm5, %%mm4; \ + movd %%mm1, 12(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm2, %%mm1; \ + movd 24(%%edi), %%mm5; \ + paddq %%mm5, %%mm6; \ + movd %%mm1, 16(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm4, %%mm1; \ + movd 28(%%edi), %%mm5; \ + paddq %%mm5, %%mm3; \ + movd %%mm1, 20(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm6, %%mm1; \ + movd %%mm1, 24(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm3, %%mm1; \ + movd %%mm1, 28(%%edi); \ + addl $32, %%edi; \ + addl $32, %%esi; \ + psrlq $32, %%mm1; \ + movd %%mm1, %%ecx; \ + " + +#define MULADDC_STOP \ + " \ + emms; \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + " \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) + +#define MULADDC_INIT \ + asm( "ld %0, %%o0 " :: "m" (s)); \ + asm( "ld %0, %%o1 " :: "m" (d)); \ + asm( "ld %0, %%o2 " :: "m" (c)); \ + asm( "ld %0, %%o3 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ld [%o0], %o4 " ); \ + asm( "inc 4, %o0 " ); \ + asm( "ld [%o1], %o5 " ); \ + asm( "umul %o3, %o4, %o4 " ); \ + asm( "addcc %o4, %o2, %o4 " ); \ + asm( "rd %y, %g1 " ); \ + asm( "addx %g1, 0, %g1 " ); \ + asm( "addcc %o4, %o5, %o4 " ); \ + asm( "st %o4, [%o1] " ); \ + asm( "addx %g1, 0, %o2 " ); \ + asm( "inc 4, %o1 " ); + +#define MULADDC_STOP \ + asm( "st %%o2, %0 " : "=m" (c)); \ + asm( "st %%o1, %0 " : "=m" (d)); \ + asm( "st %%o0, %0 " : "=m" (s) :: \ + "g1", "o0", "o1", "o2", "o3", "o4", "o5" ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) + +#define MULADDC_INIT \ + asm( "ldr r0, %0 " :: "m" (s)); \ + asm( "ldr r1, %0 " :: "m" (d)); \ + asm( "ldr r2, %0 " :: "m" (c)); \ + asm( "ldr r3, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldr r4, [r0], #4 " ); \ + asm( "mov r5, #0 " ); \ + asm( "ldr r6, [r1] " ); \ + asm( "umlal r2, r5, r3, r4 " ); \ + asm( "adds r7, r6, r2 " ); \ + asm( "adc r2, r5, #0 " ); \ + asm( "str r7, [r1], #4 " ); + +#define MULADDC_STOP \ + asm( "str r2, %0 " : "=m" (c)); \ + asm( "str r1, %0 " : "=m" (d)); \ + asm( "str r0, %0 " : "=m" (s) :: \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" ); + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_LONGLONG) + +#define MULADDC_INIT \ +{ \ + t_dbl r; \ + t_int r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_dbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_int s0, s1, b0, b1; \ + t_int r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/src/src/pdkim/polarssl/config.h b/src/src/pdkim/polarssl/config.h new file mode 100644 index 000000000..bfe1557d1 --- /dev/null +++ b/src/src/pdkim/polarssl/config.h @@ -0,0 +1,5 @@ +#define POLARSSL_BASE64_C +#define POLARSSL_BIGNUM_C +#define POLARSSL_SHA1_C +#define POLARSSL_SHA2_C +#define POLARSSL_RSA_C diff --git a/src/src/pdkim/polarssl/part-x509.h b/src/src/pdkim/polarssl/part-x509.h new file mode 100644 index 000000000..991d0ac50 --- /dev/null +++ b/src/src/pdkim/polarssl/part-x509.h @@ -0,0 +1,95 @@ +/* *************** begin copy from x509.h ************************/ +/** + * \file x509.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org> + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef POLARSSL_PART_X509_H +#define POLARSSL_PART_X509_H + +/* + * ASN1 Error codes + * + * These error codes will be OR'ed to X509 error codes for + * higher error granularity. + */ +#define POLARSSL_ERR_ASN1_OUT_OF_DATA 0x0014 +#define POLARSSL_ERR_ASN1_UNEXPECTED_TAG 0x0016 +#define POLARSSL_ERR_ASN1_INVALID_LENGTH 0x0018 +#define POLARSSL_ERR_ASN1_LENGTH_MISMATCH 0x001A +#define POLARSSL_ERR_ASN1_INVALID_DATA 0x001C + +/* + * X509 Error codes + */ +#define POLARSSL_ERR_X509_FEATURE_UNAVAILABLE -0x0020 +#define POLARSSL_ERR_X509_CERT_INVALID_PEM -0x0040 +#define POLARSSL_ERR_X509_CERT_INVALID_FORMAT -0x0060 +#define POLARSSL_ERR_X509_CERT_INVALID_VERSION -0x0080 +#define POLARSSL_ERR_X509_CERT_INVALID_SERIAL -0x00A0 +#define POLARSSL_ERR_X509_CERT_INVALID_ALG -0x00C0 +#define POLARSSL_ERR_X509_CERT_INVALID_NAME -0x00E0 +#define POLARSSL_ERR_X509_CERT_INVALID_DATE -0x0100 +#define POLARSSL_ERR_X509_CERT_INVALID_PUBKEY -0x0120 +#define POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE -0x0140 +#define POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS -0x0160 +#define POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION -0x0180 +#define POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG -0x01A0 +#define POLARSSL_ERR_X509_CERT_UNKNOWN_PK_ALG -0x01C0 +#define POLARSSL_ERR_X509_CERT_SIG_MISMATCH -0x01E0 +#define POLARSSL_ERR_X509_CERT_VERIFY_FAILED -0x0200 +#define POLARSSL_ERR_X509_KEY_INVALID_PEM -0x0220 +#define POLARSSL_ERR_X509_KEY_INVALID_VERSION -0x0240 +#define POLARSSL_ERR_X509_KEY_INVALID_FORMAT -0x0260 +#define POLARSSL_ERR_X509_KEY_INVALID_ENC_IV -0x0280 +#define POLARSSL_ERR_X509_KEY_UNKNOWN_ENC_ALG -0x02A0 +#define POLARSSL_ERR_X509_KEY_PASSWORD_REQUIRED -0x02C0 +#define POLARSSL_ERR_X509_KEY_PASSWORD_MISMATCH -0x02E0 +#define POLARSSL_ERR_X509_POINT_ERROR -0x0300 +#define POLARSSL_ERR_X509_VALUE_TO_LENGTH -0x0320 + +/* + * DER constants + */ +#define ASN1_BOOLEAN 0x01 +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_UTF8_STRING 0x0C +#define ASN1_SEQUENCE 0x10 +#define ASN1_SET 0x11 +#define ASN1_PRINTABLE_STRING 0x13 +#define ASN1_T61_STRING 0x14 +#define ASN1_IA5_STRING 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_GENERALIZED_TIME 0x18 +#define ASN1_UNIVERSAL_STRING 0x1C +#define ASN1_BMP_STRING 0x1E +#define ASN1_PRIMITIVE 0x00 +#define ASN1_CONSTRUCTED 0x20 +#define ASN1_CONTEXT_SPECIFIC 0x80 +/* *************** end copy from x509.h ************************/ + +#endif /* part-x509.h */ diff --git a/src/src/pdkim/polarssl/private-x509parse_c.h b/src/src/pdkim/polarssl/private-x509parse_c.h new file mode 100644 index 000000000..112a11400 --- /dev/null +++ b/src/src/pdkim/polarssl/private-x509parse_c.h @@ -0,0 +1,50 @@ +/* Functions defined in x509parse.c, which are not exported (static there) */ +/* + * X.509 certificate and private key decoding + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org> + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ITU-T X.509 standard defines a certificat format for PKI. + * + * http://www.ietf.org/rfc/rfc2459.txt + * http://www.ietf.org/rfc/rfc3279.txt + * + * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + + + +int asn1_get_mpi( unsigned char **p, + const unsigned char *end, + mpi *X ); + +int asn1_get_tag( unsigned char **p, + const unsigned char *end, + int *len, int tag ); + +int asn1_get_int( unsigned char **p, + const unsigned char *end, + int *val ); diff --git a/src/src/pdkim/rsa.h b/src/src/pdkim/polarssl/rsa.h index af6823b09..5fae7947c 100644 --- a/src/src/pdkim/rsa.h +++ b/src/src/pdkim/polarssl/rsa.h @@ -22,11 +22,10 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifndef POLARSSL_RSA_H #define POLARSSL_RSA_H -#include "bignum.h" +#include "polarssl/bignum.h" /* * RSA Error codes @@ -41,72 +40,6 @@ #define POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE -0x0470 #define POLARSSL_ERR_RSA_RNG_FAILED -0x0480 -/* *************** begin copy from x509.h ************************/ -/* - * ASN1 Error codes - * - * These error codes will be OR'ed to X509 error codes for - * higher error granularity. - */ -#define POLARSSL_ERR_ASN1_OUT_OF_DATA 0x0014 -#define POLARSSL_ERR_ASN1_UNEXPECTED_TAG 0x0016 -#define POLARSSL_ERR_ASN1_INVALID_LENGTH 0x0018 -#define POLARSSL_ERR_ASN1_LENGTH_MISMATCH 0x001A -#define POLARSSL_ERR_ASN1_INVALID_DATA 0x001C - -/* - * X509 Error codes - */ -#define POLARSSL_ERR_X509_FEATURE_UNAVAILABLE -0x0020 -#define POLARSSL_ERR_X509_CERT_INVALID_PEM -0x0040 -#define POLARSSL_ERR_X509_CERT_INVALID_FORMAT -0x0060 -#define POLARSSL_ERR_X509_CERT_INVALID_VERSION -0x0080 -#define POLARSSL_ERR_X509_CERT_INVALID_SERIAL -0x00A0 -#define POLARSSL_ERR_X509_CERT_INVALID_ALG -0x00C0 -#define POLARSSL_ERR_X509_CERT_INVALID_NAME -0x00E0 -#define POLARSSL_ERR_X509_CERT_INVALID_DATE -0x0100 -#define POLARSSL_ERR_X509_CERT_INVALID_PUBKEY -0x0120 -#define POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE -0x0140 -#define POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS -0x0160 -#define POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION -0x0180 -#define POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG -0x01A0 -#define POLARSSL_ERR_X509_CERT_UNKNOWN_PK_ALG -0x01C0 -#define POLARSSL_ERR_X509_CERT_SIG_MISMATCH -0x01E0 -#define POLARSSL_ERR_X509_CERT_VERIFY_FAILED -0x0200 -#define POLARSSL_ERR_X509_KEY_INVALID_PEM -0x0220 -#define POLARSSL_ERR_X509_KEY_INVALID_VERSION -0x0240 -#define POLARSSL_ERR_X509_KEY_INVALID_FORMAT -0x0260 -#define POLARSSL_ERR_X509_KEY_INVALID_ENC_IV -0x0280 -#define POLARSSL_ERR_X509_KEY_UNKNOWN_ENC_ALG -0x02A0 -#define POLARSSL_ERR_X509_KEY_PASSWORD_REQUIRED -0x02C0 -#define POLARSSL_ERR_X509_KEY_PASSWORD_MISMATCH -0x02E0 -#define POLARSSL_ERR_X509_POINT_ERROR -0x0300 -#define POLARSSL_ERR_X509_VALUE_TO_LENGTH -0x0320 - -/* - * DER constants - */ -#define ASN1_BOOLEAN 0x01 -#define ASN1_INTEGER 0x02 -#define ASN1_BIT_STRING 0x03 -#define ASN1_OCTET_STRING 0x04 -#define ASN1_NULL 0x05 -#define ASN1_OID 0x06 -#define ASN1_UTF8_STRING 0x0C -#define ASN1_SEQUENCE 0x10 -#define ASN1_SET 0x11 -#define ASN1_PRINTABLE_STRING 0x13 -#define ASN1_T61_STRING 0x14 -#define ASN1_IA5_STRING 0x16 -#define ASN1_UTC_TIME 0x17 -#define ASN1_GENERALIZED_TIME 0x18 -#define ASN1_UNIVERSAL_STRING 0x1C -#define ASN1_BMP_STRING 0x1E -#define ASN1_PRIMITIVE 0x00 -#define ASN1_CONSTRUCTED 0x20 -#define ASN1_CONTEXT_SPECIFIC 0x80 -/* *************** end copy from x509.h ************************/ - /* * PKCS#1 constants */ @@ -114,11 +47,11 @@ #define SIG_RSA_MD2 2 #define SIG_RSA_MD4 3 #define SIG_RSA_MD5 4 -#define SIG_RSA_SHA1 5 -#define SIG_RSA_SHA224 14 -#define SIG_RSA_SHA256 11 -#define SIG_RSA_SHA384 12 -#define SIG_RSA_SHA512 13 +#define SIG_RSA_SHA1 5 +#define SIG_RSA_SHA224 14 +#define SIG_RSA_SHA256 11 +#define SIG_RSA_SHA384 12 +#define SIG_RSA_SHA512 13 #define RSA_PUBLIC 0 #define RSA_PRIVATE 1 @@ -129,28 +62,28 @@ #define RSA_SIGN 1 #define RSA_CRYPT 2 -#define ASN1_STR_CONSTRUCTED_SEQUENCE "\x30" -#define ASN1_STR_NULL "\x05" -#define ASN1_STR_OID "\x06" -#define ASN1_STR_OCTET_STRING "\x04" +#define ASN1_STR_CONSTRUCTED_SEQUENCE "\x30" +#define ASN1_STR_NULL "\x05" +#define ASN1_STR_OID "\x06" +#define ASN1_STR_OCTET_STRING "\x04" -#define OID_DIGEST_ALG_MDX "\x2A\x86\x48\x86\xF7\x0D\x02\x00" -#define OID_HASH_ALG_SHA1 "\x2b\x0e\x03\x02\x1a" -#define OID_HASH_ALG_SHA2X "\x60\x86\x48\x01\x65\x03\x04\x02\x00" +#define OID_DIGEST_ALG_MDX "\x2A\x86\x48\x86\xF7\x0D\x02\x00" +#define OID_HASH_ALG_SHA1 "\x2b\x0e\x03\x02\x1a" +#define OID_HASH_ALG_SHA2X "\x60\x86\x48\x01\x65\x03\x04\x02\x00" -#define OID_ISO_MEMBER_BODIES "\x2a" -#define OID_ISO_IDENTIFIED_ORG "\x2b" +#define OID_ISO_MEMBER_BODIES "\x2a" +#define OID_ISO_IDENTIFIED_ORG "\x2b" /* * ISO Member bodies OID parts */ -#define OID_COUNTRY_US "\x86\x48" -#define OID_RSA_DATA_SECURITY "\x86\xf7\x0d" +#define OID_COUNTRY_US "\x86\x48" +#define OID_RSA_DATA_SECURITY "\x86\xf7\x0d" /* * ISO Identified organization OID parts */ -#define OID_OIW_SECSIG_SHA1 "\x0e\x03\x02\x1a" +#define OID_OIW_SECSIG_SHA1 "\x0e\x03\x02\x1a" /* * DigestInfo ::= SEQUENCE { @@ -161,31 +94,31 @@ * * Digest ::= OCTET STRING */ -#define ASN1_HASH_MDX \ -( \ - ASN1_STR_CONSTRUCTED_SEQUENCE "\x20" \ - ASN1_STR_CONSTRUCTED_SEQUENCE "\x0C" \ - ASN1_STR_OID "\x08" \ - OID_DIGEST_ALG_MDX \ - ASN1_STR_NULL "\x00" \ - ASN1_STR_OCTET_STRING "\x10" \ +#define ASN1_HASH_MDX \ +( \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x20" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0C" \ + ASN1_STR_OID "\x08" \ + OID_DIGEST_ALG_MDX \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x10" \ ) -#define ASN1_HASH_SHA1 \ - ASN1_STR_CONSTRUCTED_SEQUENCE "\x21" \ - ASN1_STR_CONSTRUCTED_SEQUENCE "\x09" \ - ASN1_STR_OID "\x05" \ - OID_HASH_ALG_SHA1 \ - ASN1_STR_NULL "\x00" \ - ASN1_STR_OCTET_STRING "\x14" - -#define ASN1_HASH_SHA2X \ - ASN1_STR_CONSTRUCTED_SEQUENCE "\x11" \ - ASN1_STR_CONSTRUCTED_SEQUENCE "\x0d" \ - ASN1_STR_OID "\x09" \ - OID_HASH_ALG_SHA2X \ - ASN1_STR_NULL "\x00" \ - ASN1_STR_OCTET_STRING "\x00" +#define ASN1_HASH_SHA1 \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x21" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x09" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA2X \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x11" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0d" \ + ASN1_STR_OID "\x09" \ + OID_HASH_ALG_SHA2X \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x00" /** * \brief RSA context structure @@ -339,7 +272,7 @@ int rsa_pkcs1_encrypt( rsa_context *ctx, * \param input buffer holding the encrypted data * \param output buffer that will hold the plaintext * \param olen will contain the plaintext length - * \param output_max_len maximum length of the output buffer + * \param output_max_len maximum length of the output buffer * * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code * @@ -351,7 +284,7 @@ int rsa_pkcs1_decrypt( rsa_context *ctx, int mode, int *olen, const unsigned char *input, unsigned char *output, - int output_max_len ); + int output_max_len ); /** * \brief Do a private RSA to sign a message digest @@ -406,11 +339,12 @@ int rsa_pkcs1_verify( rsa_context *ctx, */ void rsa_free( rsa_context *ctx ); -/* PDKIM declarations (not part of polarssl) */ -int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen ); -int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, - unsigned char *pwd, int pwdlen ); - +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int rsa_self_test( int verbose ); #ifdef __cplusplus } diff --git a/src/src/pdkim/sha1.h b/src/src/pdkim/polarssl/sha1.h index 29a9070eb..98e8cc45e 100644 --- a/src/src/pdkim/sha1.h +++ b/src/src/pdkim/polarssl/sha1.h @@ -22,7 +22,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifndef POLARSSL_SHA1_H #define POLARSSL_SHA1_H @@ -33,7 +32,6 @@ #define HAVE_SHA1_CONTEXT typedef struct sha1_context sha1_context; #endif - struct sha1_context { unsigned long total[2]; /*!< number of bytes processed */ @@ -79,7 +77,7 @@ void sha1_finish( sha1_context *ctx, unsigned char output[20] ); * \param ilen length of the input data * \param output SHA-1 checksum result */ -void sha1( const unsigned char *input, int ilen, unsigned char output[20] ); +void polarssl_sha1( const unsigned char *input, int ilen, unsigned char output[20] ); /** * \brief Output = SHA-1( file contents ) @@ -138,6 +136,13 @@ void sha1_hmac( const unsigned char *key, int keylen, const unsigned char *input, int ilen, unsigned char output[20] ); +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int sha1_self_test( int verbose ); + #ifdef __cplusplus } #endif diff --git a/src/src/pdkim/sha2.h b/src/src/pdkim/polarssl/sha2.h index ec2acfa6c..26a6f1c80 100644 --- a/src/src/pdkim/sha2.h +++ b/src/src/pdkim/polarssl/sha2.h @@ -22,7 +22,6 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #ifndef POLARSSL_SHA2_H #define POLARSSL_SHA2_H @@ -146,6 +145,13 @@ void sha2_hmac( const unsigned char *key, int keylen, const unsigned char *input, int ilen, unsigned char output[32], int is224 ); +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int sha2_self_test( int verbose ); + #ifdef __cplusplus } #endif diff --git a/src/src/pdkim/rsa.c b/src/src/pdkim/rsa.c index 68b201d89..86d9f2ffe 100644 --- a/src/src/pdkim/rsa.c +++ b/src/src/pdkim/rsa.c @@ -29,116 +29,16 @@ * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf */ -#include "rsa.h" -#include "base64.h" +#include "polarssl/config.h" + +#if defined(POLARSSL_RSA_C) + +#include "polarssl/rsa.h" #include <stdlib.h> #include <string.h> #include <stdio.h> -/* *************** begin copy from x509parse.c ********************/ -/* - * ASN.1 DER decoding routines - */ -static int asn1_get_len( unsigned char **p, - const unsigned char *end, - int *len ) -{ - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( ( **p & 0x80 ) == 0 ) - *len = *(*p)++; - else - { - switch( **p & 0x7F ) - { - case 1: - if( ( end - *p ) < 2 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = (*p)[1]; - (*p) += 2; - break; - - case 2: - if( ( end - *p ) < 3 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = ( (*p)[1] << 8 ) | (*p)[2]; - (*p) += 3; - break; - - default: - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - break; - } - } - - if( *len > (int) ( end - *p ) ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - return( 0 ); -} - -static int asn1_get_tag( unsigned char **p, - const unsigned char *end, - int *len, int tag ) -{ - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( **p != tag ) - return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); - - (*p)++; - - return( asn1_get_len( p, end, len ) ); -} - -static int asn1_get_int( unsigned char **p, - const unsigned char *end, - int *val ) -{ - int ret, len; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) - return( ret ); - - if( len > (int) sizeof( int ) || ( **p & 0x80 ) != 0 ) - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - - *val = 0; - - while( len-- > 0 ) - { - *val = ( *val << 8 ) | **p; - (*p)++; - } - - return( 0 ); -} - -static int asn1_get_mpi( unsigned char **p, - const unsigned char *end, - mpi *X ) -{ - int ret, len; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) - return( ret ); - - ret = mpi_read_binary( X, *p, len ); - - *p += len; - - return( ret ); -} -/* *************** end copy from x509parse.c ********************/ - - - - /* * Initialize an RSA context */ @@ -178,7 +78,7 @@ int rsa_gen_key( rsa_context *ctx, do { - MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, + MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, f_rng, p_rng ) ); MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0, @@ -224,7 +124,7 @@ cleanup: return( POLARSSL_ERR_RSA_KEY_GEN_FAILED | ret ); } - return( 0 ); + return( 0 ); } #endif @@ -276,7 +176,7 @@ int rsa_check_privkey( const rsa_context *ctx ) MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) ); - MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); + MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) ); /* @@ -291,7 +191,7 @@ int rsa_check_privkey( const rsa_context *ctx ) return( 0 ); } - + cleanup: mpi_free( &G, &I, &H, &Q1, &P1, &DE, &PQ, &G2, &L1, &L2, NULL ); @@ -496,7 +396,7 @@ int rsa_pkcs1_decrypt( rsa_context *ctx, } if (ilen - (int)(p - buf) > output_max_len) - return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); *olen = ilen - (int)(p - buf); memcpy( output, p, *olen ); @@ -713,7 +613,7 @@ int rsa_pkcs1_verify( rsa_context *ctx, ( len == 19 + 48 && p[14] == 2 && hash_id == SIG_RSA_SHA384 ) || ( len == 19 + 64 && p[14] == 3 && hash_id == SIG_RSA_SHA512 ) ) { - c = p[1] - 17; + c = p[1] - 17; p[1] = 17; p[14] = 0; @@ -747,211 +647,177 @@ void rsa_free( rsa_context *ctx ) &ctx->E, &ctx->N, NULL ); } +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/sha1.h" -/* PDKIM code (not copied from polarssl) */ /* - * Parse a public RSA key - -OpenSSL RSA public key ASN1 container - 0:d=0 hl=3 l= 159 cons: SEQUENCE - 3:d=1 hl=2 l= 13 cons: SEQUENCE - 5:d=2 hl=2 l= 9 prim: OBJECT:rsaEncryption - 16:d=2 hl=2 l= 0 prim: NULL - 18:d=1 hl=3 l= 141 prim: BIT STRING:RSAPublicKey (below) - -RSAPublicKey ASN1 container - 0:d=0 hl=3 l= 137 cons: SEQUENCE - 3:d=1 hl=3 l= 129 prim: INTEGER:Public modulus -135:d=1 hl=2 l= 3 prim: INTEGER:Public exponent -*/ - -int rsa_parse_public_key( rsa_context *rsa, unsigned char *buf, int buflen ) + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +static int myrand( void *rng_state ) { - unsigned char *p, *end; - int ret, len; - - p = buf; - end = buf+buflen; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); - } - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) == 0 ) { - /* Skip over embedded rsaEncryption Object */ - p+=len; - - /* The RSAPublicKey ASN1 container is wrapped in a BIT STRING */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_BIT_STRING ) ) != 0 ) { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); - } + if( rng_state != NULL ) + rng_state = NULL; - /* Limit range to that BIT STRING */ - end = p + len; - p++; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); - } - } - - if ( ( ( ret = asn1_get_mpi( &p, end, &(rsa->N) ) ) == 0 ) && - ( ( ret = asn1_get_mpi( &p, end, &(rsa->E) ) ) == 0 ) ) { - rsa->len = mpi_size( &rsa->N ); - return 0; - } - - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + return( rand() ); } /* - * Parse a private RSA key + * Checkup routine */ -int rsa_parse_key( rsa_context *rsa, unsigned char *buf, int buflen, - unsigned char *pwd, int pwdlen ) +int rsa_self_test( int verbose ) { - int ret, len, enc; - unsigned char *s1, *s2; - unsigned char *p, *end; - - s1 = (unsigned char *) strstr( (char *) buf, - "-----BEGIN RSA PRIVATE KEY-----" ); - - if( s1 != NULL ) + int len; + rsa_context rsa; + unsigned char sha1sum[20]; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + rsa.len = KEY_LEN; + mpi_read_string( &rsa.N , 16, RSA_N ); + mpi_read_string( &rsa.E , 16, RSA_E ); + mpi_read_string( &rsa.D , 16, RSA_D ); + mpi_read_string( &rsa.P , 16, RSA_P ); + mpi_read_string( &rsa.Q , 16, RSA_Q ); + mpi_read_string( &rsa.DP, 16, RSA_DP ); + mpi_read_string( &rsa.DQ, 16, RSA_DQ ); + mpi_read_string( &rsa.QP, 16, RSA_QP ); + + if( verbose != 0 ) + printf( " RSA key validation: " ); + + if( rsa_check_pubkey( &rsa ) != 0 || + rsa_check_privkey( &rsa ) != 0 ) { - s2 = (unsigned char *) strstr( (char *) buf, - "-----END RSA PRIVATE KEY-----" ); - - if( s2 == NULL || s2 <= s1 ) - return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); + if( verbose != 0 ) + printf( "failed\n" ); - s1 += 31; - if( *s1 == '\r' ) s1++; - if( *s1 == '\n' ) s1++; - else return( POLARSSL_ERR_X509_KEY_INVALID_PEM ); - - enc = 0; - - if( memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 ) - { - return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); - } - - len = 0; - ret = base64_decode( NULL, &len, s1, s2 - s1 ); - - if( ret == POLARSSL_ERR_BASE64_INVALID_CHARACTER ) - return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM ); - - if( ( buf = (unsigned char *) malloc( len ) ) == NULL ) - return( 1 ); - - if( ( ret = base64_decode( buf, &len, s1, s2 - s1 ) ) != 0 ) - { - free( buf ); - return( ret | POLARSSL_ERR_X509_KEY_INVALID_PEM ); - } - - buflen = len; - - if( enc != 0 ) - { - return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); - } + return( 1 ); } - memset( rsa, 0, sizeof( rsa_context ) ); + if( verbose != 0 ) + printf( "passed\n PKCS#1 encryption : " ); - p = buf; - end = buf + buflen; + memcpy( rsa_plaintext, RSA_PT, PT_LEN ); - /* - * RSAPrivateKey ::= SEQUENCE { - * version Version, - * modulus INTEGER, -- n - * publicExponent INTEGER, -- e - * privateExponent INTEGER, -- d - * prime1 INTEGER, -- p - * prime2 INTEGER, -- q - * exponent1 INTEGER, -- d mod (p-1) - * exponent2 INTEGER, -- d mod (q-1) - * coefficient INTEGER, -- (inverse of q) mod p - * otherPrimeInfos OtherPrimeInfos OPTIONAL - * } - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + if( rsa_pkcs1_encrypt( &rsa, &myrand, NULL, RSA_PUBLIC, PT_LEN, + rsa_plaintext, rsa_ciphertext ) != 0 ) { - if( s1 != NULL ) - free( buf ); + if( verbose != 0 ) + printf( "failed\n" ); - rsa_free( rsa ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + return( 1 ); } - end = p + len; + if( verbose != 0 ) + printf( "passed\n PKCS#1 decryption : " ); - if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) + if( rsa_pkcs1_decrypt( &rsa, RSA_PRIVATE, &len, + rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted) ) != 0 ) { - if( s1 != NULL ) - free( buf ); + if( verbose != 0 ) + printf( "failed\n" ); - rsa_free( rsa ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | ret ); + return( 1 ); } - if( rsa->ver != 0 ) + if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) { - if( s1 != NULL ) - free( buf ); + if( verbose != 0 ) + printf( "failed\n" ); - rsa_free( rsa ); - return( ret | POLARSSL_ERR_X509_KEY_INVALID_VERSION ); + return( 1 ); } - if( ( ret = asn1_get_mpi( &p, end, &rsa->N ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->E ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->D ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->P ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->Q ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->DP ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->DQ ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 ) - { - if( s1 != NULL ) - free( buf ); - - rsa_free( rsa ); - return( ret | POLARSSL_ERR_X509_KEY_INVALID_FORMAT ); - } + if( verbose != 0 ) + printf( "passed\n PKCS#1 data sign : " ); - rsa->len = mpi_size( &rsa->N ); + polarssl_sha1( rsa_plaintext, PT_LEN, sha1sum ); - if( p != end ) + if( rsa_pkcs1_sign( &rsa, RSA_PRIVATE, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) { - if( s1 != NULL ) - free( buf ); + if( verbose != 0 ) + printf( "failed\n" ); - rsa_free( rsa ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT | - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + return( 1 ); } - if( ( ret = rsa_check_privkey( rsa ) ) != 0 ) + if( verbose != 0 ) + printf( "passed\n PKCS#1 sig. verify: " ); + + if( rsa_pkcs1_verify( &rsa, RSA_PUBLIC, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) { - if( s1 != NULL ) - free( buf ); + if( verbose != 0 ) + printf( "failed\n" ); - rsa_free( rsa ); - return( ret ); + return( 1 ); } - if( s1 != NULL ) - free( buf ); + if( verbose != 0 ) + printf( "passed\n\n" ); + + rsa_free( &rsa ); return( 0 ); } + +#endif + +#endif diff --git a/src/src/pdkim/sha1.c b/src/src/pdkim/sha1.c index 81b862f8f..820488843 100644 --- a/src/src/pdkim/sha1.c +++ b/src/src/pdkim/sha1.c @@ -28,7 +28,11 @@ * http://www.itl.nist.gov/fipspubs/fip180-1.htm */ -#include "sha1.h" +#include "polarssl/config.h" + +#if defined(POLARSSL_SHA1_C) + +#include "polarssl/sha1.h" #include <string.h> #include <stdio.h> @@ -311,7 +315,7 @@ void sha1_finish( sha1_context *ctx, unsigned char output[20] ) /* * output = SHA-1( input buffer ) */ -void sha1( const unsigned char *input, int ilen, unsigned char output[20] ) +void polarssl_sha1( const unsigned char *input, int ilen, unsigned char output[20] ) { sha1_context ctx; @@ -364,7 +368,7 @@ void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, int keylen ) if( keylen > 64 ) { - sha1( key, keylen, sum ); + polarssl_sha1( key, keylen, sum ); keylen = 20; key = sum; } @@ -432,3 +436,186 @@ void sha1_hmac( const unsigned char *key, int keylen, memset( &ctx, 0, sizeof( sha1_context ) ); } + +#if defined(POLARSSL_SELF_TEST) +/* + * FIPS-180-1 test vectors + */ +static unsigned char sha1_test_buf[3][57] = +{ + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const int sha1_test_buflen[3] = +{ + 3, 56, 1000 +}; + +static const unsigned char sha1_test_sum[3][20] = +{ + { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, + 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }, + { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, + 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, + { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, + 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } +}; + +/* + * RFC 2202 test vectors + */ +static unsigned char sha1_hmac_test_key[7][26] = +{ + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B" }, + { "Jefe" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C" }, + { "" }, /* 0xAA 80 times */ + { "" } +}; + +static const int sha1_hmac_test_keylen[7] = +{ + 20, 4, 20, 25, 20, 80, 80 +}; + +static unsigned char sha1_hmac_test_buf[7][74] = +{ + { "Hi There" }, + { "what do ya want for nothing?" }, + { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, + { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, + { "Test With Truncation" }, + { "Test Using Larger Than Block-Size Key - Hash Key First" }, + { "Test Using Larger Than Block-Size Key and Larger" + " Than One Block-Size Data" } +}; + +static const int sha1_hmac_test_buflen[7] = +{ + 8, 28, 50, 50, 20, 54, 73 +}; + +static const unsigned char sha1_hmac_test_sum[7][20] = +{ + { 0xB6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xE2, 0x8B, + 0xC0, 0xB6, 0xFB, 0x37, 0x8C, 0x8E, 0xF1, 0x46, 0xBE, 0x00 }, + { 0xEF, 0xFC, 0xDF, 0x6A, 0xE5, 0xEB, 0x2F, 0xA2, 0xD2, 0x74, + 0x16, 0xD5, 0xF1, 0x84, 0xDF, 0x9C, 0x25, 0x9A, 0x7C, 0x79 }, + { 0x12, 0x5D, 0x73, 0x42, 0xB9, 0xAC, 0x11, 0xCD, 0x91, 0xA3, + 0x9A, 0xF4, 0x8A, 0xA1, 0x7B, 0x4F, 0x63, 0xF1, 0x75, 0xD3 }, + { 0x4C, 0x90, 0x07, 0xF4, 0x02, 0x62, 0x50, 0xC6, 0xBC, 0x84, + 0x14, 0xF9, 0xBF, 0x50, 0xC8, 0x6C, 0x2D, 0x72, 0x35, 0xDA }, + { 0x4C, 0x1A, 0x03, 0x42, 0x4B, 0x55, 0xE0, 0x7F, 0xE7, 0xF2, + 0x7B, 0xE1 }, + { 0xAA, 0x4A, 0xE5, 0xE1, 0x52, 0x72, 0xD0, 0x0E, 0x95, 0x70, + 0x56, 0x37, 0xCE, 0x8A, 0x3B, 0x55, 0xED, 0x40, 0x21, 0x12 }, + { 0xE8, 0xE9, 0x9D, 0x0F, 0x45, 0x23, 0x7D, 0x78, 0x6D, 0x6B, + 0xBA, 0xA7, 0x96, 0x5C, 0x78, 0x08, 0xBB, 0xFF, 0x1A, 0x91 } +}; + +/* + * Checkup routine + */ +int sha1_self_test( int verbose ) +{ + int i, j, buflen; + unsigned char buf[1024]; + unsigned char sha1sum[20]; + sha1_context ctx; + + /* + * SHA-1 + */ + for( i = 0; i < 3; i++ ) + { + if( verbose != 0 ) + printf( " SHA-1 test #%d: ", i + 1 ); + + sha1_starts( &ctx ); + + if( i == 2 ) + { + memset( buf, 'a', buflen = 1000 ); + + for( j = 0; j < 1000; j++ ) + sha1_update( &ctx, buf, buflen ); + } + else + sha1_update( &ctx, sha1_test_buf[i], + sha1_test_buflen[i] ); + + sha1_finish( &ctx, sha1sum ); + + if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + for( i = 0; i < 7; i++ ) + { + if( verbose != 0 ) + printf( " HMAC-SHA-1 test #%d: ", i + 1 ); + + if( i == 5 || i == 6 ) + { + memset( buf, '\xAA', buflen = 80 ); + sha1_hmac_starts( &ctx, buf, buflen ); + } + else + sha1_hmac_starts( &ctx, sha1_hmac_test_key[i], + sha1_hmac_test_keylen[i] ); + + sha1_hmac_update( &ctx, sha1_hmac_test_buf[i], + sha1_hmac_test_buflen[i] ); + + sha1_hmac_finish( &ctx, sha1sum ); + + buflen = ( i == 4 ) ? 12 : 20; + + if( memcmp( sha1sum, sha1_hmac_test_sum[i], buflen ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + return( 0 ); +} + +#endif + +#endif diff --git a/src/src/pdkim/sha2.c b/src/src/pdkim/sha2.c index e0beaf992..f225c098c 100644 --- a/src/src/pdkim/sha2.c +++ b/src/src/pdkim/sha2.c @@ -28,7 +28,11 @@ * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf */ -#include "sha2.h" +#include "polarssl/config.h" + +#if defined(POLARSSL_SHA2_C) + +#include "polarssl/sha2.h" #include <string.h> #include <stdio.h> @@ -439,3 +443,260 @@ void sha2_hmac( const unsigned char *key, int keylen, memset( &ctx, 0, sizeof( sha2_context ) ); } + +#if defined(POLARSSL_SELF_TEST) +/* + * FIPS-180-2 test vectors + */ +static unsigned char sha2_test_buf[3][57] = +{ + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const int sha2_test_buflen[3] = +{ + 3, 56, 1000 +}; + +static const unsigned char sha2_test_sum[6][32] = +{ + /* + * SHA-224 test vectors + */ + { 0x23, 0x09, 0x7D, 0x22, 0x34, 0x05, 0xD8, 0x22, + 0x86, 0x42, 0xA4, 0x77, 0xBD, 0xA2, 0x55, 0xB3, + 0x2A, 0xAD, 0xBC, 0xE4, 0xBD, 0xA0, 0xB3, 0xF7, + 0xE3, 0x6C, 0x9D, 0xA7 }, + { 0x75, 0x38, 0x8B, 0x16, 0x51, 0x27, 0x76, 0xCC, + 0x5D, 0xBA, 0x5D, 0xA1, 0xFD, 0x89, 0x01, 0x50, + 0xB0, 0xC6, 0x45, 0x5C, 0xB4, 0xF5, 0x8B, 0x19, + 0x52, 0x52, 0x25, 0x25 }, + { 0x20, 0x79, 0x46, 0x55, 0x98, 0x0C, 0x91, 0xD8, + 0xBB, 0xB4, 0xC1, 0xEA, 0x97, 0x61, 0x8A, 0x4B, + 0xF0, 0x3F, 0x42, 0x58, 0x19, 0x48, 0xB2, 0xEE, + 0x4E, 0xE7, 0xAD, 0x67 }, + + /* + * SHA-256 test vectors + */ + { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, + 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23, + 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, + 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD }, + { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, + 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39, + 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, + 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 }, + { 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92, + 0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67, + 0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E, + 0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 } +}; + +/* + * RFC 4231 test vectors + */ +static unsigned char sha2_hmac_test_key[7][26] = +{ + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B" }, + { "Jefe" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C" }, + { "" }, /* 0xAA 131 times */ + { "" } +}; + +static const int sha2_hmac_test_keylen[7] = +{ + 20, 4, 20, 25, 20, 131, 131 +}; + +static unsigned char sha2_hmac_test_buf[7][153] = +{ + { "Hi There" }, + { "what do ya want for nothing?" }, + { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, + { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, + { "Test With Truncation" }, + { "Test Using Larger Than Block-Size Key - Hash Key First" }, + { "This is a test using a larger than block-size key " + "and a larger than block-size data. The key needs to " + "be hashed before being used by the HMAC algorithm." } +}; + +static const int sha2_hmac_test_buflen[7] = +{ + 8, 28, 50, 50, 20, 54, 152 +}; + +static const unsigned char sha2_hmac_test_sum[14][32] = +{ + /* + * HMAC-SHA-224 test vectors + */ + { 0x89, 0x6F, 0xB1, 0x12, 0x8A, 0xBB, 0xDF, 0x19, + 0x68, 0x32, 0x10, 0x7C, 0xD4, 0x9D, 0xF3, 0x3F, + 0x47, 0xB4, 0xB1, 0x16, 0x99, 0x12, 0xBA, 0x4F, + 0x53, 0x68, 0x4B, 0x22 }, + { 0xA3, 0x0E, 0x01, 0x09, 0x8B, 0xC6, 0xDB, 0xBF, + 0x45, 0x69, 0x0F, 0x3A, 0x7E, 0x9E, 0x6D, 0x0F, + 0x8B, 0xBE, 0xA2, 0xA3, 0x9E, 0x61, 0x48, 0x00, + 0x8F, 0xD0, 0x5E, 0x44 }, + { 0x7F, 0xB3, 0xCB, 0x35, 0x88, 0xC6, 0xC1, 0xF6, + 0xFF, 0xA9, 0x69, 0x4D, 0x7D, 0x6A, 0xD2, 0x64, + 0x93, 0x65, 0xB0, 0xC1, 0xF6, 0x5D, 0x69, 0xD1, + 0xEC, 0x83, 0x33, 0xEA }, + { 0x6C, 0x11, 0x50, 0x68, 0x74, 0x01, 0x3C, 0xAC, + 0x6A, 0x2A, 0xBC, 0x1B, 0xB3, 0x82, 0x62, 0x7C, + 0xEC, 0x6A, 0x90, 0xD8, 0x6E, 0xFC, 0x01, 0x2D, + 0xE7, 0xAF, 0xEC, 0x5A }, + { 0x0E, 0x2A, 0xEA, 0x68, 0xA9, 0x0C, 0x8D, 0x37, + 0xC9, 0x88, 0xBC, 0xDB, 0x9F, 0xCA, 0x6F, 0xA8 }, + { 0x95, 0xE9, 0xA0, 0xDB, 0x96, 0x20, 0x95, 0xAD, + 0xAE, 0xBE, 0x9B, 0x2D, 0x6F, 0x0D, 0xBC, 0xE2, + 0xD4, 0x99, 0xF1, 0x12, 0xF2, 0xD2, 0xB7, 0x27, + 0x3F, 0xA6, 0x87, 0x0E }, + { 0x3A, 0x85, 0x41, 0x66, 0xAC, 0x5D, 0x9F, 0x02, + 0x3F, 0x54, 0xD5, 0x17, 0xD0, 0xB3, 0x9D, 0xBD, + 0x94, 0x67, 0x70, 0xDB, 0x9C, 0x2B, 0x95, 0xC9, + 0xF6, 0xF5, 0x65, 0xD1 }, + + /* + * HMAC-SHA-256 test vectors + */ + { 0xB0, 0x34, 0x4C, 0x61, 0xD8, 0xDB, 0x38, 0x53, + 0x5C, 0xA8, 0xAF, 0xCE, 0xAF, 0x0B, 0xF1, 0x2B, + 0x88, 0x1D, 0xC2, 0x00, 0xC9, 0x83, 0x3D, 0xA7, + 0x26, 0xE9, 0x37, 0x6C, 0x2E, 0x32, 0xCF, 0xF7 }, + { 0x5B, 0xDC, 0xC1, 0x46, 0xBF, 0x60, 0x75, 0x4E, + 0x6A, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xC7, + 0x5A, 0x00, 0x3F, 0x08, 0x9D, 0x27, 0x39, 0x83, + 0x9D, 0xEC, 0x58, 0xB9, 0x64, 0xEC, 0x38, 0x43 }, + { 0x77, 0x3E, 0xA9, 0x1E, 0x36, 0x80, 0x0E, 0x46, + 0x85, 0x4D, 0xB8, 0xEB, 0xD0, 0x91, 0x81, 0xA7, + 0x29, 0x59, 0x09, 0x8B, 0x3E, 0xF8, 0xC1, 0x22, + 0xD9, 0x63, 0x55, 0x14, 0xCE, 0xD5, 0x65, 0xFE }, + { 0x82, 0x55, 0x8A, 0x38, 0x9A, 0x44, 0x3C, 0x0E, + 0xA4, 0xCC, 0x81, 0x98, 0x99, 0xF2, 0x08, 0x3A, + 0x85, 0xF0, 0xFA, 0xA3, 0xE5, 0x78, 0xF8, 0x07, + 0x7A, 0x2E, 0x3F, 0xF4, 0x67, 0x29, 0x66, 0x5B }, + { 0xA3, 0xB6, 0x16, 0x74, 0x73, 0x10, 0x0E, 0xE0, + 0x6E, 0x0C, 0x79, 0x6C, 0x29, 0x55, 0x55, 0x2B }, + { 0x60, 0xE4, 0x31, 0x59, 0x1E, 0xE0, 0xB6, 0x7F, + 0x0D, 0x8A, 0x26, 0xAA, 0xCB, 0xF5, 0xB7, 0x7F, + 0x8E, 0x0B, 0xC6, 0x21, 0x37, 0x28, 0xC5, 0x14, + 0x05, 0x46, 0x04, 0x0F, 0x0E, 0xE3, 0x7F, 0x54 }, + { 0x9B, 0x09, 0xFF, 0xA7, 0x1B, 0x94, 0x2F, 0xCB, + 0x27, 0x63, 0x5F, 0xBC, 0xD5, 0xB0, 0xE9, 0x44, + 0xBF, 0xDC, 0x63, 0x64, 0x4F, 0x07, 0x13, 0x93, + 0x8A, 0x7F, 0x51, 0x53, 0x5C, 0x3A, 0x35, 0xE2 } +}; + +/* + * Checkup routine + */ +int sha2_self_test( int verbose ) +{ + int i, j, k, buflen; + unsigned char buf[1024]; + unsigned char sha2sum[32]; + sha2_context ctx; + + for( i = 0; i < 6; i++ ) + { + j = i % 3; + k = i < 3; + + if( verbose != 0 ) + printf( " SHA-%d test #%d: ", 256 - k * 32, j + 1 ); + + sha2_starts( &ctx, k ); + + if( j == 2 ) + { + memset( buf, 'a', buflen = 1000 ); + + for( j = 0; j < 1000; j++ ) + sha2_update( &ctx, buf, buflen ); + } + else + sha2_update( &ctx, sha2_test_buf[j], + sha2_test_buflen[j] ); + + sha2_finish( &ctx, sha2sum ); + + if( memcmp( sha2sum, sha2_test_sum[i], 32 - k * 4 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + for( i = 0; i < 14; i++ ) + { + j = i % 7; + k = i < 7; + + if( verbose != 0 ) + printf( " HMAC-SHA-%d test #%d: ", 256 - k * 32, j + 1 ); + + if( j == 5 || j == 6 ) + { + memset( buf, '\xAA', buflen = 131 ); + sha2_hmac_starts( &ctx, buf, buflen, k ); + } + else + sha2_hmac_starts( &ctx, sha2_hmac_test_key[j], + sha2_hmac_test_keylen[j], k ); + + sha2_hmac_update( &ctx, sha2_hmac_test_buf[j], + sha2_hmac_test_buflen[j] ); + + sha2_hmac_finish( &ctx, sha2sum ); + + buflen = ( j == 4 ) ? 16 : 32 - k * 4; + + if( memcmp( sha2sum, sha2_hmac_test_sum[i], buflen ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + return( 0 ); +} + +#endif + +#endif diff --git a/src/src/queue.c b/src/src/queue.c index 8876e09be..bf62aea88 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions that operate on the input queue. */ @@ -1274,6 +1274,9 @@ switch(action) { if (action == MSG_ADD_RECIPIENT) { +#ifdef SUPPORT_I18N + if (string_is_utf8(recipient)) allow_utf8_domains = message_smtputf8 = TRUE; +#endif receive_add_recipient(recipient, -1); log_write(0, LOG_MAIN, "recipient <%s> added by %s", recipient, username); @@ -1297,6 +1300,9 @@ switch(action) } else /* MSG_EDIT_SENDER */ { +#ifdef SUPPORT_I18N + if (string_is_utf8(recipient)) allow_utf8_domains = message_smtputf8 = TRUE; +#endif sender_address = recipient; log_write(0, LOG_MAIN, "sender address changed to <%s> by %s", recipient, username); @@ -1345,7 +1351,8 @@ queue_check_only(void) BOOL *set; int sep = 0; struct stat statbuf; -uschar *s, *ss, *name; +const uschar *s; +uschar *ss, *name; uschar buffer[1024]; if (queue_only_file == NULL) return; diff --git a/src/src/rda.c b/src/src/rda.c index f915ce740..2afd6dc8a 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module contains code for extracting addresses from a forwarding list @@ -444,7 +444,7 @@ Returns: -1 on error, else 0 */ static int -rda_write_string(int fd, uschar *s) +rda_write_string(int fd, const uschar *s) { int len = (s == NULL)? 0 : Ustrlen(s) + 1; return ( write(fd, &len, sizeof(int)) != sizeof(int) @@ -635,7 +635,7 @@ if ((pid = fork()) == 0) { DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not " "root or exim in this process)\n"); - log_write_selector &= ~L_address_rewrite; + BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite); } /* Now do the business */ @@ -723,7 +723,7 @@ if ((pid = fork()) == 0) != sizeof(addr->mode) || write(fd, &(addr->flags), sizeof(addr->flags)) != sizeof(addr->flags) - || rda_write_string(fd, addr->p.errors_address) != 0 + || rda_write_string(fd, addr->prop.errors_address) != 0 ) goto bad; @@ -892,7 +892,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED || if (read(fd, &(addr->mode), sizeof(addr->mode)) != sizeof(addr->mode) || read(fd, &(addr->flags), sizeof(addr->flags)) != sizeof(addr->flags) || - !rda_read_string(fd, &(addr->p.errors_address))) goto DISASTER; + !rda_read_string(fd, &(addr->prop.errors_address))) goto DISASTER; /* Next comes a possible setting for $thisaddress and any numerical variables for pipe expansion, terminated by a NULL string. The maximum diff --git a/src/src/readconf.c b/src/src/readconf.c index 60df37afc..e52f45fca 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading the configuration file, and for displaying @@ -12,7 +12,9 @@ implementation of the conditional .ifdef etc. */ #include "exim.h" static void fn_smtp_receive_timeout(const uschar * name, const uschar * str); - +static void save_config_line(const uschar* line); +static void save_config_position(const uschar *file, int line); +static void print_config(BOOL admin); #define CSTATE_STACK_SIZE 10 @@ -26,6 +28,15 @@ typedef struct config_file_item { int lineno; } config_file_item; +/* Structure for chain of configuration lines (-bP config) */ + +typedef struct config_line_item { + struct config_line_item *next; + uschar *line; +} config_line_item; + +static config_line_item* config_lines; + /* Structure of table of conditional words and their state transitions */ typedef struct cond_item { @@ -43,6 +54,8 @@ typedef struct syslog_fac_item { int value; } syslog_fac_item; +/* constants */ +static const char * const hidden = "<value not displayable>"; /* Static variables */ @@ -228,6 +241,7 @@ static optionlist optionlist_config[] = { { "dns_ipv4_lookup", opt_stringptr, &dns_ipv4_lookup }, { "dns_retrans", opt_time, &dns_retrans }, { "dns_retry", opt_int, &dns_retry }, + { "dns_trust_aa", opt_stringptr, &dns_trust_aa }, { "dns_use_edns0", opt_int, &dns_use_edns0 }, /* This option is now a no-op, retained for compability */ { "drop_cr", opt_bool, &drop_cr }, @@ -237,7 +251,7 @@ static optionlist optionlist_config[] = { { "envelope_to_remove", opt_bool, &envelope_to_remove }, { "errors_copy", opt_stringptr, &errors_copy }, { "errors_reply_to", opt_stringptr, &errors_reply_to }, -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT { "event_action", opt_stringptr, &event_action }, #endif { "exim_group", opt_gid, &exim_gid }, @@ -271,6 +285,9 @@ static optionlist optionlist_config[] = { { "host_lookup_order", opt_stringptr, &host_lookup_order }, { "host_reject_connection", opt_stringptr, &host_reject_connection }, { "hosts_connection_nolog", opt_stringptr, &hosts_connection_nolog }, +#ifdef SUPPORT_PROXY + { "hosts_proxy", opt_stringptr, &hosts_proxy }, +#endif { "hosts_treat_as_local", opt_stringptr, &hosts_treat_as_local }, #ifdef LOOKUP_IBASE { "ibase_servers", opt_stringptr, &ibase_servers }, @@ -340,9 +357,6 @@ static optionlist optionlist_config[] = { { "print_topbitchars", opt_bool, &print_topbitchars }, { "process_log_path", opt_stringptr, &process_log_path }, { "prod_requires_admin", opt_bool, &prod_requires_admin }, -#ifdef EXPERIMENTAL_PROXY - { "proxy_required_hosts", opt_stringptr, &proxy_required_hosts }, -#endif { "qualify_domain", opt_stringptr, &qualify_domain_sender }, { "qualify_recipient", opt_stringptr, &qualify_domain_recipient }, { "queue_domains", opt_stringptr, &queue_domains }, @@ -361,7 +375,7 @@ static optionlist optionlist_config[] = { { "recipient_unqualified_hosts", opt_stringptr, &recipient_unqualified_hosts }, { "recipients_max", opt_int, &recipients_max }, { "recipients_max_reject", opt_bool, &recipients_max_reject }, -#ifdef EXPERIMENTAL_REDIS +#ifdef LOOKUP_REDIS { "redis_servers", opt_stringptr, &redis_servers }, #endif { "remote_max_parallel", opt_int, &remote_max_parallel }, @@ -373,6 +387,7 @@ static optionlist optionlist_config[] = { { "rfc1413_hosts", opt_stringptr, &rfc1413_hosts }, { "rfc1413_query_timeout", opt_time, &rfc1413_query_timeout }, { "sender_unqualified_hosts", opt_stringptr, &sender_unqualified_hosts }, + { "slow_lookup_log", opt_int, &slow_lookup_log }, { "smtp_accept_keepalive", opt_bool, &smtp_accept_keepalive }, { "smtp_accept_max", opt_int, &smtp_accept_max }, { "smtp_accept_max_nonmail", opt_int, &smtp_accept_max_nonmail }, @@ -398,6 +413,9 @@ static optionlist optionlist_config[] = { { "smtp_receive_timeout", opt_func, &fn_smtp_receive_timeout }, { "smtp_reserve_hosts", opt_stringptr, &smtp_reserve_hosts }, { "smtp_return_error_details",opt_bool, &smtp_return_error_details }, +#ifdef SUPPORT_I18N + { "smtputf8_advertise_hosts", opt_stringptr, &smtputf8_advertise_hosts }, +#endif #ifdef WITH_CONTENT_SCAN { "spamd_address", opt_stringptr, &spamd_address }, #endif @@ -438,12 +456,13 @@ static optionlist optionlist_config[] = { #endif { "timeout_frozen_after", opt_time, &timeout_frozen_after }, { "timezone", opt_stringptr, &timezone_string }, -#ifdef SUPPORT_TLS { "tls_advertise_hosts", opt_stringptr, &tls_advertise_hosts }, +#ifdef SUPPORT_TLS { "tls_certificate", opt_stringptr, &tls_certificate }, { "tls_crl", opt_stringptr, &tls_crl }, { "tls_dh_max_bits", opt_int, &tls_dh_max_bits }, { "tls_dhparam", opt_stringptr, &tls_dhparam }, + { "tls_eccurve", opt_stringptr, &tls_eccurve }, # ifndef DISABLE_OCSP { "tls_ocsp_file", opt_stringptr, &tls_ocsp_file }, # endif @@ -691,6 +710,8 @@ for (;;) config_filename = config_file_stack->filename; config_lineno = config_file_stack->lineno; config_file_stack = config_file_stack->next; + if (config_lines) + save_config_position(config_filename, config_lineno); continue; } @@ -708,6 +729,9 @@ for (;;) config_lineno++; newlen = len + Ustrlen(big_buffer + len); + if (config_lines && config_lineno == 1) + save_config_position(config_filename, config_lineno); + /* Handle pathologically long physical lines - yes, it did happen - by extending big_buffer at this point. The code also copes with very long logical lines. */ @@ -896,6 +920,8 @@ for (;;) if (include_if_exists != 0 && (Ustat(ss, &statbuf) != 0)) continue; + if (config_lines) + save_config_position(config_filename, config_lineno); save = store_get(sizeof(config_file_item)); save->next = config_file_stack; config_file_stack = save; @@ -952,6 +978,10 @@ next_section, truncate it. It will be unrecognized later, because all the known section names do fit. Leave space for pluralizing. */ s = big_buffer + startoffset; /* First non-space character */ + +if (config_lines) + save_config_line(s); + if (strncmpic(s, US"begin ", 6) == 0) { s += 6; @@ -1085,7 +1115,7 @@ Returns: the value, or -1 on error */ static int -readconf_readfixed(uschar *s, int terminator) +readconf_readfixed(const uschar *s, int terminator) { int yield = 0; int value, count; @@ -1191,7 +1221,7 @@ Returns: doesn't return; dies */ static void -extra_chars_error(uschar *s, uschar *t1, uschar *t2, uschar *t3) +extra_chars_error(const uschar *s, const uschar *t1, const uschar *t2, const uschar *t3) { uschar *comment = US""; if (*s == '#') comment = US" (# is comment only at line start)"; @@ -1227,7 +1257,7 @@ Returns: the control block for the parsed rule. */ static rewrite_rule * -readconf_one_rewrite(uschar *p, int *existflags, BOOL isglobal) +readconf_one_rewrite(const uschar *p, int *existflags, BOOL isglobal) { rewrite_rule *next = store_get(sizeof(rewrite_rule)); @@ -1333,10 +1363,10 @@ Returns: pointer to the string */ static uschar * -read_string(uschar *s, uschar *name) +read_string(const uschar *s, const uschar *name) { uschar *yield; -uschar *ss; +const uschar *ss; if (*s != '\"') return string_copy(s); @@ -1359,8 +1389,6 @@ return yield; static void fn_smtp_receive_timeout(const uschar * name, const uschar * str) { -int value; - if (*str == '$') smtp_receive_timeout_s = string_copy(str); else @@ -1597,18 +1625,16 @@ switch (type) } else if (ol->type & opt_rep_str) { - uschar sep = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':'; - uschar * cp; - - /* Strip trailing whitespace and seperators */ - for (cp = sptr + Ustrlen(sptr) - 1; - cp >= sptr && (*cp == '\n' || *cp == '\t' || *cp == ' ' || *cp == sep); - cp--) *cp = '\0'; - - if (cp >= sptr) - *str_target = string_copy_malloc( - *str_target ? string_sprintf("%s%c%s", *str_target, sep, sptr) - : sptr); + uschar sep_o = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':'; + int sep_i = -(int)sep_o; + const uschar * list = sptr; + uschar * s; + uschar * list_o = *str_target; + + while ((s = string_nextinlist(&list, &sep_i, NULL, 0))) + list_o = string_append_listele(list_o, sep_o, s); + if (list_o) + *str_target = string_copy_malloc(list_o); } else { @@ -1651,8 +1677,7 @@ switch (type) flagptr = (int *)((uschar *)data_block + (long int)(ol3->value)); } - while ((p = string_nextinlist(&sptr, &sep, big_buffer, BIG_BUFFER_SIZE)) - != NULL) + while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE))) { rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE); *chain = next; @@ -1776,8 +1801,8 @@ switch (type) int count = 1; uid_t *list; int ptr = 0; - uschar *p; - uschar *op = expand_string (sptr); + const uschar *p; + const uschar *op = expand_string (sptr); if (op == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", @@ -1817,8 +1842,8 @@ switch (type) int count = 1; gid_t *list; int ptr = 0; - uschar *p; - uschar *op = expand_string (sptr); + const uschar *p; + const uschar *op = expand_string (sptr); if (op == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", @@ -2245,7 +2270,6 @@ if (ol == NULL) if (!admin_user && (ol->type & opt_secure) != 0) { - const char * const hidden = "<value not displayable>"; if (no_labels) printf("%s\n", hidden); else @@ -2510,7 +2534,9 @@ causes the value of any main configuration variable to be output if the second argument is NULL. There are some special values: all print all main configuration options - configure_file print the name of the configuration file + config_file print the name of the configuration file + (configure_file will still work, for backward + compatibility) routers print the routers' configurations transports print the transports' configuration authenticators print the authenticators' configuration @@ -2521,6 +2547,7 @@ second argument is NULL. There are some special values: macro_list print a list of macro names +name print a named list item local_scan print the local_scan options + config print the configuration as it is parsed If the second argument is not NULL, it must be one of "router", "transport", "authenticator" or "macro" in which case the first argument identifies the @@ -2577,7 +2604,8 @@ if (type == NULL) return; } - if (Ustrcmp(name, "configure_file") == 0) + if ( Ustrcmp(name, "configure_file") == 0 + ||Ustrcmp(name, "config_file") == 0) { printf("%s\n", CS config_main_filename); return; @@ -2611,6 +2639,12 @@ if (type == NULL) return; } + if (Ustrcmp(name, "config") == 0) + { + print_config(admin_user); + return; + } + if (Ustrcmp(name, "routers") == 0) { type = US"router"; @@ -2894,6 +2928,18 @@ pid_t pid; int rc, status; void (*oldsignal)(int); +/* If TLS will never be used, no point checking ciphers */ + +if ( !tls_advertise_hosts + || !*tls_advertise_hosts + || Ustrcmp(tls_advertise_hosts, ":") == 0 + ) + return TRUE; +else if (!tls_certificate) + log_write(0, LOG_MAIN|LOG_PANIC, + "Warning: No server certificate defined; TLS connections will fail.\n" + " Suggested action: either install a certificate or change tls_advertise_hosts option"); + oldsignal = signal(SIGCHLD, SIG_DFL); fflush(NULL); @@ -2968,7 +3014,7 @@ readconf_main(void) int sep = 0; struct stat statbuf; uschar *s, *filename; -uschar *list = config_main_filelist; +const uschar *list = config_main_filelist; /* Loop through the possible file names */ @@ -3039,7 +3085,7 @@ if (config_file != NULL) config_filename = config_main_filename = string_copy(filename); p = Ustrrchr(filename, '/'); - config_main_directory = p ? string_copyn(filename, p - filename) + config_main_directory = p ? string_copyn(filename, p - filename) : string_copy(US"."); } else @@ -3140,7 +3186,7 @@ don't force the case. */ if (primary_hostname == NULL) { - uschar *hostname; + const uschar *hostname; struct utsname uts; if (uname(&uts) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "uname() failed to yield host name"); @@ -3153,8 +3199,8 @@ if (primary_hostname == NULL) #if HAVE_IPV6 if (!disable_ipv6 && (dns_ipv4_lookup == NULL || - match_isinlist(hostname, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK)) + match_isinlist(hostname, CUSS &dns_ipv4_lookup, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) != OK)) af = AF_INET6; #else af = AF_INET; @@ -3214,7 +3260,7 @@ or %M. However, it must NOT contain % followed by anything else. */ if (*log_file_path != 0) { - uschar *ss, *sss; + const uschar *ss, *sss; int sep = ':'; /* Fixed for log file path */ s = expand_string(log_file_path); if (s == NULL) @@ -3701,10 +3747,11 @@ Returns: NULL if decoded correctly; else points to error text */ uschar * -readconf_retry_error(uschar *pp, uschar *p, int *basic_errno, int *more_errno) +readconf_retry_error(const uschar *pp, const uschar *p, + int *basic_errno, int *more_errno) { int len; -uschar *q = pp; +const uschar *q = pp; while (q < p && *q != '_') q++; len = q - pp; @@ -3733,7 +3780,7 @@ else if (len == 7 && strncmpic(pp, US"timeout", len) == 0) { int i; int xlen = p - q - 1; - uschar *x = q + 1; + const uschar *x = q + 1; static uschar *extras[] = { US"A", US"MX", US"connect", US"connect_A", US"connect_MX" }; @@ -3741,24 +3788,19 @@ else if (len == 7 && strncmpic(pp, US"timeout", len) == 0) { 'A', 'M', RTEF_CTOUT, RTEF_CTOUT|'A', RTEF_CTOUT|'M' }; for (i = 0; i < sizeof(extras)/sizeof(uschar *); i++) - { if (strncmpic(x, extras[i], xlen) == 0) { *more_errno = values[i]; break; } - } if (i >= sizeof(extras)/sizeof(uschar *)) - { if (strncmpic(x, US"DNS", xlen) == 0) - { log_write(0, LOG_MAIN|LOG_PANIC, "\"timeout_dns\" is no longer " "available in retry rules (it has never worked) - treated as " "\"timeout\""); - } - else return US"\"A\", \"MX\", or \"connect\" expected after \"timeout\""; - } + else + return US"\"A\", \"MX\", or \"connect\" expected after \"timeout\""; } } @@ -3785,8 +3827,8 @@ else if (strncmpic(pp, US"mail_4", 6) == 0 || return string_sprintf("%.4s_4 must be followed by xx, dx, or dd, where " "x is literal and d is any digit", pp); - *basic_errno = (*pp == 'm')? ERRNO_MAIL4XX : - (*pp == 'r')? ERRNO_RCPT4XX : ERRNO_DATA4XX; + *basic_errno = *pp == 'm' ? ERRNO_MAIL4XX : + *pp == 'r' ? ERRNO_RCPT4XX : ERRNO_DATA4XX; *more_errno = x << 8; } @@ -3800,6 +3842,9 @@ else if (strncmpic(pp, US"lost_connection", p - pp) == 0) else if (strncmpic(pp, US"tls_required", p - pp) == 0) *basic_errno = ERRNO_TLSREQUIRED; +else if (strncmpic(pp, US"lookup", p - pp) == 0) + *basic_errno = ERRNO_UNKNOWNHOST; + else if (len != 1 || Ustrncmp(pp, "*", 1) != 0) return string_sprintf("unknown or malformed retry error \"%.*s\"", (int) (p-pp), pp); @@ -3837,10 +3882,10 @@ Returns: time in seconds or fixed point number * 1000 */ static int -retry_arg(uschar **paddr, int type) +retry_arg(const uschar **paddr, int type) { -uschar *p = *paddr; -uschar *pp; +const uschar *p = *paddr; +const uschar *pp; if (*p++ != ',') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma expected"); @@ -3854,10 +3899,8 @@ if (*p != 0 && !isspace(*p) && *p != ',' && *p != ';') *paddr = p; switch (type) { - case 0: - return readconf_readtime(pp, *p, FALSE); - case 1: - return readconf_readfixed(pp, *p); + case 0: return readconf_readtime(pp, *p, FALSE); + case 1: return readconf_readfixed(pp, *p); } return 0; /* Keep picky compilers happy */ } @@ -3869,12 +3912,13 @@ readconf_retries(void) { retry_config **chain = &retries; retry_config *next; -uschar *p; +const uschar *p; -while ((p = get_config_line()) != NULL) +while ((p = get_config_line())) { retry_rule **rchain; - uschar *pp, *error; + const uschar *pp; + uschar *error; next = store_get(sizeof(retry_config)); next->next = NULL; @@ -3894,8 +3938,8 @@ while ((p = get_config_line()) != NULL) /* Test error names for things we understand. */ - if ((error = readconf_retry_error(pp, p, &(next->basic_errno), - &(next->more_errno))) != NULL) + if ((error = readconf_retry_error(pp, p, &next->basic_errno, + &next->more_errno))) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%s", error); /* There may be an optional address list of senders to be used as another @@ -3932,18 +3976,18 @@ while ((p = get_config_line()) != NULL) switch (rule->rule) { case 'F': /* Fixed interval */ - rule->p1 = retry_arg(&p, 0); - break; + rule->p1 = retry_arg(&p, 0); + break; case 'G': /* Geometrically increasing intervals */ case 'H': /* Ditto, but with randomness */ - rule->p1 = retry_arg(&p, 0); - rule->p2 = retry_arg(&p, 1); - break; + rule->p1 = retry_arg(&p, 0); + rule->p2 = retry_arg(&p, 1); + break; default: - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "unknown retry rule letter"); - break; + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "unknown retry rule letter"); + break; } if (rule->timeout <= 0 || rule->p1 <= 0 || @@ -4186,6 +4230,121 @@ while(next_section[0] != 0) (void)fclose(config_file); } +/* Init the storage for the pre-parsed config lines */ +void +readconf_save_config(const uschar *s) +{ + save_config_line(string_sprintf("# Exim Configuration (%s)", + running_in_test_harness ? US"X" : s)); +} + +static void +save_config_position(const uschar *file, int line) +{ + save_config_line(string_sprintf("# %d \"%s\"", line, file)); +} + +/* Append a pre-parsed logical line to the config lines store, +this operates on a global (static) list that holds all the pre-parsed +config lines, we do no further processing here, output formatting and +honouring of <hide> or macros will be done during output */ +static void +save_config_line(const uschar* line) +{ +static config_line_item *current; +config_line_item *next; + +next = (config_line_item*) store_get(sizeof(config_line_item)); +next->line = string_copy(line); +next->next = NULL; + +if (!config_lines) config_lines = next; +else current->next = next; + +current = next; +} + +/* List the parsed config lines, care about nice formatting and +hide the <hide> values unless we're the admin user */ +void +print_config(BOOL admin) +{ +config_line_item *i; +const int TS = 2; +int indent = 0; + +for (i = config_lines; i; i = i->next) + { + uschar *current; + uschar *p; + + /* skip over to the first non-space */ + for (current = i->line; *current && isspace(*current); ++current) + ; + + if (*current == '\0') + continue; + + /* Collapse runs of spaces. We stop this if we encounter one of the + * following characters: "'$, as this may indicate careful formatting */ + for (p = current; *p; ++p) + { + uschar *next; + if (!isspace(*p)) continue; + if (*p != ' ') *p = ' '; + + for (next = p; isspace(*next); ++next) + ; + + if (next - p > 1) + memmove(p+1, next, strlen(next)+1); + + if (*next == '"' || *next == '\'' || *next == '$') + break; + } + + /* # lines */ + if (current[0] == '#') + puts(CCS current); + + /* begin lines are left aligned */ + else if (Ustrncmp(current, "begin", 5) == 0 && isspace(current[5])) + { + puts(""); + puts(CCS current); + indent = TS; + } + + /* router/acl/transport block names */ + else if (current[Ustrlen(current)-1] == ':' && !Ustrchr(current, '=')) + { + printf("\n%*s%s\n", TS, "", current); + indent = 2 * TS; + } + + /* hidden lines (all MACROS or lines prefixed with "hide") */ + else if ( !admin + && ( isupper(*current) + || Ustrncmp(current, "hide", 4) == 0 && isspace(current[4]) + ) + ) + { + if ((p = Ustrchr(current, '='))) + { + *p = '\0'; + printf("%*s%s= %s\n", indent, "", current, hidden); + } + /* e.g.: hide split_spool_directory */ + else + printf("%*s\n", indent, hidden); + } + + else + /* rest is public */ + printf("%*s%s\n", indent, "", current); + } +} + /* vi: aw ai sw=2 */ /* End of readconf.c */ diff --git a/src/src/receive.c b/src/src/receive.c index 48862734d..585cb9025 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for receiving a message and setting up spool files. */ @@ -87,7 +87,7 @@ if (newsender == NULL || untrusted_set_sender == NULL) return FALSE; qnewsender = (Ustrchr(newsender, '@') != NULL)? newsender : string_sprintf("%s@%s", newsender, qualify_domain_sender); return - match_address_list(qnewsender, TRUE, TRUE, &untrusted_set_sender, NULL, -1, + match_address_list(qnewsender, TRUE, TRUE, CUSS &untrusted_set_sender, NULL, -1, 0, NULL) == OK; } @@ -142,17 +142,16 @@ appearance of "syslog" in it. */ else { int sep = ':'; /* Not variable - outside scripts use */ - uschar *p = log_file_path; + const uschar *p = log_file_path; name = US"log"; /* An empty log_file_path means "use the default". This is the same as an empty item in a list. */ if (*p == 0) p = US":"; - while ((path = string_nextinlist(&p, &sep, buffer, sizeof(buffer))) != NULL) - { - if (Ustrcmp(path, "syslog") != 0) break; - } + while ((path = string_nextinlist(&p, &sep, buffer, sizeof(buffer)))) + if (Ustrcmp(path, "syslog") != 0) + break; if (path == NULL) /* No log files */ { @@ -997,7 +996,7 @@ switch(where) case ACL_WHERE_DKIM: case ACL_WHERE_MIME: case ACL_WHERE_DATA: - if (cutthrough_fd >= 0 && (acl_removed_headers || acl_added_headers)) + if (cutthrough.fd >= 0 && (acl_removed_headers || acl_added_headers)) { log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs" " will not take effect on cutthrough deliveries"); @@ -1009,29 +1008,19 @@ if (acl_removed_headers != NULL) { DEBUG(D_receive|D_acl) debug_printf(">>Headers removed by %s ACL:\n", acl_name); - for (h = header_list; h != NULL; h = h->next) + for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) { - uschar *list; - BOOL include_header; - - if (h->type == htype_old) continue; - - include_header = TRUE; - list = acl_removed_headers; - + const uschar * list = acl_removed_headers; int sep = ':'; /* This is specified as a colon-separated list */ uschar *s; uschar buffer[128]; - while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) - != NULL) - { - int len = Ustrlen(s); - if (header_testname(h, s, len, FALSE)) + + while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) + if (header_testname(h, s, Ustrlen(s), FALSE)) { h->type = htype_old; DEBUG(D_receive|D_acl) debug_printf(" %s", h->text); } - } } acl_removed_headers = NULL; DEBUG(D_receive|D_acl) debug_printf(">>\n"); @@ -1129,8 +1118,7 @@ add_host_info_for_log(uschar *s, int *sizeptr, int *ptrptr) if (sender_fullhost != NULL) { s = string_append(s, sizeptr, ptrptr, 2, US" H=", sender_fullhost); - if ((log_extra_selector & LX_incoming_interface) != 0 && - interface_address != NULL) + if (LOGGING(incoming_interface) && interface_address != NULL) { uschar *ss = string_sprintf(" I=[%s]:%d", interface_address, interface_port); @@ -2314,9 +2302,23 @@ if (extract_recip) pp = recipient = store_get(ss - s + 1); for (p = s; p < ss; p++) if (*p != '\n') *pp++ = *p; *pp = 0; + +#ifdef SUPPORT_I18N + { + BOOL b = allow_utf8_domains; + allow_utf8_domains = TRUE; +#endif recipient = parse_extract_address(recipient, &errmess, &start, &end, &domain, FALSE); +#ifdef SUPPORT_I18N + if (string_is_utf8(recipient)) + message_smtputf8 = TRUE; + else + allow_utf8_domains = b; + } +#endif + /* Keep a list of all the bad addresses so we can send a single error message at the end. However, an empty address is not an error; just ignore it. This can come from an empty group list like @@ -2526,7 +2528,7 @@ if (msgid_header == NULL && rewriting. Must copy the count, because later ACLs and the local_scan() function may mess with the real recipients. */ -if ((log_extra_selector & LX_received_recipients) != 0) +if (LOGGING(received_recipients)) { raw_recipients = store_get(recipients_count * sizeof(uschar *)); for (i = 0; i < recipients_count; i++) @@ -2817,12 +2819,11 @@ if (filter_test != FTEST_NONE) } /* Cutthrough delivery: - We have to create the Received header now rather than at the end of reception, - so the timestamp behaviour is a change to the normal case. - XXX Ensure this gets documented XXX. - Having created it, send the headers to the destination. -*/ -if (cutthrough_fd >= 0) +We have to create the Received header now rather than at the end of reception, +so the timestamp behaviour is a change to the normal case. +XXX Ensure this gets documented XXX. +Having created it, send the headers to the destination. */ +if (cutthrough.fd >= 0) { if (received_count > received_headers_max) { @@ -3184,7 +3185,7 @@ else else { int sep = 0; - uschar *ptr = dkim_verify_signers_expanded; + const uschar *ptr = dkim_verify_signers_expanded; uschar *item = NULL; uschar *seen_items = NULL; int seen_items_size = 0; @@ -3194,56 +3195,61 @@ else rc = OK; while ((item = string_nextinlist(&ptr, &sep, itembuf, - sizeof(itembuf))) != NULL) + sizeof(itembuf)))) { /* Prevent running ACL for an empty item */ if (!item || (item[0] == '\0')) continue; - /* Only run ACL once for each domain or identity, no matter how often it - appears in the expanded list. */ - if (seen_items != NULL) + + /* Only run ACL once for each domain or identity, + no matter how often it appears in the expanded list. */ + if (seen_items) { uschar *seen_item = NULL; uschar seen_item_buf[256]; - uschar *seen_items_list = seen_items; - int seen_this_item = 0; + const uschar *seen_items_list = seen_items; + BOOL seen_this_item = FALSE; while ((seen_item = string_nextinlist(&seen_items_list, &sep, seen_item_buf, - sizeof(seen_item_buf))) != NULL) - { - if (Ustrcmp(seen_item,item) == 0) - { - seen_this_item = 1; - break; - } - } - - if (seen_this_item > 0) + sizeof(seen_item_buf)))) + if (Ustrcmp(seen_item,item) == 0) + { + seen_this_item = TRUE; + break; + } + + if (seen_this_item) { DEBUG(D_receive) - debug_printf("acl_smtp_dkim: skipping signer %s, already seen\n", item); + debug_printf("acl_smtp_dkim: skipping signer %s, " + "already seen\n", item); continue; } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":"); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, ":"); } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,item); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, item); seen_items[seen_items_offset] = '\0'; DEBUG(D_receive) - debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", item); + debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", + item); dkim_exim_acl_setup(item); - rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, &user_msg, &log_msg); + rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, + &user_msg, &log_msg); if (rc != OK) - { - DEBUG(D_receive) - debug_printf("acl_smtp_dkim: acl_check returned %d on %s, skipping remaining items\n", rc, item); - cancel_cutthrough_connection("dkim acl not ok"); - break; - } + { + DEBUG(D_receive) + debug_printf("acl_smtp_dkim: acl_check returned %d on %s, " + "skipping remaining items\n", rc, item); + cancel_cutthrough_connection("dkim acl not ok"); + break; + } } add_acl_headers(ACL_WHERE_DKIM, US"DKIM"); if (rc == DISCARD) @@ -3566,7 +3572,7 @@ else goto TEMPREJECT; case LOCAL_SCAN_REJECT_NOLOGHDR: - log_extra_selector &= ~LX_rejected_header; + BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header); /* Fall through */ case LOCAL_SCAN_REJECT: @@ -3575,7 +3581,7 @@ else break; case LOCAL_SCAN_TEMPREJECT_NOLOGHDR: - log_extra_selector &= ~LX_rejected_header; + BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header); /* Fall through */ case LOCAL_SCAN_TEMPREJECT: @@ -3740,15 +3746,15 @@ if (message_reference != NULL) s = add_host_info_for_log(s, &size, &sptr); #ifdef SUPPORT_TLS -if (log_extra_selector & LX_tls_cipher && tls_in.cipher) +if (LOGGING(tls_cipher) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher); -if (log_extra_selector & LX_tls_certificate_verified && tls_in.cipher) +if (LOGGING(tls_certificate_verified) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" CV=", tls_in.certificate_verified? "yes":"no"); -if (log_extra_selector & LX_tls_peerdn && tls_in.peerdn) +if (LOGGING(tls_peerdn) && tls_in.peerdn) s = string_append(s, &size, &sptr, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); -if (log_extra_selector & LX_tls_sni && tls_in.sni) +if (LOGGING(tls_sni) && tls_in.sni) s = string_append(s, &size, &sptr, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); #endif @@ -3759,7 +3765,7 @@ if (sender_host_authenticated) if (authenticated_id != NULL) { s = string_append(s, &size, &sptr, 2, US":", authenticated_id); - if (log_extra_selector & LX_smtp_mailauth && authenticated_sender != NULL) + if (LOGGING(smtp_mailauth) && authenticated_sender != NULL) s = string_append(s, &size, &sptr, 2, US":", authenticated_sender); } } @@ -3769,9 +3775,9 @@ if (prdr_requested) s = string_append(s, &size, &sptr, 1, US" PRDR"); #endif -#ifdef EXPERIMENTAL_PROXY -if (proxy_session && log_extra_selector & LX_proxy) - s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host_address); +#ifdef SUPPORT_PROXY +if (proxy_session && LOGGING(proxy)) + s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_local_address); #endif sprintf(CS big_buffer, "%d", msg_size); @@ -3781,7 +3787,7 @@ s = string_append(s, &size, &sptr, 2, US" S=", big_buffer); 0 ... no BODY= used 7 ... 7BIT 8 ... 8BITMIME */ -if (log_extra_selector & LX_8bitmime) +if (LOGGING(8bitmime)) { sprintf(CS big_buffer, "%d", body_8bitmime); s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer); @@ -3807,7 +3813,7 @@ if (msgid_header != NULL) /* If subject logging is turned on, create suitable printing-character text. By expanding $h_subject: we make use of the MIME decoding. */ -if ((log_extra_selector & LX_subject) != 0 && subject_header != NULL) +if (LOGGING(subject) && subject_header != NULL) { int i; uschar *p = big_buffer; @@ -3967,7 +3973,7 @@ for this message. */ XXX We do not handle queue-only, freezing, or blackholes. */ -if(cutthrough_fd >= 0) +if(cutthrough.fd >= 0) { uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */ /* Logging was done in finaldot() */ @@ -3996,8 +4002,8 @@ if(!smtp_reply) #endif { log_write(0, LOG_MAIN | - (((log_extra_selector & LX_received_recipients) != 0)? LOG_RECIPIENTS : 0) | - (((log_extra_selector & LX_received_sender) != 0)? LOG_SENDER : 0), + (LOGGING(received_recipients)? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender)? LOG_SENDER : 0), "%s", s); /* Log any control actions taken by an ACL or local_scan(). */ @@ -4113,7 +4119,7 @@ if (smtp_input) case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */ default:break; } - cutthrough_delivery = FALSE; + cutthrough.delivery = FALSE; } /* For batched SMTP, generate an error message on failure, and do @@ -4131,9 +4137,9 @@ starting. */ if (blackholed_by != NULL) { - uschar *detail = (local_scan_data != NULL)? - string_printing(local_scan_data) : - string_sprintf("(%s discarded recipients)", blackholed_by); + const uschar *detail = local_scan_data + ? string_printing(local_scan_data) + : string_sprintf("(%s discarded recipients)", blackholed_by); log_write(0, LOG_MAIN, "=> blackhole %s%s", detail, blackhole_log_msg); log_write(0, LOG_MAIN, "Completed"); message_id[0] = 0; diff --git a/src/src/regex.c b/src/src/regex.c index 94a867c65..3852ad8c5 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */ +/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2015 */ /* License: GPL */ /* Code for matching regular expressions against headers and body. @@ -25,221 +25,175 @@ uschar regex_match_string_buffer[1024]; extern FILE *mime_stream; extern uschar *mime_current_boundary; -int regex(uschar **listptr) { - int sep = 0; - uschar *list = *listptr; - uschar *regex_string; - uschar regex_string_buffer[1024]; - unsigned long mbox_size; - FILE *mbox_file; - pcre *re; - pcre_list *re_list_head = NULL; - pcre_list *re_list_item; - const char *pcre_error; - int pcre_erroffset; - uschar *linebuffer; - long f_pos = 0; - - /* reset expansion variable */ - regex_match_string = NULL; - - if (mime_stream == NULL) { - /* We are in the DATA ACL */ - mbox_file = spool_mbox(&mbox_size, NULL); - if (mbox_file == NULL) { - /* error while spooling */ - log_write(0, LOG_MAIN|LOG_PANIC, - "regex acl condition: error while creating mbox spool file"); - return DEFER; - }; - } - else { - f_pos = ftell(mime_stream); - mbox_file = mime_stream; - }; - - /* precompile our regexes */ - while ((regex_string = string_nextinlist(&list, &sep, - regex_string_buffer, - sizeof(regex_string_buffer))) != NULL) { - - /* parse option */ - if ( (strcmpic(regex_string,US"false") == 0) || - (Ustrcmp(regex_string,"0") == 0) ) { - /* explicitly no matching */ - continue; - }; +static pcre_list * +compile(const uschar * list) +{ +int sep = 0; +uschar *regex_string; +const char *pcre_error; +int pcre_erroffset; +pcre_list *re_list_head = NULL; +pcre_list *ri; + +/* precompile our regexes */ +while ((regex_string = string_nextinlist(&list, &sep, NULL, 0))) + if (strcmpic(regex_string, US"false") != 0 && Ustrcmp(regex_string, "0") != 0) + { + pcre *re; /* compile our regular expression */ - re = pcre_compile( CS regex_string, - 0, - &pcre_error, - &pcre_erroffset, - NULL ); - - if (re == NULL) { + if (!(re = pcre_compile( CS regex_string, + 0, &pcre_error, &pcre_erroffset, NULL ))) + { log_write(0, LOG_MAIN, - "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset); + "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", + regex_string, pcre_error, pcre_erroffset); continue; + } + + ri = store_get(sizeof(pcre_list)); + ri->re = re; + ri->pcre_text = regex_string; + ri->next = re_list_head; + re_list_head = ri; } - else { - re_list_item = store_get(sizeof(pcre_list)); - re_list_item->re = re; - re_list_item->pcre_text = string_copy(regex_string); - re_list_item->next = re_list_head; - re_list_head = re_list_item; - }; - }; - - /* no regexes -> nothing to do */ - if (re_list_head == NULL) { - return FAIL; - }; - - /* match each line against all regexes */ - linebuffer = store_get(32767); - while (fgets(CS linebuffer, 32767, mbox_file) != NULL) { - if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) { - /* check boundary */ - if (Ustrncmp(linebuffer,"--",2) == 0) { - if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0) - /* found boundary */ - break; - }; - }; - re_list_item = re_list_head; - do { - /* try matcher on the line */ - if (pcre_exec(re_list_item->re, NULL, CS linebuffer, - (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) { - Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023); - regex_match_string = regex_match_string_buffer; - if (mime_stream == NULL) - (void)fclose(mbox_file); - else { - clearerr(mime_stream); - fseek(mime_stream,f_pos,SEEK_SET); - }; - return OK; - }; - re_list_item = re_list_item->next; - } while (re_list_item != NULL); - }; - - if (mime_stream == NULL) - (void)fclose(mbox_file); - else { - clearerr(mime_stream); - fseek(mime_stream,f_pos,SEEK_SET); - }; - - /* no matches ... */ - return FAIL; +return re_list_head; } +static int +matcher(pcre_list * re_list_head, uschar * linebuffer, int len) +{ +pcre_list * ri; + +for(ri = re_list_head; ri; ri = ri->next) + { + int ovec[3*(REGEX_VARS+1)]; + int n, nn; + + /* try matcher on the line */ + n = pcre_exec(ri->re, NULL, CS linebuffer, len, 0, 0, ovec, nelem(ovec)); + if (n > 0) + { + Ustrncpy(regex_match_string_buffer, ri->pcre_text, + sizeof(regex_match_string_buffer)-1); + regex_match_string = regex_match_string_buffer; + + for (nn = 1; nn < n; nn++) + regex_vars[nn-1] = + string_copyn(linebuffer + ovec[nn*2], ovec[nn*2+1] - ovec[nn*2]); + + return OK; + } + } +return FAIL; +} -int mime_regex(uschar **listptr) { - int sep = 0; - uschar *list = *listptr; - uschar *regex_string; - uschar regex_string_buffer[1024]; - pcre *re; - pcre_list *re_list_head = NULL; - pcre_list *re_list_item; - const char *pcre_error; - int pcre_erroffset; - FILE *f; - uschar *mime_subject = NULL; - int mime_subject_len = 0; - - /* reset expansion variable */ - regex_match_string = NULL; - - /* precompile our regexes */ - while ((regex_string = string_nextinlist(&list, &sep, - regex_string_buffer, - sizeof(regex_string_buffer))) != NULL) { - - /* parse option */ - if ( (strcmpic(regex_string,US"false") == 0) || - (Ustrcmp(regex_string,"0") == 0) ) { - /* explicitly no matching */ - continue; - }; +int +regex(const uschar **listptr) +{ +unsigned long mbox_size; +FILE *mbox_file; +pcre_list *re_list_head; +uschar *linebuffer; +long f_pos = 0; +int ret = FAIL; + +/* reset expansion variable */ +regex_match_string = NULL; + +if (!mime_stream) /* We are in the DATA ACL */ + { + if (!(mbox_file = spool_mbox(&mbox_size, NULL))) + { /* error while spooling */ + log_write(0, LOG_MAIN|LOG_PANIC, + "regex acl condition: error while creating mbox spool file"); + return DEFER; + } + } +else + { + f_pos = ftell(mime_stream); + mbox_file = mime_stream; + } - /* compile our regular expression */ - re = pcre_compile( CS regex_string, - 0, - &pcre_error, - &pcre_erroffset, - NULL ); +/* precompile our regexes */ +if (!(re_list_head = compile(*listptr))) + return FAIL; /* no regexes -> nothing to do */ + +/* match each line against all regexes */ +linebuffer = store_get(32767); +while (fgets(CS linebuffer, 32767, mbox_file)) + { + if ( mime_stream && mime_current_boundary /* check boundary */ + && Ustrncmp(linebuffer, "--", 2) == 0 + && Ustrncmp((linebuffer+2), mime_current_boundary, + Ustrlen(mime_current_boundary)) == 0) + break; /* found boundary */ + + if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK) + goto done; + } +/* no matches ... */ + +done: +if (!mime_stream) + (void)fclose(mbox_file); +else + { + clearerr(mime_stream); + fseek(mime_stream, f_pos, SEEK_SET); + } - if (re == NULL) { - log_write(0, LOG_MAIN, - "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset); - continue; - } - else { - re_list_item = store_get(sizeof(pcre_list)); - re_list_item->re = re; - re_list_item->pcre_text = string_copy(regex_string); - re_list_item->next = re_list_head; - re_list_head = re_list_item; - }; - }; - - /* no regexes -> nothing to do */ - if (re_list_head == NULL) { - return FAIL; - }; - - /* check if the file is already decoded */ - if (mime_decoded_filename == NULL) { - uschar *empty = US""; - /* no, decode it first */ - mime_decode(&empty); - if (mime_decoded_filename == NULL) { - /* decoding failed */ - log_write(0, LOG_MAIN, - "mime_regex acl condition warning - could not decode MIME part to file."); - return DEFER; - }; - }; +return ret; +} - /* open file */ - f = fopen(CS mime_decoded_filename, "rb"); - if (f == NULL) { - /* open failed */ +int +mime_regex(const uschar **listptr) +{ +pcre_list *re_list_head = NULL; +FILE *f; +uschar *mime_subject = NULL; +int mime_subject_len = 0; +int ret; + +/* reset expansion variable */ +regex_match_string = NULL; + +/* precompile our regexes */ +if (!(re_list_head = compile(*listptr))) + return FAIL; /* no regexes -> nothing to do */ + +/* check if the file is already decoded */ +if (!mime_decoded_filename) + { /* no, decode it first */ + const uschar *empty = US""; + mime_decode(&empty); + if (!mime_decoded_filename) + { /* decoding failed */ log_write(0, LOG_MAIN, - "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename); + "mime_regex acl condition warning - could not decode MIME part to file"); return DEFER; - }; - - /* get 32k memory */ - mime_subject = (uschar *)store_get(32767); - - /* read max 32k chars from file */ - mime_subject_len = fread(mime_subject, 1, 32766, f); - - re_list_item = re_list_head; - do { - /* try matcher on the mmapped file */ - debug_printf("Matching '%s'\n", re_list_item->pcre_text); - if (pcre_exec(re_list_item->re, NULL, CS mime_subject, - mime_subject_len, 0, 0, NULL, 0) >= 0) { - Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023); - regex_match_string = regex_match_string_buffer; - (void)fclose(f); - return OK; - }; - re_list_item = re_list_item->next; - } while (re_list_item != NULL); - - (void)fclose(f); - - /* no matches ... */ - return FAIL; + } + } + +/* open file */ +if (!(f = fopen(CS mime_decoded_filename, "rb"))) + { + log_write(0, LOG_MAIN, + "mime_regex acl condition warning - can't open '%s' for reading", + mime_decoded_filename); + return DEFER; + } + +/* get 32k memory */ +mime_subject = store_get(32767); + +mime_subject_len = fread(mime_subject, 1, 32766, f); + +ret = matcher(re_list_head, mime_subject, mime_subject_len); +(void)fclose(f); +return ret; } #endif /* WITH_CONTENT_SCAN */ diff --git a/src/src/retry.c b/src/src/retry.c index 55ffd5c6f..0099b6e6a 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with retrying unsuccessful deliveries. */ @@ -29,7 +29,7 @@ Returns: TRUE if the ultimate timeout has been reached */ BOOL -retry_ultimate_address_timeout(uschar *retry_key, uschar *domain, +retry_ultimate_address_timeout(uschar *retry_key, const uschar *domain, dbdata_retry *retry_record, time_t now) { BOOL address_timeout; @@ -122,7 +122,7 @@ Returns: TRUE if the host has expired but is usable because */ BOOL -retry_check_address(uschar *domain, host_item *host, uschar *portstring, +retry_check_address(const uschar *domain, host_item *host, uschar *portstring, BOOL include_ip_address, uschar **retry_host_key, uschar **retry_message_key) { BOOL yield = FALSE; @@ -343,12 +343,10 @@ Returns: pointer to retry rule, or NULL */ retry_config * -retry_find_config(uschar *key, uschar *alternate, int basic_errno, +retry_find_config(const uschar *key, const uschar *alternate, int basic_errno, int more_errno) { -int replace = 0; -uschar *use_key, *use_alternate; -uschar *colon = Ustrchr(key, ':'); +const uschar *colon = Ustrchr(key, ':'); retry_config *yield; /* If there's a colon in the key, there are two possibilities: @@ -357,8 +355,7 @@ retry_config *yield; hostname:ip+port - In this case, we temporarily replace the colon with a zero, to terminate - the string after the host name. + In this case, we copy the host name. (2) This is a key for a pipe, file, or autoreply delivery, in the format @@ -369,28 +366,22 @@ retry_config *yield; with a letter or a digit. In this case we want to use the original address to search for a retry rule. */ -if (colon != NULL) - { - if (isalnum(*key)) - replace = ':'; - else - key = Ustrrchr(key, ':') + 1; /* Take from the last colon */ - } - -if (replace == 0) colon = key + Ustrlen(key); -*colon = 0; +if (colon) + key = isalnum(*key) + ? string_copyn(key, colon-key) /* the hostname */ + : Ustrrchr(key, ':') + 1; /* Take from the last colon */ /* Sort out the keys */ -use_key = (Ustrchr(key, '@') != NULL)? key : string_sprintf("*@%s", key); -use_alternate = (alternate == NULL)? NULL : string_sprintf("*@%s", alternate); +if (!Ustrchr(key, '@')) key = string_sprintf("*@%s", key); +if (alternate) alternate = string_sprintf("*@%s", alternate); /* Scan the configured retry items. */ for (yield = retries; yield != NULL; yield = yield->next) { - uschar *plist = yield->pattern; - uschar *slist = yield->senders; + const uschar *plist = yield->pattern; + const uschar *slist = yield->senders; /* If a specific error is set for this item, check that we are handling that specific error, and if so, check any additional error information if @@ -489,15 +480,14 @@ for (yield = retries; yield != NULL; yield = yield->next) /* Check for a match between the address list item at the start of this retry rule and either the main or alternate keys. */ - if (match_address_list(use_key, TRUE, TRUE, &plist, NULL, -1, UCHAR_MAX+1, + if (match_address_list(key, TRUE, TRUE, &plist, NULL, -1, UCHAR_MAX+1, NULL) == OK || - (use_alternate != NULL && - match_address_list(use_alternate, TRUE, TRUE, &plist, NULL, -1, + (alternate != NULL && + match_address_list(alternate, TRUE, TRUE, &plist, NULL, -1, UCHAR_MAX+1, NULL) == OK)) break; } -*colon = replace; return yield; } @@ -665,7 +655,7 @@ for (i = 0; i < 3; i++) message = (rti->basic_errno > 0)? US strerror(rti->basic_errno) : (rti->message == NULL)? - US"unknown error" : string_printing(rti->message); + US"unknown error" : US string_printing(rti->message); message_length = Ustrlen(message); if (message_length > 150) message_length = 150; diff --git a/src/src/rewrite.c b/src/src/rewrite.c index f9d29c08c..ca7fb6a11 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with rewriting headers */ @@ -118,7 +118,8 @@ for (rule = rewrite_rules; { int start, end, pdomain; int count = 0; - uschar *save_localpart, *save_domain; + uschar *save_localpart; + const uschar *save_domain; uschar *error, *new, *newparsed; /* Ensure that the flag matches the flags in the rule. */ @@ -162,7 +163,7 @@ for (rule = rewrite_rules; /* Use the general function for matching an address against a list (here just one item, so use the "impossible value" separator UCHAR_MAX+1). */ - if (match_address_list(subject, FALSE, TRUE, &(rule->key), NULL, 0, + if (match_address_list(subject, FALSE, TRUE, CUSS &(rule->key), NULL, 0, UCHAR_MAX + 1, NULL) != OK) continue; @@ -246,8 +247,7 @@ for (rule = rewrite_rules; /* We have a validly rewritten address */ - if ((log_write_selector & L_address_rewrite) != 0 || - (debug_selector & D_rewrite) != 0) + if (LOGGING(address_rewrite) || (debug_selector & D_rewrite) != 0) { int i; const uschar *where = CUS"?"; @@ -292,7 +292,7 @@ for (rule = rewrite_rules; { uschar *p1 = new + start - 1; uschar *p2 = new + end + 1; - uschar *pf1, *pf2; + const uschar *pf1, *pf2; uschar buff1[256], buff2[256]; while (p1 > new && p1[-1] == ' ') p1--; @@ -449,8 +449,9 @@ Returns: NULL if header unchanged; otherwise the rewritten header */ static header_line * -rewrite_one_header(header_line *h, int flag, uschar *routed_old, - uschar *routed_new, rewrite_rule *rewrite_rules, int existflags, BOOL replace) +rewrite_one_header(header_line *h, int flag, + const uschar *routed_old, const uschar *routed_new, + rewrite_rule *rewrite_rules, int existflags, BOOL replace) { int lastnewline = 0; header_line *newh = NULL; @@ -717,7 +718,8 @@ Returns: NULL if header unchanged; otherwise the rewritten header */ header_line * -rewrite_header(header_line *h, uschar *routed_old, uschar *routed_new, +rewrite_header(header_line *h, + const uschar *routed_old, const uschar *routed_new, rewrite_rule *rewrite_rules, int existflags, BOOL replace) { switch (h->type) diff --git a/src/src/route.c b/src/src/route.c index 09503044f..b289b0f34 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with routing, and the list of generic router options. */ @@ -54,6 +54,10 @@ optionlist optionlist_routers[] = { (void *)offsetof(router_instance, debug_string) }, { "disable_logging", opt_bool | opt_public, (void *)offsetof(router_instance, disable_logging) }, + { "dnssec_request_domains", opt_stringptr|opt_public, + (void *)offsetof(router_instance, dnssec.request) }, + { "dnssec_require_domains", opt_stringptr|opt_public, + (void *)offsetof(router_instance, dnssec.require) }, { "domains", opt_stringptr|opt_public, (void *)offsetof(router_instance, domains) }, { "driver", opt_stringptr|opt_public, @@ -162,7 +166,7 @@ set_router(router_instance *r, uschar *name, router_instance **ptr, BOOL after) BOOL afterthis = FALSE; router_instance *rr; -for (rr = routers; rr != NULL; rr = rr->next) +for (rr = routers; rr; rr = rr->next) { if (Ustrcmp(name, rr->name) == 0) { @@ -172,7 +176,7 @@ for (rr = routers; rr != NULL; rr = rr->next) if (rr == r) afterthis = TRUE; } -if (rr == NULL) +if (!rr) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "new_router \"%s\" not found for \"%s\" router", name, r->name); @@ -208,7 +212,7 @@ readconf_driver_init(US"router", optionlist_routers, /* generic options */ optionlist_routers_size); -for (r = routers; r != NULL; r = r->next) +for (r = routers; r; r = r->next) { uschar *s = r->self; @@ -273,12 +277,8 @@ for (r = routers; r != NULL; r = r->next) if (r->pass_router_name != NULL) set_router(r, r->pass_router_name, &(r->pass_router), TRUE); - DEBUG(D_route) { - if (r->dsn_lasthop == FALSE) - debug_printf("DSN: %s propagating DSN\n", r->name); - else - debug_printf("DSN: %s lasthop set\n", r->name); - } + DEBUG(D_route) debug_printf("DSN: %s %s\n", r->name, + r->dsn_lasthop ? "lasthop set" : "propagating DSN"); } } @@ -297,8 +297,8 @@ void route_tidyup(void) { router_instance *r; -for (r = routers; r != NULL; r = r->next) - if (r->info->tidyup != NULL) (r->info->tidyup)(r); +for (r = routers; r; r = r->next) + if (r->info->tidyup) (r->info->tidyup)(r); } @@ -319,20 +319,19 @@ Returns: length of matching prefix or zero */ int -route_check_prefix(uschar *local_part, uschar *prefixes) +route_check_prefix(const uschar *local_part, const uschar *prefixes) { int sep = 0; uschar *prefix; -uschar *listptr = prefixes; +const uschar *listptr = prefixes; uschar prebuf[64]; -while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf))) - != NULL) +while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf)))) { int plen = Ustrlen(prefix); if (prefix[0] == '*') { - uschar *p; + const uschar *p; prefix++; for (p = local_part + Ustrlen(local_part) - (--plen); p >= local_part; p--) @@ -363,21 +362,20 @@ Returns: length of matching suffix or zero */ int -route_check_suffix(uschar *local_part, uschar *suffixes) +route_check_suffix(const uschar *local_part, const uschar *suffixes) { int sep = 0; int alen = Ustrlen(local_part); uschar *suffix; -uschar *listptr = suffixes; +const uschar *listptr = suffixes; uschar sufbuf[64]; -while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf))) - != NULL) +while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf)))) { int slen = Ustrlen(suffix); if (suffix[slen-1] == '*') { - uschar *p, *pend; + const uschar *p, *pend; pend = local_part + alen - (--slen) + 1; for (p = local_part; p < pend; p++) if (strncmpic(suffix, p, slen) == 0) return alen - (p - local_part); @@ -420,46 +418,37 @@ Returns: OK item is in list */ static int -route_check_dls(uschar *rname, uschar *type, uschar *list, tree_node - **anchorptr, unsigned int *cache_bits, int listtype, uschar *domloc, - uschar **ldata, BOOL caseless, uschar **perror) +route_check_dls(uschar *rname, uschar *type, const uschar *list, + tree_node **anchorptr, unsigned int *cache_bits, int listtype, + const uschar *domloc, const uschar **ldata, BOOL caseless, uschar **perror) { -int rc; - -if (list == NULL) return OK; /* Empty list always succeeds */ +if (!list) return OK; /* Empty list always succeeds */ DEBUG(D_route) debug_printf("checking %s\n", type); /* The domain and local part use the same matching function, whereas sender has its own code. */ -if (domloc != NULL) - { - rc = match_isinlist(domloc, &list, 0, anchorptr, cache_bits, listtype, - caseless, ldata); - } -else - { - uschar *address = (sender_address == NULL)? US"" : sender_address; - rc = match_address_list(address, TRUE, TRUE, &list, cache_bits, -1, 0, - &sender_data); - } - -switch(rc) +switch(domloc + ? match_isinlist(domloc, &list, 0, anchorptr, cache_bits, listtype, + caseless, ldata) + : match_address_list(sender_address ? sender_address : US"", + TRUE, TRUE, &list, cache_bits, -1, 0, CUSS &sender_data) + ) { case OK: - return OK; + return OK; case FAIL: - *perror = string_sprintf("%s router skipped: %s mismatch", rname, type); - DEBUG(D_route) debug_printf("%s\n", *perror); - return SKIP; + *perror = string_sprintf("%s router skipped: %s mismatch", rname, type); + DEBUG(D_route) debug_printf("%s\n", *perror); + return SKIP; default: /* Paranoia, and keeps compilers happy */ case DEFER: - *perror = string_sprintf("%s check lookup or other defer", type); - DEBUG(D_route) debug_printf("%s\n", *perror); - return DEFER; + *perror = string_sprintf("%s check lookup or other defer", type); + DEBUG(D_route) debug_printf("%s\n", *perror); + return DEFER; } } @@ -506,9 +495,9 @@ uschar *sp = rp + 1; DEBUG(D_route) debug_printf("route_check_access(%s,%d,%d,%o)\n", path, (int)uid, (int)gid, bits); -if (rp == NULL) return FALSE; +if (!rp) return FALSE; -while ((slash = Ustrchr(sp, '/')) != NULL) +while ((slash = Ustrchr(sp, '/'))) { *slash = 0; DEBUG(D_route) debug_printf("stat %s\n", rp); @@ -570,23 +559,23 @@ Returns: OK if s == NULL or all tests are as required SKIP otherwise */ -int -check_files(uschar *s, uschar **perror) +static int +check_files(const uschar *s, uschar **perror) { int sep = 0; /* List has default separators */ uid_t uid = 0; /* For picky compilers */ gid_t gid = 0; /* For picky compilers */ BOOL ugid_set = FALSE; -uschar *check, *listptr; +const uschar *listptr; +uschar *check; uschar buffer[1024]; -if (s == NULL) return OK; +if (!s) return OK; DEBUG(D_route) debug_printf("checking require_files\n"); listptr = s; -while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) - != NULL) +while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) { int rc; int eacces_code = 0; @@ -594,7 +583,7 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) struct stat statbuf; uschar *ss = expand_string(check); - if (ss == NULL) + if (!ss) { if (expand_string_forcedfail) continue; *perror = string_sprintf("failed to expand \"%s\" for require_files: %s", @@ -885,8 +874,8 @@ if (verify == v_expn && !r->expn) /* Skip this router if there's a domain mismatch. */ if ((rc = route_check_dls(r->name, US"domains", r->domains, &domainlist_anchor, - addr->domain_cache, TRUE, addr->domain, &deliver_domain_data, MCL_DOMAIN, - perror)) != OK) + addr->domain_cache, TRUE, addr->domain, CUSS &deliver_domain_data, + MCL_DOMAIN, perror)) != OK) return rc; /* Skip this router if there's a local part mismatch. We want to pass over the @@ -896,7 +885,7 @@ because that doesn't have the prefix or suffix stripped. A bit of massaging is required. Also, we only use the match cache for local parts that have not had a prefix or suffix stripped. */ -if (addr->prefix == NULL && addr->suffix == NULL) +if (!addr->prefix && !addr->suffix) { localpart_cache = addr->localpart_cache; check_local_part = addr->cc_local_part; @@ -905,16 +894,16 @@ else { localpart_cache = NULL; check_local_part = string_copy(addr->cc_local_part); - if (addr->prefix != NULL) + if (addr->prefix) check_local_part += Ustrlen(addr->prefix); - if (addr->suffix != NULL) + if (addr->suffix) check_local_part[Ustrlen(check_local_part) - Ustrlen(addr->suffix)] = 0; } if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts, &localpartlist_anchor, localpart_cache, MCL_LOCALPART, - check_local_part, &deliver_localpart_data, !r->caseful_local_part, - perror)) != OK) + check_local_part, CUSS &deliver_localpart_data, + !r->caseful_local_part, perror)) != OK) return rc; /* If the check_local_user option is set, check that the local_part is the @@ -943,10 +932,10 @@ check_local_user before any subsequent expansions are done. Otherwise, $home could mean different things for different options, which would be extremely confusing. */ -if (r->router_home_directory != NULL) +if (r->router_home_directory) { uschar *router_home = expand_string(r->router_home_directory); - if (router_home == NULL) + if (!router_home) { if (!expand_string_forcedfail) { @@ -988,7 +977,7 @@ if ((rc = check_files(r->require_files, perror)) != OK) /* Now the general condition test. */ -if (r->condition != NULL) +if (r->condition) { DEBUG(D_route) debug_printf("checking \"condition\"\n"); if (!expand_check_condition(r->condition, r->name, US"router")) @@ -1007,42 +996,44 @@ if (r->condition != NULL) #ifdef EXPERIMENTAL_BRIGHTMAIL /* check if a specific Brightmail AntiSpam rule fired on the message */ -if (r->bmi_rule != NULL) { +if (r->bmi_rule) + { DEBUG(D_route) debug_printf("checking bmi_rule\n"); - if (bmi_check_rule(bmi_base64_verdict, r->bmi_rule) == 0) { - /* none of the rules fired */ + if (bmi_check_rule(bmi_base64_verdict, r->bmi_rule) == 0) + { /* none of the rules fired */ DEBUG(D_route) debug_printf("%s router skipped: none of bmi_rule rules fired\n", r->name); return SKIP; - }; -}; + } + } /* check if message should not be delivered */ -if (r->bmi_dont_deliver) { - if (bmi_deliver == 1) { - DEBUG(D_route) - debug_printf("%s router skipped: bmi_dont_deliver is FALSE\n", r->name); - return SKIP; - }; -}; +if (r->bmi_dont_deliver && bmi_deliver == 1) + { + DEBUG(D_route) + debug_printf("%s router skipped: bmi_dont_deliver is FALSE\n", r->name); + return SKIP; + } /* check if message should go to an alternate location */ -if (r->bmi_deliver_alternate) { - if ((bmi_deliver == 0) || (bmi_alt_location == NULL)) { - DEBUG(D_route) - debug_printf("%s router skipped: bmi_deliver_alternate is FALSE\n", r->name); - return SKIP; - }; -}; +if ( r->bmi_deliver_alternate + && (bmi_deliver == 0 || !bmi_alt_location) + ) + { + DEBUG(D_route) + debug_printf("%s router skipped: bmi_deliver_alternate is FALSE\n", r->name); + return SKIP; + } /* check if message should go to default location */ -if (r->bmi_deliver_default) { - if ((bmi_deliver == 0) || (bmi_alt_location != NULL)) { - DEBUG(D_route) - debug_printf("%s router skipped: bmi_deliver_default is FALSE\n", r->name); - return SKIP; - }; -}; +if ( r->bmi_deliver_default + && (bmi_deliver == 0 || bmi_alt_location) + ) + { + DEBUG(D_route) + debug_printf("%s router skipped: bmi_deliver_default is FALSE\n", r->name); + return SKIP; + } #endif /* All the checks passed. */ @@ -1088,7 +1079,7 @@ static uschar lastgecos[128]; static uschar lastshell[128]; BOOL -route_finduser(uschar *s, struct passwd **pw, uid_t *return_uid) +route_finduser(const uschar *s, struct passwd **pw, uid_t *return_uid) { BOOL cache_set = (Ustrcmp(lastname, s) == 0); @@ -1099,11 +1090,11 @@ if (!cache_set) { int i = 0; - if (return_uid != NULL && (isdigit(*s) || *s == '-') && + if (return_uid && (isdigit(*s) || *s == '-') && s[Ustrspn(s+1, "0123456789")+1] == 0) { *return_uid = (uid_t)Uatoi(s); - if (pw != NULL) *pw = NULL; + if (pw) *pw = NULL; return TRUE; } @@ -1123,12 +1114,12 @@ if (!cache_set) else for (;;) { errno = 0; - if ((lastpw = getpwnam(CS s)) != NULL) break; + if ((lastpw = getpwnam(CS s))) break; if (++i > finduser_retries) break; sleep(1); } - if (lastpw != NULL) + if (lastpw) { pwcopy.pw_uid = lastpw->pw_uid; pwcopy.pw_gid = lastpw->pw_gid; @@ -1142,26 +1133,21 @@ if (!cache_set) lastpw = &pwcopy; } - else DEBUG(D_uid) - { - if (errno != 0) debug_printf("getpwnam(%s) failed: %s\n", s, - strerror(errno)); - } + else DEBUG(D_uid) if (errno != 0) + debug_printf("getpwnam(%s) failed: %s\n", s, strerror(errno)); } -if (lastpw == NULL) +if (!lastpw) { DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n"); return FALSE; } -else - { - DEBUG(D_uid) debug_printf("getpwnam() succeeded uid=%d gid=%d\n", + +DEBUG(D_uid) debug_printf("getpwnam() succeeded uid=%d gid=%d\n", lastpw->pw_uid, lastpw->pw_gid); - } -if (return_uid != NULL) *return_uid = lastpw->pw_uid; -if (pw != NULL) *pw = lastpw; +if (return_uid) *return_uid = lastpw->pw_uid; +if (pw) *pw = lastpw; return TRUE; } @@ -1199,7 +1185,7 @@ if ((isdigit(*s) || *s == '-') && s[Ustrspn(s+1, "0123456789")+1] == 0) for (;;) { - if ((gr = getgrnam(CS s)) != NULL) + if ((gr = getgrnam(CS s))) { *return_gid = gr->gr_gid; return TRUE; @@ -1238,7 +1224,7 @@ route_find_expanded_user(uschar *string, uschar *driver_name, { uschar *user = expand_string(string); -if (user == NULL) +if (!user) { *errmsg = string_sprintf("Failed to expand user string \"%s\" for the " "%s %s: %s", string, driver_name, driver_type, expand_string_message); @@ -1280,7 +1266,7 @@ route_find_expanded_group(uschar *string, uschar *driver_name, uschar *driver_ty BOOL yield = TRUE; uschar *group = expand_string(string); -if (group == NULL) +if (!group) { *errmsg = string_sprintf("Failed to expand group string \"%s\" for the " "%s %s: %s", string, driver_name, driver_type, expand_string_message); @@ -1342,8 +1328,8 @@ from the original address' parent, if present, otherwise unset. */ *parent = *addr; parent->child_count = 2; -parent->p.errors_address = - (addr->parent == NULL)? NULL : addr->parent->p.errors_address; +parent->prop.errors_address = + addr->parent ? addr->parent->prop.errors_address : NULL; /* The routed address gets a new parent. */ @@ -1354,12 +1340,12 @@ was set from the original parent (or to NULL) - see above. We do NOT want to take the errors address from the unseen router. */ new->parent = parent; -new->p.errors_address = parent->p.errors_address; +new->prop.errors_address = parent->prop.errors_address; /* Copy the propagated flags and address_data from the original. */ copyflag(new, addr, af_propagate); -new->p.address_data = addr->p.address_data; +new->prop.address_data = addr->prop.address_data; new->dsn_flags = addr->dsn_flags; new->dsn_orcpt = addr->dsn_orcpt; @@ -1390,8 +1376,7 @@ delivered. If so, we take it off the relevant queue so that it isn't delivered again. Otherwise, it was an alias or something, and the addresses it generated are handled in the normal way. */ -if (addr->transport != NULL && - tree_search(tree_nonrecipients, addr->unique) != NULL) +if (addr->transport && tree_search(tree_nonrecipients, addr->unique)) { DEBUG(D_route) debug_printf("\"unseen\" delivery previously done - discarded\n"); @@ -1437,7 +1422,7 @@ route_address(address_item *addr, address_item **paddr_local, int yield = OK; BOOL unseen; router_instance *r, *nextr; -uschar *old_domain = addr->domain; +const uschar *old_domain = addr->domain; HDEBUG(D_route) { @@ -1449,8 +1434,7 @@ HDEBUG(D_route) encounters an error. If the address has start_router set, we begin from there instead of at the first router. */ -for (r = (addr->start_router == NULL)? routers : addr->start_router; - r != NULL; r = nextr) +for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) { uschar *error; struct passwd *pw = NULL; @@ -1489,7 +1473,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; continally adding to an address, for example), put a long stop counter on the number of parents. */ - for (parent = addr->parent; parent != NULL; parent = parent->parent) + for (parent = addr->parent; parent; parent = parent->parent) { if (parent->router == r) { @@ -1543,7 +1527,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; and setting the prefix. Skip the router if the prefix doesn't match, unless the prefix is optional. */ - if (r->prefix != NULL) + if (r->prefix) { int plen = route_check_prefix(addr->local_part, r->prefix); if (plen > 0) @@ -1562,7 +1546,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; /* Handle any configured suffix likewise. */ - if (r->suffix != NULL) + if (r->suffix) { int slen = route_check_suffix(addr->local_part, r->suffix); if (slen > 0) @@ -1609,11 +1593,11 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; success, the string is attached to the address for all subsequent processing. */ - if (r->address_data != NULL) + if (r->address_data) { DEBUG(D_route) debug_printf("processing address_data\n"); deliver_address_data = expand_string(r->address_data); - if (deliver_address_data == NULL) + if (!deliver_address_data) { if (expand_string_forcedfail) { @@ -1645,7 +1629,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; goto ROUTE_EXIT; } } - addr->p.address_data = deliver_address_data; + addr->prop.address_data = deliver_address_data; } /* We are finally cleared for take-off with this router. Clear the the flag @@ -1656,7 +1640,7 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; clearflag(addr, af_local_host_removed); - if (pw != NULL) + if (pw) { pwcopy.pw_name = CS string_copy(US pw->pw_name); pwcopy.pw_uid = pw->pw_uid; @@ -1669,10 +1653,10 @@ for (r = (addr->start_router == NULL)? routers : addr->start_router; /* Run the router, and handle the consequences. */ -/* ... but let us check on DSN before. If this should be the last hop for DSN - set flag -*/ - if ((r->dsn_lasthop == TRUE) && ((addr->dsn_flags & rf_dsnlasthop) == 0)) + /* ... but let us check on DSN before. If this should be the last hop for DSN + set flag. */ + + if (r->dsn_lasthop && !(addr->dsn_flags & rf_dsnlasthop)) { addr->dsn_flags |= rf_dsnlasthop; HDEBUG(D_route) debug_printf("DSN: last hop for %s\n", addr->address); @@ -1750,16 +1734,16 @@ prematurely, either because a router succeeded, or because of some special router response. Note that FAIL errors and errors detected before actually running a router go direct to ROUTE_EXIT from code above. */ -if (r == NULL) +if (!r) { HDEBUG(D_route) debug_printf("no more routers\n"); - if (addr->message == NULL) + if (!addr->message) { uschar *message = US"Unrouteable address"; - if (addr->router != NULL && addr->router->cannot_route_message != NULL) + if (addr->router && addr->router->cannot_route_message) { uschar *expmessage = expand_string(addr->router->cannot_route_message); - if (expmessage == NULL) + if (!expmessage) { if (!expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " @@ -1812,23 +1796,23 @@ networking, so it is included in the binary only if requested. */ #ifdef SUPPORT_TRANSLATE_IP_ADDRESS -if (r->translate_ip_address != NULL) +if (r->translate_ip_address) { int rc; int old_pool = store_pool; host_item *h; - for (h = addr->host_list; h != NULL; h = h->next) + for (h = addr->host_list; h; h = h->next) { uschar *newaddress; uschar *oldaddress, *oldname; - if (h->address == NULL) continue; + if (!h->address) continue; deliver_host_address = h->address; newaddress = expand_string(r->translate_ip_address); deliver_host_address = NULL; - if (newaddress == NULL) + if (!newaddress) { if (expand_string_forcedfail) continue; addr->basic_errno = ERRNO_EXPANDFAIL; @@ -1877,11 +1861,8 @@ if (yield != OK) goto ROUTE_EXIT; /* Debugging output recording a successful routing */ -HDEBUG(D_route) - { - debug_printf("routed by %s router%s\n", r->name, +HDEBUG(D_route) debug_printf("routed by %s router%s\n", r->name, unseen? " (unseen)" : ""); - } DEBUG(D_route) { @@ -1891,13 +1872,13 @@ DEBUG(D_route) debug_printf(" transport: %s\n", (addr->transport == NULL)? US"<none>" : addr->transport->name); - if (addr->p.errors_address != NULL) - debug_printf(" errors to %s\n", addr->p.errors_address); + if (addr->prop.errors_address) + debug_printf(" errors to %s\n", addr->prop.errors_address); - for (h = addr->host_list; h != NULL; h = h->next) + for (h = addr->host_list; h; h = h->next) { debug_printf(" host %s", h->name); - if (h->address != NULL) debug_printf(" [%s]", h->address); + if (h->address) debug_printf(" [%s]", h->address); if (h->mx >= 0) debug_printf(" MX=%d", h->mx); else if (h->mx != MX_NONE) debug_printf(" rgroup=%d", h->mx); if (h->port != PORT_NONE) debug_printf(" port=%d", h->port); @@ -1910,30 +1891,27 @@ DEBUG(D_route) the "unseen" option (ignore if there are no further routers). */ addr->message = NULL; -if (unseen && r->next != NULL) +if (unseen && r->next) route_unseen(r->name, addr, paddr_local, paddr_remote, addr_new); /* Unset the address expansions, and return the final result. */ ROUTE_EXIT: -if (yield == DEFER) { - if ( - ((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) && - ( - Ustrstr(addr->message, "mysql") != NULL || - Ustrstr(addr->message, "pgsql") != NULL || -#ifdef EXPERIMENTAL_REDIS - Ustrstr(addr->message, "redis") != NULL || -#endif - Ustrstr(addr->message, "sqlite") != NULL || - Ustrstr(addr->message, "ldap:") != NULL || - Ustrstr(addr->message, "ldapdn:") != NULL || - Ustrstr(addr->message, "ldapm:") != NULL - ) - ) { - addr->message = string_sprintf("Temporary internal error"); - } -} +if ( yield == DEFER + && addr->message + && ( Ustrstr(addr->message, "failed to expand") != NULL + || Ustrstr(addr->message, "expansion of ") != NULL + ) + && ( Ustrstr(addr->message, "mysql") != NULL + || Ustrstr(addr->message, "pgsql") != NULL + || Ustrstr(addr->message, "redis") != NULL + || Ustrstr(addr->message, "sqlite") != NULL + || Ustrstr(addr->message, "ldap:") != NULL + || Ustrstr(addr->message, "ldapdn:") != NULL + || Ustrstr(addr->message, "ldapm:") != NULL + ) + ) + addr->message = string_sprintf("Temporary internal error"); deliver_set_expansions(NULL); router_name = NULL; diff --git a/src/src/routers/accept.c b/src/src/routers/accept.c index 8049eaffd..6c76c7119 100644 --- a/src/src/routers/accept.c +++ b/src/src/routers/accept.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -118,9 +118,9 @@ if (!rf_get_transport(rblock->transport_name, &(rblock->transport), addr, rblock->name, NULL)) return DEFER; addr->transport = rblock->transport; -addr->p.errors_address = errors_to; -addr->p.extra_headers = extra_headers; -addr->p.remove_headers = remove_headers; +addr->prop.errors_address = errors_to; +addr->prop.extra_headers = extra_headers; +addr->prop.remove_headers = remove_headers; return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? OK : DEFER; } diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index c8fd3f991..83ba5f689 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -18,10 +18,8 @@ optionlist dnslookup_router_options[] = { (void *)(offsetof(dnslookup_router_options_block, check_secondary_mx)) }, { "check_srv", opt_stringptr, (void *)(offsetof(dnslookup_router_options_block, check_srv)) }, - { "dnssec_request_domains", opt_stringptr, - (void *)(offsetof(dnslookup_router_options_block, dnssec_request_domains)) }, - { "dnssec_require_domains", opt_stringptr, - (void *)(offsetof(dnslookup_router_options_block, dnssec_require_domains)) }, + { "fail_defer_domains", opt_stringptr, + (void *)(offsetof(dnslookup_router_options_block, fail_defer_domains)) }, { "mx_domains", opt_stringptr, (void *)(offsetof(dnslookup_router_options_block, mx_domains)) }, { "mx_fail_domains", opt_stringptr, @@ -58,8 +56,7 @@ dnslookup_router_options_block dnslookup_router_option_defaults = { NULL, /* mx_fail_domains */ NULL, /* srv_fail_domains */ NULL, /* check_srv */ - NULL, /* dnssec_request_domains */ - NULL /* dnssec_require_domains */ + NULL /* fail_defer_domains */ }; @@ -146,10 +143,10 @@ dnslookup_router_options_block *ob = (dnslookup_router_options_block *)(rblock->options_block); uschar *srv_service = NULL; uschar *widen = NULL; -uschar *pre_widen = addr->domain; -uschar *post_widen = NULL; -uschar *fully_qualified_name; -uschar *listptr; +const uschar *pre_widen = addr->domain; +const uschar *post_widen = NULL; +const uschar *fully_qualified_name; +const uschar *listptr; uschar widen_buffer[256]; addr_new = addr_new; /* Keep picky compilers happy */ @@ -161,10 +158,10 @@ DEBUG(D_route) /* If an SRV check is required, expand the service name */ -if (ob->check_srv != NULL) +if (ob->check_srv) { - srv_service = expand_string(ob->check_srv); - if (srv_service == NULL && !expand_string_forcedfail) + if ( !(srv_service = expand_string(ob->check_srv)) + && !expand_string_forcedfail) { addr->message = string_sprintf("%s router: failed to expand \"%s\": %s", rblock->name, ob->check_srv, expand_string_message); @@ -195,8 +192,8 @@ does not appear in the message header so it is also OK to widen. The suppression of widening for sender addresses is silent because it is the normal desirable behaviour. */ -if (ob->widen_domains != NULL && - (verify != v_sender || !ob->rewrite_headers || addr->parent != NULL)) +if ( ob->widen_domains + && (verify != v_sender || !ob->rewrite_headers || addr->parent)) { listptr = ob->widen_domains; widen = string_nextinlist(&listptr, &widen_sep, widen_buffer, @@ -220,12 +217,12 @@ for (;;) int flags = whichrrs; BOOL removed = FALSE; - if (pre_widen != NULL) + if (pre_widen) { h.name = pre_widen; pre_widen = NULL; } - else if (widen != NULL) + else if (widen) { h.name = string_sprintf("%s.%s", addr->domain, widen); widen = string_nextinlist(&listptr, &widen_sep, widen_buffer, @@ -233,7 +230,7 @@ for (;;) DEBUG(D_route) debug_printf("%s router widened %s to %s\n", rblock->name, addr->domain, h.name); } - else if (post_widen != NULL) + else if (post_widen) { h.name = post_widen; post_widen = NULL; @@ -260,16 +257,15 @@ for (;;) subsequently. We use the same logic as that for widen_domains above to avoid requesting a header rewrite that cannot work. */ - if (verify != v_sender || !ob->rewrite_headers || addr->parent != NULL) + if (verify != v_sender || !ob->rewrite_headers || addr->parent) { if (ob->qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE; if (ob->search_parents) flags |= HOST_FIND_SEARCH_PARENTS; } - rc = host_find_bydns(&h, rblock->ignore_target_hosts, flags, srv_service, + rc = host_find_bydns(&h, CUS rblock->ignore_target_hosts, flags, srv_service, ob->srv_fail_domains, ob->mx_fail_domains, - ob->dnssec_request_domains, ob->dnssec_require_domains, - &fully_qualified_name, &removed); + &rblock->dnssec, &fully_qualified_name, &removed); if (removed) setflag(addr, af_local_host_removed); /* If host found with only address records, test for the domain's being in @@ -277,9 +273,9 @@ for (;;) the option is historical. */ if ((rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) && h.mx < 0 && - ob->mx_domains != NULL) - { - switch(match_isinlist(fully_qualified_name, &(ob->mx_domains), 0, + ob->mx_domains) + switch(match_isinlist(fully_qualified_name, + CUSS &(ob->mx_domains), 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) { case DEFER: @@ -291,7 +287,6 @@ for (;;) rblock->name, fully_qualified_name); continue; } - } /* Deferral returns forthwith, and anything other than failure breaks the loop. */ @@ -328,6 +323,7 @@ for (;;) addr->message = US"an MX or SRV record indicated no SMTP service"; else { + addr->basic_errno = ERRNO_UNKNOWNHOST; addr->message = US"all relevant MX records point to non-existent hosts"; if (!allow_mx_to_ip && string_is_ip_address(h.name, NULL) != 0) { @@ -339,6 +335,22 @@ for (;;) addr->message); } } + if (ob->fail_defer_domains) + { + switch(match_isinlist(fully_qualified_name, + CUSS &ob->fail_defer_domains, 0, + &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) + { + case DEFER: + addr->message = US"lookup defer for fail_defer_domains"; + return DEFER; + + case OK: + DEBUG(D_route) debug_printf("%s router: matched fail_defer_domains\n", + rblock->name); + return DEFER; + } + } return DECLINE; } @@ -402,13 +414,13 @@ else if (ob->check_secondary_mx && !testflag(addr, af_local_host_removed)) /* Set up the errors address, if any. */ -rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address); if (rc != OK) return rc; /* Set up the additional and removeable headers for this address. */ -rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers), - &(addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers, + &addr->prop.remove_headers); if (rc != OK) return rc; /* Get store in which to preserve the original host item, chained on @@ -430,3 +442,5 @@ return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? } /* End of routers/dnslookup.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/routers/dnslookup.h b/src/src/routers/dnslookup.h index 518b7f478..3979ef231 100644 --- a/src/src/routers/dnslookup.h +++ b/src/src/routers/dnslookup.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Private structure for the private options. */ @@ -17,8 +17,7 @@ typedef struct { uschar *mx_fail_domains; uschar *srv_fail_domains; uschar *check_srv; - uschar *dnssec_request_domains; - uschar *dnssec_require_domains; + uschar *fail_defer_domains; } dnslookup_router_options_block; /* Data for reading the private options. */ diff --git a/src/src/routers/ipliteral.c b/src/src/routers/ipliteral.c index 7be96e5e0..22d292d88 100644 --- a/src/src/routers/ipliteral.c +++ b/src/src/routers/ipliteral.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -99,8 +99,8 @@ ipliteral_router_options_block *ob = (ipliteral_router_options_block *)(rblock->options_block); */ host_item *h; -uschar *domain = addr->domain; -uschar *ip; +const uschar *domain = addr->domain; +const uschar *ip; int len = Ustrlen(domain); int rc, ipv; @@ -116,29 +116,23 @@ declines. Otherwise route to the single IP address, setting the host name to "(unnamed)". */ if (domain[0] != '[' || domain[len-1] != ']') return DECLINE; -domain[len-1] = 0; /* temporarily */ - -ip = domain + 1; +ip = string_copyn(domain+1, len-2); if (strncmpic(ip, US"IPV6:", 5) == 0 || strncmpic(ip, US"IPV4:", 5) == 0) ip += 5; ipv = string_is_ip_address(ip, NULL); if (ipv == 0 || (disable_ipv6 && ipv == 6)) - { - domain[len-1] = ']'; return DECLINE; - } /* It seems unlikely that ignore_target_hosts will be used with this router, but if it is set, it should probably work. */ -if (verify_check_this_host(&(rblock->ignore_target_hosts), NULL, domain, - ip, NULL) == OK) +if (verify_check_this_host(CUSS&rblock->ignore_target_hosts, + NULL, domain, ip, NULL) == OK) { DEBUG(D_route) debug_printf("%s is in ignore_target_hosts\n", ip); addr->message = US"IP literal host explicitly ignored"; - domain[len-1] = ']'; return DECLINE; } @@ -149,8 +143,7 @@ h = store_get(sizeof(host_item)); h->next = NULL; h->address = string_copy(ip); h->port = PORT_NONE; -domain[len-1] = ']'; /* restore */ -h->name = string_copy(domain); +h->name = domain; h->mx = MX_NONE; h->status = hstatus_unknown; h->why = hwhy_unknown; @@ -172,13 +165,13 @@ addr->host_list = h; /* Set up the errors address, if any. */ -rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address); if (rc != OK) return rc; /* Set up the additional and removeable headers for this address. */ -rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers), - &(addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers, + &addr->prop.remove_headers); if (rc != OK) return rc; /* Fill in the transport, queue the address for local or remote delivery, and diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index 372800783..310e4d66d 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -143,7 +143,8 @@ iplookup_router_entry( { uschar *query = NULL; uschar *reply; -uschar *hostname, *reroute, *domain, *listptr; +uschar *hostname, *reroute, *domain; +const uschar *listptr; uschar host_buffer[256]; host_item *host = store_get(sizeof(host_item)); address_item *new_addr; @@ -206,6 +207,7 @@ while ((hostname = string_nextinlist(&listptr, &sep, host_buffer, host->address = host->name; else { +/*XXX might want dnssec request/require on an iplookup router? */ int rc = host_find_byname(host, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, TRUE); if (rc == HOST_FIND_FAILED || rc == HOST_FIND_AGAIN) continue; } @@ -376,7 +378,7 @@ new_addr = deliver_make_addr(reroute, TRUE); new_addr->parent = addr; copyflag(new_addr, addr, af_propagate); -new_addr->p = addr->p; +new_addr->prop = addr->prop; if (addr->child_count == SHRT_MAX) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d " @@ -388,11 +390,11 @@ new_addr->next = *addr_new; /* Set up the errors address, if any, and the additional and removeable headers for this new address. */ -rc = rf_get_errors_address(addr, rblock, verify, &(new_addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &new_addr->prop.errors_address); if (rc != OK) return rc; -rc = rf_get_munge_headers(addr, rblock, &(new_addr->p.extra_headers), - &(new_addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &new_addr->prop.extra_headers, + &new_addr->prop.remove_headers); if (rc != OK) return rc; return OK; diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index cb047e2ca..46177df43 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -139,8 +139,8 @@ Returns: FALSE if domain expected and string is empty; */ static BOOL -parse_route_item(uschar *s, uschar **domain, uschar **hostlist, - uschar **options) +parse_route_item(const uschar *s, const uschar **domain, const uschar **hostlist, + const uschar **options) { while (*s != 0 && isspace(*s)) s++; @@ -226,9 +226,11 @@ manualroute_router_entry( { int rc, lookup_type; uschar *route_item = NULL; -uschar *options = NULL; -uschar *hostlist = NULL; -uschar *domain, *newhostlist, *listptr; +const uschar *options = NULL; +const uschar *hostlist = NULL; +const uschar *domain; +uschar *newhostlist; +const uschar *listptr; manualroute_router_options_block *ob = (manualroute_router_options_block *)(rblock->options_block); transport_instance *transport = NULL; @@ -260,7 +262,7 @@ if (ob->route_list != NULL) /* Check the current domain; if it matches, break the loop */ if ((rc = match_isinlist(addr->domain, &domain, UCHAR_MAX+1, - &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, &lookup_value)) == OK) + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, CUSS &lookup_value)) == OK) break; /* If there was a problem doing the check, defer */ @@ -318,28 +320,26 @@ lookup_type = lk_default; while (*options != 0) { - int term; - uschar *s = options; + unsigned n; + const uschar *s = options; while (*options != 0 && !isspace(*options)) options++; - term = *options; - *options = 0; + n = options-s; - if (Ustrcmp(s, "randomize") == 0) randomize = TRUE; - else if (Ustrcmp(s, "no_randomize") == 0) randomize = FALSE; - else if (Ustrcmp(s, "byname") == 0) lookup_type = lk_byname; - else if (Ustrcmp(s, "bydns") == 0) lookup_type = lk_bydns; + if (Ustrncmp(s, "randomize", n) == 0) randomize = TRUE; + else if (Ustrncmp(s, "no_randomize", n) == 0) randomize = FALSE; + else if (Ustrncmp(s, "byname", n) == 0) lookup_type = lk_byname; + else if (Ustrncmp(s, "bydns", n) == 0) lookup_type = lk_bydns; else { transport_instance *t; for (t = transports; t != NULL; t = t->next) - { if (Ustrcmp(t->name, s) == 0) { transport = t; individual_transport_set = TRUE; break; } - } + if (t == NULL) { s = string_sprintf("unknown routing option or transport name \"%s\"", s); @@ -349,7 +349,7 @@ while (*options != 0) } } - if (term != 0) + if (*options) { options++; while (*options != 0 && isspace(*options)) options++; @@ -358,13 +358,13 @@ while (*options != 0) /* Set up the errors address, if any. */ -rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address); if (rc != OK) return rc; /* Set up the additional and removeable headers for this address. */ -rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers), - &(addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers, + &addr->prop.remove_headers); if (rc != OK) return rc; /* If an individual transport is not set, get the transport for this router, if diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 11e1fdc34..138062e16 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -114,7 +114,7 @@ while (generated != NULL) next->parent = addr; orflag(next, addr, af_propagate); - next->p = *addr_prop; + next->prop = *addr_prop; next->start_router = rblock->redirect_router; next->next = *addr_new; @@ -192,7 +192,7 @@ int fd_in, fd_out, len, rc; pid_t pid; struct passwd *upw = NULL; uschar buffer[1024]; -uschar **argvptr; +const uschar **argvptr; uschar *rword, *rdata, *s; address_item_propagated addr_prop; queryprogram_router_options_block *ob = @@ -216,11 +216,11 @@ errors address and extra header stuff. */ addr_prop.address_data = deliver_address_data; -rc = rf_get_errors_address(addr, rblock, verify, &(addr_prop.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr_prop.errors_address); if (rc != OK) return rc; -rc = rf_get_munge_headers(addr, rblock, &(addr_prop.extra_headers), - &(addr_prop.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr_prop.extra_headers, + &addr_prop.remove_headers); if (rc != OK) return rc; /* Get the fixed or expanded uid under which the command is to run @@ -526,7 +526,7 @@ lookup_value = NULL; /* Put the errors address, extra headers, and address_data into this address */ -addr->p = addr_prop; +addr->prop = addr_prop; /* Queue the address for local or remote delivery. */ diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index c6705e5b5..7bbaa82e4 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -277,11 +277,11 @@ sort_errors_and_headers(router_instance *rblock, address_item *addr, int verify, address_item_propagated *addr_prop) { int frc = rf_get_errors_address(addr, rblock, verify, - &(addr_prop->errors_address)); + &addr_prop->errors_address); if (frc != OK) return frc; -addr->p.errors_address = addr_prop->errors_address; -return rf_get_munge_headers(addr, rblock, &(addr_prop->extra_headers), - &(addr_prop->remove_headers)); +addr->prop.errors_address = addr_prop->errors_address; +return rf_get_munge_headers(addr, rblock, &addr_prop->extra_headers, + &addr_prop->remove_headers); } @@ -329,7 +329,7 @@ while (generated != NULL) { address_item *parent; address_item *next = generated; - uschar *errors_address = next->p.errors_address; + uschar *errors_address = next->prop.errors_address; generated = next->next; next->parent = addr; @@ -378,8 +378,8 @@ while (generated != NULL) If so, we must take care to re-instate it when we copy in the propagated data so that it overrides any errors_to setting on the router. */ - next->p = *addr_prop; - if (errors_address != NULL) next->p.errors_address = errors_address; + next->prop = *addr_prop; + if (errors_address != NULL) next->prop.errors_address = errors_address; /* For pipes, files, and autoreplies, record this router as handling them, because they don't go through the routing process again. Then set up uid, @@ -451,13 +451,18 @@ while (generated != NULL) } } +#ifdef SUPPORT_I18N + next->prop.utf8_msg = string_is_utf8(next->address) + || (sender_address && string_is_utf8(sender_address)); +#endif + DEBUG(D_route) { debug_printf("%s router generated %s\n %serrors_to=%s transport=%s\n", rblock->name, next->address, testflag(next, af_pfr)? "pipe, file, or autoreply\n " : "", - next->p.errors_address, + next->prop.errors_address, (next->transport == NULL)? US"NULL" : next->transport->name); if (testflag(next, af_uid_set)) @@ -470,6 +475,10 @@ while (generated != NULL) else debug_printf("gid=unset "); +#ifdef SUPPORT_I18N + if (next->prop.utf8_msg) debug_printf("utf8 "); +#endif + debug_printf("home=%s\n", next->home_dir); } } @@ -517,7 +526,7 @@ int redirect_router_entry( redirect_router_options_block *ob = (redirect_router_options_block *)(rblock->options_block); address_item *generated = NULL; -uschar *save_qualify_domain_recipient = qualify_domain_recipient; +const uschar *save_qualify_domain_recipient = qualify_domain_recipient; uschar *discarded = US"discarded"; address_item_propagated addr_prop; error_block *eblock = NULL; @@ -891,7 +900,7 @@ else data that propagates. */ copyflag(next, addr, af_propagate); - next->p = addr_prop; + next->prop = addr_prop; DEBUG(D_route) debug_printf("%s router autogenerated %s\n%s%s%s", rblock->name, diff --git a/src/src/routers/rf_change_domain.c b/src/src/routers/rf_change_domain.c index 2343e3e0d..8986925c1 100644 --- a/src/src/routers/rf_change_domain.c +++ b/src/src/routers/rf_change_domain.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -32,7 +32,7 @@ Returns: nothing */ void -rf_change_domain(address_item *addr, uschar *domain, BOOL rewrite, +rf_change_domain(address_item *addr, const uschar *domain, BOOL rewrite, address_item **addr_new) { address_item *parent = store_get(sizeof(address_item)); @@ -52,7 +52,7 @@ domain cache. Then copy over the propagating fields from the parent. Then set up the new fields. */ *addr = address_defaults; -addr->p = parent->p; +addr->prop = parent->prop; addr->address = address; addr->unique = string_copy(address); diff --git a/src/src/routers/rf_functions.h b/src/src/routers/rf_functions.h index 29f37cf5d..1344faadf 100644 --- a/src/src/routers/rf_functions.h +++ b/src/src/routers/rf_functions.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Header for the functions that are shared by the routers */ @@ -11,7 +11,7 @@ extern void rf_add_generated(router_instance *, address_item **, address_item *, address_item *, uschar *, header_line *, uschar *, ugid_block *, struct passwd *); -extern void rf_change_domain(address_item *, uschar *, BOOL, address_item **); +extern void rf_change_domain(address_item *, const uschar *, BOOL, address_item **); extern uschar *rf_expand_data(address_item *, uschar *, int *); extern int rf_get_errors_address(address_item *, router_instance *, BOOL, uschar **); diff --git a/src/src/routers/rf_get_errors_address.c b/src/src/routers/rf_get_errors_address.c index 457397a57..d7172d7ac 100644 --- a/src/src/routers/rf_get_errors_address.c +++ b/src/src/routers/rf_get_errors_address.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -38,7 +38,7 @@ rf_get_errors_address(address_item *addr, router_instance *rblock, { uschar *s; -*errors_to = addr->p.errors_address; +*errors_to = addr->prop.errors_address; if (rblock->errors_to == NULL) return OK; s = expand_string(rblock->errors_to); @@ -84,8 +84,8 @@ else BOOL save_address_test_mode = address_test_mode; int save1 = 0; int i; - uschar ***p; - uschar *address_expansions_save[ADDRESS_EXPANSIONS_COUNT]; + const uschar ***p; + const uschar *address_expansions_save[ADDRESS_EXPANSIONS_COUNT]; address_item *snew = deliver_make_addr(s, FALSE); if (sender_address != NULL) diff --git a/src/src/routers/rf_get_munge_headers.c b/src/src/routers/rf_get_munge_headers.c index a4a13b04f..ecb4ee097 100644 --- a/src/src/routers/rf_get_munge_headers.c +++ b/src/src/routers/rf_get_munge_headers.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -32,11 +32,11 @@ rf_get_munge_headers(address_item *addr, router_instance *rblock, header_line **extra_headers, uschar **remove_headers) { /* Default is to retain existing headers */ -*extra_headers = addr->p.extra_headers; +*extra_headers = addr->prop.extra_headers; if (rblock->extra_headers) { - uschar * list = rblock->extra_headers; + const uschar * list = rblock->extra_headers; int sep = '\n'; uschar * s; int slen; @@ -46,8 +46,9 @@ if (rblock->extra_headers) { if (!expand_string_forcedfail) { - addr->message = string_sprintf("%s router failed to expand \"%s\": %s", - rblock->name, rblock->extra_headers, expand_string_message); + addr->message = string_sprintf( + "%s router failed to expand add_headers item \"%s\": %s", + rblock->name, s, expand_string_message); return DEFER; } } @@ -82,23 +83,23 @@ if (rblock->extra_headers) } /* Default is to retain existing removes */ -*remove_headers = addr->p.remove_headers; +*remove_headers = addr->prop.remove_headers; /* Expand items from colon-sep list separately, then build new list */ if (rblock->remove_headers) { - uschar * list = rblock->remove_headers; + const uschar * list = rblock->remove_headers; int sep = ':'; uschar * s; - uschar buffer[128]; - while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) + while ((s = string_nextinlist(&list, &sep, NULL, 0))) if (!(s = expand_string(s))) { if (!expand_string_forcedfail) { - addr->message = string_sprintf("%s router failed to expand \"%s\": %s", - rblock->name, rblock->remove_headers, expand_string_message); + addr->message = string_sprintf( + "%s router failed to expand remove_headers item \"%s\": %s", + rblock->name, s, expand_string_message); return DEFER; } } @@ -109,4 +110,6 @@ if (rblock->remove_headers) return OK; } +/* vi: aw ai sw=4 +*/ /* End of rf_get_munge_headers.c */ diff --git a/src/src/routers/rf_lookup_hostlist.c b/src/src/routers/rf_lookup_hostlist.c index 0eae31e61..78eda22fb 100644 --- a/src/src/routers/rf_lookup_hostlist.c +++ b/src/src/routers/rf_lookup_hostlist.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -61,14 +61,13 @@ host, omit it and any subsequent hosts - i.e. treat the list like an ordered list of MX hosts. If the first host is the local host, act according to the "self" option in the configuration. */ -prev = NULL; -for (h = addr->host_list; h != NULL; h = next_h) +for (prev = NULL, h = addr->host_list; h; h = next_h) { - uschar *canonical_name; - int rc, len, port; + const uschar *canonical_name; + int rc, len, port, mx, sort_key; next_h = h->next; - if (h->address != NULL) { prev = h; continue; } + if (h->address) { prev = h; continue; } DEBUG(D_route|D_host_lookup) debug_printf("finding IP address for %s\n", h->name); @@ -78,8 +77,15 @@ for (h = addr->host_list; h != NULL; h = next_h) port = host_item_get_port(h); + /* Store the previous mx and sort_key values, which were assigned in + host_build_hostlist and will be overwritten by host_find_bydns. */ + + mx = h->mx; + sort_key = h->sort_key; + /* If the name ends with "/MX", we interpret it to mean "the list of hosts - pointed to by MX records with this name". */ + pointed to by MX records with this name", and the MX record values override + the ordering from host_build_hostlist. */ len = Ustrlen(h->name); if (len > 3 && strcmpic(h->name + len - 3, US"/mx") == 0) @@ -87,6 +93,7 @@ for (h = addr->host_list; h != NULL; h = next_h) DEBUG(D_route|D_host_lookup) debug_printf("doing DNS MX lookup for %s\n", h->name); + mx = MX_NONE; h->name = string_copyn(h->name, len - 3); rc = host_find_bydns(h, ignore_target_hosts, @@ -94,8 +101,7 @@ for (h = addr->host_list; h != NULL; h = next_h) NULL, /* SRV service not relevant */ NULL, /* failing srv domains not relevant */ NULL, /* no special mx failing domains */ - NULL, /* no dnssec request XXX ? */ - NULL, /* no dnssec require XXX ? */ + &rblock->dnssec, /* dnssec request/require */ NULL, /* fully_qualified_name */ NULL); /* indicate local host removed */ } @@ -118,23 +124,23 @@ for (h = addr->host_list; h != NULL; h = next_h) { BOOL removed; DEBUG(D_route|D_host_lookup) debug_printf("doing DNS lookup\n"); - rc = host_find_bydns(h, ignore_target_hosts, HOST_FIND_BY_A, NULL, NULL, - NULL, - NULL, NULL, /*XXX dnssec? */ - &canonical_name, &removed); - if (rc == HOST_FOUND) - { - if (removed) setflag(addr, af_local_host_removed); - } - else if (rc == HOST_FIND_FAILED) + switch (rc = host_find_bydns(h, ignore_target_hosts, HOST_FIND_BY_A, NULL, + NULL, NULL, + &rblock->dnssec, /* domains for request/require */ + &canonical_name, &removed)) { - if (lookup_type == lk_default) - { - DEBUG(D_route|D_host_lookup) - debug_printf("DNS lookup failed: trying getipnodebyname\n"); - rc = host_find_byname(h, ignore_target_hosts, HOST_FIND_QUALIFY_SINGLE, - &canonical_name, TRUE); - } + case HOST_FOUND: + if (removed) setflag(addr, af_local_host_removed); + break; + case HOST_FIND_FAILED: + if (lookup_type == lk_default) + { + DEBUG(D_route|D_host_lookup) + debug_printf("DNS lookup failed: trying getipnodebyname\n"); + rc = host_find_byname(h, ignore_target_hosts, HOST_FIND_QUALIFY_SINGLE, + &canonical_name, TRUE); + } + break; } } @@ -168,6 +174,7 @@ for (h = addr->host_list; h != NULL; h = next_h) if (hff_code == hff_pass) return PASS; if (hff_code == hff_decline) return DECLINE; + addr->basic_errno = ERRNO_UNKNOWNHOST; addr->message = string_sprintf("lookup of host \"%s\" failed in %s router%s", h->name, rblock->name, @@ -180,7 +187,8 @@ for (h = addr->host_list; h != NULL; h = next_h) return DEFER; } - /* Deal with a port setting */ + /* Deal with the settings that were previously cleared: + port, mx and sort_key. */ if (port != PORT_NONE) { @@ -188,13 +196,23 @@ for (h = addr->host_list; h != NULL; h = next_h) for (hh = h; hh != next_h; hh = hh->next) hh->port = port; } + if (mx != MX_NONE) + { + host_item *hh; + for (hh = h; hh != next_h; hh = hh->next) + { + hh->mx = mx; + hh->sort_key = sort_key; + } + } + /* A local host gets chopped, with its successors, if there are previous hosts. Otherwise the self option is used. If it is set to "send", any subsequent hosts that are also the local host do NOT get chopped. */ if (rc == HOST_FOUND_LOCAL && !self_send) { - if (prev != NULL) + if (prev) { DEBUG(D_route) { diff --git a/src/src/routers/rf_queue_add.c b/src/src/routers/rf_queue_add.c index 06cdb6c6d..784a5477f 100644 --- a/src/src/routers/rf_queue_add.c +++ b/src/src/routers/rf_queue_add.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -36,8 +36,8 @@ BOOL rf_queue_add(address_item *addr, address_item **paddr_local, address_item **paddr_remote, router_instance *rblock, struct passwd *pw) { -addr->p.domain_data = deliver_domain_data; /* Save these values for */ -addr->p.localpart_data = deliver_localpart_data; /* use in the transport */ +addr->prop.domain_data = deliver_domain_data; /* Save these values for */ +addr->prop.localpart_data = deliver_localpart_data; /* use in the transport */ /* Handle a local transport */ @@ -95,9 +95,9 @@ DEBUG(D_route) debug_printf("queued for %s transport: local_part = %s\ndomain = %s\n" " errors_to=%s\n", (addr->transport == NULL)? US"<unset>" : addr->transport->name, - addr->local_part, addr->domain, addr->p.errors_address); - debug_printf(" domain_data=%s localpart_data=%s\n", addr->p.domain_data, - addr->p.localpart_data); + addr->local_part, addr->domain, addr->prop.errors_address); + debug_printf(" domain_data=%s localpart_data=%s\n", addr->prop.domain_data, + addr->prop.localpart_data); } return TRUE; diff --git a/src/src/search.c b/src/src/search.c index 15db52aa6..ccad25021 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* A set of functions to search databases in various formats. An open @@ -62,7 +62,7 @@ Returns: +ve => valid lookup name; value is offset in lookup_list */ int -search_findtype(uschar *name, int len) +search_findtype(const uschar *name, int len) { int bot = 0; int top = lookup_list_count; @@ -121,12 +121,12 @@ Returns: +ve => valid lookup name; value is offset in lookup_list */ int -search_findtype_partial(uschar *name, int *ptypeptr, uschar **ptypeaff, +search_findtype_partial(const uschar *name, int *ptypeptr, const uschar **ptypeaff, int *afflen, int *starflags) { int len, stype; int pv = -1; -uschar *ss = name; +const uschar *ss = name; *starflags = 0; *ptypeaff = NULL; @@ -466,6 +466,7 @@ internal_search_find(void *handle, uschar *filename, uschar *keystring) { tree_node *t = (tree_node *)handle; search_cache *c = (search_cache *)(t->data.ptr); +expiring_data *e; uschar *data = NULL; int search_type = t->name[0] - '0'; int old_pool = store_pool; @@ -491,18 +492,27 @@ store_pool = POOL_SEARCH; /* Look up the data for the key, unless it is already in the cache for this file. No need to check c->item_cache for NULL, tree_search will do so. */ -if ((t = tree_search(c->item_cache, keystring)) == NULL) +if ( (t = tree_search(c->item_cache, keystring)) + && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL)) + ) + { /* Data was in the cache already; set the pointer from the tree node */ + data = e->ptr; + DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n", + keystring, + filename ? US"\n in " : US"", filename ? filename : US""); + } +else { - BOOL do_cache = TRUE; + uint do_cache = UINT_MAX; int keylength = Ustrlen(keystring); DEBUG(D_lookup) { - if (filename != NULL) - debug_printf("file lookup required for %s\n in %s\n", - keystring, filename); - else - debug_printf("database lookup required for %s\n", keystring); + if (t) debug_printf("cached data found but past valid time; "); + debug_printf("%s lookup required for %s%s%s\n", + filename ? US"file" : US"database", + keystring, + filename ? US"\n in " : US"", filename ? filename : US""); } /* Call the code for the different kinds of search. DEFER is handled @@ -511,9 +521,7 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL) if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength, &data, &search_error_message, &do_cache) == DEFER) - { search_find_defer = TRUE; - } /* A record that has been found is now in data, which is either NULL or points to a bit of dynamic store. Cache the result of the lookup if @@ -524,10 +532,22 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL) else if (do_cache) { int len = keylength + 1; - t = store_get(sizeof(tree_node) + len); - memcpy(t->name, keystring, len); - t->data.ptr = data; - tree_insertnode(&c->item_cache, t); + + if (t) /* Previous, out-of-date cache entry. Update with the */ + { /* new result and forget the old one */ + e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; + e->ptr = data; + } + else + { + e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len); + e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; + e->ptr = data; + t = (tree_node *)(e+1); + memcpy(t->name, keystring, len); + t->data.ptr = e; + tree_insertnode(&c->item_cache, t); + } } /* If caching was disabled, empty the cache tree. We just set the cache @@ -540,34 +560,19 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL) } } -/* Data was in the cache already; set the pointer from the tree node */ - -else - { - data = US t->data.ptr; - DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n", - keystring, - (filename == NULL)? US"" : US"\n in ", - (filename == NULL)? US"" : filename); - } - -/* Debug: output the answer */ - DEBUG(D_lookup) { - if (data == NULL) - { - if (search_find_defer) debug_printf("lookup deferred: %s\n", - search_error_message); - else debug_printf("lookup failed\n"); - } - else debug_printf("lookup yielded: %s\n", data); + if (data) + debug_printf("lookup yielded: %s\n", data); + else if (search_find_defer) + debug_printf("lookup deferred: %s\n", search_error_message); + else debug_printf("lookup failed\n"); } /* Return it in new dynamic store in the regular pool */ store_pool = old_pool; -return (data == NULL)? NULL : string_copy(data); +return data ? string_copy(data) : NULL; } @@ -601,7 +606,7 @@ Returns: a pointer to a dynamic string containing the answer, uschar * search_find(void *handle, uschar *filename, uschar *keystring, int partial, - uschar *affix, int affixlen, int starflags, int *expand_setup) + const uschar *affix, int affixlen, int starflags, int *expand_setup) { tree_node *t = (tree_node *)handle; BOOL set_null_wild = FALSE; diff --git a/src/src/setenv.c b/src/src/setenv.c new file mode 100644 index 000000000..6da56d58d --- /dev/null +++ b/src/src/setenv.c @@ -0,0 +1,55 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Michael Haardt 2015 */ +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* This module provides (un)setenv routines for those environments +lacking them in libraries. */ + + +static int +setenv(const char * name, const char * val, int overwrite) +{ +uschar * s; +if (Ustrchr(name, '=')) return -1; +if (overwrite || !getenv(name)) + putenv(CS string_copy_malloc(string_sprintf("%s=%s", name, val))); +return 0; +} + +static int +unsetenv(const char *name) +{ +size_t len; +const char * end; +char ** e; +extern char ** environ; + +if (!name) + { + errno = EINVAL; + return -1; + } + +for (end = name; *end != '=' && *end; ) end++; +len = end - name; + +/* Find name in environment and move remaining variables down. +Do not early-out in case there are duplicate names. */ + +for (e = environ; *e; e++) + if (strncmp(*e, name, len) == 0 && (*e)[len] == '=') + { + char ** sp = e; + do *sp = sp[1]; while (*++sp); + } + +return 0; +} + +/* vi: aw ai sw=2 +*/ +/* End of setenv.c */ diff --git a/src/src/sieve.c b/src/src/sieve.c index 1303646ab..3d7e99b27 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Michael Haardt 2003-2008 */ +/* Copyright (c) Michael Haardt 2003 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This code was contributed by Michael Haardt. */ @@ -1072,7 +1072,7 @@ if (file) setflag(new_addr, af_pfr|af_file); new_addr->mode = 0; } -new_addr->p.errors_address = NULL; +new_addr->prop.errors_address = NULL; new_addr->next = *generated; *generated = new_addr; } @@ -2737,8 +2737,8 @@ Returns: 2 success by stop 1 other success -1 syntax or execution error */ -static int parse_commands(struct Sieve *filter, int exec, - address_item **generated) +static int +parse_commands(struct Sieve *filter, int exec, address_item **generated) { while (*filter->pc) { @@ -2970,7 +2970,6 @@ while (*filter->pc) int m; struct String from; struct String importance; - struct String *options; struct String message; struct String method; struct Notification *already; @@ -2991,7 +2990,6 @@ while (*filter->pc) from.length=-1; importance.character=(uschar*)0; importance.length=-1; - options=(struct String*)0; message.character=(uschar*)0; message.length=-1; recipient=NULL; @@ -3362,7 +3360,8 @@ while (*filter->pc) /* Allocation is larger than neccessary, but enough even for split MIME words */ buffer_capacity=32+4*subject.length; buffer=store_get(buffer_capacity); - addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE); + /* deconst cast safe as we pass in a non-const item */ + addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE); addr->reply->oncelog=once; addr->reply->once_repeat=days*86400; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 4fc2cfd41..b48e436e3 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2,13 +2,14 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ #include "exim.h" +#include <assert.h> /* Initialize for TCP wrappers if so configured. It appears that the macro @@ -36,10 +37,10 @@ uschar *tcp_wrappers_name; /* Size of buffer for reading SMTP commands. We used to use 512, as defined by RFC 821. However, RFC 1869 specifies that this must be increased for SMTP commands that accept arguments, and this in particular applies to AUTH, where -the data can be quite long. More recently this value was 2048 in Exim; +the data can be quite long. More recently this value was 2048 in Exim; however, RFC 4954 (circa 2007) recommends 12288 bytes to handle AUTH. Clients -such as Thunderbird will send an AUTH with an initial-response for GSSAPI. -The maximum size of a Kerberos ticket under Windows 2003 is 12000 bytes, and +such as Thunderbird will send an AUTH with an initial-response for GSSAPI. +The maximum size of a Kerberos ticket under Windows 2003 is 12000 bytes, and we need room to handle large base64-encoded AUTHs for GSSAPI. */ @@ -71,6 +72,7 @@ enum { VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */ ETRN_CMD, /* This by analogy with TURN from the RFC */ STARTTLS_CMD, /* Required by the STARTTLS RFC */ + TLS_AUTH_CMD, /* auto-command at start of SSL */ /* This is a dummy to identify the non-sync commands when pipelining */ @@ -94,7 +96,7 @@ enum { QUIT_CMD, HELP_CMD, -#ifdef EXPERIMENTAL_PROXY +#ifdef SUPPORT_PROXY PROXY_FAIL_IGNORE_CMD, #endif @@ -133,6 +135,9 @@ static BOOL rcpt_smtp_response_same; static BOOL rcpt_in_progress; static int nonmail_command_count; static BOOL smtp_exit_function_called = 0; +#ifdef SUPPORT_I18N +static BOOL smtputf8_advertised; +#endif static int synprot_error_count; static int unknown_command_count; static int sync_cmd_limit; @@ -155,15 +160,21 @@ AUTH is already forbidden. After a TLS session is started, AUTH's flag is again forced TRUE, to allow for the re-authentication that can happen at that point. QUIT is also "falsely" labelled as a mail command so that it doesn't up the -count of non-mail commands and possibly provoke an error. */ +count of non-mail commands and possibly provoke an error. + +tls_auth is a pseudo-command, never expected in input. It is activated +on TLS startup and looks for a tls authenticator. */ static smtp_cmd_list cmd_list[] = { + /* name len cmd has_arg is_mail_cmd */ + { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE }, { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, #ifdef SUPPORT_TLS { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, + { "tls_auth", 0, TLS_AUTH_CMD, FALSE, TRUE }, #endif /* If you change anything above here, also fix the definitions below. */ @@ -187,6 +198,7 @@ static smtp_cmd_list *cmd_list_end = #define CMD_LIST_EHLO 2 #define CMD_LIST_AUTH 3 #define CMD_LIST_STARTTLS 4 +#define CMD_LIST_TLS_AUTH 5 /* This list of names is used for performing the smtp_no_mail logging action. It must be kept in step with the SCH_xxx enumerations. */ @@ -197,7 +209,7 @@ static uschar *smtp_names[] = US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", US"STARTTLS", US"VRFY" }; -static uschar *protocols[] = { +static uschar *protocols_local[] = { US"local-smtp", /* HELO */ US"local-smtps", /* The rare case EHLO->STARTTLS->HELO */ US"local-esmtp", /* EHLO */ @@ -205,21 +217,31 @@ static uschar *protocols[] = { US"local-esmtpa", /* EHLO->AUTH */ US"local-esmtpsa" /* EHLO->STARTTLS->EHLO->AUTH */ }; +static uschar *protocols[] = { + US"smtp", /* HELO */ + US"smtps", /* The rare case EHLO->STARTTLS->HELO */ + US"esmtp", /* EHLO */ + US"esmtps", /* EHLO->STARTTLS->EHLO */ + US"esmtpa", /* EHLO->AUTH */ + US"esmtpsa" /* EHLO->STARTTLS->EHLO->AUTH */ + }; #define pnormal 0 #define pextend 2 #define pcrpted 1 /* added to pextend or pnormal */ #define pauthed 2 /* added to pextend */ -#define pnlocal 6 /* offset to remove "local" */ /* Sanity check and validate optional args to MAIL FROM: envelope */ enum { + ENV_MAIL_OPT_NULL, ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH, #ifndef DISABLE_PRDR ENV_MAIL_OPT_PRDR, #endif ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID, - ENV_MAIL_OPT_NULL +#ifdef SUPPORT_I18N + ENV_MAIL_OPT_UTF8, +#endif }; typedef struct { uschar * name; /* option requested during MAIL cmd */ @@ -236,7 +258,11 @@ static env_mail_type_t env_mail_type_list[] = { #endif { US"RET", ENV_MAIL_OPT_RET, TRUE }, { US"ENVID", ENV_MAIL_OPT_ENVID, TRUE }, - { US"NULL", ENV_MAIL_OPT_NULL, FALSE } +#ifdef SUPPORT_I18N + { US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */ +#endif + /* keep this the last entry */ + { US"NULL", ENV_MAIL_OPT_NULL, FALSE }, }; /* When reading SMTP from a remote host, we have to use our own versions of the @@ -557,7 +583,7 @@ exim_exit(EXIT_FAILURE); -#ifdef EXPERIMENTAL_PROXY +#ifdef SUPPORT_PROXY /************************************************* * Restore socket timeout to previous value * *************************************************/ @@ -594,7 +620,7 @@ int rc; /* Cannot configure local connection as a proxy inbound */ if (sender_host_address == NULL) return proxy_session; -rc = verify_check_this_host(&proxy_required_hosts, NULL, NULL, +rc = verify_check_this_host(CUSS &hosts_proxy, NULL, NULL, sender_host_address, NULL); if (rc == OK) { @@ -735,10 +761,10 @@ if (ret >= 16 && DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype); return ERRNO_PROXYFAIL; } - proxy_host_address = sender_host_address; + proxy_local_address = sender_host_address; sender_host_address = string_copy(US tmpip); tmpport = ntohs(hdr.v2.addr.ip4.src_port); - proxy_host_port = sender_host_port; + proxy_local_port = sender_host_port; sender_host_port = tmpport; /* Save dest ip/port */ tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr; @@ -748,9 +774,9 @@ if (ret >= 16 && DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype); return ERRNO_PROXYFAIL; } - proxy_target_address = string_copy(US tmpip); + proxy_external_address = string_copy(US tmpip); tmpport = ntohs(hdr.v2.addr.ip4.dst_port); - proxy_target_port = tmpport; + proxy_external_port = tmpport; goto done; case 0x21: /* TCPv6 address type */ iptype = US"IPv6"; @@ -761,10 +787,10 @@ if (ret >= 16 && DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype); return ERRNO_PROXYFAIL; } - proxy_host_address = sender_host_address; + proxy_local_address = sender_host_address; sender_host_address = string_copy(US tmpip6); tmpport = ntohs(hdr.v2.addr.ip6.src_port); - proxy_host_port = sender_host_port; + proxy_local_port = sender_host_port; sender_host_port = tmpport; /* Save dest ip/port */ memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16); @@ -774,9 +800,9 @@ if (ret >= 16 && DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype); return ERRNO_PROXYFAIL; } - proxy_target_address = string_copy(US tmpip6); + proxy_external_address = string_copy(US tmpip6); tmpport = ntohs(hdr.v2.addr.ip6.dst_port); - proxy_target_port = tmpport; + proxy_external_port = tmpport; goto done; default: DEBUG(D_receive) @@ -855,7 +881,7 @@ else if (ret >= 8 && debug_printf("Proxied src arg is not an %s address\n", iptype); goto proxyfail; } - proxy_host_address = sender_host_address; + proxy_local_address = sender_host_address; sender_host_address = p; p = sp + 1; if ((sp = Ustrchr(p, ' ')) == NULL) @@ -871,7 +897,7 @@ else if (ret >= 8 && debug_printf("Proxy dest arg is not an %s address\n", iptype); goto proxyfail; } - proxy_target_address = p; + proxy_external_address = p; p = sp + 1; if ((sp = Ustrchr(p, ' ')) == NULL) { @@ -886,7 +912,7 @@ else if (ret >= 8 && debug_printf("Proxied src port '%s' not an integer\n", p); goto proxyfail; } - proxy_host_port = sender_host_port; + proxy_local_port = sender_host_port; sender_host_port = tmp_port; p = sp + 1; if ((sp = Ustrchr(p, '\0')) == NULL) @@ -901,7 +927,7 @@ else if (ret >= 8 && debug_printf("Proxy dest port '%s' not an integer\n", p); goto proxyfail; } - proxy_target_port = tmp_port; + proxy_external_port = tmp_port; /* Already checked for /r /n above. Good V1 header received. */ goto done; } @@ -999,7 +1025,7 @@ if required. */ for (p = cmd_list; p < cmd_list_end; p++) { - #ifdef EXPERIMENTAL_PROXY + #ifdef SUPPORT_PROXY /* Only allow QUIT command if Proxy Protocol parsing failed */ if (proxy_session && proxy_session_failed) { @@ -1007,10 +1033,12 @@ for (p = cmd_list; p < cmd_list_end; p++) continue; } #endif - if (strncmpic(smtp_cmd_buffer, US p->name, p->len) == 0 && - (smtp_cmd_buffer[p->len-1] == ':' || /* "mail from:" or "rcpt to:" */ - smtp_cmd_buffer[p->len] == 0 || - smtp_cmd_buffer[p->len] == ' ')) + if ( p->len + && strncmpic(smtp_cmd_buffer, US p->name, p->len) == 0 + && ( smtp_cmd_buffer[p->len-1] == ':' /* "mail from:" or "rcpt to:" */ + || smtp_cmd_buffer[p->len] == 0 + || smtp_cmd_buffer[p->len] == ' ' + ) ) { if (smtp_inptr < smtp_inend && /* Outstanding input */ p->cmd < sync_cmd_limit && /* Command should sync */ @@ -1054,7 +1082,7 @@ for (p = cmd_list; p < cmd_list_end; p++) } } -#ifdef EXPERIMENTAL_PROXY +#ifdef SUPPORT_PROXY /* Only allow QUIT command if Proxy Protocol parsing failed */ if (proxy_session && proxy_session_failed) return PROXY_FAIL_IGNORE_CMD; @@ -1206,8 +1234,7 @@ if (sender_host_unknown || sender_host_notsocket) if (is_inetd) return string_sprintf("SMTP connection from %s (via inetd)", hostname); -if ((log_extra_selector & LX_incoming_interface) != 0 && - interface_address != NULL) +if (LOGGING(incoming_interface) && interface_address != NULL) return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname, interface_address, interface_port); @@ -1232,16 +1259,15 @@ s_tlslog(uschar * s, int * sizep, int * ptrp) int size = sizep ? *sizep : 0; int ptr = ptrp ? *ptrp : 0; - if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL) + if (LOGGING(tls_cipher) && tls_in.cipher != NULL) s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher); - if ((log_extra_selector & LX_tls_certificate_verified) != 0 && - tls_in.cipher != NULL) + if (LOGGING(tls_certificate_verified) && tls_in.cipher != NULL) s = string_append(s, &size, &ptr, 2, US" CV=", tls_in.certificate_verified? "yes":"no"); - if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL) + if (LOGGING(tls_peerdn) && tls_in.peerdn != NULL) s = string_append(s, &size, &ptr, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); - if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL) + if (LOGGING(tls_sni) && tls_in.sni != NULL) s = string_append(s, &size, &ptr, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); @@ -1273,7 +1299,7 @@ smtp_log_no_mail(void) int size, ptr, i; uschar *s, *sep; -if (smtp_mailcmd_count > 0 || (log_extra_selector & LX_smtp_no_mail) == 0) +if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail)) return; s = NULL; @@ -1494,6 +1520,10 @@ sender_verified_list = NULL; /* No senders verified */ memset(sender_address_cache, 0, sizeof(sender_address_cache)); memset(sender_domain_cache, 0, sizeof(sender_domain_cache)); +#ifndef DISABLE_PRDR +prdr_requested = FALSE; +#endif + /* Reset the DSN flags */ dsn_ret = 0; dsn_envid = NULL; @@ -1514,6 +1544,9 @@ spf_received = NULL; spf_result = NULL; spf_smtp_comment = NULL; #endif +#ifdef SUPPORT_I18N +message_smtputf8 = FALSE; +#endif body_linecount = body_zerocount = 0; sender_rate = sender_rate_limit = sender_rate_period = NULL; @@ -1622,6 +1655,7 @@ while (done <= 0) it is the canonical extracted address which is all that is kept. */ case MAIL_CMD: + smtp_mailcmd_count++; /* Count for no-mail log */ if (sender_address != NULL) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given"); @@ -1832,8 +1866,6 @@ pipelining_enable = TRUE; sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING; smtp_exit_function_called = FALSE; /* For avoiding loop in not-quit exit */ -memset(sender_host_cache, 0, sizeof(sender_host_cache)); - /* If receiving by -bs from a trusted user, or testing with -bh, we allow authentication settings from -oMaa to remain in force. */ @@ -1848,6 +1880,9 @@ tls_in.ocsp = OCSP_NOT_REQ; tls_advertised = FALSE; #endif dsn_advertised = FALSE; +#ifdef SUPPORT_I18N +smtputf8_advertised = FALSE; +#endif /* Reset ACL connection variables */ @@ -1875,7 +1910,7 @@ reset later if any of EHLO/AUTH/STARTTLS are received. */ else received_protocol = - protocols[pnormal] + ((sender_host_address != NULL)? pnlocal : 0); + (sender_host_address ? protocols : protocols_local) [pnormal]; /* Set up the buffer for inputting using direct read() calls, and arrange to call the local functions instead of the standard C ones. */ @@ -2274,7 +2309,7 @@ if (!sender_host_unknown) if (smtp_batched_input) return TRUE; -#ifdef EXPERIMENTAL_PROXY +#ifdef SUPPORT_PROXY /* If valid Proxy Protocol source is connecting, set up session. * Failure will not allow any SMTP function other than QUIT. */ proxy_session = FALSE; @@ -2438,7 +2473,7 @@ if (++synprot_error_count > smtp_max_synprot_errors) yield = 1; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "syntax or protocol errors (last command was \"%s\")", - host_and_ident(FALSE), smtp_cmd_buffer); + host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); } if (code > 0) @@ -2471,8 +2506,8 @@ static void incomplete_transaction_log(uschar *what) { if (sender_address == NULL || /* No transaction in progress */ - (log_write_selector & L_smtp_incomplete_transaction) == 0 /* Not logging */ - ) return; + !LOGGING(smtp_incomplete_transaction)) + return; /* Build list of recipients for logging */ @@ -2723,7 +2758,7 @@ if (sender_verified_failed != NULL && setflag(sender_verified_failed, af_sverify_told); - if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0) + if (rc != FAIL || LOGGING(sender_verify_fail)) log_write(0, LOG_MAIN|LOG_REJECT, "%s sender verify %s for <%s>%s", host_and_ident(TRUE), ((sender_verified_failed->special_action & 255) == DEFER)? "defer":"fail", @@ -2977,29 +3012,25 @@ else /* If a host name is known, check it and all its aliases. */ - if (sender_host_name != NULL) - { - helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0; - - if (helo_verified) + if (sender_host_name) + if ((helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0)) { + sender_helo_dnssec = sender_host_dnssec; HDEBUG(D_receive) debug_printf("matched host name\n"); } else { uschar **aliases = sender_host_aliases; - while (*aliases != NULL) - { - helo_verified = strcmpic(*aliases++, sender_helo_name) == 0; - if (helo_verified) break; - } - HDEBUG(D_receive) - { - if (helo_verified) + while (*aliases) + if ((helo_verified = strcmpic(*aliases++, sender_helo_name) == 0)) + { + sender_helo_dnssec = sender_host_dnssec; + break; + } + + HDEBUG(D_receive) if (helo_verified) debug_printf("matched alias %s\n", *(--aliases)); - } } - } /* Final attempt: try a forward lookup of the helo name */ @@ -3007,29 +3038,34 @@ else { int rc; host_item h; + dnssec_domains d; + host_item *hh; + h.name = sender_helo_name; h.address = NULL; h.mx = MX_NONE; h.next = NULL; + d.request = US"*"; + d.require = US""; + HDEBUG(D_receive) debug_printf("getting IP address for %s\n", sender_helo_name); - rc = host_find_byname(&h, NULL, 0, NULL, TRUE); + rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A, + NULL, NULL, NULL, &d, NULL, NULL); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) - { - host_item *hh = &h; - while (hh != NULL) - { + for (hh = &h; hh; hh = hh->next) if (Ustrcmp(hh->address, sender_host_address) == 0) { helo_verified = TRUE; + if (h.dnssec == DS_YES) sender_helo_dnssec = TRUE; HDEBUG(D_receive) - debug_printf("IP address for %s matches calling address\n", - sender_helo_name); + { + debug_printf("IP address for %s matches calling address\n" + "Forward DNS security status: %sverified\n", + sender_helo_name, sender_helo_dnssec ? "" : "un"); + } break; } - hh = hh->next; - } - } } } @@ -3066,6 +3102,113 @@ smtp_respond(code, len, TRUE, user_msg); +static int +smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss) +{ +const uschar *set_id = NULL; +int rc, i; + +/* Run the checking code, passing the remainder of the command line as +data. Initials the $auth<n> variables as empty. Initialize $0 empty and set +it as the only set numerical variable. The authenticator may set $auth<n> +and also set other numeric variables. The $auth<n> variables are preferred +nowadays; the numerical variables remain for backwards compatibility. + +Afterwards, have a go at expanding the set_id string, even if +authentication failed - for bad passwords it can be useful to log the +userid. On success, require set_id to expand and exist, and put it in +authenticated_id. Save this in permanent store, as the working store gets +reset at HELO, RSET, etc. */ + +for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; +expand_nmax = 0; +expand_nlength[0] = 0; /* $0 contains nothing */ + +rc = (au->info->servercode)(au, smtp_cmd_data); +if (au->set_id) set_id = expand_string(au->set_id); +expand_nmax = -1; /* Reset numeric variables */ +for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */ + +/* The value of authenticated_id is stored in the spool file and printed in +log lines. It must not contain binary zeros or newline characters. In +normal use, it never will, but when playing around or testing, this error +can (did) happen. To guard against this, ensure that the id contains only +printing characters. */ + +if (set_id) set_id = string_printing(set_id); + +/* For the non-OK cases, set up additional logging data if set_id +is not empty. */ + +if (rc != OK) + set_id = set_id && *set_id + ? string_sprintf(" (set_id=%s)", set_id) : US""; + +/* Switch on the result */ + +switch(rc) + { + case OK: + if (!au->set_id || set_id) /* Complete success */ + { + if (set_id) authenticated_id = string_copy_malloc(set_id); + sender_host_authenticated = au->name; + authentication_failed = FALSE; + authenticated_fail_id = NULL; /* Impossible to already be set? */ + + received_protocol = + (sender_host_address ? protocols : protocols_local) + [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)]; + *s = *ss = US"235 Authentication succeeded"; + authenticated_by = au; + break; + } + + /* Authentication succeeded, but we failed to expand the set_id string. + Treat this as a temporary error. */ + + auth_defer_msg = expand_string_message; + /* Fall through */ + + case DEFER: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = string_sprintf("435 Unable to authenticate at present%s", + auth_defer_user_msg); + *ss = string_sprintf("435 Unable to authenticate at present%s: %s", + set_id, auth_defer_msg); + break; + + case BAD64: + *s = *ss = US"501 Invalid base64 data"; + break; + + case CANCELLED: + *s = *ss = US"501 Authentication cancelled"; + break; + + case UNEXPECTED: + *s = *ss = US"553 Initial data not expected"; + break; + + case FAIL: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = US"535 Incorrect authentication data"; + *ss = string_sprintf("535 Incorrect authentication data%s", set_id); + break; + + default: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = US"435 Internal error"; + *ss = string_sprintf("435 Internal error%s: return %d from authentication " + "check", set_id, rc); + break; + } + +return rc; +} + + + /************************************************* * Initialize for SMTP incoming message * *************************************************/ @@ -3117,6 +3260,7 @@ cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; #ifdef SUPPORT_TLS cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; +cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; #endif /* Set the local signal handler for SIGTERM - it tries to end off tidily */ @@ -3132,7 +3276,7 @@ value. The values are 2 larger than the required yield of the function. */ while (done <= 0) { - uschar **argv; + const uschar **argv; uschar *etrn_command; uschar *etrn_serialize_key; uschar *errmess; @@ -3140,7 +3284,6 @@ while (done <= 0) uschar *user_msg = NULL; uschar *recipient = NULL; uschar *hello = NULL; - uschar *set_id = NULL; uschar *s, *ss; BOOL was_rej_mail = FALSE; BOOL was_rcpt = FALSE; @@ -3148,11 +3291,44 @@ while (done <= 0) pid_t pid; int start, end, sender_domain, recipient_domain; int ptr, size, rc; - int c, i; + int c; auth_instance *au; uschar *orcpt = NULL; int flags; +#if defined(SUPPORT_TLS) && defined(AUTH_TLS) + /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */ + if ( tls_in.active >= 0 + && tls_in.peercert + && tls_in.certificate_verified + && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd + ) + { + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE; + if (acl_smtp_auth) + { + rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); + if (rc != OK) + { + done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); + continue; + } + } + + for (au = auths; au; au = au->next) + if (strcmpic(US"tls", au->driver_name) == 0) + { + smtp_cmd_data = NULL; + + if (smtp_in_auth(au, &s, &ss) == OK) + DEBUG(D_auth) debug_printf("tls auth succeeded\n"); + else + DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); + break; + } + } +#endif + switch(smtp_read_command(TRUE)) { /* The AUTH command is not permitted to occur inside a transaction, and may @@ -3180,13 +3356,13 @@ while (done <= 0) US"AUTH command used when not advertised"); break; } - if (sender_host_authenticated != NULL) + if (sender_host_authenticated) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"already authenticated"); break; } - if (sender_address != NULL) + if (sender_address) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"not permitted in mail transaction"); @@ -3195,7 +3371,7 @@ while (done <= 0) /* Check the ACL */ - if (acl_smtp_auth != NULL) + if (acl_smtp_auth) { rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); if (rc != OK) @@ -3232,121 +3408,23 @@ while (done <= 0) as a server and which has been advertised (unless, sigh, allow_auth_ unadvertised is set). */ - for (au = auths; au != NULL; au = au->next) - { + for (au = auths; au; au = au->next) if (strcmpic(s, au->public_name) == 0 && au->server && - (au->advertised || allow_auth_unadvertised)) break; - } - - if (au == NULL) - { - done = synprot_error(L_smtp_protocol_error, 504, NULL, - string_sprintf("%s authentication mechanism not supported", s)); - break; - } - - /* Run the checking code, passing the remainder of the command line as - data. Initials the $auth<n> variables as empty. Initialize $0 empty and set - it as the only set numerical variable. The authenticator may set $auth<n> - and also set other numeric variables. The $auth<n> variables are preferred - nowadays; the numerical variables remain for backwards compatibility. - - Afterwards, have a go at expanding the set_id string, even if - authentication failed - for bad passwords it can be useful to log the - userid. On success, require set_id to expand and exist, and put it in - authenticated_id. Save this in permanent store, as the working store gets - reset at HELO, RSET, etc. */ + (au->advertised || allow_auth_unadvertised)) + break; - for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; - expand_nmax = 0; - expand_nlength[0] = 0; /* $0 contains nothing */ - - c = (au->info->servercode)(au, smtp_cmd_data); - if (au->set_id != NULL) set_id = expand_string(au->set_id); - expand_nmax = -1; /* Reset numeric variables */ - for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */ - - /* The value of authenticated_id is stored in the spool file and printed in - log lines. It must not contain binary zeros or newline characters. In - normal use, it never will, but when playing around or testing, this error - can (did) happen. To guard against this, ensure that the id contains only - printing characters. */ - - if (set_id != NULL) set_id = string_printing(set_id); - - /* For the non-OK cases, set up additional logging data if set_id - is not empty. */ - - if (c != OK) + if (au) { - if (set_id != NULL && *set_id != 0) - set_id = string_sprintf(" (set_id=%s)", set_id); - else set_id = US""; - } + c = smtp_in_auth(au, &s, &ss); - /* Switch on the result */ - - switch(c) - { - case OK: - if (au->set_id == NULL || set_id != NULL) /* Complete success */ - { - if (set_id != NULL) authenticated_id = string_copy_malloc(set_id); - sender_host_authenticated = au->name; - authentication_failed = FALSE; - authenticated_fail_id = NULL; /* Impossible to already be set? */ - received_protocol = - protocols[pextend + pauthed + ((tls_in.active >= 0)? pcrpted:0)] + - ((sender_host_address != NULL)? pnlocal : 0); - s = ss = US"235 Authentication succeeded"; - authenticated_by = au; - break; - } - - /* Authentication succeeded, but we failed to expand the set_id string. - Treat this as a temporary error. */ - - auth_defer_msg = expand_string_message; - /* Fall through */ - - case DEFER: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = string_sprintf("435 Unable to authenticate at present%s", - auth_defer_user_msg); - ss = string_sprintf("435 Unable to authenticate at present%s: %s", - set_id, auth_defer_msg); - break; - - case BAD64: - s = ss = US"501 Invalid base64 data"; - break; - - case CANCELLED: - s = ss = US"501 Authentication cancelled"; - break; - - case UNEXPECTED: - s = ss = US"553 Initial data not expected"; - break; - - case FAIL: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = US"535 Incorrect authentication data"; - ss = string_sprintf("535 Incorrect authentication data%s", set_id); - break; - - default: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = US"435 Internal error"; - ss = string_sprintf("435 Internal error%s: return %d from authentication " - "check", set_id, c); - break; + smtp_printf("%s\r\n", s); + if (c != OK) + log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", + au->name, host_and_ident(FALSE), ss); } - - smtp_printf("%s\r\n", s); - if (c != OK) - log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", - au->name, host_and_ident(FALSE), ss); + else + done = synprot_error(L_smtp_protocol_error, 504, NULL, + string_sprintf("%s authentication mechanism not supported", s)); break; /* AUTH_CMD */ @@ -3397,7 +3475,7 @@ while (done <= 0) { log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "syntax or protocol errors (last command was \"%s\")", - host_and_ident(FALSE), smtp_cmd_buffer); + host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); done = 1; } @@ -3424,7 +3502,7 @@ while (done <= 0) if (sender_host_name == NULL && (deliver_domain = sender_helo_name, /* set $domain */ - match_isinlist(sender_helo_name, &helo_lookup_domains, 0, + match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK) (void)host_name_lookup(); @@ -3442,7 +3520,7 @@ while (done <= 0) now obsolescent, since the verification can now be requested selectively at ACL time. */ - helo_verified = helo_verify_failed = FALSE; + helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE; if (helo_required || helo_verify) { BOOL tempfail = !smtp_verify_helo(); @@ -3493,10 +3571,13 @@ while (done <= 0) auth_advertised = FALSE; pipelining_advertised = FALSE; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_advertised = FALSE; - #endif +#endif dsn_advertised = FALSE; +#ifdef SUPPORT_I18N + smtputf8_advertised = FALSE; +#endif smtp_code = US"250 "; /* Default response code plus space*/ if (user_msg == NULL) @@ -3581,7 +3662,7 @@ while (done <= 0) } /* Advertise DSN support if configured to do so. */ - if (verify_check_host(&dsn_advertise_hosts) != FAIL) + if (verify_check_host(&dsn_advertise_hosts) != FAIL) { s = string_cat(s, &size, &ptr, smtp_code, 3); s = string_cat(s, &size, &ptr, US"-DSN\r\n", 6); @@ -3629,45 +3710,47 @@ while (done <= 0) letters, so output the names in upper case, though we actually recognize them in either case in the AUTH command. */ - if (auths != NULL) - { - if (verify_check_host(&auth_advertise_hosts) == OK) - { - auth_instance *au; - BOOL first = TRUE; - for (au = auths; au != NULL; au = au->next) - { - if (au->server && (au->advertise_condition == NULL || - expand_check_condition(au->advertise_condition, au->name, - US"authenticator"))) - { - int saveptr; - if (first) - { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-AUTH", 5); - first = FALSE; - auth_advertised = TRUE; - } - saveptr = ptr; - s = string_cat(s, &size, &ptr, US" ", 1); - s = string_cat(s, &size, &ptr, au->public_name, - Ustrlen(au->public_name)); - while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); - au->advertised = TRUE; - } - else au->advertised = FALSE; - } - if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2); - } - } + if ( auths +#if defined(SUPPORT_TLS) && defined(AUTH_TLS) + && !sender_host_authenticated +#endif + && verify_check_host(&auth_advertise_hosts) == OK + ) + { + auth_instance *au; + BOOL first = TRUE; + for (au = auths; au; au = au->next) + if (au->server && (au->advertise_condition == NULL || + expand_check_condition(au->advertise_condition, au->name, + US"authenticator"))) + { + int saveptr; + if (first) + { + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-AUTH", 5); + first = FALSE; + auth_advertised = TRUE; + } + saveptr = ptr; + s = string_cat(s, &size, &ptr, US" ", 1); + s = string_cat(s, &size, &ptr, au->public_name, + Ustrlen(au->public_name)); + while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); + au->advertised = TRUE; + } + else + au->advertised = FALSE; + + if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2); + } /* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer) if it has been included in the binary, and the host matches tls_advertise_hosts. We must *not* advertise if we are already in a secure connection. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_in.active < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) { @@ -3675,16 +3758,26 @@ while (done <= 0) s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11); tls_advertised = TRUE; } - #endif +#endif - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ if (prdr_enable) { s = string_cat(s, &size, &ptr, smtp_code, 3); s = string_cat(s, &size, &ptr, US"-PRDR\r\n", 7); } - #endif +#endif + +#ifdef SUPPORT_I18N + if ( accept_8bitmime + && verify_check_host(&smtputf8_advertise_hosts) != FAIL) + { + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-SMTPUTF8\r\n", 11); + smtputf8_advertised = TRUE; + } +#endif /* Finish off the multiline reply with one that is always available. */ @@ -3697,9 +3790,9 @@ while (done <= 0) s[ptr] = 0; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr); else - #endif +#endif { int i = fwrite(s, 1, ptr, smtp_out); i = i; /* compiler quietening */ @@ -3714,16 +3807,13 @@ while (done <= 0) helo_seen = TRUE; /* Reset the protocol and the state, abandoning any previous message. */ - - received_protocol = (esmtp? - protocols[pextend + - ((sender_host_authenticated != NULL)? pauthed : 0) + - ((tls_in.active >= 0)? pcrpted : 0)] - : - protocols[pnormal + ((tls_in.active >= 0)? pcrpted : 0)]) - + - ((sender_host_address != NULL)? pnlocal : 0); - + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active >= 0 ? pcrpted : 0) + ]; smtp_reset(reset_point); toomany = FALSE; break; /* HELO/EHLO */ @@ -3793,13 +3883,11 @@ while (done <= 0) if (!extract_option(&name, &value)) break; for (mail_args = env_mail_type_list; - (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list); + mail_args->value != ENV_MAIL_OPT_NULL; mail_args++ ) - { if (strcmpic(name, mail_args->name) == 0) break; - } if (mail_args->need_value && strcmpic(value, US"") == 0) break; @@ -3827,16 +3915,17 @@ while (done <= 0) and "7BIT" as body types, but take no action. */ case ENV_MAIL_OPT_BODY: if (accept_8bitmime) { - if (strcmpic(value, US"8BITMIME") == 0) { + if (strcmpic(value, US"8BITMIME") == 0) body_8bitmime = 8; - } else if (strcmpic(value, US"7BIT") == 0) { + else if (strcmpic(value, US"7BIT") == 0) body_8bitmime = 7; - } else { + else + { body_8bitmime = 0; done = synprot_error(L_smtp_syntax_error, 501, NULL, US"invalid data for BODY"); goto COMMAND_LOOP; - } + } DEBUG(D_receive) debug_printf("8BITMIME: %d\n", body_8bitmime); break; } @@ -3848,35 +3937,43 @@ while (done <= 0) is included only if configured in at build time. */ case ENV_MAIL_OPT_RET: - if (dsn_advertised) { + if (dsn_advertised) + { /* Check if RET has already been set */ - if (dsn_ret > 0) { + if (dsn_ret > 0) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"RET can be specified once only"); goto COMMAND_LOOP; - } - dsn_ret = (strcmpic(value, US"HDRS") == 0)? dsn_ret_hdrs : - (strcmpic(value, US"FULL") == 0)? dsn_ret_full : 0; + } + dsn_ret = strcmpic(value, US"HDRS") == 0 + ? dsn_ret_hdrs + : strcmpic(value, US"FULL") == 0 + ? dsn_ret_full + : 0; DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret); /* Check for invalid invalid value, and exit with error */ - if (dsn_ret == 0) { + if (dsn_ret == 0) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"Value for RET is invalid"); goto COMMAND_LOOP; - } - } + } + } break; case ENV_MAIL_OPT_ENVID: - if (dsn_advertised) { + if (dsn_advertised) + { /* Check if the dsn envid has been already set */ - if (dsn_envid != NULL) { + if (dsn_envid != NULL) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"ENVID can be specified once only"); goto COMMAND_LOOP; - } + } dsn_envid = string_copy(value); DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid); - } + } break; /* Handle the AUTH extension. If the value given is not "<>" and either @@ -3912,38 +4009,38 @@ while (done <= 0) rc = acl_check(ACL_WHERE_MAILAUTH, NULL, acl_smtp_mailauth, &user_msg, &log_msg); } - + switch (rc) { case OK: - if (authenticated_by == NULL || - authenticated_by->mail_auth_condition == NULL || - expand_check_condition(authenticated_by->mail_auth_condition, - authenticated_by->name, US"authenticator")) - break; /* Accept the AUTH */ - - ignore_msg = US"server_mail_auth_condition failed"; - if (authenticated_id != NULL) - ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"", - ignore_msg, authenticated_id); - + if (authenticated_by == NULL || + authenticated_by->mail_auth_condition == NULL || + expand_check_condition(authenticated_by->mail_auth_condition, + authenticated_by->name, US"authenticator")) + break; /* Accept the AUTH */ + + ignore_msg = US"server_mail_auth_condition failed"; + if (authenticated_id != NULL) + ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"", + ignore_msg, authenticated_id); + /* Fall through */ - + case FAIL: - authenticated_sender = NULL; - log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)", - value, host_and_ident(TRUE), ignore_msg); - break; - + authenticated_sender = NULL; + log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)", + value, host_and_ident(TRUE), ignore_msg); + break; + /* Should only get DEFER or ERROR here. Put back terminator overrides for error message */ - + default: - value[-1] = '='; - name[-1] = ' '; - (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg, - log_msg); - goto COMMAND_LOOP; + value[-1] = '='; + name[-1] = ' '; + (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg, + log_msg); + goto COMMAND_LOOP; } } break; @@ -3955,15 +4052,27 @@ while (done <= 0) break; #endif - /* Unknown option. Stick back the terminator characters and break +#ifdef SUPPORT_I18N + case ENV_MAIL_OPT_UTF8: + if (smtputf8_advertised) + { + DEBUG(D_receive) debug_printf("smtputf8 requested\n"); + message_smtputf8 = allow_utf8_domains = TRUE; + received_protocol = string_sprintf("utf8%s", received_protocol); + } + break; +#endif + /* No valid option. Stick back the terminator characters and break the loop. Do the name-terminator second as extract_option sets - value==name when it found no equal-sign. - An error for a malformed address will occur. */ - default: + value==name when it found no equal-sign. + An error for a malformed address will occur. */ + case ENV_MAIL_OPT_NULL: value[-1] = '='; name[-1] = ' '; arg_error = TRUE; break; + + default: assert(0); } /* Break out of for loop if switch() had bad argument or when start of the email address is reached */ @@ -3987,9 +4096,10 @@ while (done <= 0) /* Now extract the address, first applying any SMTP-time rewriting. The TRUE flag allows "<>" as a sender address. */ - raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)? - rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", - global_rewrite_rules) : smtp_cmd_data; + raw_sender = rewrite_existflags & rewrite_smtp + ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + global_rewrite_rules) + : smtp_cmd_data; /* rfc821_domains = TRUE; << no longer needed */ raw_sender = @@ -4094,7 +4204,7 @@ while (done <= 0) US"", #endif US"\r\n"); - else + else { #ifndef DISABLE_PRDR if (prdr_requested) @@ -4352,18 +4462,14 @@ while (done <= 0) if (user_msg == NULL) smtp_printf("250 Accepted\r\n"); else smtp_user_msg(US"250", user_msg); receive_add_recipient(recipient, -1); - + /* Set the dsn flags in the recipients_list */ - if (orcpt != NULL) - recipients_list[recipients_count-1].orcpt = orcpt; - else - recipients_list[recipients_count-1].orcpt = NULL; + recipients_list[recipients_count-1].orcpt = orcpt; + recipients_list[recipients_count-1].dsn_flags = flags; - if (flags != 0) - recipients_list[recipients_count-1].dsn_flags = flags; - else - recipients_list[recipients_count-1].dsn_flags = 0; - DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", recipients_list[recipients_count-1].orcpt, recipients_list[recipients_count-1].dsn_flags); + DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", + recipients_list[recipients_count-1].orcpt, + recipients_list[recipients_count-1].dsn_flags); } /* The recipient was discarded */ @@ -4374,13 +4480,11 @@ while (done <= 0) else smtp_user_msg(US"250", user_msg); rcpt_fail_count++; discarded = TRUE; - log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: " + log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: " "discarded by %s ACL%s%s", host_and_ident(TRUE), - (sender_address_unrewritten != NULL)? - sender_address_unrewritten : sender_address, + sender_address_unrewritten? sender_address_unrewritten : sender_address, smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT", - (log_msg == NULL)? US"" : US": ", - (log_msg == NULL)? US"" : log_msg); + log_msg ? US": " : US"", log_msg ? log_msg : US""); } /* Either the ACL failed the address, or it was deferred. */ @@ -4448,7 +4552,7 @@ while (done <= 0) ACL may have delayed. To handle cutthrough delivery enforce a dummy call to get the DATA command sent. */ - if (acl_smtp_predata == NULL && cutthrough_fd < 0) rc = OK; else + if (acl_smtp_predata == NULL && cutthrough.fd < 0) rc = OK; else { uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept"; enable_dollar_recipients = TRUE; @@ -4616,6 +4720,7 @@ while (done <= 0) helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE; + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; if (sender_helo_name != NULL) { store_free(sender_helo_name); @@ -4624,13 +4729,13 @@ while (done <= 0) set_process_info("handling incoming TLS connection from %s", host_and_ident(FALSE)); } - received_protocol = (esmtp? - protocols[pextend + pcrpted + - ((sender_host_authenticated != NULL)? pauthed : 0)] - : - protocols[pnormal + pcrpted]) - + - ((sender_host_address != NULL)? pnlocal : 0); + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active >= 0 ? pcrpted : 0) + ]; sender_host_authenticated = NULL; authenticated_id = NULL; @@ -4850,7 +4955,7 @@ while (done <= 0) break; } etrn_command = US"exim -R"; - argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R", + argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R", smtp_cmd_data); } @@ -4872,7 +4977,7 @@ while (done <= 0) /* If ETRN queue runs are to be serialized, check the database to ensure one isn't already running. */ - if (smtp_etrn_serialize && !enq_start(etrn_serialize_key)) + if (smtp_etrn_serialize && !enq_start(etrn_serialize_key, 1)) { smtp_printf("458 Already processing %s\r\n", smtp_cmd_data); break; @@ -4996,11 +5101,11 @@ while (done <= 0) done = 1; /* Pretend eof - drops connection */ break; - #ifdef EXPERIMENTAL_PROXY +#ifdef SUPPORT_PROXY case PROXY_FAIL_IGNORE_CMD: smtp_printf("503 Command refused, required Proxy negotiation failed\r\n"); break; - #endif +#endif default: if (unknown_command_count++ >= smtp_max_unknown_commands) @@ -5015,7 +5120,7 @@ while (done <= 0) done = 2; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "unrecognized commands (last was \"%s\")", host_and_ident(FALSE), - smtp_cmd_buffer); + string_printing(smtp_cmd_buffer)); } else done = synprot_error(L_smtp_syntax_error, 500, NULL, diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 530fcfec7..c55b29254 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -2,13 +2,14 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* A number of functions for driving outgoing SMTP calls. */ #include "exim.h" +#include "transports/smtp.h" @@ -25,7 +26,6 @@ Arguments: which case the function does nothing host_af AF_INET or AF_INET6 for the outgoing IP address addr the mail address being handled (for setting errors) - changed if not NULL, set TRUE if expansion actually changed istring interface point this to the interface msg to add to any error message @@ -35,9 +35,9 @@ Returns: TRUE on success, FALSE on failure, with error message BOOL smtp_get_interface(uschar *istring, int host_af, address_item *addr, - BOOL *changed, uschar **interface, uschar *msg) + uschar **interface, uschar *msg) { -uschar *expint; +const uschar * expint; uschar *iface; int sep = 0; @@ -53,8 +53,6 @@ if (expint == NULL) return FALSE; } -if (changed != NULL) *changed = expint != istring; - while (isspace(*expint)) expint++; if (*expint == 0) return TRUE; @@ -143,75 +141,26 @@ return TRUE; -/************************************************* -* Connect to remote host * -*************************************************/ - -/* Create a socket, and connect it to a remote host. IPv6 addresses are -detected by checking for a colon in the address. AF_INET6 is defined even on -non-IPv6 systems, to enable the code to be less messy. However, on such systems -host->address will always be an IPv4 address. - -The port field in the host item is used if it is set (usually router from SRV -records or elsewhere). In other cases, the default passed as an argument is -used, and the host item is updated with its value. - -Arguments: - host host item containing name and address (and sometimes port) - host_af AF_INET or AF_INET6 - port default remote port to connect to, in host byte order, for those - hosts whose port setting is PORT_NONE - interface outgoing interface address or NULL - timeout timeout value or 0 - keepalive TRUE to use keepalive - dscp DSCP value to assign to socket - event event expansion - -Returns: connected socket number, or -1 with errno set -*/ - int -smtp_connect(host_item *host, int host_af, int port, uschar *interface, - int timeout, BOOL keepalive, const uschar *dscp -#ifdef EXPERIMENTAL_EVENT - , uschar * event -#endif - ) +smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface, + transport_instance * tb, int timeout) { -int on = 1; -int save_errno = 0; +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; +const uschar * dscp = ob->dscp; int dscp_value; int dscp_level; int dscp_option; int sock; +int on = 1; +int save_errno = 0; -if (host->port != PORT_NONE) - { - HDEBUG(D_transport|D_acl|D_v) - debug_printf("Transport port=%d replaced by host-specific port=%d\n", port, - host->port); - port = host->port; - } -else host->port = port; /* Set the port actually used */ - -HDEBUG(D_transport|D_acl|D_v) - { - if (interface == NULL) - debug_printf("Connecting to %s [%s]:%d ... ",host->name,host->address,port); - else - debug_printf("Connecting to %s [%s]:%d from %s ... ", host->name, - host->address, port, interface); - } - -#ifdef EXPERIMENTAL_EVENT - deliver_host_address = host->address; - deliver_host_port = port; - if (event_raise(event, US"tcp:connect", NULL)) return -1; - /* Logging? Debug? */ +#ifndef DISABLE_EVENT +deliver_host_address = host->address; +deliver_host_port = port; +if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1; #endif -/* Create the socket */ - if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1; /* Set TCP_NODELAY; Exim does its own buffering. */ @@ -232,15 +181,13 @@ if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value)) option for both; ignore failures here */ if (host_af == AF_INET6 && dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value)) - { (void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)); - } } /* Bind to a specific interface if requested. Caller must ensure the interface is the same type (IPv4 or IPv6) as the outgoing address. */ -if (interface != NULL && ip_bind(sock, host_af, interface, 0) < 0) +if (interface && ip_bind(sock, host_af, interface, 0) < 0) { save_errno = errno; HDEBUG(D_transport|D_acl|D_v) @@ -286,11 +233,76 @@ else close(sock); return -1; } - if (keepalive) ip_keepalive(sock, host->address, TRUE); + if (ob->keepalive) ip_keepalive(sock, host->address, TRUE); return sock; } } +/************************************************* +* Connect to remote host * +*************************************************/ + +/* Create a socket, and connect it to a remote host. IPv6 addresses are +detected by checking for a colon in the address. AF_INET6 is defined even on +non-IPv6 systems, to enable the code to be less messy. However, on such systems +host->address will always be an IPv4 address. + +The port field in the host item is used if it is set (usually router from SRV +records or elsewhere). In other cases, the default passed as an argument is +used, and the host item is updated with its value. + +Arguments: + host host item containing name and address (and sometimes port) + host_af AF_INET or AF_INET6 + port default remote port to connect to, in host byte order, for those + hosts whose port setting is PORT_NONE + interface outgoing interface address or NULL + timeout timeout value or 0 + tb transport + +Returns: connected socket number, or -1 with errno set +*/ + +int +smtp_connect(host_item *host, int host_af, int port, uschar *interface, + int timeout, transport_instance * tb) +{ +#ifdef SUPPORT_SOCKS +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; +#endif + +if (host->port != PORT_NONE) + { + HDEBUG(D_transport|D_acl|D_v) + debug_printf("Transport port=%d replaced by host-specific port=%d\n", port, + host->port); + port = host->port; + } +else host->port = port; /* Set the port actually used */ + +callout_address = string_sprintf("[%s]:%d", host->address, port); + +HDEBUG(D_transport|D_acl|D_v) + { + uschar * s = US" "; + if (interface) s = string_sprintf(" from %s ", interface); +#ifdef SUPPORT_SOCKS + if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s); +#endif + debug_printf("Connecting to %s %s%s... ", host->name, callout_address, s); + } + +/* Create and connect the socket */ + +#ifdef SUPPORT_SOCKS +if (ob->socks_proxy) + return socks_sock_connect(host, host_af, port, interface, tb, timeout); +#endif + +return smtp_sock_connect(host, host_af, port, interface, tb, timeout); +} + /************************************************* * Flush outgoing command buffer * @@ -581,3 +593,5 @@ return buffer[0] == okdigit; } /* End of smtp_out.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/spam.c b/src/src/spam.c index c0c3fb373..6a0ca3c20 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */ +/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015 */ /* License: GPL */ /* Code for calling spamassassin's spamd. Called from acl.c. */ @@ -14,457 +14,638 @@ uschar spam_score_buffer[16]; uschar spam_score_int_buffer[16]; uschar spam_bar_buffer[128]; +uschar spam_action_buffer[32]; uschar spam_report_buffer[32600]; uschar prev_user_name[128] = ""; int spam_ok = 0; int spam_rc = 0; uschar *prev_spamd_address_work = NULL; -int -spam(uschar **listptr) +static const uschar * loglabel = US"spam acl condition:"; + + +static int +spamd_param_init(spamd_address_container *spamd) { - int sep = 0; - uschar *list = *listptr; - uschar *user_name; - uschar user_name_buffer[128]; - unsigned long mbox_size; - FILE *mbox_file; - int spamd_sock = -1; - uschar spamd_buffer[32600]; - int i, j, offset, result; - uschar spamd_version[8]; - uschar spamd_score_char; - double spamd_threshold, spamd_score; - int spamd_report_offset; - uschar *p,*q; - int override = 0; - time_t start; - size_t read, wrote; - struct sockaddr_un server; -#ifndef NO_POLL_H - struct pollfd pollfd; -#else /* Patch posted by Erik ? for OS X */ - struct timeval select_tv; /* and applied by PH */ - fd_set select_fd; -#endif - uschar *spamd_address_work; - static const uschar * loglabel = US"spam acl condition:"; +/* default spamd server weight, time and priority value */ +spamd->is_rspamd = FALSE; +spamd->is_failed = FALSE; +spamd->weight = SPAMD_WEIGHT; +spamd->timeout = SPAMD_TIMEOUT; +spamd->retry = 0; +spamd->priority = 1; +return 0; +} - /* stop compiler warning */ - result = 0; - /* find the username from the option list */ - if ((user_name = string_nextinlist(&list, &sep, - user_name_buffer, - sizeof(user_name_buffer))) == NULL) +static int +spamd_param(const uschar * param, spamd_address_container * spamd) +{ +static int timesinceday = -1; +const uschar * s; +const uschar * name; + +/*XXX more clever parsing could discard embedded spaces? */ + +if (sscanf(CCS param, "pri=%u", &spamd->priority)) + return 0; /* OK */ + +if (sscanf(CCS param, "weight=%u", &spamd->weight)) + { + if (spamd->weight == 0) /* this server disabled: skip it */ + return 1; + return 0; /* OK */ + } + +if (Ustrncmp(param, "time=", 5) == 0) + { + unsigned int start_h = 0, start_m = 0, start_s = 0; + unsigned int end_h = 24, end_m = 0, end_s = 0; + unsigned int time_start, time_end; + const uschar * end_string; + + name = US"time"; + s = param+5; + if ((end_string = Ustrchr(s, '-'))) { - /* no username given, this means no scanning should be done */ - return FAIL; + end_string++; + if ( sscanf(CS end_string, "%u.%u.%u", &end_h, &end_m, &end_s) == 0 + || sscanf(CS s, "%u.%u.%u", &start_h, &start_m, &start_s) == 0 + ) + goto badval; } + else + goto badval; - /* if username is "0" or "false", do not scan */ - if ( (Ustrcmp(user_name,"0") == 0) || - (strcmpic(user_name,US"false") == 0) ) - return FAIL; - - /* if there is an additional option, check if it is "true" */ - if (strcmpic(list,US"true") == 0) - /* in that case, always return true later */ - override = 1; - - /* expand spamd_address if needed */ - if (*spamd_address == '$') + if (timesinceday < 0) { - spamd_address_work = expand_string(spamd_address); - if (spamd_address_work == NULL) - { - log_write(0, LOG_MAIN|LOG_PANIC, - "%s spamd_address starts with $, but expansion failed: %s", - loglabel, expand_string_message); - return DEFER; - } + time_t now = time(NULL); + struct tm *tmp = localtime(&now); + timesinceday = tmp->tm_hour*3600 + tmp->tm_min*60 + tmp->tm_sec; } - else - spamd_address_work = spamd_address; - /* check if previous spamd_address was expanded and has changed. dump cached results if so */ - if ( spam_ok - && prev_spamd_address_work != NULL - && Ustrcmp(prev_spamd_address_work, spamd_address_work) != 0 - ) - spam_ok = 0; + time_start = start_h*3600 + start_m*60 + start_s; + time_end = end_h*3600 + end_m*60 + end_s; + + if (timesinceday < time_start || timesinceday >= time_end) + return 1; /* skip spamd server */ + + return 0; /* OK */ + } + +if (Ustrcmp(param, "variant=rspamd") == 0) + { + spamd->is_rspamd = TRUE; + return 0; + } + +if (Ustrncmp(param, "tmo=", 4) == 0) + { + int sec = readconf_readtime((s = param+4), '\0', FALSE); + name = US"timeout"; + if (sec < 0) + goto badval; + spamd->timeout = sec; + return 0; + } + +if (Ustrncmp(param, "retry=", 6) == 0) + { + int sec = readconf_readtime((s = param+6), '\0', FALSE); + name = US"retry"; + if (sec < 0) + goto badval; + spamd->retry = sec; + return 0; + } + +log_write(0, LOG_MAIN, "%s warning - invalid spamd parameter: '%s'", + loglabel, param); +return -1; /* syntax error */ + +badval: + log_write(0, LOG_MAIN, + "%s warning - invalid spamd %s value: '%s'", loglabel, name, s); + return -1; /* syntax error */ +} + - /* if we scanned for this username last time, just return */ - if (spam_ok && Ustrcmp(prev_user_name, user_name) == 0) - return override ? OK : spam_rc; +static int +spamd_get_server(spamd_address_container ** spamds, int num_servers) +{ +unsigned int i; +spamd_address_container * sd; +long rnd, weights; +unsigned pri; +static BOOL srandomed = FALSE; + +/* seedup, if we have only 1 server */ +if (num_servers == 1) + return (spamds[0]->is_failed ? -1 : 0); + +/* init ranmod */ +if (!srandomed) + { + struct timeval tv; + gettimeofday(&tv, NULL); + srandom((unsigned int)(tv.tv_usec/1000)); + srandomed = TRUE; + } + +/* scan for highest pri */ +for (pri = 0, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority > pri) pri = sd->priority; + } + +/* get sum of weights */ +for (weights = 0, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority == pri) weights += sd->weight; + } +if (weights == 0) /* all servers failed */ + return -1; + +for (rnd = random() % weights, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority == pri) + if ((rnd -= sd->weight) <= 0) + return i; + } + +log_write(0, LOG_MAIN|LOG_PANIC, + "%s unknown error (memory/cpu corruption?)", loglabel); +return -1; +} - /* make sure the eml mbox file is spooled up */ - mbox_file = spool_mbox(&mbox_size, NULL); - if (mbox_file == NULL) +int +spam(const uschar **listptr) +{ +int sep = 0; +const uschar *list = *listptr; +uschar *user_name; +uschar user_name_buffer[128]; +unsigned long mbox_size; +FILE *mbox_file; +int spamd_sock = -1; +uschar spamd_buffer[32600]; +int i, j, offset, result; +uschar spamd_version[8]; +uschar spamd_short_result[8]; +uschar spamd_score_char; +double spamd_threshold, spamd_score, spamd_reject_score; +int spamd_report_offset; +uschar *p,*q; +int override = 0; +time_t start; +size_t read, wrote; +#ifndef NO_POLL_H +struct pollfd pollfd; +#else /* Patch posted by Erik ? for OS X */ +struct timeval select_tv; /* and applied by PH */ +fd_set select_fd; +#endif +uschar *spamd_address_work; +spamd_address_container * sd; + +/* stop compiler warning */ +result = 0; + +/* find the username from the option list */ +if ((user_name = string_nextinlist(&list, &sep, + user_name_buffer, + sizeof(user_name_buffer))) == NULL) + { + /* no username given, this means no scanning should be done */ + return FAIL; + } + +/* if username is "0" or "false", do not scan */ +if ( (Ustrcmp(user_name,"0") == 0) || + (strcmpic(user_name,US"false") == 0) ) + return FAIL; + +/* if there is an additional option, check if it is "true" */ +if (strcmpic(list,US"true") == 0) + /* in that case, always return true later */ + override = 1; + +/* expand spamd_address if needed */ +if (*spamd_address == '$') + { + spamd_address_work = expand_string(spamd_address); + if (spamd_address_work == NULL) { - /* error while spooling */ log_write(0, LOG_MAIN|LOG_PANIC, - "%s error while creating mbox spool file", loglabel); + "%s spamd_address starts with $, but expansion failed: %s", + loglabel, expand_string_message); return DEFER; } - - start = time(NULL); - - /* socket does not start with '/' -> network socket */ - if (*spamd_address_work != '/') + } +else + spamd_address_work = spamd_address; + +DEBUG(D_acl) debug_printf("spamd: addrlist '%s'\n", spamd_address_work); + +/* check if previous spamd_address was expanded and has changed. dump cached results if so */ +if ( spam_ok + && prev_spamd_address_work != NULL + && Ustrcmp(prev_spamd_address_work, spamd_address_work) != 0 + ) + spam_ok = 0; + +/* if we scanned for this username last time, just return */ +if (spam_ok && Ustrcmp(prev_user_name, user_name) == 0) + return override ? OK : spam_rc; + +/* make sure the eml mbox file is spooled up */ +mbox_file = spool_mbox(&mbox_size, NULL); + +if (mbox_file == NULL) + { + /* error while spooling */ + log_write(0, LOG_MAIN|LOG_PANIC, + "%s error while creating mbox spool file", loglabel); + return DEFER; + } + +start = time(NULL); + + { + int num_servers = 0; + int current_server; + uschar * address; + const uschar * spamd_address_list_ptr = spamd_address_work; + spamd_address_container * spamd_address_vector[32]; + + /* Check how many spamd servers we have + and register their addresses */ + sep = 0; /* default colon-sep */ + while ((address = string_nextinlist(&spamd_address_list_ptr, &sep, + NULL, 0)) != NULL) { - int num_servers = 0; - int current_server; - uschar *address = NULL; - uschar *spamd_address_list_ptr = spamd_address_work; - uschar address_buffer[256]; - spamd_address_container * spamd_address_vector[32]; - - /* Check how many spamd servers we have - and register their addresses */ - while ((address = string_nextinlist(&spamd_address_list_ptr, &sep, - address_buffer, - sizeof(address_buffer))) != NULL) + const uschar * sublist; + int sublist_sep = -(int)' '; /* default space-sep */ + unsigned args; + uschar * s; + + DEBUG(D_acl) debug_printf("spamd: addr entry '%s'\n", address); + sd = (spamd_address_container *)store_get(sizeof(spamd_address_container)); + + for (sublist = address, args = 0, spamd_param_init(sd); + (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0)); + args++ + ) { - - /* Potential memory leak as we never free the store. */ - spamd_address_container *this_spamd = - (spamd_address_container *)store_get(sizeof(spamd_address_container)); - - /* grok spamd address and port */ - if (sscanf(CS address, "%23s %u", this_spamd->tcp_addr, &this_spamd->tcp_port) != 2) - { - log_write(0, LOG_MAIN, - "%s warning - invalid spamd address: '%s'", loglabel, address); - continue; + DEBUG(D_acl) debug_printf("spamd: addr parm '%s'\n", s); + switch (args) + { + case 0: sd->hostspec = s; + if (*s == '/') args++; /* local; no port */ + break; + case 1: sd->hostspec = string_sprintf("%s %s", sd->hostspec, s); + break; + default: spamd_param(s, sd); + break; } - - spamd_address_vector[num_servers] = this_spamd; - if ( ++num_servers - >= sizeof(spamd_address_vector)/sizeof(spamd_address_vector[0])) - break; } - - /* check if we have at least one server */ - if (!num_servers) + if (args < 2) { - log_write(0, LOG_MAIN|LOG_PANIC, - "%s no useable spamd server addresses in spamd_address configuration option.", - loglabel); - (void)fclose(mbox_file); - return DEFER; + log_write(0, LOG_MAIN, + "%s warning - invalid spamd address: '%s'", loglabel, address); + continue; } - while (num_servers > 0) - { - int i; - - /* Randomly pick a server to try */ - current_server = random_number(num_servers); - - debug_printf("trying server %s, port %u\n", - spamd_address_vector[current_server]->tcp_addr, - spamd_address_vector[current_server]->tcp_port); - - /* contact a spamd */ - if ((spamd_sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, - "%s error creating IP socket for spamd", loglabel); - (void)fclose(mbox_file); - return DEFER; - } - - if (ip_connect(spamd_sock, - AF_INET, - spamd_address_vector[current_server]->tcp_addr, - spamd_address_vector[current_server]->tcp_port, - 5 ) > -1) - /* connection OK */ - break; - - log_write(0, LOG_MAIN|LOG_PANIC, - "%s warning - spamd connection to %s, port %u failed: %s", - loglabel, - spamd_address_vector[current_server]->tcp_addr, - spamd_address_vector[current_server]->tcp_port, - strerror(errno)); - - (void)close(spamd_sock); - - /* Remove the server from the list. XXX We should free the memory */ - num_servers--; - for (i = current_server; i < num_servers; i++) - spamd_address_vector[i] = spamd_address_vector[i+1]; - } + spamd_address_vector[num_servers] = sd; + if (++num_servers > 31) + break; + } - if (num_servers == 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "%s all spamd servers failed", loglabel); - (void)fclose(mbox_file); - return DEFER; - } + /* check if we have at least one server */ + if (!num_servers) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s no useable spamd server addresses in spamd_address configuration option.", + loglabel); + goto defer; } - else + + current_server = spamd_get_server(spamd_address_vector, num_servers); + sd = spamd_address_vector[current_server]; + for(;;) { - /* open the local socket */ + uschar * errstr; - if ((spamd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + DEBUG(D_acl) debug_printf("spamd: trying server %s\n", sd->hostspec); + + for (;;) { - log_write(0, LOG_MAIN|LOG_PANIC, - "%s spamd: unable to acquire socket (%s)", - loglabel, - strerror(errno)); - (void)fclose(mbox_file); - return DEFER; + if ( (spamd_sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0 + || sd->retry <= 0 + ) + break; + DEBUG(D_acl) debug_printf("spamd: server %s: retry conn\n", sd->hostspec); + while (sd->retry > 0) sd->retry = sleep(sd->retry); } + if (spamd_sock >= 0) + break; - server.sun_family = AF_UNIX; - Ustrcpy(server.sun_path, spamd_address_work); + log_write(0, LOG_MAIN, "%s spamd: %s", loglabel, errstr); + sd->is_failed = TRUE; - if (connect(spamd_sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) + current_server = spamd_get_server(spamd_address_vector, num_servers); + if (current_server < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, - "%s spamd: unable to connect to UNIX socket %s (%s)", - loglabel, - spamd_address_work, strerror(errno) ); - (void)fclose(mbox_file); - (void)close(spamd_sock); - return DEFER; + log_write(0, LOG_MAIN|LOG_PANIC, "%s all spamd servers failed", loglabel); + goto defer; } + sd = spamd_address_vector[current_server]; } - - if (spamd_sock == -1) - { - log_write(0, LOG_MAIN|LOG_PANIC, - "programming fault, spamd_sock unexpectedly unset"); - (void)fclose(mbox_file); - (void)close(spamd_sock); - return DEFER; - } - - /* now we are connected to spamd on spamd_sock */ + } + +if (spamd_sock == -1) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "programming fault, spamd_sock unexpectedly unset"); + goto defer; + } + +(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK); +/* now we are connected to spamd on spamd_sock */ +if (sd->is_rspamd) + { /* rspamd variant */ + uschar *req_str; + const uschar * helo; + const uschar * fcrdns; + const uschar * authid; + + req_str = string_sprintf("CHECK RSPAMC/1.3\r\nContent-length: %lu\r\n" + "Queue-Id: %s\r\nFrom: <%s>\r\nRecipient-Number: %d\r\n", + mbox_size, message_id, sender_address, recipients_count); + for (i = 0; i < recipients_count; i ++) + req_str = string_sprintf("%sRcpt: <%s>\r\n", req_str, recipients_list[i].address); + if ((helo = expand_string(US"$sender_helo_name")) != NULL && *helo != '\0') + req_str = string_sprintf("%sHelo: %s\r\n", req_str, helo); + if ((fcrdns = expand_string(US"$sender_host_name")) != NULL && *fcrdns != '\0') + req_str = string_sprintf("%sHostname: %s\r\n", req_str, fcrdns); + if (sender_host_address != NULL) + req_str = string_sprintf("%sIP: %s\r\n", req_str, sender_host_address); + if ((authid = expand_string(US"$authenticated_id")) != NULL && *authid != '\0') + req_str = string_sprintf("%sUser: %s\r\n", req_str, authid); + req_str = string_sprintf("%s\r\n", req_str); + wrote = send(spamd_sock, req_str, Ustrlen(req_str), 0); + } +else + { /* spamassassin variant */ (void)string_format(spamd_buffer, - sizeof(spamd_buffer), - "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n", - user_name, - mbox_size); - + sizeof(spamd_buffer), + "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n", + user_name, + mbox_size); /* send our request */ - if (send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0) < 0) - { - (void)close(spamd_sock); - log_write(0, LOG_MAIN|LOG_PANIC, - "%s spamd send failed: %s", loglabel, strerror(errno)); - (void)fclose(mbox_file); - (void)close(spamd_sock); - return DEFER; - } + wrote = send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0); + } - /* now send the file */ - /* spamd sometimes accepts conections but doesn't read data off - * the connection. We make the file descriptor non-blocking so - * that the write will only write sufficient data without blocking - * and we poll the desciptor to make sure that we can write without - * blocking. Short writes are gracefully handled and if the whole - * trasaction takes too long it is aborted. - * Note: poll() is not supported in OSX 10.2 and is reported to be - * broken in more recent versions (up to 10.4). - */ +if (wrote == -1) + { + (void)close(spamd_sock); + log_write(0, LOG_MAIN|LOG_PANIC, + "%s spamd %s send failed: %s", loglabel, callout_address, strerror(errno)); + goto defer; + } + +/* now send the file */ +/* spamd sometimes accepts conections but doesn't read data off + * the connection. We make the file descriptor non-blocking so + * that the write will only write sufficient data without blocking + * and we poll the desciptor to make sure that we can write without + * blocking. Short writes are gracefully handled and if the whole + * trasaction takes too long it is aborted. + * Note: poll() is not supported in OSX 10.2 and is reported to be + * broken in more recent versions (up to 10.4). + */ #ifndef NO_POLL_H - pollfd.fd = spamd_sock; - pollfd.events = POLLOUT; +pollfd.fd = spamd_sock; +pollfd.events = POLLOUT; #endif - (void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK); - do +(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK); +do + { + read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file); + if (read > 0) { - read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file); - if (read > 0) - { - offset = 0; + offset = 0; again: #ifndef NO_POLL_H - result = poll(&pollfd, 1, 1000); + result = poll(&pollfd, 1, 1000); /* Patch posted by Erik ? for OS X and applied by PH */ #else - select_tv.tv_sec = 1; - select_tv.tv_usec = 0; - FD_ZERO(&select_fd); - FD_SET(spamd_sock, &select_fd); - result = select(spamd_sock+1, NULL, &select_fd, NULL, &select_tv); + select_tv.tv_sec = 1; + select_tv.tv_usec = 0; + FD_ZERO(&select_fd); + FD_SET(spamd_sock, &select_fd); + result = select(spamd_sock+1, NULL, &select_fd, NULL, &select_tv); #endif /* End Erik's patch */ - if (result == -1 && errno == EINTR) - goto again; - else if (result < 1) - { - if (result == -1) - log_write(0, LOG_MAIN|LOG_PANIC, - "%s %s on spamd socket", loglabel, strerror(errno)); - else - { - if (time(NULL) - start < SPAMD_TIMEOUT) - goto again; - log_write(0, LOG_MAIN|LOG_PANIC, - "%s timed out writing spamd socket", loglabel); - } - (void)close(spamd_sock); - (void)fclose(mbox_file); - return DEFER; - } - - wrote = send(spamd_sock,spamd_buffer + offset,read - offset,0); - if (wrote == -1) - { + if (result == -1 && errno == EINTR) + goto again; + else if (result < 1) + { + if (result == -1) log_write(0, LOG_MAIN|LOG_PANIC, - "%s %s on spamd socket", loglabel, strerror(errno)); - (void)close(spamd_sock); - (void)fclose(mbox_file); - return DEFER; - } - if (offset + wrote != read) + "%s %s on spamd %s socket", loglabel, callout_address, strerror(errno)); + else { - offset += wrote; - goto again; + if (time(NULL) - start < sd->timeout) + goto again; + log_write(0, LOG_MAIN|LOG_PANIC, + "%s timed out writing spamd %s, socket", loglabel, callout_address); } + (void)close(spamd_sock); + goto defer; } - } - while (!feof(mbox_file) && !ferror(mbox_file)); - if (ferror(mbox_file)) - { - log_write(0, LOG_MAIN|LOG_PANIC, - "%s error reading spool file: %s", loglabel, strerror(errno)); - (void)close(spamd_sock); - (void)fclose(mbox_file); - return DEFER; + wrote = send(spamd_sock,spamd_buffer + offset,read - offset,0); + if (wrote == -1) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s %s on spamd %s socket", loglabel, callout_address, strerror(errno)); + (void)close(spamd_sock); + goto defer; + } + if (offset + wrote != read) + { + offset += wrote; + goto again; + } } + } +while (!feof(mbox_file) && !ferror(mbox_file)); - (void)fclose(mbox_file); - - /* we're done sending, close socket for writing */ - shutdown(spamd_sock,SHUT_WR); - - /* read spamd response using what's left of the timeout. - */ - memset(spamd_buffer, 0, sizeof(spamd_buffer)); - offset = 0; - while ((i = ip_recv(spamd_sock, - spamd_buffer + offset, - sizeof(spamd_buffer) - offset - 1, - SPAMD_TIMEOUT - time(NULL) + start)) > 0 ) - offset += i; - - /* error handling */ - if (i <= 0 && errno != 0) +if (ferror(mbox_file)) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s error reading spool file: %s", loglabel, strerror(errno)); + (void)close(spamd_sock); + goto defer; + } + +(void)fclose(mbox_file); + +/* we're done sending, close socket for writing */ +shutdown(spamd_sock,SHUT_WR); + +/* read spamd response using what's left of the timeout. */ +memset(spamd_buffer, 0, sizeof(spamd_buffer)); +offset = 0; +while ((i = ip_recv(spamd_sock, + spamd_buffer + offset, + sizeof(spamd_buffer) - offset - 1, + sd->timeout - time(NULL) + start)) > 0 ) + offset += i; + +/* error handling */ +if (i <= 0 && errno != 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s error reading from spamd %s, socket: %s", loglabel, callout_address, strerror(errno)); + (void)close(spamd_sock); + return DEFER; + } + +/* reading done */ +(void)close(spamd_sock); + +if (sd->is_rspamd) + { /* rspamd variant of reply */ + int r; + if ((r = sscanf(CS spamd_buffer, + "RSPAMD/%7s 0 EX_OK\r\nMetric: default; %7s %lf / %lf / %lf\r\n%n", + spamd_version, spamd_short_result, &spamd_score, &spamd_threshold, + &spamd_reject_score, &spamd_report_offset)) != 5) { log_write(0, LOG_MAIN|LOG_PANIC, - "%s error reading from spamd socket: %s", loglabel, strerror(errno)); - (void)close(spamd_sock); + "%s cannot parse spamd %s, output: %d", loglabel, callout_address, r); return DEFER; } + /* now parse action */ + p = &spamd_buffer[spamd_report_offset]; - /* reading done */ - (void)close(spamd_sock); - - /* dig in the spamd output and put the report in a multiline header, if requested */ - if (sscanf(CS spamd_buffer, - "SPAMD/%7s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n", - spamd_version, &spamd_score, &spamd_threshold, - &spamd_report_offset) != 3) + if (Ustrncmp(p, "Action: ", sizeof("Action: ") - 1) == 0) { - - /* try to fall back to pre-2.50 spamd output */ - if (sscanf(CS spamd_buffer, - "SPAMD/%7s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n", - spamd_version, &spamd_score, &spamd_threshold, - &spamd_report_offset) != 3 ) - { - log_write(0, LOG_MAIN|LOG_PANIC, - "%s cannot parse spamd output", loglabel); - return DEFER; - } + p += sizeof("Action: ") - 1; + q = &spam_action_buffer[0]; + while (*p && *p != '\r' && (q - spam_action_buffer) < sizeof(spam_action_buffer) - 1) + *q++ = *p++; + *q = '\0'; } - - /* Create report. Since this is a multiline string, - we must hack it into shape first */ - p = &spamd_buffer[spamd_report_offset]; - q = spam_report_buffer; - while (*p != '\0') - { - /* skip \r */ - if (*p == '\r') - { - p++; - continue; - } - *q++ = *p; - if (*p++ == '\n') - { - /* add an extra space after the newline to ensure - that it is treated as a header continuation line */ - *q++ = ' '; - } - } - /* NULL-terminate */ - *q-- = '\0'; - /* cut off trailing leftovers */ - while (*q <= ' ') - *q-- = '\0'; - - spam_report = spam_report_buffer; - - /* create spam bar */ - spamd_score_char = spamd_score > 0 ? '+' : '-'; - j = abs((int)(spamd_score)); - i = 0; - if (j != 0) - while ((i < j) && (i <= MAX_SPAM_BAR_CHARS)) - spam_bar_buffer[i++] = spamd_score_char; - else + } +else + { /* spamassassin */ + /* dig in the spamd output and put the report in a multiline header, + if requested */ + if (sscanf(CS spamd_buffer, + "SPAMD/%7s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n", + spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3) { - spam_bar_buffer[0] = '/'; - i = 1; + /* try to fall back to pre-2.50 spamd output */ + if (sscanf(CS spamd_buffer, + "SPAMD/%7s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n", + spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s cannot parse spamd %s output", loglabel, callout_address); + return DEFER; + } } - spam_bar_buffer[i] = '\0'; - spam_bar = spam_bar_buffer; - - /* create "float" spam score */ - (void)string_format(spam_score_buffer, sizeof(spam_score_buffer),"%.1f", spamd_score); - spam_score = spam_score_buffer; - /* create "int" spam score */ - j = (int)((spamd_score + 0.001)*10); - (void)string_format(spam_score_int_buffer, sizeof(spam_score_int_buffer), "%d", j); - spam_score_int = spam_score_int_buffer; - - /* compare threshold against score */ - if (spamd_score >= spamd_threshold) + Ustrcpy(spam_action_buffer, + spamd_score >= spamd_threshold ? "reject" : "no action"); + } + +/* Create report. Since this is a multiline string, +we must hack it into shape first */ +p = &spamd_buffer[spamd_report_offset]; +q = spam_report_buffer; +while (*p != '\0') + { + /* skip \r */ + if (*p == '\r') { - /* spam as determined by user's threshold */ - spam_rc = OK; + p++; + continue; } - else + *q++ = *p; + if (*p++ == '\n') { - /* not spam */ - spam_rc = FAIL; + /* add an extra space after the newline to ensure + that it is treated as a header continuation line */ + *q++ = ' '; } + } +/* NULL-terminate */ +*q-- = '\0'; +/* cut off trailing leftovers */ +while (*q <= ' ') + *q-- = '\0'; - /* remember expanded spamd_address if needed */ - if (spamd_address_work != spamd_address) - prev_spamd_address_work = string_copy(spamd_address_work); - - /* remember user name and "been here" for it */ - Ustrcpy(prev_user_name, user_name); - spam_ok = 1; - - if (override) /* always return OK, no matter what the score */ - return OK; - else - return spam_rc; +spam_report = spam_report_buffer; +spam_action = spam_action_buffer; + +/* create spam bar */ +spamd_score_char = spamd_score > 0 ? '+' : '-'; +j = abs((int)(spamd_score)); +i = 0; +if (j != 0) + while ((i < j) && (i <= MAX_SPAM_BAR_CHARS)) + spam_bar_buffer[i++] = spamd_score_char; +else + { + spam_bar_buffer[0] = '/'; + i = 1; + } +spam_bar_buffer[i] = '\0'; +spam_bar = spam_bar_buffer; + +/* create "float" spam score */ +(void)string_format(spam_score_buffer, sizeof(spam_score_buffer), + "%.1f", spamd_score); +spam_score = spam_score_buffer; + +/* create "int" spam score */ +j = (int)((spamd_score + 0.001)*10); +(void)string_format(spam_score_int_buffer, sizeof(spam_score_int_buffer), + "%d", j); +spam_score_int = spam_score_int_buffer; + +/* compare threshold against score */ +spam_rc = spamd_score >= spamd_threshold + ? OK /* spam as determined by user's threshold */ + : FAIL; /* not spam */ + +/* remember expanded spamd_address if needed */ +if (spamd_address_work != spamd_address) + prev_spamd_address_work = string_copy(spamd_address_work); + +/* remember user name and "been here" for it */ +Ustrcpy(prev_user_name, user_name); +spam_ok = 1; + +return override + ? OK /* always return OK, no matter what the score */ + : spam_rc; + +defer: + (void)fclose(mbox_file); + return DEFER; } #endif +/* vi: aw ai sw=2 +*/ diff --git a/src/src/spam.h b/src/src/spam.h index ba700c8b6..2fe73809e 100644 --- a/src/src/spam.h +++ b/src/src/spam.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */ +/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015 */ /* License: GPL */ /* spam defines */ @@ -12,17 +12,27 @@ /* timeout for reading and writing spamd */ #define SPAMD_TIMEOUT 120 -/* maximum length of the spam bar */ +/* maximum length of the spam bar, please update the + * spec, the max length is mentioned there */ #define MAX_SPAM_BAR_CHARS 50 /* SHUT_WR seems to be undefined on Unixware ? */ #ifndef SHUT_WR -#define SHUT_WR 1 +# define SHUT_WR 1 #endif -typedef struct spamd_address_container { - uschar tcp_addr[24]; - unsigned int tcp_port; +/* default weight */ +#define SPAMD_WEIGHT 1 + +typedef struct spamd_address_container +{ + uschar * hostspec; + int is_rspamd:1; + int is_failed:1; + unsigned int weight; + unsigned int timeout; + unsigned int retry; + unsigned int priority; } spamd_address_container; #endif diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 79970cb40..59192ef30 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading spool files. When compiling for a utility (eximon), @@ -288,17 +288,26 @@ tls_in.certificate_verified = FALSE; tls_in.dane_verified = FALSE; # endif tls_in.cipher = NULL; -tls_in.ourcert = NULL; -tls_in.peercert = NULL; +# ifndef COMPILE_UTILITY /* tls support fns not built in */ +tls_free_cert(&tls_in.ourcert); +tls_free_cert(&tls_in.peercert); +# endif tls_in.peerdn = NULL; tls_in.sni = NULL; tls_in.ocsp = OCSP_NOT_REQ; #endif #ifdef WITH_CONTENT_SCAN +spam_bar = NULL; +spam_score = NULL; spam_score_int = NULL; #endif +#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY) +message_smtputf8 = FALSE; +message_utf8_downconvert = 0; +#endif + dsn_ret = 0; dsn_envid = NULL; @@ -566,9 +575,17 @@ for (;;) if (Ustrncmp(p, "ender_set_untrusted", 19) == 0) sender_set_untrusted = TRUE; #ifdef WITH_CONTENT_SCAN + else if (Ustrncmp(p, "pam_bar ", 8) == 0) + spam_bar = string_copy(big_buffer + 10); + else if (Ustrncmp(p, "pam_score ", 10) == 0) + spam_score = string_copy(big_buffer + 12); else if (Ustrncmp(p, "pam_score_int ", 14) == 0) spam_score_int = string_copy(big_buffer + 16); #endif +#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY) + else if (Ustrncmp(p, "mtputf8", 7) == 0) + message_smtputf8 = TRUE; +#endif break; #ifdef SUPPORT_TLS @@ -592,6 +609,15 @@ for (;;) break; #endif +#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY) + case 'u': + if (Ustrncmp(p, "tf8_downcvt", 11) == 0) + message_utf8_downconvert = 1; + else if (Ustrncmp(p, "tf8_optdowncvt", 15) == 0) + message_utf8_downconvert = -1; + break; +#endif + default: /* Present because some compilers complain if all */ break; /* possibilities are not covered. */ } @@ -735,7 +761,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) { p -= len; errors_to = string_copy(p); - } + } } *(--p) = 0; /* Terminate address */ @@ -749,7 +775,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) { p -= len; orcpt = string_copy(p); - } + } } *(--p) = 0; /* Terminate address */ diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index edc28034c..8d04f8e17 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */ +/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003 - 2015 */ /* License: GPL */ /* Code for setting up a MBOX style spool file inside a /scan/<msgid> diff --git a/src/src/spool_out.c b/src/src/spool_out.c index fc56057c1..8d0ee7c71 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for writing spool files, and moving them about. */ @@ -218,7 +218,9 @@ if (sender_local) fprintf(f, "-local\n"); if (local_error_message) fprintf(f, "-localerror\n"); if (local_scan_data != NULL) fprintf(f, "-local_scan %s\n", local_scan_data); #ifdef WITH_CONTENT_SCAN -if (spam_score_int != NULL) fprintf(f,"-spam_score_int %s\n", spam_score_int); +if (spam_bar) fprintf(f,"-spam_bar %s\n", spam_bar); +if (spam_score) fprintf(f,"-spam_score %s\n", spam_score); +if (spam_score_int) fprintf(f,"-spam_score_int %s\n", spam_score_int); #endif if (deliver_manual_thaw) fprintf(f, "-manual_thaw\n"); if (sender_set_untrusted) fprintf(f, "-sender_set_untrusted\n"); @@ -245,6 +247,15 @@ if (tls_in.ourcert) if (tls_in.ocsp) fprintf(f, "-tls_ocsp %d\n", tls_in.ocsp); #endif +#ifdef SUPPORT_I18N +if (message_smtputf8) + { + fprintf(f, "-smtputf8\n"); + if (message_utf8_downconvert) + fprintf(f, "-utf8_%sdowncvt\n", message_utf8_downconvert < 0 ? "opt" : ""); + } +#endif + /* Write the dsn flags to the spool header file */ DEBUG(D_deliver) debug_printf("DSN: Write SPOOL :-dsn_envid %s\n", dsn_envid); if (dsn_envid != NULL) fprintf(f, "-dsn_envid %s\n", dsn_envid); @@ -273,7 +284,7 @@ DEBUG(D_deliver) debug_printf("DSN: Flags :%d\n", r->dsn_flags); fprintf(f, "%s %s %d,%d %s %d,%d#3\n", r->address, orcpt, Ustrlen(orcpt), r->dsn_flags, errors_to, Ustrlen(errors_to), r->pno); } - + DEBUG(D_deliver) debug_printf("DSN: **** SPOOL_OUT - address: |%s| errorsto: |%s| orcpt: |%s| dsn_flags: %d\n", r->address, r->errors_to, r->orcpt, r->dsn_flags); } @@ -504,3 +515,5 @@ return TRUE; #endif /* End of spool_out.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/store.c b/src/src/store.c index 790f79ddb..2bb3490e1 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -177,7 +177,7 @@ if (size > yield_length[store_pool]) yield_length[store_pool] = newblock->length; next_yield[store_pool] = (void *)((char *)current_block[store_pool] + ALIGNED_SIZEOF_STOREBLOCK); - VALGRIND_MAKE_MEM_NOACCESS(next_yield[store_pool], yield_length[store_pool]); + (void) VALGRIND_MAKE_MEM_NOACCESS(next_yield[store_pool], yield_length[store_pool]); } /* There's (now) enough room in the current block; the yield is the next @@ -202,7 +202,7 @@ DEBUG(D_memory) } #endif /* COMPILE_UTILITY */ -VALGRIND_MAKE_MEM_UNDEFINED(store_last_get[store_pool], size); +(void) VALGRIND_MAKE_MEM_UNDEFINED(store_last_get[store_pool], size); /* Update next pointer and number of bytes left in the current block. */ next_yield[store_pool] = (void *)((char *)next_yield[store_pool] + size); @@ -297,7 +297,7 @@ DEBUG(D_memory) if (newsize % alignment != 0) newsize += alignment - (newsize % alignment); next_yield[store_pool] = (char *)ptr + newsize; yield_length[store_pool] -= newsize - rounded_oldsize; -VALGRIND_MAKE_MEM_UNDEFINED(ptr + oldsize, inc); +(void) VALGRIND_MAKE_MEM_UNDEFINED(ptr + oldsize, inc); return TRUE; } @@ -356,7 +356,7 @@ newlength = bc + b->length - (char *)ptr; #ifndef COMPILE_UTILITY if (running_in_test_harness) memset(ptr, 0xF0, newlength); #endif -VALGRIND_MAKE_MEM_NOACCESS(ptr, newlength); +(void) VALGRIND_MAKE_MEM_NOACCESS(ptr, newlength); yield_length[store_pool] = newlength - (newlength % alignment); next_yield[store_pool] = (char *)ptr + (newlength % alignment); current_block[store_pool] = b; @@ -370,7 +370,8 @@ if (yield_length[store_pool] < STOREPOOL_MIN_SIZE && b->next->length == STORE_BLOCK_SIZE) { b = b->next; - VALGRIND_MAKE_MEM_NOACCESS((char *)b + ALIGNED_SIZEOF_STOREBLOCK, b->length - ALIGNED_SIZEOF_STOREBLOCK); + (void) VALGRIND_MAKE_MEM_NOACCESS((char *)b + ALIGNED_SIZEOF_STOREBLOCK, + b->length - ALIGNED_SIZEOF_STOREBLOCK); } bb = b->next; diff --git a/src/src/string.c b/src/src/string.c index 775709dc6..d01383f65 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Miscellaneous string-handling functions. Some are not required for @@ -224,13 +224,13 @@ Returns: the value of the character escape */ int -string_interpret_escape(uschar **pp) +string_interpret_escape(const uschar **pp) { #ifdef COMPILE_UTILITY const uschar *hex_digits= CUS"0123456789abcdef"; #endif int ch; -uschar *p = *pp; +const uschar *p = *pp; ch = *(++p); if (isdigit(ch) && ch != '8' && ch != '9') { @@ -284,12 +284,12 @@ Arguments: Returns: string with non-printers encoded as printing sequences */ -uschar * -string_printing2(uschar *s, BOOL allow_tab) +const uschar * +string_printing2(const uschar *s, BOOL allow_tab) { int nonprintcount = 0; int length = 0; -uschar *t = s; +const uschar *t = s; uschar *ss, *tt; while (*t != 0) @@ -374,7 +374,7 @@ while (*p) { if (*p == '\\') { - *q++ = string_interpret_escape(&p); + *q++ = string_interpret_escape((const uschar **)&p); p++; } else @@ -437,7 +437,7 @@ Returns: copy of string in new store */ uschar * -string_copy_malloc(uschar *s) +string_copy_malloc(const uschar *s) { int len = Ustrlen(s) + 1; uschar *ss = store_malloc(len); @@ -457,7 +457,7 @@ Returns: copy of string in new store, with letters lowercased */ uschar * -string_copylc(uschar *s) +string_copylc(const uschar *s) { uschar *ss = store_get(Ustrlen(s) + 1); uschar *p = ss; @@ -483,7 +483,7 @@ Returns: copy of string in new store */ uschar * -string_copyn(uschar *s, int n) +string_copyn(const uschar *s, int n) { uschar *ss = store_get(n + 1); Ustrncpy(ss, s, n); @@ -639,9 +639,9 @@ Returns: the new string */ uschar * -string_dequote(uschar **sptr) +string_dequote(const uschar **sptr) { -uschar *s = *sptr; +const uschar *s = *sptr; uschar *t, *yield; /* First find the end of the string */ @@ -717,8 +717,9 @@ uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; va_start(ap, format); if (!string_vformat(buffer, sizeof(buffer), format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "string_sprintf expansion was longer than " SIZE_T_FMT " (%s)", - sizeof(buffer), format); + "string_sprintf expansion was longer than " SIZE_T_FMT + "; format string was (%s)\nexpansion started '%.32s'", + sizeof(buffer), format, buffer); va_end(ap); return string_copy(buffer); } @@ -868,10 +869,10 @@ Returns: pointer to buffer, containing the next substring, */ uschar * -string_nextinlist(uschar **listptr, int *separator, uschar *buffer, int buflen) +string_nextinlist(const uschar **listptr, int *separator, uschar *buffer, int buflen) { -register int sep = *separator; -register uschar *s = *listptr; +int sep = *separator; +const uschar *s = *listptr; BOOL sep_is_special; if (s == NULL) return NULL; @@ -928,7 +929,7 @@ else { int size = 0; int ptr = 0; - uschar *ss; + const uschar *ss; /* We know that *s != 0 at this point. However, it might be pointing to a separator, which could indicate an empty string, or (if an ispunct() @@ -1008,6 +1009,51 @@ new = string_cat(new, &sz, &off, ele, Ustrlen(ele)); new[off] = '\0'; return new; } + + +static const uschar * +Ustrnchr(const uschar * s, int c, unsigned * len) +{ +unsigned siz = *len; +while (siz) + { + if (!*s) return NULL; + if (*s == c) + { + *len = siz; + return s; + } + s++; + siz--; + } +return NULL; +} + +uschar * +string_append_listele_n(uschar * list, uschar sep, const uschar * ele, + unsigned len) +{ +uschar * new = NULL; +int sz = 0, off = 0; +const uschar * sp; + +if (list) + { + new = string_cat(new, &sz, &off, list, Ustrlen(list)); + new = string_cat(new, &sz, &off, &sep, 1); + } + +while((sp = Ustrnchr(ele, sep, &len))) + { + new = string_cat(new, &sz, &off, ele, sp-ele+1); + new = string_cat(new, &sz, &off, &sep, 1); + ele = sp+1; + len--; + } +new = string_cat(new, &sz, &off, ele, len); +new[off] = '\0'; +return new; +} #endif /* COMPILE_UTILITY */ @@ -1502,14 +1548,35 @@ static uschar * string_get_localpart(address_item *addr, uschar *yield, int *sizeptr, int *ptrptr) { -if (testflag(addr, af_include_affixes) && addr->prefix != NULL) - yield = string_cat(yield, sizeptr, ptrptr, addr->prefix, - Ustrlen(addr->prefix)); -yield = string_cat(yield, sizeptr, ptrptr, addr->local_part, - Ustrlen(addr->local_part)); -if (testflag(addr, af_include_affixes) && addr->suffix != NULL) - yield = string_cat(yield, sizeptr, ptrptr, addr->suffix, - Ustrlen(addr->suffix)); +uschar * s; + +s = addr->prefix; +if (testflag(addr, af_include_affixes) && s) + { +#ifdef SUPPORT_I18N + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, sizeptr, ptrptr, s, Ustrlen(s)); + } + +s = addr->local_part; +#ifdef SUPPORT_I18N +if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif +yield = string_cat(yield, sizeptr, ptrptr, s, Ustrlen(s)); + +s = addr->suffix; +if (testflag(addr, af_include_affixes) && s) + { +#ifdef SUPPORT_I18N + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, sizeptr, ptrptr, s, Ustrlen(s)); + } + return yield; } @@ -1570,10 +1637,15 @@ else { if (addr->local_part != NULL) { + const uschar * s; yield = string_get_localpart(addr, yield, &size, &ptr); yield = string_cat(yield, &size, &ptr, US"@", 1); - yield = string_cat(yield, &size, &ptr, addr->domain, - Ustrlen(addr->domain) ); + s = addr->domain; +#ifdef SUPPORT_I18N + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s) ); } else { diff --git a/src/src/structs.h b/src/src/structs.h index 3cdf12a8f..78f5a8087 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -38,7 +38,7 @@ typedef struct macro_item { typedef struct bit_table { uschar *name; - unsigned int bit; + int bit; } bit_table; /* Block for holding a uid and gid, possibly unset, and an initgroups flag. */ @@ -59,8 +59,8 @@ typedef enum {DS_UNK=-1, DS_NO, DS_YES} dnssec_status_t; typedef struct host_item { struct host_item *next; - uschar *name; /* Host name */ - uschar *address; /* IP address in text form */ + const uschar *name; /* Host name */ + const uschar *address; /* IP address in text form */ int port; /* port value in host order (if SRV lookup) */ int mx; /* MX value if found via MX records */ int sort_key; /* MX*1000 plus random "fraction" */ @@ -171,6 +171,7 @@ typedef struct transport_instance { uschar *remove_headers; /* Remove these headers */ uschar *return_path; /* Overriding (rewriting) return path */ uschar *debug_string; /* Debugging output */ + uschar *max_parallel; /* Number of concurrent instances */ uschar *message_size_limit; /* Biggest message this transport handles */ uschar *headers_rewrite; /* Rules for rewriting headers */ rewrite_rule *rewrite_rules; /* Parsed rewriting rules */ @@ -188,7 +189,7 @@ typedef struct transport_instance { BOOL log_fail_output; BOOL log_defer_output; BOOL retry_use_local_part; /* Defaults true for local, false for remote */ -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT uschar *event_action; /* String to expand on notable events */ #endif } transport_instance; @@ -218,6 +219,11 @@ typedef struct transport_info { +typedef struct { + uschar *request; + uschar *require; +} dnssec_domains; + /* Structure for holding information about the configured routers. */ typedef struct router_instance { @@ -296,6 +302,8 @@ typedef struct router_instance { transport_instance *transport; /* Transport block (when found) */ struct router_instance *pass_router; /* Actual router for passed address */ struct router_instance *redirect_router; /* Actual router for generated address */ + + dnssec_domains dnssec; } router_instance; @@ -459,6 +467,11 @@ typedef struct address_item_propagated { #ifdef EXPERIMENTAL_SRS uschar *srs_sender; /* Change return path when delivering */ #endif + #ifdef SUPPORT_I18N + BOOL utf8_msg:1; /* requires SMTPUTF8 processing */ + BOOL utf8_downcvt:1; /* mandatory downconvert on delivery */ + BOOL utf8_downcvt_maybe:1; /* optional downconvert on delivery */ + #endif } address_item_propagated; /* Bits for the flags field below */ @@ -497,6 +510,9 @@ typedef struct address_item_propagated { #ifdef EXPERIMENTAL_DANE # define af_dane_verified 0x20000000 /* TLS cert verify done with DANE */ #endif +#ifdef SUPPORT_I18N +# define af_utf8_downcvt 0x40000000 /* downconvert was done for delivery */ +#endif /* These flags must be propagated when a child is created */ @@ -530,7 +546,7 @@ typedef struct address_item { uschar *local_part; /* points to cc or lc version */ uschar *prefix; /* stripped prefix of local part */ uschar *suffix; /* stripped suffix of local part */ - uschar *domain; /* working domain (lower cased) */ + const uschar *domain; /* working domain (lower cased) */ uschar *address_retry_key; /* retry key including full address */ uschar *domain_retry_key; /* retry key for domain only */ @@ -546,13 +562,18 @@ typedef struct address_item { uschar *self_hostname; /* after self=pass */ uschar *shadow_message; /* info about shadow transporting */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS uschar *cipher; /* Cipher used for transport */ void *ourcert; /* Certificate offered to peer, binary */ void *peercert; /* Certificate from peer, binary */ uschar *peerdn; /* DN of server's certificate */ int ocsp; /* OCSP status of peer cert */ - #endif +#endif + +#ifdef EXPERIMENTAL_DSN_INFO + const uschar *smtp_greeting; /* peer self-identification */ + const uschar *helo_response; /* peer message */ +#endif uschar *authenticator; /* auth driver name used by transport */ uschar *auth_id; /* auth "login" name used by transport */ @@ -581,7 +602,7 @@ typedef struct address_item { /* ( also */ /* ( contains verify rc in sender verify cache */ short int transport_return; /* result of delivery attempt */ - address_item_propagated p; /* fields that are propagated to children */ + address_item_propagated prop; /* fields that are propagated to children */ } address_item; /* The table of header names consists of items of this type */ @@ -597,7 +618,7 @@ typedef struct { typedef struct error_block { struct error_block *next; - uschar *text1; + const uschar *text1; uschar *text2; } error_block; @@ -642,6 +663,16 @@ typedef struct tree_node { uschar name[1]; /* node name - variable length */ } tree_node; +/* Structure for holding time-limited data such as DNS returns. +We use this rather than extending tree_node to avoid wasting +space for most tree use (variables...) at the cost of complexity +for the lookups cache */ + +typedef struct expiring_data { + time_t expiry; /* if nonzero, data invalid after this time */ + void *ptr; /* pointer to data */ +} expiring_data; + /* Structure for holding the handle and the cached last lookup for searches. This block is pointed to by the tree entry for the file. The file can get closed if too many are opened at once. There is a LRU chain for deciding which @@ -661,6 +692,7 @@ uncompressed, but the data pointer is into the raw data. */ typedef struct { uschar name[DNS_MAXNAME]; /* domain name */ int type; /* record type */ + unsigned short ttl; /* time-to-live, seconds */ int size; /* size of data */ uschar *data; /* pointer to data */ } dns_record; @@ -750,9 +782,9 @@ typedef struct redirect_block { /* Structure for passing arguments to check_host() */ typedef struct check_host_block { - uschar *host_name; - uschar *host_address; - uschar *host_ipv4; + const uschar *host_name; + const uschar *host_address; + const uschar *host_ipv4; BOOL negative; } check_host_block; @@ -768,7 +800,7 @@ typedef struct namedlist_cacheblock { /* Structure for holding data for an entry in a named list */ typedef struct namedlist_block { - uschar *string; /* the list string */ + const uschar *string; /* the list string */ namedlist_cacheblock *cache_data; /* cached domain_data or localpart_data */ int number; /* the number of the list for caching */ } namedlist_block; @@ -791,4 +823,7 @@ typedef struct acl_block { int verb; } acl_block; +/* smtp transport calc outbound_ip */ +typedef BOOL (*oicf) (uschar *message_id, void *data); + /* End of structs.h */ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 8b28d5566..7364a66ff 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Copyright (c) Phil Pennock 2012 */ @@ -47,16 +47,16 @@ require current GnuTLS, then we'll drop support for the ancient libraries). # warning "GnuTLS library version too old; define DISABLE_OCSP in Makefile" # define DISABLE_OCSP #endif -#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_EVENT) +#if GNUTLS_VERSION_NUMBER < 0x020a00 && !defined(DISABLE_EVENT) # warning "GnuTLS library version too old; tls:cert event unsupported" -# undef EXPERIMENTAL_EVENT +# define DISABLE_EVENT #endif #if GNUTLS_VERSION_NUMBER >= 0x030306 # define SUPPORT_CA_DIR #else # undef SUPPORT_CA_DIR #endif -#if GNUTLS_VERSION_NUMBER >= 0x030314 +#if GNUTLS_VERSION_NUMBER >= 0x030014 # define SUPPORT_SYSDEFAULT_CABUNDLE #endif @@ -120,8 +120,8 @@ typedef struct exim_gnutls_state { uschar *exp_tls_crl; uschar *exp_tls_require_ciphers; uschar *exp_tls_ocsp_file; - uschar *exp_tls_verify_cert_hostnames; -#ifdef EXPERIMENTAL_EVENT + const uschar *exp_tls_verify_cert_hostnames; +#ifndef DISABLE_EVENT uschar *event_action; #endif @@ -140,7 +140,7 @@ static const exim_gnutls_state_st exim_gnutls_state_init = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT NULL, #endif NULL, @@ -176,6 +176,10 @@ static const char * const exim_default_gnutls_priority = "NORMAL"; static BOOL exim_gnutls_base_init_done = FALSE; +#ifndef DISABLE_OCSP +static BOOL gnutls_buggy_ocsp = FALSE; +#endif + /* ------------------------------------------------------------------------ */ /* macros */ @@ -335,7 +339,7 @@ tls_error(when, msg, state->host); } while (0) static int -import_cert(const gnutls_datum * cert, gnutls_x509_crt_t * crtp) +import_cert(const gnutls_datum_t * cert, gnutls_x509_crt_t * crtp) { int rc; @@ -421,7 +425,7 @@ tlsp->sni = state->received_sni; /* record our certificate */ { - const gnutls_datum * cert = gnutls_certificate_get_ours(state->session); + const gnutls_datum_t * cert = gnutls_certificate_get_ours(state->session); gnutls_x509_crt_t crt; tlsp->ourcert = cert && import_cert(cert, &crt)==0 ? crt : NULL; @@ -453,7 +457,7 @@ init_server_dh(void) { int fd, rc; unsigned int dh_bits; -gnutls_datum m; +gnutls_datum_t m; uschar filename_buf[PATH_MAX]; uschar *filename = NULL; size_t sz; @@ -831,18 +835,25 @@ if ( !host /* server */ && tls_ocsp_file ) { - if (!expand_check(tls_ocsp_file, US"tls_ocsp_file", - &state->exp_tls_ocsp_file)) - return DEFER; + if (gnutls_buggy_ocsp) + { + DEBUG(D_tls) debug_printf("GnuTLS library is buggy for OCSP; avoiding\n"); + } + else + { + if (!expand_check(tls_ocsp_file, US"tls_ocsp_file", + &state->exp_tls_ocsp_file)) + return DEFER; - /* Use the full callback method for stapling just to get observability. - More efficient would be to read the file once only, if it never changed - (due to SNI). Would need restart on file update, or watch datestamp. */ + /* Use the full callback method for stapling just to get observability. + More efficient would be to read the file once only, if it never changed + (due to SNI). Would need restart on file update, or watch datestamp. */ - gnutls_certificate_set_ocsp_status_request_function(state->x509_cred, - server_ocsp_stapling_cb, state->exp_tls_ocsp_file); + gnutls_certificate_set_ocsp_status_request_function(state->x509_cred, + server_ocsp_stapling_cb, state->exp_tls_ocsp_file); - DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", &state->exp_tls_ocsp_file); + DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file); + } } #endif @@ -1011,6 +1022,38 @@ return OK; * Initialize for GnuTLS * *************************************************/ + +#ifndef DISABLE_OCSP + +static BOOL +tls_is_buggy_ocsp(void) +{ +const uschar * s; +uschar maj, mid, mic; + +s = CUS gnutls_check_version(NULL); +maj = atoi(CCS s); +if (maj == 3) + { + while (*s && *s != '.') s++; + mid = atoi(CCS ++s); + if (mid <= 2) + return TRUE; + else if (mid >= 5) + return FALSE; + else + { + while (*s && *s != '.') s++; + mic = atoi(CCS ++s); + return mic <= (mid == 3 ? 16 : 3); + } + } +return FALSE; +} + +#endif + + /* Called from both server and client code. In the case of a server, errors before actual TLS negotiation return DEFER. @@ -1074,6 +1117,11 @@ if (!exim_gnutls_base_init_done) } #endif +#ifndef DISABLE_OCSP + if (tls_ocsp_file && (gnutls_buggy_ocsp = tls_is_buggy_ocsp())) + log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version"); +#endif + exim_gnutls_base_init_done = TRUE; } @@ -1229,7 +1277,7 @@ static int peer_status(exim_gnutls_state_st *state) { uschar cipherbuf[256]; -const gnutls_datum *cert_list; +const gnutls_datum_t *cert_list; int old_pool, rc; unsigned int cert_list_size = 0; gnutls_protocol_t protocol; @@ -1398,7 +1446,7 @@ else if (state->exp_tls_verify_cert_hostnames) { int sep = 0; - uschar * list = state->exp_tls_verify_cert_hostnames; + const uschar * list = state->exp_tls_verify_cert_hostnames; uschar * name; while (name = string_nextinlist(&list, &sep, NULL, 0)) if (gnutls_x509_crt_check_hostname(state->tlsp->peercert, CS name)) @@ -1550,7 +1598,7 @@ return 0; #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT /* We use this callback to get observability and detail-level control for an exim TLS connection (either direction), raising a tls:cert event @@ -1563,7 +1611,7 @@ Return 0 for the handshake to continue or non-zero to terminate. static int verify_cb(gnutls_session_t session) { -const gnutls_datum * cert_list; +const gnutls_datum_t * cert_list; unsigned int cert_list_size = 0; gnutls_x509_crt_t crt; int rc; @@ -1674,7 +1722,7 @@ else gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE); } -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (event_action) { state->event_action = event_action; @@ -1705,8 +1753,8 @@ if (!state->tlsp->on_connect) that the GnuTLS library doesn't. */ gnutls_transport_set_ptr2(state->session, - (gnutls_transport_ptr)(long) fileno(smtp_in), - (gnutls_transport_ptr)(long) fileno(smtp_out)); + (gnutls_transport_ptr_t)(long) fileno(smtp_in), + (gnutls_transport_ptr_t)(long) fileno(smtp_out)); state->fd_in = fileno(smtp_in); state->fd_out = fileno(smtp_out); @@ -1785,7 +1833,12 @@ tls_client_setup_hostname_checks(host_item * host, exim_gnutls_state_st * state, { if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) { - state->exp_tls_verify_cert_hostnames = host->name; + state->exp_tls_verify_cert_hostnames = +#ifdef SUPPORT_I18N + string_domain_utf8_to_alabel(host->name, NULL); +#else + host->name; +#endif DEBUG(D_tls) debug_printf("TLS: server cert verification includes hostname: \"%s\".\n", state->exp_tls_verify_cert_hostnames); @@ -1854,7 +1907,7 @@ if ((rc = tls_init(host, ob->tls_certificate, ob->tls_privatekey, gnutls_dh_set_prime_bits(state->session, dh_min_bits); } -/* Stick to the old behaviour for compatibility if tls_verify_certificates is +/* Stick to the old behaviour for compatibility if tls_verify_certificates is set but both tls_verify_hosts and tls_try_verify_hosts are unset. Check only the specified host patterns if one of them is defined */ @@ -1900,7 +1953,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (tb->event_action) { state->event_action = tb->event_action; @@ -1909,7 +1962,7 @@ if (tb->event_action) } #endif -gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)(long) fd); +gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr_t)(long) fd); state->fd_in = fd; state->fd_out = fd; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 944e33bc9..9944a8f60 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Portions Copyright (c) The OpenSSL Project 1999 */ @@ -22,6 +22,9 @@ functions from the OpenSSL library. */ #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/rand.h> +#ifndef OPENSSL_NO_ECDH +# include <openssl/ec.h> +#endif #ifndef DISABLE_OCSP # include <openssl/ocsp.h> #endif @@ -38,12 +41,37 @@ functions from the OpenSSL library. */ #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) # define EXIM_HAVE_OPENSSL_TLSEXT #endif -#if OPENSSL_VERSION_NUMBER >= 0x010100000L -# define EXIM_HAVE_OPENSSL_CHECKHOST -#endif -#if OPENSSL_VERSION_NUMBER >= 0x010000000L \ + +/* + * X509_check_host provides sane certificate hostname checking, but was added + * to OpenSSL late, after other projects forked off the code-base. So in + * addition to guarding against the base version number, beware that LibreSSL + * does not (at this time) support this function. + * + * If LibreSSL gains a different API, perhaps via libtls, then we'll probably + * opt to disentangle and ask a LibreSSL user to provide glue for a third + * crypto provider for libtls instead of continuing to tie the OpenSSL glue + * into even twistier knots. If LibreSSL gains the same API, we can just + * change this guard and punt the issue for a while longer. + */ +#ifndef LIBRESSL_VERSION_NUMBER +# if OPENSSL_VERSION_NUMBER >= 0x010100000L +# define EXIM_HAVE_OPENSSL_CHECKHOST +# endif +# if OPENSSL_VERSION_NUMBER >= 0x010000000L \ && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L -# define EXIM_HAVE_OPENSSL_CHECKHOST +# define EXIM_HAVE_OPENSSL_CHECKHOST +# endif + +# if !defined(OPENSSL_NO_ECDH) +# if OPENSSL_VERSION_NUMBER >= 0x0090800fL +# define EXIM_HAVE_ECDH +# endif +# if OPENSSL_VERSION_NUMBER >= 0x10002000L +# define EXIM_HAVE_OPENSSL_ECDH_AUTO +# define EXIM_HAVE_OPENSSL_EC_NIST2NID +# endif +# endif #endif #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP) @@ -123,8 +151,8 @@ typedef struct tls_ext_ctx_cb { uschar *server_cipher_list; /* only passed down to tls_error: */ host_item *host; - uschar * verify_cert_hostnames; -#ifdef EXPERIMENTAL_EVENT + const uschar * verify_cert_hostnames; +#ifndef DISABLE_EVENT uschar * event_action; #endif } tls_ext_ctx_cb; @@ -169,7 +197,7 @@ Returns: OK/DEFER/FAIL */ static int -tls_error(uschar *prefix, host_item *host, uschar *msg) +tls_error(uschar * prefix, const host_item * host, uschar * msg) { if (!msg) { @@ -245,6 +273,7 @@ for(i= 0; i<sk_X509_OBJECT_num(roots); i++) { X509 * current_cert= tmp_obj->data.x509; X509_NAME_oneline(X509_get_subject_name(current_cert), CS name, sizeof(name)); + name[sizeof(name)-1] = '\0'; debug_printf(" %s\n", name); } } @@ -253,21 +282,59 @@ for(i= 0; i<sk_X509_OBJECT_num(roots); i++) */ +#ifndef DISABLE_EVENT +static int +verify_event(tls_support * tlsp, X509 * cert, int depth, const uschar * dn, + BOOL *calledp, const BOOL *optionalp, const uschar * what) +{ +uschar * ev; +uschar * yield; +X509 * old_cert; + +ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; +if (ev) + { + DEBUG(D_tls) debug_printf("verify_event: %s %d\n", what, depth); + old_cert = tlsp->peercert; + tlsp->peercert = X509_dup(cert); + /* NB we do not bother setting peerdn */ + if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth)))) + { + log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: " + "depth=%d cert=%s: %s", + tlsp == &tls_out ? deliver_host_address : sender_host_address, + what, depth, dn, yield); + *calledp = TRUE; + if (!*optionalp) + { + if (old_cert) tlsp->peercert = old_cert; /* restore 1st failing cert */ + return 1; /* reject (leaving peercert set) */ + } + DEBUG(D_tls) debug_printf("Event-action verify failure overridden " + "(host in tls_try_verify_hosts)\n"); + } + X509_free(tlsp->peercert); + tlsp->peercert = old_cert; + } +return 0; +} +#endif + /************************************************* * Callback for verification * *************************************************/ /* The SSL library does certificate verification if set up to do so. This callback has the current yes/no state is in "state". If verification succeeded, -we set up the tls_peerdn string. If verification failed, what happens depends -on whether the client is required to present a verifiable certificate or not. +we set the certificate-verified flag. If verification failed, what happens +depends on whether the client is required to present a verifiable certificate +or not. If verification is optional, we change the state to yes, but still log the verification error. For some reason (it really would help to have proper documentation of OpenSSL), this callback function then gets called again, this -time with state = 1. In fact, that's useful, because we can set up the peerdn -value, but we must take care not to set the private verified flag on the second -time through. +time with state = 1. We must take care not to set the private verified flag on +the second time through. Note: this function is not called if the client fails to present a certificate when asked. We get here only if a certificate has been received. Handling of @@ -278,38 +345,39 @@ May be called multiple times for different issues with a certificate, even for a given "depth" in the certificate chain. Arguments: - state current yes/no state as 1/0 - x509ctx certificate information. - client TRUE for client startup, FALSE for server startup + preverify_ok current yes/no state as 1/0 + x509ctx certificate information. + tlsp per-direction (client vs. server) support data + calledp has-been-called flag + optionalp verification-is-optional flag -Returns: 1 if verified, 0 if not +Returns: 0 if verification should fail, otherwise 1 */ static int -verify_callback(int state, X509_STORE_CTX *x509ctx, +verify_callback(int preverify_ok, X509_STORE_CTX *x509ctx, tls_support *tlsp, BOOL *calledp, BOOL *optionalp) { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); int depth = X509_STORE_CTX_get_error_depth(x509ctx); -static uschar txt[256]; -#ifdef EXPERIMENTAL_EVENT -uschar * ev; -uschar * yield; -#endif +uschar dn[256]; -X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); +X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)); +dn[sizeof(dn)-1] = '\0'; -if (state == 0) +if (preverify_ok == 0) { - log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s", + log_write(0, LOG_MAIN, "[%s] SSL verify error: depth=%d error=%s cert=%s", + tlsp == &tls_out ? deliver_host_address : sender_host_address, depth, X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), - txt); + dn); *calledp = TRUE; if (!*optionalp) { - tlsp->peercert = X509_dup(cert); - return 0; /* reject */ + if (!tlsp->peercert) + tlsp->peercert = X509_dup(cert); /* record failing cert */ + return 0; /* reject */ } DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " "tls_try_verify_hosts)\n"); @@ -317,114 +385,85 @@ if (state == 0) else if (depth != 0) { - DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, txt); + DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, dn); #ifndef DISABLE_OCSP if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store) { /* client, wanting stapling */ /* Add the server cert's signing chain as the one for the verification of the OCSP stapled information. */ - + if (!X509_STORE_add_cert(client_static_cbinfo->u_ocsp.client.verify_store, cert)) ERR_clear_error(); } #endif -#ifdef EXPERIMENTAL_EVENT - ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; - if (ev) - { - tlsp->peercert = X509_dup(cert); - if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth)))) - { - log_write(0, LOG_MAIN, "SSL verify denied by event-action: " - "depth=%d cert=%s: %s", depth, txt, yield); - *calledp = TRUE; - if (!*optionalp) - return 0; /* reject */ - DEBUG(D_tls) debug_printf("Event-action verify failure overridden " - "(host in tls_try_verify_hosts)\n"); - } - X509_free(tlsp->peercert); - tlsp->peercert = NULL; - } +#ifndef DISABLE_EVENT + if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL")) + return 0; /* reject, with peercert set */ #endif } else { - uschar * verify_cert_hostnames; - - tlsp->peerdn = txt; - tlsp->peercert = X509_dup(cert); + const uschar * verify_cert_hostnames; if ( tlsp == &tls_out && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames))) /* client, wanting hostname check */ - -# if EXIM_HAVE_OPENSSL_CHECKHOST -# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS -# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 -# endif -# ifndef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS -# define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 -# endif { + +#ifdef EXIM_HAVE_OPENSSL_CHECKHOST +# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS +# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 +# endif +# ifndef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS +# define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 +# endif int sep = 0; - uschar * list = verify_cert_hostnames; + const uschar * list = verify_cert_hostnames; uschar * name; int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) - if ((rc = X509_check_host(cert, name, 0, + if ((rc = X509_check_host(cert, CCS name, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS - | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS))) + | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, + NULL))) { if (rc < 0) { - log_write(0, LOG_MAIN, "SSL verify error: internal error\n"); + log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error", + tlsp == &tls_out ? deliver_host_address : sender_host_address); name = NULL; } break; } if (!name) - { - log_write(0, LOG_MAIN, - "SSL verify error: certificate name mismatch: \"%s\"\n", txt); - *calledp = TRUE; - if (!*optionalp) - return 0; /* reject */ - DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " - "tls_try_verify_hosts)\n"); - } - } -# else +#else if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) +#endif { log_write(0, LOG_MAIN, - "SSL verify error: certificate name mismatch: \"%s\"\n", txt); + "[%s] SSL verify error: certificate name mismatch: \"%s\"", + tlsp == &tls_out ? deliver_host_address : sender_host_address, + dn); *calledp = TRUE; if (!*optionalp) - return 0; /* reject */ + { + if (!tlsp->peercert) + tlsp->peercert = X509_dup(cert); /* record failing cert */ + return 0; /* reject */ + } DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " "tls_try_verify_hosts)\n"); } -# endif + } -#ifdef EXPERIMENTAL_EVENT - ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; - if (ev) - if ((yield = event_raise(ev, US"tls:cert", US"0"))) - { - log_write(0, LOG_MAIN, "SSL verify denied by event-action: " - "depth=0 cert=%s: %s", txt, yield); - *calledp = TRUE; - if (!*optionalp) - return 0; /* reject */ - DEBUG(D_tls) debug_printf("Event-action verify failure overridden " - "(host in tls_try_verify_hosts)\n"); - } +#ifndef DISABLE_EVENT + if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL")) + return 0; /* reject, with peercert set */ #endif DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", - *calledp ? "" : " authenticated", txt); + *calledp ? "" : " authenticated", dn); if (!*calledp) tlsp->certificate_verified = TRUE; *calledp = TRUE; } @@ -433,15 +472,17 @@ return 1; /* accept, at least for this level */ } static int -verify_callback_client(int state, X509_STORE_CTX *x509ctx) +verify_callback_client(int preverify_ok, X509_STORE_CTX *x509ctx) { -return verify_callback(state, x509ctx, &tls_out, &client_verify_callback_called, &client_verify_optional); +return verify_callback(preverify_ok, x509ctx, &tls_out, + &client_verify_callback_called, &client_verify_optional); } static int -verify_callback_server(int state, X509_STORE_CTX *x509ctx) +verify_callback_server(int preverify_ok, X509_STORE_CTX *x509ctx) { -return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, &server_verify_optional); +return verify_callback(preverify_ok, x509ctx, &tls_in, + &server_verify_callback_called, &server_verify_optional); } @@ -451,44 +492,39 @@ return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, itself. */ static int -verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx) +verify_callback_client_dane(int preverify_ok, X509_STORE_CTX * x509ctx) { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); -static uschar txt[256]; -#ifdef EXPERIMENTAL_EVENT +uschar dn[256]; +#ifndef DISABLE_EVENT int depth = X509_STORE_CTX_get_error_depth(x509ctx); -uschar * yield; +BOOL dummy_called, optional = FALSE; #endif -X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); +X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)); +dn[sizeof(dn)-1] = '\0'; -DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", txt); -tls_out.peerdn = txt; -tls_out.peercert = X509_dup(cert); +DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n", + preverify_ok ? "ok":"BAD", depth, dn); -#ifdef EXPERIMENTAL_EVENT - if (client_static_cbinfo->event_action) - { - if ((yield = event_raise(client_static_cbinfo->event_action, - US"tls:cert", string_sprintf("%d", depth)))) - { - log_write(0, LOG_MAIN, "DANE verify denied by event-action: " - "depth=%d cert=%s: %s", depth, txt, yield); - tls_out.certificate_verified = FALSE; - return 0; /* reject */ - } - if (depth != 0) - { - X509_free(tls_out.peercert); - tls_out.peercert = NULL; - } - } +#ifndef DISABLE_EVENT + if (verify_event(&tls_out, cert, depth, dn, + &dummy_called, &optional, US"DANE")) + return 0; /* reject, with peercert set */ #endif -if (state == 1) +if (preverify_ok == 1) tls_out.dane_verified = tls_out.certificate_verified = TRUE; -return 1; +else + { + int err = X509_STORE_CTX_get_error(x509ctx); + DEBUG(D_tls) + debug_printf(" - err %d '%s'\n", err, X509_verify_cert_error_string(err)); + if (err = X509_V_ERR_APPLICATION_VERIFICATION) + preverify_ok = 1; + } +return preverify_ok; } #endif /*EXPERIMENTAL_DANE*/ @@ -527,6 +563,7 @@ DEBUG(D_tls) debug_printf("SSL info: %s\n", SSL_state_string_long(s)); /* If dhparam is set, expand it, and load up the parameters for DH encryption. Arguments: + sctx The current SSL CTX (inbound or outbound) dhparam DH parameter file or fixed parameter identity string host connected host, if client; NULL if server @@ -534,7 +571,7 @@ Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_dh(SSL_CTX *sctx, uschar *dhparam, host_item *host) +init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host) { BIO *bio; DH *dh; @@ -606,6 +643,107 @@ return TRUE; +/************************************************* +* Initialize for ECDH * +*************************************************/ + +/* Load parameters for ECDH encryption. + +For now, we stick to NIST P-256 because: it's simple and easy to configure; +it avoids any patent issues that might bite redistributors; despite events in +the news and concerns over curve choices, we're not cryptographers, we're not +pretending to be, and this is "good enough" to be better than no support, +protecting against most adversaries. Given another year or two, there might +be sufficient clarity about a "right" way forward to let us make an informed +decision, instead of a knee-jerk reaction. + +Longer-term, we should look at supporting both various named curves and +external files generated with "openssl ecparam", much as we do for init_dh(). +We should also support "none" as a value, to explicitly avoid initialisation. + +Patches welcome. + +Arguments: + sctx The current SSL CTX (inbound or outbound) + host connected host, if client; NULL if server + +Returns: TRUE if OK (nothing to set up, or setup worked) +*/ + +static BOOL +init_ecdh(SSL_CTX * sctx, host_item * host) +{ +#ifdef OPENSSL_NO_ECDH +return TRUE; +#else + +EC_KEY * ecdh; +uschar * exp_curve; +int nid; +BOOL rv; + +if (host) /* No ECDH setup for clients, only for servers */ + return TRUE; + +# ifndef EXIM_HAVE_ECDH +DEBUG(D_tls) + debug_printf("No OpenSSL API to define ECDH parameters, skipping\n"); +return TRUE; +# else + +if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve)) + return FALSE; +if (!exp_curve || !*exp_curve) + return TRUE; + +# ifdef EXIM_HAVE_OPENSSL_ECDH_AUTO +/* check if new enough library to support auto ECDH temp key parameter selection */ +if (Ustrcmp(exp_curve, "auto") == 0) + { + DEBUG(D_tls) debug_printf( + "ECDH temp key parameter settings: OpenSSL 1.2+ autoselection\n"); + SSL_CTX_set_ecdh_auto(sctx, 1); + return TRUE; + } +# endif + +DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve); +if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef +# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID + && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef +# endif + ) + { + tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", + exp_curve), + host, NULL); + return FALSE; + } + +if (!(ecdh = EC_KEY_new_by_curve_name(nid))) + { + tls_error(US"Unable to create ec curve", host, NULL); + return FALSE; + } + +/* The "tmp" in the name here refers to setting a temporary key +not to the stability of the interface. */ + +if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) + tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL); +else + DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve); + +EC_KEY_free(ecdh); +return !rv; + +# endif /*EXIM_HAVE_ECDH*/ +#endif /*OPENSSL_NO_ECDH*/ +} + + + + #ifndef DISABLE_OCSP /************************************************* * Load OCSP information into state * @@ -794,7 +932,7 @@ if (cbinfo->privatekey != NULL && of the expansion is an empty string, ignore it also, and assume the private key is in the same file as the certificate. */ -if (expanded != NULL && *expanded != 0) +if (expanded && *expanded) { DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM)) @@ -803,21 +941,22 @@ if (expanded != NULL && *expanded != 0) } #ifndef DISABLE_OCSP -if (cbinfo->is_server && cbinfo->u_ocsp.server.file != NULL) +if (cbinfo->is_server && cbinfo->u_ocsp.server.file) { if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded)) return DEFER; - if (expanded != NULL && *expanded != 0) + if (expanded && *expanded) { DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded); - if (cbinfo->u_ocsp.server.file_expanded && - (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0)) + if ( cbinfo->u_ocsp.server.file_expanded + && (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0)) { - DEBUG(D_tls) - debug_printf("tls_ocsp_file value unchanged, using existing values.\n"); - } else { - ocsp_load_response(sctx, cbinfo, expanded); + DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n"); + } + else + { + ocsp_load_response(sctx, cbinfo, expanded); } } } @@ -889,6 +1028,12 @@ SSL_CTX_set_options(server_sni, SSL_CTX_get_options(server_ctx)); SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(server_ctx)); SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo); + +if ( !init_dh(server_sni, cbinfo->dhparam, NULL) + || !init_ecdh(server_sni, NULL) + ) + return SSL_TLSEXT_ERR_NOACK; + if (cbinfo->server_cipher_list) SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list); #ifndef DISABLE_OCSP @@ -904,10 +1049,7 @@ if (rc != OK) return SSL_TLSEXT_ERR_NOACK; /* do this after setup_certs, because this can require the certs for verifying OCSP information. */ -rc = tls_expand_session_files(server_sni, cbinfo); -if (rc != OK) return SSL_TLSEXT_ERR_NOACK; - -if (!init_dh(server_sni, cbinfo->dhparam, NULL)) +if ((rc = tls_expand_session_files(server_sni, cbinfo)) != OK) return SSL_TLSEXT_ERR_NOACK; DEBUG(D_tls) debug_printf("Switching SSL context.\n"); @@ -942,7 +1084,7 @@ uschar *response_der; int response_der_len; DEBUG(D_tls) - debug_printf("Received TLS status request (OCSP stapling); %s response.", + debug_printf("Received TLS status request (OCSP stapling); %s response\n", cbinfo->u_ocsp.server.response ? "have" : "lack"); tls_in.ocsp = OCSP_NOT_RESP; @@ -984,8 +1126,7 @@ len = SSL_get_tlsext_status_ocsp_resp(s, &p); if(!p) { /* Expect this when we requested ocsp but got none */ - if ( cbinfo->u_ocsp.client.verify_required - && log_extra_selector & LX_tls_cipher) + if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS status callback, null content"); else DEBUG(D_tls) debug_printf(" null\n"); @@ -995,7 +1136,7 @@ if(!p) if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, parse error"); else DEBUG(D_tls) debug_printf(" parse error\n"); @@ -1005,7 +1146,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) if(!(bs = OCSP_response_get1_basic(rsp))) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response"); else DEBUG(D_tls) debug_printf(" error parsing response\n"); @@ -1036,7 +1177,7 @@ if(!(bs = OCSP_response_get1_basic(rsp))) cbinfo->u_ocsp.client.verify_store, 0)) <= 0) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable"); BIO_printf(bp, "OCSP response verify failure\n"); ERR_print_errors(bp); @@ -1158,7 +1299,7 @@ else cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT cbinfo->event_action = NULL; #endif @@ -1210,7 +1351,7 @@ if (!RAND_status()) /* Set up the information callback, which outputs if debugging is at a suitable level. */ -SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); +DEBUG(D_tls) SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); /* Automatically re-try reads/writes after renegotiation. */ (void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY); @@ -1239,8 +1380,12 @@ else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); /* Initialize with DH parameters if supplied */ +/* Initialize ECDH temp key parameter selection */ -if (!init_dh(*ctxp, dhparam, host)) return DEFER; +if ( !init_dh(*ctxp, dhparam, host) + || !init_ecdh(*ctxp, host) + ) + return DEFER; /* Set up certificate and key (and perhaps OCSP info) */ @@ -1334,6 +1479,29 @@ DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf); } +static void +peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned bsize) +{ +/*XXX we might consider a list-of-certs variable for the cert chain. +SSL_get_peer_cert_chain(SSL*). We'd need a new variable type and support +in list-handling functions, also consider the difference between the entire +chain and the elements sent by the peer. */ + +/* Will have already noted peercert on a verify fail; possibly not the leaf */ +if (!tlsp->peercert) + tlsp->peercert = SSL_get_peer_certificate(ssl); +/* Beware anonymous ciphers which lead to server_cert being NULL */ +if (tlsp->peercert) + { + X509_NAME_oneline(X509_get_subject_name(tlsp->peercert), CS peerdn, bsize); + peerdn[bsize-1] = '\0'; + tlsp->peerdn = peerdn; /*XXX a static buffer... */ + } +else + tlsp->peerdn = NULL; +} + + @@ -1364,26 +1532,18 @@ uschar *expcerts, *expcrl; if (!expand_check(certs, US"tls_verify_certificates", &expcerts)) return DEFER; -if (expcerts != NULL && *expcerts != '\0') +if (expcerts && *expcerts) { - if (Ustrcmp(expcerts, "system") == 0) - { - /* Tell the library to use its compiled-in location for the system default - CA bundle, only */ + /* Tell the library to use its compiled-in location for the system default + CA bundle. Then add the ones specified in the config, if any. */ - if (!SSL_CTX_set_default_verify_paths(sctx)) - return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - } - else + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); + + if (Ustrcmp(expcerts, "system") != 0) { struct stat statbuf; - /* Tell the library to use its compiled-in location for the system default - CA bundle. Those given by the exim config are additional to these */ - - if (!SSL_CTX_set_default_verify_paths(sctx)) - return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - if (Ustat(expcerts, &statbuf) < 0) { log_write(0, LOG_MAIN|LOG_PANIC, @@ -1403,8 +1563,8 @@ if (expcerts != NULL && *expcerts != '\0') certificates are recognized, but the error message is still misleading (it says no certificate was supplied.) But this is better. */ - if ((file == NULL || statbuf.st_size > 0) && - !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) + if ( (!file || statbuf.st_size > 0) + && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); /* Load the list of CAs for which we will accept certs, for sending @@ -1413,15 +1573,16 @@ if (expcerts != NULL && *expcerts != '\0') If a list isn't loaded into the server, but some verify locations are set, the server end appears to make a wildcard reqest for client certs. - Meanwhile, the client library as deafult behaviour *ignores* the list + Meanwhile, the client library as default behaviour *ignores* the list we send over the wire - see man SSL_CTX_set_client_cert_cb. Because of this, and that the dir variant is likely only used for the public-CA bundle (not for a private CA), not worth fixing. */ - if (file != NULL) + if (file) { STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); - DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", + + DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", sk_X509_NAME_num(names)); SSL_CTX_set_client_CA_list(sctx, names); } @@ -1430,20 +1591,20 @@ if (expcerts != NULL && *expcerts != '\0') /* Handle a certificate revocation list. */ - #if OPENSSL_VERSION_NUMBER > 0x00907000L +#if OPENSSL_VERSION_NUMBER > 0x00907000L /* This bit of code is now the version supplied by Lars Mainka. (I have - * merely reformatted it into the Exim code style.) + merely reformatted it into the Exim code style.) - * "From here I changed the code to add support for multiple crl's - * in pem format in one file or to support hashed directory entries in - * pem format instead of a file. This method now uses the library function - * X509_STORE_load_locations to add the CRL location to the SSL context. - * OpenSSL will then handle the verify against CA certs and CRLs by - * itself in the verify callback." */ + "From here I changed the code to add support for multiple crl's + in pem format in one file or to support hashed directory entries in + pem format instead of a file. This method now uses the library function + X509_STORE_load_locations to add the CRL location to the SSL context. + OpenSSL will then handle the verify against CA certs and CRLs by + itself in the verify callback." */ if (!expand_check(crl, US"tls_crl", &expcrl)) return DEFER; - if (expcrl != NULL && *expcrl != 0) + if (expcrl && *expcrl) { struct stat statbufcrl; if (Ustat(expcrl, &statbufcrl) < 0) @@ -1479,7 +1640,7 @@ if (expcerts != NULL && *expcerts != '\0') } } - #endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */ +#endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */ /* If verification is optional, don't fail if no certificate */ @@ -1516,6 +1677,7 @@ tls_server_start(const uschar *require_ciphers) int rc; uschar *expciphers; tls_ext_ctx_cb *cbinfo; +static uschar peerdn[256]; static uschar cipherbuf[256]; /* Check for previous activation */ @@ -1638,6 +1800,8 @@ DEBUG(D_tls) debug_printf("SSL_accept was successful\n"); /* TLS has been set up. Adjust the input functions to read via TLS, and initialize things. */ +peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn)); + construct_cipher_name(server_ssl, cipherbuf, sizeof(cipherbuf), &tls_in.bits); tls_in.cipher = cipherbuf; @@ -1682,7 +1846,7 @@ tls_client_basic_ctx_init(SSL_CTX * ctx, ) { int rc; -/* stick to the old behaviour for compatibility if tls_verify_certificates is +/* stick to the old behaviour for compatibility if tls_verify_certificates is set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only the specified host patterns if one of them is defined */ @@ -1703,7 +1867,12 @@ if ((rc = setup_certs(ctx, ob->tls_verify_certificates, if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) { - cbinfo->verify_cert_hostnames = host->name; + cbinfo->verify_cert_hostnames = +#ifdef SUPPORT_I18N + string_domain_utf8_to_alabel(host->name, NULL); +#else + host->name; +#endif DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", cbinfo->verify_cert_hostnames); } @@ -1764,7 +1933,7 @@ if (found) return OK; log_write(0, LOG_MAIN, "DANE error: No usable TLSA records"); -return FAIL; +return DEFER; } #endif /*EXPERIMENTAL_DANE*/ @@ -1798,9 +1967,8 @@ tls_client_start(int fd, host_item *host, address_item *addr, { smtp_transport_options_block * ob = (smtp_transport_options_block *)tb->options_block; -static uschar txt[256]; +static uschar peerdn[256]; uschar * expciphers; -X509 * server_cert; int rc; static uschar cipherbuf[256]; @@ -1872,7 +2040,9 @@ if (expciphers != NULL) #ifdef EXPERIMENTAL_DANE if (tlsa_dnsa) { - SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane); + SSL_CTX_set_verify(client_ctx, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback_client_dane); if (!DANESSL_library_init()) return tls_error(US"library init", host, NULL); @@ -1950,7 +2120,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT client_static_cbinfo->event_action = tb->event_action; #endif @@ -1972,17 +2142,7 @@ if (rc <= 0) DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); -/* Beware anonymous ciphers which lead to server_cert being NULL */ -/*XXX server_cert is never freed... use X509_free() */ -server_cert = SSL_get_peer_certificate (client_ssl); -if (server_cert) - { - tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert), - CS txt, sizeof(txt)); - tls_out.peerdn = txt; /*XXX a static buffer... */ - } -else - tls_out.peerdn = NULL; +peer_cert(client_ssl, &tls_out, peerdn, sizeof(peerdn)); construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits); tls_out.cipher = cipherbuf; diff --git a/src/src/tls.c b/src/src/tls.c index 1c6312720..5958dfc1c 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is @@ -84,11 +84,15 @@ return TRUE; * Timezone environment flipping * *************************************************/ +#ifdef MISSING_UNSETENV_3 +# include "setenv.c" +#endif + static uschar * to_tz(uschar * tz) { uschar * old = US getenv("TZ"); - setenv("TZ", CS tz, 1); + (void) setenv("TZ", CCS tz, 1); tzset(); return old; } @@ -96,9 +100,9 @@ static void restore_tz(uschar * tz) { if (tz) - setenv("TZ", CS tz, 1); + (void) setenv("TZ", CCS tz, 1); else - unsetenv("TZ"); + (void) unsetenv("TZ"); tzset(); } @@ -107,18 +111,18 @@ restore_tz(uschar * tz) *************************************************/ #ifdef USE_GNUTLS -#include "tls-gnu.c" -#include "tlscert-gnu.c" +# include "tls-gnu.c" +# include "tlscert-gnu.c" -#define ssl_xfer_buffer (state_server.xfer_buffer) -#define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm) -#define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm) -#define ssl_xfer_eof (state_server.xfer_eof) -#define ssl_xfer_error (state_server.xfer_error) +# define ssl_xfer_buffer (state_server.xfer_buffer) +# define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm) +# define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm) +# define ssl_xfer_eof (state_server.xfer_eof) +# define ssl_xfer_error (state_server.xfer_error) #else -#include "tls-openssl.c" -#include "tlscert-openssl.c" +# include "tls-openssl.c" +# include "tlscert-openssl.c" #endif @@ -246,7 +250,7 @@ NOTE: We modify the supplied dn string during operation. Arguments: dn Distinguished Name string - mod string containing optional list-sep and + mod list containing optional output list-sep and field selector match, comma-separated Return: allocated string with list of matching fields, @@ -254,7 +258,7 @@ Return: */ uschar * -tls_field_from_dn(uschar * dn, uschar * mod) +tls_field_from_dn(uschar * dn, const uschar * mod) { int insep = ','; uschar outsep = '\n'; @@ -267,13 +271,15 @@ while ((ele = string_nextinlist(&mod, &insep, NULL, 0))) if (ele[0] != '>') match = ele; /* field tag to match */ else if (ele[1]) - outsep = ele[1]; /* nondefault separator */ + outsep = ele[1]; /* nondefault output separator */ dn_to_list(dn); insep = ','; -len = Ustrlen(match); -while ((ele = string_nextinlist(&dn, &insep, NULL, 0))) - if (Ustrncmp(ele, match, len) == 0 && ele[len] == '=') +len = match ? Ustrlen(match) : -1; +while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0))) + if ( !match + || Ustrncmp(ele, match, len) == 0 && ele[len] == '=' + ) list = string_append_listele(list, outsep, ele+len+1); return list; } @@ -311,7 +317,7 @@ Returns: */ BOOL -tls_is_name_for_cert(uschar * namelist, void * cert) +tls_is_name_for_cert(const uschar * namelist, void * cert) { uschar * altnames = tls_cert_subject_altname(cert, US"dns"); uschar * subjdn; @@ -324,7 +330,7 @@ if ((altnames = tls_cert_subject_altname(cert, US"dns"))) int alt_sep = '\n'; while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { - uschar * an = altnames; + const uschar * an = altnames; while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0))) if (is_name_match(cmpname, certname)) return TRUE; @@ -338,7 +344,7 @@ else if ((subjdn = tls_cert_subject(cert, NULL))) dn_to_list(subjdn); while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { - uschar * sn = subjdn; + const uschar * sn = subjdn; while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0))) if ( *certname++ == 'C' && *certname++ == 'N' diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c index ffe958892..d00258b9e 100644 --- a/src/src/tlscert-gnu.c +++ b/src/src/tlscert-gnu.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Jeremy Harris 2014 */ +/* Copyright (c) Jeremy Harris 2014 - 2015 */ /* This file provides TLS/SSL support for Exim using the GnuTLS library, one of the available supported implementations. This file is #included into @@ -27,7 +27,7 @@ tls_export_cert(uschar * buf, size_t buflen, void * cert) size_t sz = buflen; void * reset_point = store_get(0); int fail; -uschar * cp; +const uschar * cp; if ((fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert, GNUTLS_X509_FMT_PEM, buf, &sz))) @@ -51,10 +51,14 @@ tls_import_cert(const uschar * buf, void ** cert) { void * reset_point = store_get(0); gnutls_datum_t datum; -gnutls_x509_crt_t crt; +gnutls_x509_crt_t crt = *(gnutls_x509_crt_t *)cert; int fail = 0; -gnutls_global_init(); +if (crt) + gnutls_x509_crt_deinit(crt); +else + gnutls_global_init(); + gnutls_x509_crt_init(&crt); datum.data = string_unprinting(US buf); @@ -73,10 +77,15 @@ return fail; } void -tls_free_cert(void * cert) +tls_free_cert(void ** cert) { -gnutls_x509_crt_deinit((gnutls_x509_crt_t) cert); -gnutls_global_deinit(); +gnutls_x509_crt_t crt = *(gnutls_x509_crt_t *)cert; +if (crt) + { + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + *cert = NULL; + } } /***************************************************** @@ -182,7 +191,7 @@ return string_copy(sp); uschar * tls_cert_signature(void * cert, uschar * mod) { -uschar * cp1; +uschar * cp1 = NULL; uschar * cp2; uschar * cp3; size_t len = 0; @@ -319,11 +328,11 @@ for(index = 0;; index++) switch (ret) { case GNUTLS_SAN_DNSNAME: tag = US"DNS"; break; - case GNUTLS_SAN_URI: tag = US"URI"; break; + case GNUTLS_SAN_URI: tag = US"URI"; break; case GNUTLS_SAN_RFC822NAME: tag = US"MAIL"; break; default: continue; /* ignore unrecognised types */ } - list = string_append_listele(list, sep, + list = string_append_listele(list, sep, match == -1 ? string_sprintf("%s=%s", tag, ele) : ele); } /*NOTREACHED*/ @@ -359,7 +368,7 @@ for(index = 0;; index++) #else -expand_string_message = +expand_string_message = string_sprintf("%s: OCSP support with GnuTLS requires version 3.0.0\n", __FUNCTION__); return NULL; diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c index de6979a18..94534d808 100644 --- a/src/src/tlscert-openssl.c +++ b/src/src/tlscert-openssl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Jeremy Harris 2014 */ +/* Copyright (c) Jeremy Harris 2014 - 2015 */ /* This module provides TLS (aka SSL) support for Exim using the OpenSSL library. It is #included into the tls.c file when that library is used. @@ -55,9 +55,11 @@ tls_import_cert(const uschar * buf, void ** cert) void * reset_point = store_get(0); const uschar * cp = string_unprinting(US buf); BIO * bp; -X509 * x; +X509 * x = *(X509 **)cert; int fail = 0; +if (x) X509_free(x); + bp = BIO_new_mem_buf(US cp, -1); if (!(x = PEM_read_bio_X509(bp, NULL, 0, NULL))) { @@ -73,9 +75,14 @@ return fail; } void -tls_free_cert(void * cert) +tls_free_cert(void ** cert) { -X509_free((X509 *)cert); +X509 * x = *(X509 **)cert; +if (x) + { + X509_free(x); + *cert = NULL; + } } @@ -120,7 +127,7 @@ else { struct tm tm; struct tm * tm_p = &tm; - BOOL mod_tz; + BOOL mod_tz = TRUE; uschar * tz = to_tz(US"GMT0"); /* need to call strptime with baseline TZ */ /* Parse OpenSSL ASN1_TIME_print output. A shame there seems to @@ -157,7 +164,7 @@ else } } - if (mod_tz); + if (mod_tz) restore_tz(tz); } BIO_free(bp); @@ -234,9 +241,9 @@ BIO * bp = BIO_new(BIO_s_mem()); if (!bp) return badalloc(); if (X509_print_ex(bp, (X509 *)cert, 0, - X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION | X509_FLAG_NO_SERIAL | - X509_FLAG_NO_SIGNAME | X509_FLAG_NO_ISSUER | X509_FLAG_NO_VALIDITY | - X509_FLAG_NO_SUBJECT | X509_FLAG_NO_PUBKEY | X509_FLAG_NO_EXTENSIONS | + X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION | X509_FLAG_NO_SERIAL | + X509_FLAG_NO_SIGNAME | X509_FLAG_NO_ISSUER | X509_FLAG_NO_VALIDITY | + X509_FLAG_NO_SUBJECT | X509_FLAG_NO_PUBKEY | X509_FLAG_NO_EXTENSIONS | /* X509_FLAG_NO_SIGDUMP is the missing one */ X509_FLAG_NO_AUX) == 1) { @@ -260,10 +267,10 @@ BIO * bp = BIO_new(BIO_s_mem()); if (!bp) return badalloc(); if (X509_print_ex(bp, (X509 *)cert, 0, - X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION | X509_FLAG_NO_SERIAL | + X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION | X509_FLAG_NO_SERIAL | /* X509_FLAG_NO_SIGNAME is the missing one */ - X509_FLAG_NO_ISSUER | X509_FLAG_NO_VALIDITY | - X509_FLAG_NO_SUBJECT | X509_FLAG_NO_PUBKEY | X509_FLAG_NO_EXTENSIONS | + X509_FLAG_NO_ISSUER | X509_FLAG_NO_VALIDITY | + X509_FLAG_NO_SUBJECT | X509_FLAG_NO_PUBKEY | X509_FLAG_NO_EXTENSIONS | X509_FLAG_NO_SIGDUMP | X509_FLAG_NO_AUX) == 1) { long len = BIO_get_mem_data(bp, &cp); @@ -331,7 +338,7 @@ tls_cert_subject_altname(void * cert, uschar * mod) uschar * list = NULL; STACK_OF(GENERAL_NAME) * san = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i((X509 *)cert, NID_subject_alt_name, NULL, NULL); -uschar sep = '\n'; +uschar osep = '\n'; uschar * tag = US""; uschar * ele; int match = -1; @@ -339,16 +346,15 @@ int len; if (!san) return NULL; -while (mod) +while (mod && *mod) { - if (*mod == '>' && *++mod) sep = *mod++; - else if (Ustrcmp(mod, "dns")==0) { match = GEN_DNS; mod += 3; } - else if (Ustrcmp(mod, "uri")==0) { match = GEN_URI; mod += 3; } - else if (Ustrcmp(mod, "mail")==0) { match = GEN_EMAIL; mod += 4; } - else continue; + if (*mod == '>' && *++mod) osep = *mod++; + else if (Ustrncmp(mod,"dns",3)==0) { match = GEN_DNS; mod += 3; } + else if (Ustrncmp(mod,"uri",3)==0) { match = GEN_URI; mod += 3; } + else if (Ustrncmp(mod,"mail",4)==0) { match = GEN_EMAIL; mod += 4; } + else mod++; - if (*mod++ != ',') - break; + if (*mod == ',') mod++; } while (sk_GENERAL_NAME_num(san) > 0) @@ -380,7 +386,7 @@ while (sk_GENERAL_NAME_num(san) > 0) ele = string_copyn(ele, len); if (Ustrlen(ele) == len) /* ignore any with embedded nul */ - list = string_append_listele(list, sep, + list = string_append_listele(list, osep, match == -1 ? string_sprintf("%s=%s", tag, ele) : ele); } @@ -406,9 +412,13 @@ for (i = 0; i < adsnum; i++) ACCESS_DESCRIPTION * ad = sk_ACCESS_DESCRIPTION_value(ads, i); if (ad && OBJ_obj2nid(ad->method) == NID_ad_OCSP) - list = string_append_listele(list, sep, - ASN1_STRING_data(ad->location->d.ia5)); + { + uschar * ele = ASN1_STRING_data(ad->location->d.ia5); + int len = ASN1_STRING_length(ad->location->d.ia5); + list = string_append_listele_n(list, sep, ele, len); + } } +sk_ACCESS_DESCRIPTION_free(ads); return list; } @@ -439,9 +449,13 @@ if (dps) for (i = 0; i < dpsnum; i++) if ( (np = sk_GENERAL_NAME_value(names, j)) && np->type == GEN_URI ) - list = string_append_listele(list, sep, - ASN1_STRING_data(np->d.uniformResourceIdentifier)); + { + uschar * ele = ASN1_STRING_data(np->d.uniformResourceIdentifier); + int len = ASN1_STRING_length(np->d.uniformResourceIdentifier); + list = string_append_listele_n(list, sep, ele, len); + } } +sk_DIST_POINT_free(dps); return list; } @@ -468,19 +482,19 @@ for (j = 0; j < (int)n; j++) sprintf(CS cp+2*j, "%02X", md[j]); return(cp); } -uschar * +uschar * tls_cert_fprt_md5(void * cert) { return fingerprint((X509 *)cert, EVP_md5()); } -uschar * +uschar * tls_cert_fprt_sha1(void * cert) { return fingerprint((X509 *)cert, EVP_sha1()); } -uschar * +uschar * tls_cert_fprt_sha256(void * cert) { return fingerprint((X509 *)cert, EVP_sha256()); diff --git a/src/src/tod.c b/src/src/tod.c index 0297e375e..8f095aec6 100644 --- a/src/src/tod.c +++ b/src/src/tod.c @@ -60,7 +60,7 @@ if (type == tod_epoch_l) struct timeval tv; gettimeofday(&tv, NULL); /* Unix epoch/usec format */ - (void) sprintf(CS timebuf, "%ld%06ld", tv.tv_sec, (long) tv.tv_usec ); + (void) sprintf(CS timebuf, TIME_T_FMT "%06ld", tv.tv_sec, (long) tv.tv_usec ); return timebuf; } diff --git a/src/src/transport.c b/src/src/transport.c index 58fc4ec93..13f3c07fc 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* General functions concerned with transportation, and generic options for all @@ -66,7 +66,7 @@ optionlist optionlist_transports[] = { (void *)offsetof(transport_instance, driver_name) }, { "envelope_to_add", opt_bool|opt_public, (void *)(offsetof(transport_instance, envelope_to_add)) }, -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT { "event_action", opt_stringptr | opt_public, (void *)offsetof(transport_instance, event_action) }, #endif @@ -84,6 +84,8 @@ optionlist optionlist_transports[] = { (void *)offsetof(transport_instance, home_dir) }, { "initgroups", opt_bool|opt_public, (void *)offsetof(transport_instance, initgroups) }, + { "max_parallel", opt_stringptr|opt_public, + (void *)offsetof(transport_instance, max_parallel) }, { "message_size_limit", opt_stringptr|opt_public, (void *)offsetof(transport_instance, message_size_limit) }, { "rcpt_include_affixes", opt_bool|opt_public, @@ -628,16 +630,16 @@ that means they were rewritten, or are a record of envelope rewriting, or were removed (e.g. Bcc). If remove_headers is not null, skip any headers that match any entries therein. It is a colon-sep list; expand the items separately and squash any empty ones. -Then check addr->p.remove_headers too, provided that addr is not NULL. */ +Then check addr->prop.remove_headers too, provided that addr is not NULL. */ for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) { int i; - uschar *list = remove_headers; + const uschar *list = remove_headers; BOOL include_header = TRUE; - for (i = 0; i < 2; i++) /* For remove_headers && addr->p.remove_headers */ + for (i = 0; i < 2; i++) /* For remove_headers && addr->prop.remove_headers */ { if (list) { @@ -661,7 +663,7 @@ for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) } if (s != NULL) { include_header = FALSE; break; } } - if (addr != NULL) list = addr->p.remove_headers; + if (addr != NULL) list = addr->prop.remove_headers; } /* If this header is to be output, try to rewrite it if there are rewriting @@ -709,7 +711,7 @@ Headers added to an address by a router are guaranteed to end with a newline. if (addr) { int i; - header_line *hprev = addr->p.extra_headers; + header_line *hprev = addr->prop.extra_headers; header_line *hnext; for (i = 0; i < 2; i++) { @@ -740,7 +742,7 @@ if (add_headers) int sep = '\n'; uschar * s; - while ((s = string_nextinlist(&add_headers, &sep, NULL, 0))) + while ((s = string_nextinlist(CUSS &add_headers, &sep, NULL, 0))) if (!(s = expand_string(s))) { if (!expand_string_forcedfail) @@ -914,7 +916,7 @@ if ((options & topt_no_headers) == 0) /* Then the message's headers. Don't write any that are flagged as "old"; that means they were rewritten, or are a record of envelope rewriting, or were removed (e.g. Bcc). If remove_headers is not null, skip any headers that - match any entries therein. Then check addr->p.remove_headers too, provided that + match any entries therein. Then check addr->prop.remove_headers too, provided that addr is not NULL. */ if (!transport_headers_send(addr, fd, add_headers, remove_headers, &write_chunk, use_crlf, rewrite_rules, rewrite_existflags)) @@ -1066,7 +1068,7 @@ if (dkim_private_key && dkim_domain && dkim_selector) uschar *dkim_strict_result = expand_string(dkim_strict); if (dkim_strict_result) if ( (strcmpic(dkim_strict,US"1") == 0) || - (strcmpic(dkim_strict,US"true") == 0) ) + (strcmpic(dkim_strict,US"true") == 0) ) { /* Set errno to something halfway meaningful */ save_errno = EACCES; @@ -1211,7 +1213,10 @@ transport_filter_timed_out = FALSE; /* If there is no filter command set up, call the internal function that does the actual work, passing it the incoming fd, and return its result. */ -if (transport_filter_argv == NULL) +if ( !transport_filter_argv + || !*transport_filter_argv + || !**transport_filter_argv + ) return internal_transport_write_message(addr, fd, options, size_limit, add_headers, remove_headers, check_string, escape_string, rewrite_rules, rewrite_existflags); @@ -1245,8 +1250,8 @@ yield = FALSE; write_pid = (pid_t)(-1); (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); -filter_pid = child_open(transport_filter_argv, NULL, 077, &fd_write, &fd_read, - FALSE); +filter_pid = child_open(USS transport_filter_argv, NULL, 077, + &fd_write, &fd_read, FALSE); (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC); if (filter_pid < 0) goto TIDY_UP; /* errno set */ @@ -1484,7 +1489,7 @@ void transport_update_waiting(host_item *hostlist, uschar *tpname) { uschar buffer[256]; -uschar *prevname = US""; +const uschar *prevname = US""; host_item *host; open_db dbblock; open_db *dbm_file; @@ -1625,20 +1630,39 @@ Arguments: as set by the caller transport new_message_id set to the message id of a waiting message more set TRUE if there are yet more messages waiting + oicf_func function to call to validate if it is ok to send + to this message_id from the current instance. + oicf_data opaque data for oicf_func Returns: TRUE if new_message_id set; FALSE otherwise */ +typedef struct msgq_s +{ + uschar message_id [MESSAGE_ID_LENGTH + 1]; + BOOL bKeep; +} msgq_t; + BOOL -transport_check_waiting(uschar *transport_name, uschar *hostname, - int local_message_max, uschar *new_message_id, BOOL *more) +transport_check_waiting(const uschar *transport_name, const uschar *hostname, + int local_message_max, uschar *new_message_id, BOOL *more, oicf oicf_func, void *oicf_data) { dbdata_wait *host_record; -int host_length, path_len; +int host_length; open_db dbblock; open_db *dbm_file; uschar buffer[256]; +msgq_t *msgq = NULL; +int msgq_count = 0; +int msgq_actual = 0; +int i; +BOOL bFound = FALSE; +uschar spool_dir [PATH_MAX]; +uschar spool_file [PATH_MAX]; +struct stat statbuf; +BOOL bContinuation = FALSE; + *more = FALSE; DEBUG(D_transport) @@ -1691,58 +1715,107 @@ until one is found for which a spool file actually exists. If the record gets emptied, delete it and continue with any continuation records that may exist. */ -host_length = host_record->count * MESSAGE_ID_LENGTH; +/* For Bug 1141, I refactored this major portion of the routine, it is risky +but the 1 off will remain without it. This code now allows me to SKIP over +a message I do not want to send out on this run. */ -/* Loop to handle continuation host records in the database */ +sprintf(CS spool_dir, "%s/input/", spool_directory); -for (;;) +host_length = host_record->count * MESSAGE_ID_LENGTH; + +while (1) { - BOOL found = FALSE; + /* create an array to read entire message queue into memory for processing */ - sprintf(CS buffer, "%s/input/", spool_directory); - path_len = Ustrlen(buffer); + msgq = (msgq_t*) malloc(sizeof(msgq_t) * host_record->count); + msgq_count = host_record->count; + msgq_actual = msgq_count; - for (host_length -= MESSAGE_ID_LENGTH; host_length >= 0; - host_length -= MESSAGE_ID_LENGTH) + for (i = 0; i < host_record->count; ++i) { - struct stat statbuf; - Ustrncpy(new_message_id, host_record->text + host_length, + msgq[i].bKeep = TRUE; + + Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH); - new_message_id[MESSAGE_ID_LENGTH] = 0; + msgq[i].message_id[MESSAGE_ID_LENGTH] = 0; + } + + /* first thing remove current message id if it exists */ + + for (i = 0; i < msgq_count; ++i) + if (Ustrcmp(msgq[i].message_id, message_id) == 0) + { + msgq[i].bKeep = FALSE; + break; + } + /* now find the next acceptable message_id */ + + bFound = FALSE; + + for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep) + { if (split_spool_directory) - sprintf(CS(buffer + path_len), "%c/%s-D", new_message_id[5], new_message_id); + sprintf(CS spool_file, "%s%c/%s-D", + spool_dir, msgq[i].message_id[5], msgq[i].message_id); else - sprintf(CS(buffer + path_len), "%s-D", new_message_id); - - /* The listed message may be the one we are currently processing. If - so, we want to remove it from the list without doing anything else. - If not, do a stat to see if it is an existing message. If it is, break - the loop to handle it. No need to bother about locks; as this is all - "hint" processing, it won't matter if it doesn't exist by the time exim - actually tries to deliver it. */ + sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id); - if (Ustrcmp(new_message_id, message_id) != 0 && - Ustat(buffer, &statbuf) == 0) + if (Ustat(spool_file, &statbuf) != 0) + msgq[i].bKeep = FALSE; + else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data)) { - found = TRUE; + Ustrcpy(new_message_id, msgq[i].message_id); + msgq[i].bKeep = FALSE; + bFound = TRUE; break; } } - /* If we have removed all the message ids from the record delete the record. - If there is a continuation record, fetch it and remove it from the file, - as it will be rewritten as the main record. Repeat in the case of an - empty continuation. */ + /* re-count */ + for (msgq_actual = 0, i = 0; i < msgq_count; ++i) + if (msgq[i].bKeep) + msgq_actual++; + + /* reassemble the host record, based on removed message ids, from in + * memory queue. + */ + + if (msgq_actual <= 0) + { + host_length = 0; + host_record->count = 0; + } + else + { + host_length = msgq_actual * MESSAGE_ID_LENGTH; + host_record->count = msgq_actual; + + if (msgq_actual < msgq_count) + { + int new_count; + for (new_count = 0, i = 0; i < msgq_count; ++i) + if (msgq[i].bKeep) + Ustrncpy(&host_record->text[new_count++ * MESSAGE_ID_LENGTH], + msgq[i].message_id, MESSAGE_ID_LENGTH); + + host_record->text[new_count * MESSAGE_ID_LENGTH] = 0; + } + } + +/* Jeremy: check for a continuation record, this code I do not know how to +test but the code should work */ + + bContinuation = FALSE; while (host_length <= 0) { int i; - dbdata_wait *newr = NULL; + dbdata_wait * newr = NULL; /* Search for a continuation */ - for (i = host_record->sequence - 1; i >= 0 && newr == NULL; i--) + for (i = host_record->sequence - 1; i >= 0 && !newr; i--) { sprintf(CS buffer, "%.200s:%d", hostname, i); newr = dbfn_read(dbm_file, buffer); @@ -1750,7 +1823,7 @@ for (;;) /* If no continuation, delete the current and break the loop */ - if (newr == NULL) + if (!newr) { dbfn_delete(dbm_file, hostname); break; @@ -1761,11 +1834,12 @@ for (;;) dbfn_delete(dbm_file, buffer); host_record = newr; host_length = host_record->count * MESSAGE_ID_LENGTH; - } - /* If we found an existing message, break the continuation loop. */ + bContinuation = TRUE; + } - if (found) break; + if (bFound) + break; /* If host_length <= 0 we have emptied a record and not found a good message, and there are no continuation records. Otherwise there is a continuation @@ -1777,6 +1851,26 @@ for (;;) DEBUG(D_transport) debug_printf("waiting messages already delivered\n"); return FALSE; } + + /* we were not able to find an acceptable message, nor was there a + * continuation record. So bug out, outer logic will clean this up. + */ + + if (!bContinuation) + { + Ustrcpy (new_message_id, message_id); + dbfn_close(dbm_file); + return FALSE; + } + } /* we need to process a continuation record */ + +/* clean up in memory queue */ +if (msgq) + { + free (msgq); + msgq = NULL; + msgq_count = 0; + msgq_actual = 0; } /* Control gets here when an existing message has been encountered; its @@ -1786,7 +1880,19 @@ record if required, close the database, and return TRUE. */ if (host_length > 0) { + uschar msg [MESSAGE_ID_LENGTH + 1]; + int i; + host_record->count = host_length/MESSAGE_ID_LENGTH; + + /* rebuild the host_record->text */ + + for (i = 0; i < host_record->count; ++i) + { + Ustrncpy(msg, host_record->text + (i*MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH); + msg[MESSAGE_ID_LENGTH] = 0; + } + dbfn_write(dbm_file, hostname, host_record, (int)sizeof(dbdata_wait) + host_length); *more = TRUE; } @@ -1795,8 +1901,6 @@ dbfn_close(dbm_file); return TRUE; } - - /************************************************* * Deliver waiting message down same socket * *************************************************/ @@ -1816,8 +1920,8 @@ Returns: FALSE if fork fails; TRUE otherwise */ BOOL -transport_pass_socket(uschar *transport_name, uschar *hostname, - uschar *hostaddress, uschar *id, int socket_fd) +transport_pass_socket(const uschar *transport_name, const uschar *hostname, + const uschar *hostaddress, uschar *id, int socket_fd) { pid_t pid; int status; @@ -1827,7 +1931,7 @@ DEBUG(D_transport) debug_printf("transport_pass_socket entered\n"); if ((pid = fork()) == 0) { int i = 16; - uschar **argv; + const uschar **argv; /* Disconnect entirely from the parent process. If we are running in the test harness, wait for a bit to allow the previous process time to finish, @@ -1840,7 +1944,7 @@ if ((pid = fork()) == 0) /* Set up the calling arguments; use the standard function for the basics, but we have a number of extras that may be added. */ - argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); + argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); /* Call with the dsn flag */ if (smtp_use_dsn) argv[i++] = US"-MCD"; @@ -1862,9 +1966,9 @@ if ((pid = fork()) == 0) } argv[i++] = US"-MC"; - argv[i++] = transport_name; - argv[i++] = hostname; - argv[i++] = hostaddress; + argv[i++] = US transport_name; + argv[i++] = US hostname; + argv[i++] = US hostaddress; argv[i++] = string_sprintf("%d", continue_sequence + 1); argv[i++] = id; argv[i++] = NULL; @@ -1918,7 +2022,7 @@ case, no addresses are passed. Arguments: argvptr pointer to anchor for argv vector - cmd points to the command string + cmd points to the command string (modified IN PLACE) expand_arguments true if expansion is to occur expand_failed error value to set if expansion fails; not relevant if addr == NULL @@ -1932,11 +2036,12 @@ Returns: TRUE if all went well; otherwise an error will be */ BOOL -transport_set_up_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_failed, address_item *addr, uschar *etext, uschar **errptr) +transport_set_up_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_failed, address_item *addr, + uschar *etext, uschar **errptr) { address_item *ad; -uschar **argv; +const uschar **argv; uschar *s, *ss; int address_count = 0; int argcount = 0; @@ -1970,7 +2075,7 @@ while (*s != 0 && argcount < max_args) if (*s != 0) s++; *ss++ = 0; } - else argv[argcount++] = string_dequote(&s); + else argv[argcount++] = string_copy(string_dequote(CUSS &s)); while (isspace(*s)) s++; } @@ -2099,7 +2204,8 @@ if (expand_arguments) if (*s != 0) s++; *ss++ = 0; } - else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s); + else address_pipe_argv[address_pipe_argcount++] = + string_copy(string_dequote(CUSS &s)); while (isspace(*s)) s++; /* strip space after arg */ } @@ -2167,9 +2273,9 @@ if (expand_arguments) else { - uschar *expanded_arg; + const uschar *expanded_arg; enable_dollar_recipients = allow_dollar_recipients; - expanded_arg = expand_string(argv[i]); + expanded_arg = expand_cstring(argv[i]); enable_dollar_recipients = FALSE; if (expanded_arg == NULL) diff --git a/src/src/transports/Makefile b/src/src/transports/Makefile index 02cf0c8d7..25973d5df 100644 --- a/src/src/transports/Makefile +++ b/src/src/transports/Makefile @@ -2,7 +2,7 @@ # calling it transports.a. This is called from the main make file, after cd'ing # to the transports subdirectory. -OBJ = appendfile.o autoreply.o lmtp.o pipe.o smtp.o tf_maildir.o +OBJ = appendfile.o autoreply.o lmtp.o pipe.o smtp.o smtp_socks.o tf_maildir.o transports.a: $(OBJ) @$(RM_COMMAND) -f transports.a @@ -19,6 +19,7 @@ autoreply.o: $(HDRS) autoreply.c autoreply.h lmtp.o: $(HDRS) lmtp.c lmtp.h pipe.o: $(HDRS) pipe.c pipe.h smtp.o: $(HDRS) smtp.c smtp.h +smtp_socks.o: $(HDRS) smtp_socks.c smtp.h tf_maildir.o: $(HDRS) tf_maildir.c tf_maildir.h appendfile.h diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index f56862bec..c8abe9bc5 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -186,7 +186,7 @@ appendfile_transport_options_block appendfile_transport_option_defaults = { NULL, /* quota_warn_threshold */ NULL, /* mailbox_size_string */ NULL, /* mailbox_filecount_string */ - NULL, /* expand_maildir_use_size_file */ + NULL, /* expand_maildir_use_size_file */ US"^(?:cur|new|\\..*)$", /* maildir_dir_regex */ NULL, /* maildir_tag */ NULL, /* maildirfolder_create_regex */ @@ -276,7 +276,7 @@ uid = uid; gid = gid; if (ob->expand_maildir_use_size_file) - ob->maildir_use_size_file = expand_check_condition(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); /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size, @@ -664,7 +664,7 @@ Returns: pointer to the required transport, or NULL transport_instance * check_file_format(int cfd, transport_instance *tblock, address_item *addr) { -uschar *format = +const uschar *format = ((appendfile_transport_options_block *)(tblock->options_block))->file_format; uschar data[256]; int len = read(cfd, data, sizeof(data)); @@ -2540,8 +2540,8 @@ else uschar *basename; (void)gettimeofday(&msg_tv, NULL); - basename = string_sprintf("%lu.H%luP%lu.%s", msg_tv.tv_sec, - msg_tv.tv_usec, getpid(), primary_hostname); + basename = string_sprintf(TIME_T_FMT ".H%luP%lu.%s", + msg_tv.tv_sec, msg_tv.tv_usec, getpid(), primary_hostname); filename = dataname = string_sprintf("tmp/%s", basename); newname = string_sprintf("new/%s", basename); diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index a90a6852e..d2aad542a 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -157,12 +157,13 @@ if (ss == NULL) if (type != cke_text) for (t = ss; *t != 0; t++) { int c = *t; + const uschar * sp; if (mac_isprint(c)) continue; if (type == cke_hdr && c == '\n' && (t[1] == ' ' || t[1] == '\t')) continue; - s = string_printing(s); + sp = string_printing(s); addr->transport_return = FAIL; addr->message = string_sprintf("Expansion of \"%s\" in %s transport " - "contains non-printing character %d", s, name, c); + "contains non-printing character %d", sp, name, c); return NULL; } @@ -187,7 +188,7 @@ Returns: nothing */ static void -check_never_mail(uschar **listptr, uschar *never_mail) +check_never_mail(uschar **listptr, const uschar *never_mail) { uschar *s = *listptr; @@ -379,7 +380,7 @@ remove those that match. */ if (ob->never_mail != NULL) { - uschar *never_mail = expand_string(ob->never_mail); + const uschar *never_mail = expand_string(ob->never_mail); if (never_mail == NULL) { diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index 84bbeb939..1f4d7a6bf 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -171,7 +171,7 @@ if (*errno_value == ERRNO_WRITEINCOMPLETE) if (buffer[0] != 0) { - uschar *s = string_printing(buffer); + const uschar *s = string_printing(buffer); *message = string_sprintf("LMTP error after %s: %s", big_buffer, s); *yield = buffer[0]; return TRUE; @@ -460,7 +460,7 @@ BOOL yield = FALSE; address_item *addr; uschar *igquotstr = US""; uschar *sockname = NULL; -uschar **argv; +const uschar **argv; uschar buffer[256]; DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name); @@ -469,13 +469,29 @@ DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name); not both. When a command is specified, call the common function for creating an argument list and expanding the items. */ -if (ob->cmd != NULL) +if (ob->cmd) { DEBUG(D_transport) debug_printf("using command %s\n", ob->cmd); sprintf(CS buffer, "%.50s transport", tblock->name); if (!transport_set_up_command(&argv, ob->cmd, TRUE, PANIC, addrlist, buffer, NULL)) return FALSE; + + /* If the -N option is set, can't do any more. Presume all has gone well. */ + if (dont_deliver) + goto MINUS_N; + +/* As this is a local transport, we are already running with the required +uid/gid and current directory. Request that the new process be a process group +leader, so we can kill it and all its children on an error. */ + + if ((pid = child_open(USS argv, NULL, 0, &fd_in, &fd_out, TRUE)) < 0) + { + addrlist->message = string_sprintf( + "Failed to create child process for %s transport: %s", tblock->name, + strerror(errno)); + return FALSE; + } } /* When a socket is specified, expand the string and create a socket. */ @@ -498,38 +514,11 @@ else ob->skt, tblock->name, strerror(errno)); return FALSE; } - } -/* If the -N option is set, can't do any more. Presume all has gone well. */ + /* If the -N option is set, can't do any more. Presume all has gone well. */ + if (dont_deliver) + goto MINUS_N; -if (dont_deliver) - { - DEBUG(D_transport) - debug_printf("*** delivery by %s transport bypassed by -N option", - tblock->name); - addrlist->transport_return = OK; - return FALSE; - } - -/* As this is a local transport, we are already running with the required -uid/gid and current directory. Request that the new process be a process group -leader, so we can kill it and all its children on an error. */ - -if (ob->cmd != NULL) - { - if ((pid = child_open(argv, NULL, 0, &fd_in, &fd_out, TRUE)) < 0) - { - addrlist->message = string_sprintf( - "Failed to create child process for %s transport: %s", tblock->name, - strerror(errno)); - return FALSE; - } - } - -/* For a socket, try to make the connection */ - -else - { sockun.sun_family = AF_UNIX; sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname); if(connect(fd_out, (struct sockaddr *)(&sockun), sizeof(sockun)) == -1) @@ -541,6 +530,7 @@ else } } + /* Make the output we are going to read into a file. */ out = fdopen(fd_out, "rb"); @@ -664,10 +654,11 @@ if (send_data) if (lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout)) { addr->transport_return = OK; - if ((log_extra_selector & LX_smtp_confirmation) != 0) + if (LOGGING(smtp_confirmation)) { - uschar *s = string_printing(buffer); - addr->message = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* de-const safe here as string_printing known to have alloc'n'copied */ + addr->message = (s == buffer)? (uschar *)string_copy(s) : US s; } } /* If the response has failed badly, use it for all the remaining pending @@ -784,6 +775,14 @@ DEBUG(D_transport) debug_printf("%s transport yields %d\n", tblock->name, yield); return yield; + + +MINUS_N: + DEBUG(D_transport) + debug_printf("*** delivery by %s transport bypassed by -N option", + tblock->name); + addrlist->transport_return = OK; + return FALSE; } /* End of transport/lmtp.c */ diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 3366a6dcf..eaf04d150 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -326,12 +326,12 @@ Returns: TRUE if all went well; otherwise an error will be */ static BOOL -set_up_direct_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_fail, address_item *addr, uschar *tname, +set_up_direct_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname, pipe_transport_options_block *ob) { BOOL permitted = FALSE; -uschar **argv; +const uschar **argv; uschar buffer[64]; /* Set up "transport <name>" to be put in any error messages, and then @@ -353,11 +353,11 @@ argv = *argvptr; if (ob->allow_commands != NULL) { int sep = 0; - uschar *s, *p; + const uschar *s; + uschar *p; uschar buffer[256]; - s = expand_string(ob->allow_commands); - if (s == NULL) + if (!(s = expand_string(ob->allow_commands))) { addr->transport_return = DEFER; addr->message = string_sprintf("failed to expand string \"%s\" " @@ -365,10 +365,8 @@ if (ob->allow_commands != NULL) return FALSE; } - while ((p = string_nextinlist(&s, &sep, buffer, sizeof(buffer))) != NULL) - { + while ((p = string_nextinlist(&s, &sep, buffer, sizeof(buffer)))) if (Ustrcmp(p, argv[0]) == 0) { permitted = TRUE; break; } - } } /* If permitted is TRUE it means the command was found in the allowed list, and @@ -407,7 +405,7 @@ if (argv[0][0] != '/') { int sep = 0; uschar *p; - uschar *listptr = ob->path; + const uschar *listptr = ob->path; uschar buffer[1024]; while ((p = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) != NULL) @@ -453,10 +451,10 @@ Returns: TRUE if all went well; otherwise an error will be */ static BOOL -set_up_shell_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_fail, address_item *addr, uschar *tname) +set_up_shell_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname) { -uschar **argv; +const uschar **argv; *argvptr = argv = store_get((4)*sizeof(uschar *)); @@ -551,9 +549,9 @@ pipe_transport_options_block *ob = int timeout = ob->timeout; BOOL written_ok = FALSE; BOOL expand_arguments; -uschar **argv; +const uschar **argv; uschar *envp[50]; -uschar *envlist = ob->environment; +const uschar *envlist = ob->environment; uschar *cmd, *ss; uschar *eol = (ob->use_crlf)? US"\r\n" : US"\n"; @@ -669,9 +667,9 @@ else if (timezone_string != NULL && timezone_string[0] != 0) /* Add any requested items */ -if (envlist != NULL) +if (envlist) { - envlist = expand_string(envlist); + envlist = expand_cstring(envlist); if (envlist == NULL) { addr->transport_return = DEFER; @@ -729,7 +727,7 @@ reading of the output pipe. */ uid/gid and current directory. Request that the new process be a process group leader, so we can kill it and all its children on a timeout. */ -if ((pid = child_open(argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) +if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) { addr->transport_return = DEFER; addr->message = string_sprintf( @@ -1073,16 +1071,14 @@ if ((rc = child_close(pid, timeout)) != 0) else { - uschar *s = ob->temp_errors; + const uschar *s = ob->temp_errors; uschar *p; uschar buffer[64]; int sep = 0; addr->transport_return = FAIL; - while ((p = string_nextinlist(&s,&sep,buffer,sizeof(buffer))) != NULL) - { + while ((p = string_nextinlist(&s,&sep,buffer,sizeof(buffer)))) if (rc == Uatoi(p)) { addr->transport_return = DEFER; break; } - } } /* Ensure the message contains the expanded command and arguments. This diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 3387aed29..135069d0f 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -61,9 +61,9 @@ optionlist smtp_transport_options[] = { { "dns_search_parents", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_search_parents) }, { "dnssec_request_domains", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dnssec_request_domains) }, + (void *)offsetof(smtp_transport_options_block, dnssec.request) }, { "dnssec_require_domains", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dnssec_require_domains) }, + (void *)offsetof(smtp_transport_options_block, dnssec.require) }, { "dscp", opt_stringptr, (void *)offsetof(smtp_transport_options_block, dscp) }, { "fallback_hosts", opt_stringptr, @@ -159,6 +159,10 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, serialize_hosts) }, { "size_addition", opt_int, (void *)offsetof(smtp_transport_options_block, size_addition) } +#ifdef SUPPORT_SOCKS + ,{ "socks_proxy", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, socks_proxy) } +#endif #ifdef SUPPORT_TLS ,{ "tls_certificate", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_certificate) }, @@ -220,7 +224,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { #endif NULL, /* hosts_require_tls */ NULL, /* hosts_avoid_tls */ - US"*", /* hosts_verify_avoid_tls */ + NULL, /* hosts_verify_avoid_tls */ NULL, /* hosts_avoid_pipelining */ NULL, /* hosts_avoid_esmtp */ NULL, /* hosts_nopass_tls */ @@ -237,8 +241,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { FALSE, /* gethostbyname */ TRUE, /* dns_qualify_single */ FALSE, /* dns_search_parents */ - NULL, /* dnssec_request_domains */ - NULL, /* dnssec_require_domains */ + { NULL, NULL }, /* dnssec_domains {request,require} */ TRUE, /* delay_after_cutoff */ FALSE, /* hosts_override */ FALSE, /* hosts_randomize */ @@ -246,6 +249,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { FALSE, /* lmtp_ignore_quota */ NULL, /* expand_retry_include_ip_address */ TRUE /* retry_include_ip_address */ +#ifdef SUPPORT_SOCKS + ,NULL /* socks_proxy */ +#endif #ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ NULL, /* tls_crl */ @@ -434,6 +440,8 @@ Arguments: rc to put in each address's transport_return field pass_message if TRUE, set the "pass message" flag in the address host if set, mark addrs as having used this host + smtp_greeting from peer + helo_response from peer If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate @@ -444,7 +452,11 @@ Returns: nothing static void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc, - BOOL pass_message, host_item * host) + BOOL pass_message, host_item * host +#ifdef EXPERIMENTAL_DSN_INFO + , const uschar * smtp_greeting, const uschar * helo_response +#endif + ) { address_item *addr; int orvalue = 0; @@ -453,7 +465,7 @@ if (errno_value == ERRNO_CONNECTTIMEOUT) errno_value = ETIMEDOUT; orvalue = RTEF_CTOUT; } -for (addr = addrlist; addr != NULL; addr = addr->next) +for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return >= PENDING) { addr->basic_errno = errno_value; @@ -465,10 +477,31 @@ for (addr = addrlist; addr != NULL; addr = addr->next) } addr->transport_return = rc; if (host) + { addr->host_used = host; +#ifdef EXPERIMENTAL_DSN_INFO + if (smtp_greeting) + {uschar * s = Ustrchr(smtp_greeting, '\n'); if (s) *s = '\0';} + addr->smtp_greeting = smtp_greeting; + + if (helo_response) + {uschar * s = Ustrchr(helo_response, '\n'); if (s) *s = '\0';} + addr->helo_response = helo_response; +#endif + } } } +static void +set_errno_nohost(address_item *addrlist, int errno_value, uschar *msg, int rc, + BOOL pass_message) +{ +set_errno(addrlist, errno_value, msg, rc, pass_message, NULL +#ifdef EXPERIMENTAL_DSN_INFO + , NULL, NULL +#endif + ); +} /************************************************* @@ -523,7 +556,7 @@ if (*errno_value == ETIMEDOUT) if (*errno_value == ERRNO_SMTPFORMAT) { - uschar *malfresp = string_printing(buffer); + const uschar *malfresp = string_printing(buffer); while (isspace(*malfresp)) malfresp++; *message = *malfresp == 0 ? string_sprintf("Malformed SMTP reply (an empty line) " @@ -563,11 +596,21 @@ if (*errno_value == ERRNO_WRITEINCOMPLETE) return FALSE; } +#ifdef SUPPORT_I18N +/* Handle lack of advertised SMTPUTF8, for international message */ +if (*errno_value == ERRNO_UTF8_FWD) + { + *message = US string_sprintf("utf8 support required but not offered for forwarding"); + DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message); + return TRUE; + } +#endif + /* Handle error responses from the remote mailer. */ if (buffer[0] != 0) { - uschar *s = string_printing(buffer); + const uschar *s = string_printing(buffer); *message = US string_sprintf("SMTP error from remote mail server after %s%s: " "%s", pl, smtp_command, s); *pass_message = TRUE; @@ -612,6 +655,9 @@ write_logs(address_item *addr, host_item *host) { uschar * message = string_sprintf("H=%s [%s]", host->name, host->address); +if (LOGGING(outgoing_port)) + message = string_sprintf("%s:%d", message, + host->port == PORT_NONE ? 25 : host->port); if (addr->message) { message = string_sprintf("%s: %s", message, addr->message); @@ -622,9 +668,6 @@ if (addr->message) } else { - if (log_extra_selector & LX_outgoing_port) - message = string_sprintf("%s:%d", message, - host->port == PORT_NONE ? 25 : host->port); log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno)); deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message, strerror(addr->basic_errno)); @@ -640,7 +683,7 @@ msglog_line(host_item * host, uschar * message) -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT /************************************************* * Post-defer action * *************************************************/ @@ -659,7 +702,7 @@ static void deferred_event_raise(address_item *addr, host_item *host) { uschar * action = addr->transport->event_action; -uschar * save_domain; +const uschar * save_domain; uschar * save_local; if (!action) @@ -693,8 +736,6 @@ router_name = transport_name = NULL; } #endif - - /************************************************* * Synchronize SMTP responses * *************************************************/ @@ -833,7 +874,7 @@ while (count-- > 0) { uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>", transport_rcpt_address(addr, include_affixes)); - set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ETIMEDOUT, message, DEFER, FALSE); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; return -1; @@ -878,12 +919,22 @@ while (count-- > 0) addr->basic_errno = ERRNO_RCPT4XX; addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; +#ifndef DISABLE_EVENT + event_defer_errno = addr->more_errno; + msg_event_raise(US"msg:rcpt:host:defer", addr); +#endif + /* Log temporary errors if there are more hosts to be tried. If not, log this last one in the == line. */ if (host->next) log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message); +#ifndef DISABLE_EVENT + else + msg_event_raise(US"msg:rcpt:defer", addr); +#endif + /* Do not put this message on the list of those waiting for specific hosts, as otherwise it is likely to be tried too often. */ @@ -1082,8 +1133,8 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) /* Internal problem, message in buffer. */ case ERROR: - set_errno(addrlist, ERRNO_AUTHPROB, string_copy(buffer), - DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer), + DEFER, FALSE); return ERROR; } @@ -1097,9 +1148,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) if (require_auth == OK && !smtp_authenticated) { - set_errno(addrlist, ERRNO_AUTHFAIL, + set_errno_nohost(addrlist, ERRNO_AUTHFAIL, string_sprintf("authentication required but %s", fail_reason), DEFER, - FALSE, NULL); + FALSE); return DEFER; } @@ -1138,7 +1189,7 @@ if (ob->authenticated_sender != NULL) { uschar *message = string_sprintf("failed to expand " "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); return TRUE; } } @@ -1165,12 +1216,12 @@ return FALSE; #ifdef EXPERIMENTAL_DANE int -tlsa_lookup(host_item * host, dns_answer * dnsa, +tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required, BOOL * dane) { /* move this out to host.c given the similarity to dns_lookup() ? */ uschar buffer[300]; -uschar * fullname = buffer; +const uschar * fullname = buffer; /* TLSA lookup string */ (void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name); @@ -1183,10 +1234,7 @@ switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname)) default: case DNS_FAIL: if (dane_required) - { - log_write(0, LOG_MAIN, "DANE error: TLSA lookup failed"); return FAIL; - } break; case DNS_SUCCEED: @@ -1203,6 +1251,85 @@ return OK; #endif + +typedef struct smtp_compare_s +{ + uschar *current_sender_address; + struct transport_instance *tblock; +} smtp_compare_t; + +/* +Create a unique string that identifies this message, it is based on +sender_address, helo_data and tls_certificate if enabled. */ + +static uschar * +smtp_local_identity(uschar * sender, struct transport_instance * tblock) +{ +address_item * addr1; +uschar * if1 = US""; +uschar * helo1 = US""; +#ifdef SUPPORT_TLS +uschar * tlsc1 = US""; +#endif +uschar * save_sender_address = sender_address; +uschar * local_identity = NULL; +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tblock->options_block; + +sender_address = sender; + +addr1 = deliver_make_addr (sender, TRUE); +deliver_set_expansions(addr1); + +if (ob->interface) + if1 = expand_string(ob->interface); + +if (ob->helo_data) + helo1 = expand_string(ob->helo_data); + +#ifdef SUPPORT_TLS +if (ob->tls_certificate) + tlsc1 = expand_string(ob->tls_certificate); +local_identity = string_sprintf ("%s^%s^%s", if1, helo1, tlsc1); +#else +local_identity = string_sprintf ("%s^%s", if1, helo1); +#endif + +deliver_set_expansions(NULL); +sender_address = save_sender_address; + +return local_identity; +} + + + +/* This routine is a callback that is called from transport_check_waiting. +This function will evaluate the incoming message versus the previous +message. If the incoming message is using a different local identity then +we will veto this new message. */ + +static BOOL +smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare) +{ + +uschar * message_local_identity, + * current_local_identity, + * new_sender_address; + +current_local_identity = + smtp_local_identity(s_compare->current_sender_address, s_compare->tblock); + +if (!(new_sender_address = deliver_get_sender_address(message_id))) + return 0; + +message_local_identity = + smtp_local_identity(new_sender_address, s_compare->tblock); + +return Ustrcmp(current_local_identity, message_local_identity) == 0; +} + + + /************************************************* * Deliver address list to given host * *************************************************/ @@ -1229,8 +1356,6 @@ Arguments: port default TCP/IP port to use, in host byte order interface interface to bind to, or NULL tblock transport instance block - copy_host TRUE if host set in addr->host_used must be copied, because - it is specific to this call of the transport message_defer set TRUE if yield is OK, but all addresses were deferred because of a non-recipient, non-host failure, that is, a 4xx response to MAIL FROM, DATA, or ".". This is a defer @@ -1251,7 +1376,7 @@ Returns: OK - the connection was made and the delivery attempted; static int smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port, - uschar *interface, transport_instance *tblock, BOOL copy_host, + uschar *interface, transport_instance *tblock, BOOL *message_defer, BOOL suppress_tls) { address_item *addr; @@ -1278,16 +1403,27 @@ BOOL pass_message = FALSE; BOOL prdr_offered = FALSE; BOOL prdr_active; #endif +#ifdef SUPPORT_I18N +BOOL utf8_needed = FALSE; +BOOL utf8_offered = FALSE; +#endif BOOL dsn_all_lasthop = TRUE; #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) BOOL dane = FALSE; +BOOL dane_required = verify_check_given_host(&ob->hosts_require_dane, host) == OK; dns_answer tlsa_dnsa; #endif smtp_inblock inblock; smtp_outblock outblock; int max_rcpt = tblock->max_addresses; uschar *igquotstr = US""; + +#ifdef EXPERIMENTAL_DSN_INFO +uschar *smtp_greeting = NULL; +uschar *helo_response = NULL; +#endif uschar *helo_data = NULL; + uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; uschar *p; @@ -1337,8 +1473,8 @@ tls_modify_variables(&tls_out); #ifndef SUPPORT_TLS if (smtps) { - set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", - DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", + DEFER, FALSE); return ERROR; } #endif @@ -1351,41 +1487,42 @@ if (continue_hostname == NULL) { /* This puts port into host->port */ inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, ob->connect_timeout, - ob->keepalive, ob->dscp -#ifdef EXPERIMENTAL_EVENT - , tblock->event_action -#endif - ); + smtp_connect(host, host_af, port, interface, ob->connect_timeout, tblock); if (inblock.sock < 0) { - set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, - NULL, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, + NULL, DEFER, FALSE); return DEFER; } #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) { - BOOL dane_required; - tls_out.dane_verified = FALSE; tls_out.tlsa_usage = 0; - dane_required = verify_check_given_host(&ob->hosts_require_dane, host) == OK; - if (host->dnssec == DS_YES) { - if( dane_required - || verify_check_given_host(&ob->hosts_try_dane, host) == OK + if( ( dane_required + || verify_check_given_host(&ob->hosts_try_dane, host) == OK + ) + && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK + && dane_required /* do not error on only dane-requested */ ) - if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK) - return rc; + { + set_errno_nohost(addrlist, ERRNO_DNSDEFER, + string_sprintf("DANE error: tlsa lookup %s", + rc == DEFER ? "DEFER" : "FAIL"), + rc, FALSE); + return rc; + } } else if (dane_required) { - log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name); - return FAIL; + set_errno_nohost(addrlist, ERRNO_DNSDEFER, + string_sprintf("DANE error: %s lookup not DNSSEC", host->name), + FAIL, FALSE); + return FAIL; } if (dane) @@ -1398,6 +1535,19 @@ if (continue_hostname == NULL) delayed till here so that $sending_interface and $sending_port are set. */ helo_data = expand_string(ob->helo_data); +#ifdef SUPPORT_I18N + if (helo_data) + { + uschar * errstr = NULL; + if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr) + { + errstr = string_sprintf("failed to expand helo_data: %s", errstr); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); + yield = DEFER; + goto SEND_QUIT; + } + } +#endif /* The first thing is to wait for an initial OK response. The dreaded "goto" is nevertheless a reasonably clean way of programming this kind of logic, @@ -1405,10 +1555,14 @@ if (continue_hostname == NULL) if (!smtps) { - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + BOOL good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + smtp_greeting = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT { uschar * s; lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes" @@ -1416,9 +1570,9 @@ if (continue_hostname == NULL) s = event_raise(tblock->event_action, US"smtp:connect", buffer); if (s) { - set_errno(addrlist, ERRNO_EXPANDFAIL, + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, string_sprintf("deferred by smtp:connect event expansion: %s", s), - DEFER, FALSE, NULL); + DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1432,7 +1586,7 @@ if (continue_hostname == NULL) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1497,9 +1651,18 @@ goto SEND_QUIT; if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) { - if (errno != 0 || buffer[0] == 0 || lmtp) goto RESPONSE_FAILED; + if (errno != 0 || buffer[0] == 0 || lmtp) + { +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + goto RESPONSE_FAILED; + } esmtp = FALSE; } +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif } else { @@ -1509,10 +1672,16 @@ goto SEND_QUIT; if (!esmtp) { + BOOL good_response; + if (smtp_write_command(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0) goto SEND_FAILED; - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } /* Set IGNOREQUOTA if the response to LHLO specifies support and the @@ -1539,6 +1708,20 @@ goto SEND_QUIT; if (prdr_offered) {DEBUG(D_transport) debug_printf("PRDR usable\n");} #endif + +#ifdef SUPPORT_I18N + if (addrlist->prop.utf8_msg) + { + utf8_needed = !addrlist->prop.utf8_downcvt + && !addrlist->prop.utf8_downcvt_maybe; + DEBUG(D_transport) if (!utf8_needed) debug_printf("utf8: %s downconvert\n", + addrlist->prop.utf8_downcvt ? "mandatory" : "optional"); + + utf8_offered = esmtp + && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; + } +#endif } /* For continuing deliveries down the same channel, the socket is the standard @@ -1548,6 +1731,11 @@ error messages. Note that smtp_use_size and smtp_use_pipelining will have been set from the command line if they were set in the process that passed the connection on. */ +/*XXX continue case needs to propagate DSN_INFO, prob. in deliver.c +as the contine goes via transport_pass_socket() and doublefork and exec. +It does not wait. Unclear how we keep separate host's responses +separate - we could match up by host ip+port as a bodge. */ + else { inblock.sock = outblock.sock = fileno(stdin); @@ -1607,6 +1795,17 @@ if ( tls_offered if (rc != OK) { +# ifdef EXPERIMENTAL_DANE + if (rc == DEFER && dane && !dane_required) + { + log_write(0, LOG_MAIN, "DANE attempt failed;" + " trying CA-root TLS to %s [%s] (not in hosts_require_dane)", + host->name, host->address); + dane = FALSE; + goto TLS_NEGOTIATE; + } +# endif + save_errno = ERRNO_TLSFAILURE; message = US"failure while setting up TLS session"; send_quit = FALSE; @@ -1615,7 +1814,7 @@ if ( tls_offered /* TLS session is set up */ - for (addr = addrlist; addr != NULL; addr = addr->next) + for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return == PENDING_DEFER) { addr->cipher = tls_out.cipher; @@ -1640,6 +1839,8 @@ start of the Exim process (in exim.c). */ if (tls_out.active >= 0) { char *greeting_cmd; + BOOL good_response; + if (helo_data == NULL) { helo_data = expand_string(ob->helo_data); @@ -1647,7 +1848,7 @@ if (tls_out.active >= 0) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1656,8 +1857,12 @@ if (tls_out.active >= 0) /* For SMTPS we need to wait for the initial OK response. */ if (smtps) { - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + smtp_greeting = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } if (esmtp) @@ -1672,9 +1877,12 @@ if (tls_out.active >= 0) if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : greeting_cmd, helo_data) < 0) goto SEND_FAILED; - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) - goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } /* If the host is required to use a secure channel, ensure that we @@ -1735,16 +1943,24 @@ if (continue_hostname == NULL #ifndef DISABLE_PRDR prdr_offered = esmtp && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0, - PCRE_EOPT, NULL, 0) >= 0 + PCRE_EOPT, NULL, 0) >= 0 && verify_check_given_host(&ob->hosts_try_prdr, host) == OK; if (prdr_offered) {DEBUG(D_transport) debug_printf("PRDR usable\n");} #endif +#ifdef SUPPORT_I18N + if (addrlist->prop.utf8_msg) + utf8_offered = esmtp + && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; +#endif + /* Note if the server supports DSN */ - smtp_use_dsn = esmtp && pcre_exec(regex_DSN, NULL, CS buffer, (int)Ustrlen(CS buffer), 0, - PCRE_EOPT, NULL, 0) >= 0; + smtp_use_dsn = esmtp + && pcre_exec(regex_DSN, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn); /* Note if the response to EHLO specifies support for the AUTH extension. @@ -1767,6 +1983,15 @@ message-specific. */ setting_up = FALSE; +#ifdef SUPPORT_I18N +/* If this is an international message we need the host to speak SMTPUTF8 */ +if (utf8_needed && !utf8_offered) + { + errno = ERRNO_UTF8_FWD; + goto RESPONSE_FAILED; + } +#endif + /* If there is a filter command specified for this transport, we can now set it up. This cannot be done until the identify of the host is known. */ @@ -1784,8 +2009,8 @@ if (tblock->filter_command != NULL) if (!rc) { - set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, - FALSE, NULL); + set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, + FALSE); yield = ERROR; goto SEND_QUIT; } @@ -1843,18 +2068,25 @@ if (prdr_offered) } #endif +#ifdef SUPPORT_I18N +if (addrlist->prop.utf8_msg && !addrlist->prop.utf8_downcvt && utf8_offered) + sprintf(CS p, " SMTPUTF8"), p += 9; +#endif + /* check if all addresses have lasthop flag */ /* do not send RET and ENVID if true */ -dsn_all_lasthop = TRUE; -for (addr = first_addr; +for (dsn_all_lasthop = TRUE, addr = first_addr; address_count < max_rcpt && addr != NULL; addr = addr->next) if ((addr->dsn_flags & rf_dsnlasthop) != 1) + { dsn_all_lasthop = FALSE; + break; + } /* Add any DSN flags to the mail command */ -if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE)) +if (smtp_use_dsn && !dsn_all_lasthop) { if (dsn_ret == dsn_ret_hdrs) { @@ -1894,28 +2126,51 @@ buffer. */ pending_MAIL = TRUE; /* The block starts with MAIL */ -rc = smtp_write_command(&outblock, smtp_use_pipelining, - "MAIL FROM:<%s>%s\r\n", return_path, buffer); + { + uschar * s = return_path; +#ifdef SUPPORT_I18N + uschar * errstr = NULL; + + /* If we must downconvert, do the from-address here. Remember we had to + for the to-addresses (done below), and also (ugly) for re-doing when building + the delivery log line. */ + + if (addrlist->prop.utf8_msg && (addrlist->prop.utf8_downcvt || !utf8_offered)) + { + if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr) + { + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); + yield = ERROR; + goto SEND_QUIT; + } + setflag(addrlist, af_utf8_downcvt); + } +#endif + + rc = smtp_write_command(&outblock, smtp_use_pipelining, + "MAIL FROM:<%s>%s\r\n", s, buffer); + } + mail_command = string_copy(big_buffer); /* Save for later error message */ switch(rc) { case -1: /* Transmission error */ - goto SEND_FAILED; + goto SEND_FAILED; case +1: /* Block was sent */ - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) - { - if (errno == 0 && buffer[0] == '4') { - errno = ERRNO_MAIL4XX; - addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + if (errno == 0 && buffer[0] == '4') + { + errno = ERRNO_MAIL4XX; + addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + } + goto RESPONSE_FAILED; } - goto RESPONSE_FAILED; - } - pending_MAIL = FALSE; - break; + pending_MAIL = FALSE; + break; } /* Pass over all the relevant recipient addresses for this host, which are the @@ -1937,6 +2192,7 @@ for (addr = first_addr; { int count; BOOL no_flush; + uschar * rcpt_addr; addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no; @@ -1981,8 +2237,24 @@ for (addr = first_addr; yield as OK, because this error can often mean that there is a problem with just one address, so we don't want to delay the host. */ + rcpt_addr = transport_rcpt_address(addr, tblock->rcpt_include_affixes); + +#ifdef SUPPORT_I18N + { + uschar * dummy_errstr; + if ( testflag(addrlist, af_utf8_downcvt) + && (rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, &dummy_errstr), + dummy_errstr + ) ) + { + errno = ERRNO_EXPANDFAIL; + goto SEND_FAILED; + } + } +#endif + count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", - transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer); + rcpt_addr, igquotstr, buffer); if (count < 0) goto SEND_FAILED; if (count > 0) @@ -2019,8 +2291,8 @@ if (mua_wrapper) if (badaddr->transport_return != PENDING_OK) { /*XXX could we find a better errno than 0 here? */ - set_errno(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message), NULL); + set_errno_nohost(addrlist, 0, badaddr->message, FAIL, + testflag(badaddr, af_pass_message)); ok = FALSE; break; } @@ -2180,33 +2452,21 @@ if (!ok) ok = TRUE; else int flag = '='; int delivery_time = (int)(time(NULL) - start_delivery_time); int len; - host_item *thost; uschar *conf = NULL; send_rset = FALSE; - /* Make a copy of the host if it is local to this invocation - of the transport. */ - - if (copy_host) - { - thost = store_get(sizeof(host_item)); - *thost = *host; - thost->name = string_copy(host->name); - thost->address = string_copy(host->address); - } - else thost = host; - /* Set up confirmation if needed - applies only to SMTP */ if ( -#ifndef EXPERIMENTAL_EVENT - (log_extra_selector & LX_smtp_confirmation) != 0 && +#ifdef DISABLE_EVENT + LOGGING(smtp_confirmation) && #endif !lmtp ) { - uschar *s = string_printing(buffer); - conf = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ + conf = (s == buffer)? (uschar *)string_copy(s) : US s; } /* Process all transported addresses - for LMTP or PRDR, read a status for @@ -2254,10 +2514,11 @@ if (!ok) ok = TRUE; else continue; } completed_address = TRUE; /* NOW we can set this flag */ - if ((log_extra_selector & LX_smtp_confirmation) != 0) + if (LOGGING(smtp_confirmation)) { - uschar *s = string_printing(buffer); - conf = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ + conf = (s == buffer)? (uschar *)string_copy(s) : US s; } } @@ -2266,7 +2527,7 @@ if (!ok) ok = TRUE; else addr->transport_return = OK; addr->more_errno = delivery_time; - addr->host_used = thost; + addr->host_used = host; addr->special_action = flag; addr->message = conf; #ifndef DISABLE_PRDR @@ -2288,7 +2549,7 @@ if (!ok) ok = TRUE; else else sprintf(CS buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling %s", buffer); + DEBUG(D_deliver) debug_printf("journalling %s\n", buffer); len = Ustrlen(CS buffer); if (write(journal_fd, buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -2325,7 +2586,7 @@ if (!ok) ok = TRUE; else else sprintf(CS buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling(PRDR) %s", buffer); + DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", buffer); len = Ustrlen(CS buffer); if (write(journal_fd, buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -2355,22 +2616,27 @@ the problem is not related to this specific message. */ if (!ok) { - int code; + int code, set_rc; + uschar * set_message; RESPONSE_FAILED: - save_errno = errno; - message = NULL; - send_quit = check_response(host, &save_errno, addrlist->more_errno, - buffer, &code, &message, &pass_message); - goto FAILED; + { + save_errno = errno; + message = NULL; + send_quit = check_response(host, &save_errno, addrlist->more_errno, + buffer, &code, &message, &pass_message); + goto FAILED; + } SEND_FAILED: - save_errno = errno; - code = '4'; - message = US string_sprintf("send() to %s [%s] failed: %s", - host->name, host->address, strerror(save_errno)); - send_quit = FALSE; - goto FAILED; + { + save_errno = errno; + code = '4'; + message = US string_sprintf("send() to %s [%s] failed: %s", + host->name, host->address, strerror(save_errno)); + send_quit = FALSE; + goto FAILED; + } /* This label is jumped to directly when a TLS negotiation has failed, or was not done for a host for which it is required. Values will be set @@ -2391,16 +2657,14 @@ if (!ok) FAILED: ok = FALSE; /* For when reached by GOTO */ + set_message = message; if (setting_up) { if (code == '5') - set_errno(addrlist, save_errno, message, FAIL, pass_message, host); + set_rc = FAIL; else - { - set_errno(addrlist, save_errno, message, DEFER, pass_message, host); - yield = DEFER; - } + yield = set_rc = DEFER; } /* We want to handle timeouts after MAIL or "." and loss of connection after @@ -2415,24 +2679,29 @@ if (!ok) switch(save_errno) { +#ifdef SUPPORT_I18N + case ERRNO_UTF8_FWD: + code = '5'; + /*FALLTHROUGH*/ +#endif case 0: case ERRNO_MAIL4XX: case ERRNO_DATA4XX: - message_error = TRUE; - break; + message_error = TRUE; + break; case ETIMEDOUT: - message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 || - Ustrncmp(smtp_command,"end ",4) == 0; - break; + message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 || + Ustrncmp(smtp_command,"end ",4) == 0; + break; case ERRNO_SMTPCLOSED: - message_error = Ustrncmp(smtp_command,"end ",4) == 0; - break; + message_error = Ustrncmp(smtp_command,"end ",4) == 0; + break; default: - message_error = FALSE; - break; + message_error = FALSE; + break; } /* Handle the cases that are treated as message errors. These are: @@ -2440,6 +2709,7 @@ if (!ok) (a) negative response or timeout after MAIL (b) negative response after DATA (c) negative response or timeout or dropped connection after "." + (d) utf8 support required and not offered It won't be a negative response or timeout after RCPT, as that is dealt with separately above. The action in all cases is to set an appropriate @@ -2453,14 +2723,15 @@ if (!ok) if (message_error) { if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */ - set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER, - pass_message, host); /* If there's an errno, the message contains just the identity of the host. */ - if (code != '5') /* Anything other than 5 is treated as temporary */ + if (code == '5') + set_rc = FAIL; + else /* Anything other than 5 is treated as temporary */ { + set_rc = DEFER; if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message); @@ -2477,11 +2748,17 @@ if (!ok) else { + set_rc = DEFER; yield = (save_errno == ERRNO_CHHEADER_FAIL || save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER; - set_errno(addrlist, save_errno, message, DEFER, pass_message, host); } } + + set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host +#ifdef EXPERIMENTAL_DSN_INFO + , smtp_greeting, helo_response +#endif + ); } @@ -2521,6 +2798,11 @@ DEBUG(D_transport) if (completed_address && ok && send_quit) { BOOL more; + smtp_compare_t t_compare; + + t_compare.tblock = tblock; + t_compare.current_sender_address = sender_address; + if ( first_addr != NULL || continue_more || ( ( tls_out.active < 0 @@ -2528,7 +2810,8 @@ if (completed_address && ok && send_quit) ) && transport_check_waiting(tblock->name, host->name, - tblock->connection_max_messages, new_message_id, &more) + tblock->connection_max_messages, new_message_id, &more, + (oicf)smtp_are_same_identities, (void*)&t_compare) ) ) { uschar *msg; @@ -2588,6 +2871,9 @@ if (completed_address && ok && send_quit) /* If the socket is successfully passed, we musn't send QUIT (or indeed anything!) from here. */ +/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to +propagate it from the initial +*/ if (ok && transport_pass_socket(tblock->name, host->name, host->address, new_message_id, inblock.sock)) { @@ -2597,7 +2883,11 @@ if (completed_address && ok && send_quit) /* If RSET failed and there are addresses left, they get deferred. */ - else set_errno(first_addr, errno, msg, DEFER, FALSE, host); + else set_errno(first_addr, errno, msg, DEFER, FALSE, host +#ifdef EXPERIMENTAL_DSN_INFO + , smtp_greeting, helo_response +#endif + ); } } @@ -2640,7 +2930,7 @@ case continue_more won't get set. */ (void)close(inblock.sock); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT (void) event_raise(tblock->event_action, US"tcp:close", NULL); #endif @@ -2739,6 +3029,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next) addr->peerdn = NULL; addr->ocsp = OCSP_NOT_REQ; #endif +#ifdef EXPERIMENTAL_DSN_INFO + addr->smtp_greeting = NULL; + addr->helo_response = NULL; +#endif } return first_addr; } @@ -2828,8 +3122,7 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL)) if (Ustrchr(s, '$') != NULL) { - expanded_hosts = expand_string(s); - if (expanded_hosts == NULL) + if (!(expanded_hosts = expand_string(s))) { addrlist->message = string_sprintf("failed to expand list of hosts " "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message); @@ -2857,13 +3150,14 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL)) /* If there was no expansion of hosts, save the host list for next time. */ - if (expanded_hosts == NULL) ob->hostlist = hostlist; + if (!expanded_hosts) ob->hostlist = hostlist; } /* This is not the first time this transport has been run in this delivery; the host list was built previously. */ - else hostlist = ob->hostlist; + else + hostlist = ob->hostlist; } /* The host list was supplied with the address. If hosts_randomize is set, we @@ -2907,12 +3201,10 @@ else if (ob->hosts_randomize && hostlist->mx == MX_NONE && !continuing) hostlist = addrlist->host_list = newlist; } - /* Sort out the default port. */ if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE; - /* For each host-plus-IP-address on the list: . If this is a continued delivery and the host isn't the one with the @@ -2977,7 +3269,6 @@ for (cutoff_retry = 0; expired && BOOL serialized = FALSE; BOOL host_is_expired = FALSE; BOOL message_defer = FALSE; - BOOL ifchanges = FALSE; BOOL some_deferred = FALSE; address_item *first_addr = NULL; uschar *interface = NULL; @@ -3009,7 +3300,6 @@ for (cutoff_retry = 0; expired && { int new_port, flags; host_item *hh; - uschar *canonical_name; if (host->status >= hstatus_unusable) { @@ -3037,11 +3327,11 @@ for (cutoff_retry = 0; expired && if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS; if (ob->gethostbyname || string_is_ip_address(host->name, NULL) != 0) - rc = host_find_byname(host, NULL, flags, &canonical_name, TRUE); + rc = host_find_byname(host, NULL, flags, NULL, TRUE); else rc = host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - ob->dnssec_request_domains, ob->dnssec_require_domains, - &canonical_name, NULL); + &ob->dnssec, /* domains for request/require */ + NULL, NULL); /* Update the host (and any additional blocks, resulting from multihoming) with a host-specific port, if any. */ @@ -3117,7 +3407,8 @@ for (cutoff_retry = 0; expired && doing a two-stage queue run, don't do this if forcing. */ if ((!deliver_force || queue_2stage) && (queue_smtp || - match_isinlist(addrlist->domain, &queue_smtp_domains, 0, + match_isinlist(addrlist->domain, + (const uschar **)&queue_smtp_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)) { expired = FALSE; @@ -3153,15 +3444,18 @@ for (cutoff_retry = 0; expired && if (Ustrcmp(pistring, ":25") == 0) pistring = US""; /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface - string changes upon expansion, we must add it to the key that is used for - retries, because connections to the same host from a different interface - should be treated separately. */ + string is set, even if constant (as different transports can have different + constant settings), we must add it to the key that is used for retries, + because connections to the same host from a different interface should be + treated separately. */ host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET : AF_INET6; - if (!smtp_get_interface(ob->interface, host_af, addrlist, &ifchanges, - &interface, tid)) - return FALSE; - if (ifchanges) pistring = string_sprintf("%s/%s", pistring, interface); + if ((rs = ob->interface) && *rs) + { + if (!smtp_get_interface(rs, host_af, addrlist, &interface, tid)) + return FALSE; + pistring = string_sprintf("%s/%s", pistring, interface); + } /* The first time round the outer loop, check the status of the host by inspecting the retry data. The second time round, we are interested only @@ -3248,7 +3542,7 @@ for (cutoff_retry = 0; expired && verify_check_given_host(&ob->serialize_hosts, host) == OK) { serialize_key = string_sprintf("host-serialize-%s", host->name); - if (!enq_start(serialize_key)) + if (!enq_start(serialize_key, 1)) { DEBUG(D_transport) debug_printf("skipping host %s because another Exim process " @@ -3282,7 +3576,7 @@ for (cutoff_retry = 0; expired && if (dont_deliver) { host_item *host2; - set_errno(addrlist, 0, NULL, OK, FALSE, NULL); + set_errno_nohost(addrlist, 0, NULL, OK, FALSE); for (addr = addrlist; addr != NULL; addr = addr->next) { addr->host_used = host; @@ -3315,27 +3609,40 @@ for (cutoff_retry = 0; expired && else { + host_item * thost; + /* Make a copy of the host if it is local to this invocation + of the transport. */ + + if (expanded_hosts) + { + thost = store_get(sizeof(host_item)); + *thost = *host; + thost->name = string_copy(host->name); + thost->address = string_copy(host->address); + } + else + thost = host; + if (!host_is_expired && ++unexpired_hosts_tried >= ob->hosts_max_try) { host_item *h; DEBUG(D_transport) debug_printf("hosts_max_try limit reached with this host\n"); - for (h = host; h != NULL; h = h->next) - if (h->mx != host->mx) break; - if (h != NULL) - { - nexthost = h; - unexpired_hosts_tried--; - DEBUG(D_transport) debug_printf("however, a higher MX host exists " - "and will be tried\n"); - } + for (h = host; h; h = h->next) if (h->mx != host->mx) + { + nexthost = h; + unexpired_hosts_tried--; + DEBUG(D_transport) debug_printf("however, a higher MX host exists " + "and will be tried\n"); + break; + } } /* Attempt the delivery. */ total_hosts_tried++; - rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock, - expanded_hosts != NULL, &message_defer, FALSE); + rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + &message_defer, FALSE); /* Yield is one of: OK => connection made, each address contains its result; @@ -3356,7 +3663,7 @@ for (cutoff_retry = 0; expired && first_addr->basic_errno != ERRNO_TLSFAILURE) write_logs(first_addr, host); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (rc == DEFER) deferred_event_raise(first_addr, host); #endif @@ -3380,11 +3687,11 @@ for (cutoff_retry = 0; expired && log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " "to %s [%s] (not in hosts_require_tls)", host->name, host->address); first_addr = prepare_addresses(addrlist, host); - rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock, - expanded_hosts != NULL, &message_defer, TRUE); + rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + &message_defer, TRUE); if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL) write_logs(first_addr, host); -# ifdef EXPERIMENTAL_EVENT +# ifndef DISABLE_EVENT if (rc == DEFER) deferred_event_raise(first_addr, host); # endif @@ -3487,16 +3794,12 @@ for (cutoff_retry = 0; expired && case, see if any of them are deferred. */ if (rc == OK) - { - for (addr = addrlist; addr != NULL; addr = addr->next) - { + for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return == DEFER) { some_deferred = TRUE; break; } - } - } /* If no addresses deferred or the result was ERROR, return. We do this for ERROR because a failing filter set-up or add_headers expansion is likely to diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 1b51c133d..07b601a96 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Private structure for the private options and other private data. */ @@ -51,8 +51,7 @@ typedef struct { BOOL gethostbyname; BOOL dns_qualify_single; BOOL dns_search_parents; - uschar *dnssec_request_domains; - uschar *dnssec_require_domains; + dnssec_domains dnssec; BOOL delay_after_cutoff; BOOL hosts_override; BOOL hosts_randomize; @@ -60,6 +59,9 @@ typedef struct { BOOL lmtp_ignore_quota; uschar *expand_retry_include_ip_address; BOOL retry_include_ip_address; +#ifdef SUPPORT_SOCKS + uschar *socks_proxy; +#endif #ifdef SUPPORT_TLS uschar *tls_certificate; uschar *tls_crl; @@ -109,4 +111,9 @@ extern int smtp_auth(uschar *, unsigned, address_item *, host_item *, extern BOOL smtp_mail_auth_str(uschar *, unsigned, address_item *, smtp_transport_options_block *); +#ifdef SUPPORT_SOCKS +extern int socks_sock_connect(host_item *, int, int, uschar *, + transport_instance *, int); +#endif + /* End of transports/smtp.h */ diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c new file mode 100644 index 000000000..33b25d1da --- /dev/null +++ b/src/src/transports/smtp_socks.c @@ -0,0 +1,412 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* SOCKS version 5 proxy, client-mode */ + +#include "../exim.h" +#include "smtp.h" + +#ifdef SUPPORT_SOCKS /* entire file */ + +#ifndef nelem +# define nelem(arr) (sizeof(arr)/sizeof(*arr)) +#endif + + +/* Defaults */ +#define SOCKS_PORT 1080 +#define SOCKS_TIMEOUT 5 +#define SOCKS_WEIGHT 1 +#define SOCKS_PRIORITY 1 + +#define AUTH_NONE 0 +#define AUTH_NAME 2 /* user/password per RFC 1929 */ +#define AUTH_NAME_VER 1 + +struct socks_err + { + uschar * reason; + int errcode; + } socks_errs[] = + { + {NULL, 0}, + {US"general SOCKS server failure", EIO}, + {US"connection not allowed by ruleset", EACCES}, + {US"Network unreachable", ENETUNREACH}, + {US"Host unreachable", EHOSTUNREACH}, + {US"Connection refused", ECONNREFUSED}, + {US"TTL expired", ECANCELED}, + {US"Command not supported", EOPNOTSUPP}, + {US"Address type not supported", EAFNOSUPPORT} + }; + +typedef struct + { + const uschar * proxy_host; + uschar auth_type; /* RFC 1928 encoding */ + const uschar * auth_name; + const uschar * auth_pwd; + short port; + BOOL is_failed; + unsigned timeout; + unsigned weight; + unsigned priority; + } socks_opts; + +static void +socks_option_defaults(socks_opts * sob) +{ +sob->proxy_host = NULL; +sob->auth_type = AUTH_NONE; +sob->auth_name = US""; +sob->auth_pwd = US""; +sob->is_failed = FALSE; +sob->port = SOCKS_PORT; +sob->timeout = SOCKS_TIMEOUT; +sob->weight = SOCKS_WEIGHT; +sob->priority = SOCKS_PRIORITY; +} + +static void +socks_option(socks_opts * sob, const uschar * opt) +{ +const uschar * s; + +if (Ustrncmp(opt, "auth=", 5) == 0) + { + opt += 5; + if (Ustrcmp(opt, "none") == 0) sob->auth_type = AUTH_NONE; + else if (Ustrcmp(opt, "name") == 0) sob->auth_type = AUTH_NAME; + } +else if (Ustrncmp(opt, "name=", 5) == 0) + sob->auth_name = opt + 5; +else if (Ustrncmp(opt, "pass=", 5) == 0) + sob->auth_pwd = opt + 5; +else if (Ustrncmp(opt, "port=", 5) == 0) + sob->port = atoi(opt + 5); +else if (Ustrncmp(opt, "tmo=", 4) == 0) + sob->timeout = atoi(opt + 4); +else if (Ustrncmp(opt, "pri=", 4) == 0) + sob->priority = atoi(opt + 4); +else if (Ustrncmp(opt, "weight=", 7) == 0) + sob->weight = atoi(opt + 7); +return; +} + +static int +socks_auth(int fd, int method, socks_opts * sob, time_t tmo) +{ +uschar * s; +int len, i, j; + +switch(method) + { + default: + log_write(0, LOG_MAIN|LOG_PANIC, + "Unrecognised socks auth method %d", method); + return FAIL; + case AUTH_NONE: + return OK; + case AUTH_NAME: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" socks auth NAME '%s' '%s'\n", + sob->auth_name, sob->auth_pwd); + i = Ustrlen(sob->auth_name); + j = Ustrlen(sob->auth_pwd); + s = string_sprintf("%c%c%.255s%c%.255s", AUTH_NAME_VER, + i, sob->auth_name, j, sob->auth_pwd); + len = i + j + 3; + HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; i<len; i++) debug_printf(" %02x", s[i]); + debug_printf("\n"); + } + if ( send(fd, s, len, 0) < 0 + || !fd_ready(fd, tmo-time(NULL)) + || read(fd, s, 2) != 2 + ) + return FAIL; + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" SOCKS<< %02x %02x\n", s[0], s[1]); + if (s[0] == AUTH_NAME_VER && s[1] == 0) + { + HDEBUG(D_transport|D_acl|D_v) debug_printf(" socks auth OK\n"); + return OK; + } + + log_write(0, LOG_MAIN|LOG_PANIC, "socks auth failed"); + errno = EPROTO; + return FAIL; + } +} + + + +/* Find a suitable proxy to use from the list. +Possible common code with spamd_get_server() ? + +Return: index into proxy spec array, or -1 +*/ + +static int +socks_get_proxy(socks_opts * proxies, unsigned nproxies) +{ +unsigned int i; +socks_opts * sd; +socks_opts * lim = &proxies[nproxies]; +long rnd, weights; +unsigned pri; +static BOOL srandomed = FALSE; + +if (nproxies == 1) /* shortcut, if we have only 1 server */ + return (proxies[0].is_failed ? -1 : 0); + +/* init random */ +if (!srandomed) + { + struct timeval tv; + gettimeofday(&tv, NULL); + srandom((unsigned int)(tv.tv_usec/1000)); + srandomed = TRUE; + } + +/* scan for highest pri */ +for (pri = 0, sd = proxies; sd < lim; sd++) + if (!sd->is_failed && sd->priority > pri) + pri = sd->priority; + +/* get sum of weights at this pri */ +for (weights = 0, sd = proxies; sd < lim; sd++) + if (!sd->is_failed && sd->priority == pri) + weights += sd->weight; +if (weights == 0) /* all servers failed */ + return -1; + +for (rnd = random() % weights, i = 0; i < nproxies; i++) + { + sd = &proxies[i]; + if (!sd->is_failed && sd->priority == pri) + if ((rnd -= sd->weight) <= 0) + return i; + } + +log_write(0, LOG_MAIN|LOG_PANIC, + "%s unknown error (memory/cpu corruption?)", __FUNCTION__); +return -1; +} + + + +/* Make a connection via a socks proxy + +Arguments: + host smtp target host + host_af address family + port remote tcp port number + interface local interface + tb transport + timeout connection timeout (zero for indefinite) + +Return value: + 0 on success; -1 on failure, with errno set +*/ + +int +socks_sock_connect(host_item * host, int host_af, int port, uschar * interface, + transport_instance * tb, int timeout) +{ +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; +const uschar * proxy_list; +const uschar * proxy_spec; +int sep = 0; +int fd; +time_t tmo; +const uschar * state; +uschar buf[24]; +socks_opts proxies[32]; /* max #proxies handled */ +unsigned nproxies; +socks_opts * sob; +unsigned size; + +if (!timeout) timeout = 24*60*60; /* use 1 day for "indefinite" */ +tmo = time(NULL) + timeout; + +if (!(proxy_list = expand_string(ob->socks_proxy))) + { + log_write(0, LOG_MAIN|LOG_PANIC, "Bad expansion for socks_proxy in %s", + tb->name); + return -1; + } + +/* Read proxy list */ + +for (nproxies = 0; + nproxies < nelem(proxies) + && (proxy_spec = string_nextinlist(&proxy_list, &sep, NULL, 0)); + nproxies++) + { + int subsep = -' '; + const uschar * option; + + socks_option_defaults(sob = &proxies[nproxies]); + + if (!(sob->proxy_host = string_nextinlist(&proxy_spec, &subsep, NULL, 0))) + { + /* paniclog config error */ + return -1; + } + + /*XXX consider global options eg. "hide socks_password = wibble" on the tpt */ + /* extract any further per-proxy options */ + while ((option = string_nextinlist(&proxy_spec, &subsep, NULL, 0))) + socks_option(sob, option); + } + +/* Try proxies until a connection succeeds */ + +for(;;) + { + int idx; + host_item proxy; + int proxy_af; + + if ((idx = socks_get_proxy(proxies, nproxies)) < 0) + { + HDEBUG(D_transport|D_acl|D_v) debug_printf(" no proxies left\n"); + errno = EBUSY; + return -1; + } + sob = &proxies[idx]; + + /* bodge up a host struct for the proxy */ + proxy.address = sob->proxy_host; + proxy_af = Ustrchr(sob->proxy_host, ':') ? AF_INET6 : AF_INET; + + if ((fd = smtp_sock_connect(&proxy, proxy_af, sob->port, + interface, tb, sob->timeout)) >= 0) + { + proxy_local_address = string_copy(proxy.address); + proxy_local_port = sob->port; + break; + } + + log_write(0, LOG_MAIN, "%s: %s", __FUNCTION__, strerror(errno)); + sob->is_failed = TRUE; + } + +/* Do the socks protocol stuff */ +/* Send method-selection */ + +state = US"method select"; +HDEBUG(D_transport|D_acl|D_v) debug_printf(" SOCKS>> 05 01 %02x\n", sob->auth_type); +buf[0] = 5; buf[1] = 1; buf[2] = sob->auth_type; +if (send(fd, buf, 3, 0) < 0) + goto snd_err; + +/* expect method response */ + +if ( !fd_ready(fd, tmo-time(NULL)) + || read(fd, buf, 2) != 2 + ) + goto rcv_err; +HDEBUG(D_transport|D_acl|D_v) + debug_printf(" SOCKS<< %02x %02x\n", buf[0], buf[1]); +if ( buf[0] != 5 + || socks_auth(fd, buf[1], sob, tmo) != OK + ) + goto proxy_err; + + { + union sockaddr_46 sin; + (void) ip_addr(&sin, host_af, host->address, port); + + /* send connect (ipver, ipaddr, port) */ + + buf[0] = 5; buf[1] = 1; buf[2] = 0; buf[3] = host_af == AF_INET6 ? 4 : 1; + #if HAVE_IPV6 + if (host_af == AF_INET6) + { + memcpy(buf+4, &sin.v6.sin6_addr, sizeof(sin.v6.sin6_addr)); + memcpy(buf+4+sizeof(sin.v6.sin6_addr), + &sin.v6.sin6_port, sizeof(sin.v6.sin6_port)); + size = 4+sizeof(sin.v6.sin6_addr)+sizeof(sin.v6.sin6_port); + } + else + #endif + { + memcpy(buf+4, &sin.v4.sin_addr.s_addr, sizeof(sin.v4.sin_addr.s_addr)); + memcpy(buf+4+sizeof(sin.v4.sin_addr.s_addr), + &sin.v4.sin_port, sizeof(sin.v4.sin_port)); + size = 4+sizeof(sin.v4.sin_addr.s_addr)+sizeof(sin.v4.sin_port); + } + } + +state = US"connect"; +HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; i<size; i++) debug_printf(" %02x", buf[i]); + debug_printf("\n"); + } +if (send(fd, buf, size, 0) < 0) + goto snd_err; + +/* expect conn-reply (success, local(ipver, addr, port)) +of same length as conn-request, or non-success fail code */ + +if ( !fd_ready(fd, tmo-time(NULL)) + || (size = read(fd, buf, size)) < 2 + ) + goto rcv_err; +HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; i<size; i++) debug_printf(" %02x", buf[i]); + debug_printf("\n"); + } +if ( buf[0] != 5 + || buf[1] != 0 + ) + goto proxy_err; + +proxy_external_address = string_copy( + host_ntoa(buf[3] == 4 ? AF_INET6 : AF_INET, buf+4, NULL, NULL)); +proxy_external_port = ntohs(*((uint16_t *)(buf + (buf[3] == 4 ? 20 : 8)))); +proxy_session = TRUE; + +HDEBUG(D_transport|D_acl|D_v) + debug_printf(" proxy farside: [%s]:%d\n", proxy_external_address, proxy_external_port); + +return fd; + +snd_err: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" proxy snd_err %s: %s\n", state, strerror(errno)); + return -1; + +proxy_err: + { + struct socks_err * se = + buf[1] > nelem(socks_errs) ? NULL : socks_errs + buf[1]; + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" proxy %s: %s\n", state, se ? se->reason : US"unknown error code received"); + errno = se ? se->errcode : EPROTO; + } + +rcv_err: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" proxy rcv_err %s: %s\n", state, strerror(errno)); + if (!errno) errno = EPROTO; + else if (errno == ENOENT) errno = ECONNABORTED; + return -1; +} + +#endif /* entire file */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index cfe7cb291..93bbba455 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -554,8 +554,8 @@ else FALSE); (void)gettimeofday(&tv, NULL); - tempname = string_sprintf("%s/tmp/%lu.H%luP%lu.%s", path, tv.tv_sec, - tv.tv_usec, (long unsigned) getpid(), primary_hostname); + tempname = string_sprintf("%s/tmp/" TIME_T_FMT ".H%luP%lu.%s", + path, tv.tv_sec, tv.tv_usec, (long unsigned) getpid(), primary_hostname); fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600); if (fd >= 0) diff --git a/src/src/tree.c b/src/src/tree.c index 2a9d90a62..72c084a6e 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for maintaining binary balanced trees and some associated @@ -328,7 +328,7 @@ Returns: pointer to node, or NULL if not found */ tree_node * -tree_search(tree_node *p, uschar *name) +tree_search(tree_node *p, const uschar *name) { while (p != NULL) { diff --git a/src/src/utf8.c b/src/src/utf8.c new file mode 100644 index 000000000..84ad1dc18 --- /dev/null +++ b/src/src/utf8.c @@ -0,0 +1,195 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + + +#include "exim.h" + +#ifdef SUPPORT_I18N + +#include <idna.h> +#include <punycode.h> +#include <stringprep.h> + +BOOL +string_is_utf8(const uschar * s) +{ +uschar c; +while ((c = *s++)) if (c & 0x80) return TRUE; +return FALSE; +} + +/**************************************************/ +/* Domain conversions */ +/* the *err string pointer should be null before the call */ + +uschar * +string_domain_utf8_to_alabel(const uschar * utf8, uschar ** err) +{ +uschar * s1; +uschar * s; +int rc; + +s = US stringprep_utf8_nfkc_normalize(CCS utf8, -1); +if ( (rc = idna_to_ascii_8z(CCS s, CSS &s1, IDNA_ALLOW_UNASSIGNED)) + != IDNA_SUCCESS) + { + free(s); + if (err) *err = US idna_strerror(rc); + return NULL; + } +free(s); +s = string_copy(s1); +free(s1); +return s; +} + + + +uschar * +string_domain_alabel_to_utf8(const uschar * alabel, uschar ** err) +{ +uschar * s1; +uschar * s; +int rc; + +if ( (rc = idna_to_unicode_8z8z(CCS alabel, CSS &s1, IDNA_USE_STD3_ASCII_RULES)) + != IDNA_SUCCESS) + { + if (err) *err = US idna_strerror(rc); + return NULL; + } +s = string_copy(s1); +free(s1); +return s; +} + +/**************************************************/ +/* localpart conversions */ +/* the *err string pointer should be null before the call */ + + +uschar * +string_localpart_utf8_to_alabel(const uschar * utf8, uschar ** err) +{ +size_t ucs4_len; +punycode_uint * p; +size_t p_len; +uschar * res; +int rc; + +if (!string_is_utf8(utf8)) return string_copy(utf8); + +p = (punycode_uint *) stringprep_utf8_to_ucs4(CCS utf8, -1, &ucs4_len); +p_len = ucs4_len*4; /* this multiplier is pure guesswork */ +res = store_get(p_len+5); + +res[0] = 'x'; res[1] = 'n'; res[2] = res[3] = '-'; + +if ((rc = punycode_encode(ucs4_len, p, NULL, &p_len, CS res+4)) != PUNYCODE_SUCCESS) + { + DEBUG(D_expand) debug_printf("l_u2a: bad '%s'\n", punycode_strerror(rc)); + free(p); + if (err) *err = US punycode_strerror(rc); + return NULL; + } +p_len += 4; +free(p); +res[p_len] = '\0'; +return res; +} + + +uschar * +string_localpart_alabel_to_utf8(const uschar * alabel, uschar ** err) +{ +size_t p_len = Ustrlen(alabel); +punycode_uint * p; +uschar * s; +uschar * res; +int rc; + +if (alabel[0] != 'x' || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-') + { + if (err) *err = US"bad alabel prefix"; + return NULL; + } + +p_len -= 4; +p = (punycode_uint *) store_get((p_len+1) * sizeof(*p)); + +if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUCCESS) + { + if (err) *err = US punycode_strerror(rc); + return NULL; + } + +s = US stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len); +res = string_copyn(s, p_len); +free(s); +return res; +} + + +/**************************************************/ +/* whole address conversion */ +/* the *err string pointer should be null before the call */ + +uschar * +string_address_utf8_to_alabel(const uschar * utf8, uschar ** err) +{ +const uschar * s; +uschar * l; +uschar * d; + +if (!*utf8) return string_copy(utf8); + +DEBUG(D_expand) debug_printf("addr from utf8 <%s>", utf8); + +for (s = utf8; *s; s++) + if (*s == '@') + { + l = string_copyn(utf8, s - utf8); + if ( (l = string_localpart_utf8_to_alabel(l, err), err && *err) + || (d = string_domain_utf8_to_alabel(++s, err), err && *err) + ) + return NULL; + l = string_sprintf("%s@%s", l, d); + DEBUG(D_expand) debug_printf(" -> <%s>\n", l); + return l; + } + +l = string_localpart_utf8_to_alabel(utf8, err); +DEBUG(D_expand) debug_printf(" -> <%s>\n", l); +return l; +} + + + +/************************************************* +* Report the library versions. * +*************************************************/ + +/* See a description in tls-openssl.c for an explanation of why this exists. + +Arguments: a FILE* to print the results to +Returns: nothing +*/ + +void +utf8_version_report(FILE *f) +{ +fprintf(f, "Library version: IDN: Compile: %s\n" + " Runtime: %s\n", + STRINGPREP_VERSION, + stringprep_check_version(NULL)); +} + +#endif /* whole file */ + +/* vi: aw ai sw=2 +*/ +/* End of utf8.c */ diff --git a/src/src/valgrind.h b/src/src/valgrind.h index 4d41690ab..f16e70177 100644 --- a/src/src/valgrind.h +++ b/src/src/valgrind.h @@ -21,16 +21,16 @@ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. The origin of this software must not be misrepresented; you must - not claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 4. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS @@ -52,13 +52,13 @@ the terms of the GNU General Public License, version 2. See the COPYING file in the source distribution for details. - ---------------------------------------------------------------- + ---------------------------------------------------------------- */ /* This file is for inclusion into client (your!) code. - You can use these macros to manipulate and query Valgrind's + You can use these macros to manipulate and query Valgrind's execution inside your own programs. The resulting executables will still run without Valgrind, just a @@ -180,8 +180,8 @@ this is executed not under Valgrind. Args are passed in a memory block, and so there's no intrinsic limit to the number that could be passed, but it's currently five. - - The macro args are: + + The macro args are: _zzq_rlval result lvalue _zzq_default default value (result returned when running on real CPU) _zzq_request request code @@ -208,7 +208,7 @@ || (defined(PLAT_x86_win32) && defined(__GNUC__)) typedef - struct { + struct { unsigned int nraddr; /* where's the code? */ } OrigFn; @@ -262,7 +262,7 @@ typedef #if defined(PLAT_x86_win32) && !defined(__GNUC__) typedef - struct { + struct { unsigned int nraddr; /* where's the code? */ } OrigFn; @@ -317,7 +317,7 @@ typedef #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) typedef - struct { + struct { unsigned long long int nraddr; /* where's the code? */ } OrigFn; @@ -371,7 +371,7 @@ typedef #if defined(PLAT_ppc32_linux) typedef - struct { + struct { unsigned int nraddr; /* where's the code? */ } OrigFn; @@ -431,7 +431,7 @@ typedef #if defined(PLAT_ppc64_linux) typedef - struct { + struct { unsigned long long int nraddr; /* where's the code? */ unsigned long long int r2; /* what tocptr do we need? */ } @@ -497,7 +497,7 @@ typedef #if defined(PLAT_arm_linux) typedef - struct { + struct { unsigned int nraddr; /* where's the code? */ } OrigFn; @@ -556,7 +556,7 @@ typedef #if defined(PLAT_ppc32_aix5) typedef - struct { + struct { unsigned int nraddr; /* where's the code? */ unsigned int r2; /* what tocptr do we need? */ } @@ -628,7 +628,7 @@ typedef #if defined(PLAT_ppc64_aix5) typedef - struct { + struct { unsigned long long int nraddr; /* where's the code? */ unsigned long long int r2; /* what tocptr do we need? */ } @@ -1753,7 +1753,7 @@ typedef "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" -/* These CALL_FN_ macros assume that on ppc32-linux, +/* These CALL_FN_ macros assume that on ppc32-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ @@ -4269,7 +4269,7 @@ typedef #define VG_IS_TOOL_USERREQ(a, b, v) \ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) -/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! This enum comprises an ABI exported by Valgrind to programs which use client requests. DO NOT CHANGE THE ORDER OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end. */ @@ -4452,7 +4452,7 @@ VALGRIND_PRINTF(const char *format, ...) VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF_VALIST_BY_REF, (unsigned long)format, - (unsigned long)&vargs, + (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); @@ -4482,7 +4482,7 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...) VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (unsigned long)format, - (unsigned long)&vargs, + (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); @@ -4494,7 +4494,7 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...) /* These requests allow control to move from the simulated CPU to the real CPU, calling an arbitary function. - + Note that the current ThreadId is inserted as the first argument. So this call: @@ -4599,7 +4599,7 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...) - It marks the block as being addressable and undefined (if 'is_zeroed' is not set), or addressable and defined (if 'is_zeroed' is set). This controls how accesses to the block by the program are handled. - + 'addr' is the start of the usable block (ie. after any redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator can apply redzones -- these are blocks of padding at the start and end of @@ -4607,7 +4607,7 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...) Valgrind will spot block overruns. `is_zeroed' indicates if the memory is zeroed (or filled with another predictable value), as is the case for calloc(). - + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a heap block -- that will be used by the client program -- is allocated. It's best to put it at the outermost level of the allocator if possible; @@ -4653,7 +4653,7 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...) Note: there is currently no VALGRIND_REALLOCLIKE_BLOCK client request; it has to be emulated with MALLOCLIKE/FREELIKE and memory copying. - + Ignored if addr == 0. */ #define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ diff --git a/src/src/verify.c b/src/src/verify.c index 96740f8f3..ef95394d3 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with verifying things. The original code for callout @@ -14,7 +14,6 @@ caching was contributed by Kevin Fleming (but I hacked it around a bit). */ #define CUTTHROUGH_CMD_TIMEOUT 30 /* timeout for cutthrough-routing calls */ #define CUTTHROUGH_DATA_TIMEOUT 60 /* timeout for cutthrough-routing calls */ -address_item cutthrough_addr; static smtp_outblock ctblock; uschar ctbuffer[8192]; @@ -22,6 +21,7 @@ uschar ctbuffer[8192]; /* Structure for caching DNSBL lookups */ typedef struct dnsbl_cache_block { + time_t expiry; dns_address *rhs; uschar *text; int rc; @@ -39,6 +39,7 @@ static tree_node *dnsbl_cache = NULL; #define MT_NOT 1 #define MT_ALL 2 +static uschar cutthrough_response(char, uschar **); /************************************************* @@ -58,7 +59,7 @@ Returns: the cache record if a non-expired one exists, else NULL */ static dbdata_callout_cache * -get_callout_cache_record(open_db *dbm_file, uschar *key, uschar *type, +get_callout_cache_record(open_db *dbm_file, const uschar *key, uschar *type, int positive_expire, int negative_expire) { BOOL negative; @@ -70,7 +71,7 @@ cache_record = dbfn_read_with_length(dbm_file, key, &length); if (cache_record == NULL) { - HDEBUG(D_verify) debug_printf("callout cache: no %s record found\n", type); + HDEBUG(D_verify) debug_printf("callout cache: no %s record found for %s\n", type, key); return NULL; } @@ -84,7 +85,7 @@ now = time(NULL); if (now - cache_record->time_stamp > expire) { - HDEBUG(D_verify) debug_printf("callout cache: %s record expired\n", type); + HDEBUG(D_verify) debug_printf("callout cache: %s record expired for %s\n", type, key); return NULL; } @@ -111,7 +112,7 @@ if (type[0] == 'd' && cache_record->result != ccache_reject) cache_record->random_result = ccache_unknown; } -HDEBUG(D_verify) debug_printf("callout cache: found %s record\n", type); +HDEBUG(D_verify) debug_printf("callout cache: found %s record for %s\n", type, key); return cache_record; } @@ -164,7 +165,7 @@ BOOL done = FALSE; uschar *address_key; uschar *from_address; uschar *random_local_part = NULL; -uschar *save_deliver_domain = deliver_domain; +const uschar *save_deliver_domain = deliver_domain; uschar **failure_ptr = is_recipient? &recipient_verify_failure : &sender_verify_failure; open_db dbblock; @@ -173,6 +174,9 @@ dbdata_callout_cache new_domain_record; dbdata_callout_cache_address new_address_record; host_item *host; time_t callout_start_time; +#ifdef SUPPORT_I18N +BOOL utf8_offered = FALSE; +#endif new_domain_record.result = ccache_unknown; new_domain_record.postmaster_result = ccache_unknown; @@ -189,12 +193,12 @@ from_address = US""; if (is_recipient) { - if ((options & vopt_callout_recipsender) != 0) + if (options & vopt_callout_recipsender) { address_key = string_sprintf("%s/<%s>", addr->address, sender_address); from_address = sender_address; } - else if ((options & vopt_callout_recippmaster) != 0) + else if (options & vopt_callout_recippmaster) { address_key = string_sprintf("%s/<postmaster@%s>", addr->address, qualify_domain_sender); @@ -388,12 +392,9 @@ else log the fact, but carry on without randomming. */ if (callout_random && callout_random_local_part != NULL) - { - random_local_part = expand_string(callout_random_local_part); - if (random_local_part == NULL) + if (!(random_local_part = expand_string(callout_random_local_part))) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " "callout_random_local_part: %s", expand_string_message); - } /* Default the connect and overall callout timeouts if not set, and record the time we are starting so that we can enforce it. */ @@ -410,6 +411,113 @@ else if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush(); +/* cutthrough-multi: if a nonfirst rcpt has the same routing as the first, +and we are holding a cutthrough conn open, we can just append the rcpt to +that conn for verification purposes (and later delivery also). Simplest +coding means skipping this whole loop and doing the append separately. + +We will need to remember it has been appended so that rcpt-acl tail code +can do it there for the non-rcpt-verify case. For this we keep an addresscount. +*/ + + /* Can we re-use an open cutthrough connection? */ + if ( cutthrough.fd >= 0 + && (options & (vopt_callout_recipsender | vopt_callout_recippmaster)) + == vopt_callout_recipsender + && !random_local_part + && !pm_mailfrom + ) + { + if (addr->transport == cutthrough.addr.transport) + for (host = host_list; host; host = host->next) + if (Ustrcmp(host->address, cutthrough.host.address) == 0) + { + int host_af; + uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ + int port = 25; + + deliver_host = host->name; + deliver_host_address = host->address; + deliver_host_port = host->port; + deliver_domain = addr->domain; + transport_name = addr->transport->name; + + host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6; + + if (!smtp_get_interface(tf->interface, host_af, addr, &interface, + US"callout") || + !smtp_get_port(tf->port, addr, &port, US"callout")) + log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, + addr->message); + + if ( ( interface == cutthrough.interface + || ( interface + && cutthrough.interface + && Ustrcmp(interface, cutthrough.interface) == 0 + ) ) + && port == cutthrough.host.port + ) + { + uschar * resp; + + /* Match! Send the RCPT TO, append the addr, set done */ + done = + smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n", + transport_rcpt_address(addr, + (addr->transport == NULL)? FALSE : + addr->transport->rcpt_include_affixes)) >= 0 && + cutthrough_response('2', &resp) == '2'; + + /* This would go horribly wrong if a callout fail was ignored by ACL. + We punt by abandoning cutthrough on a reject, like the + first-rcpt does. */ + + if (done) + { + address_item * na = store_get(sizeof(address_item)); + *na = cutthrough.addr; + cutthrough.addr = *addr; + cutthrough.addr.host_used = &cutthrough.host; + cutthrough.addr.next = na; + + cutthrough.nrcpt++; + } + else + { + cancel_cutthrough_connection("recipient rejected"); + if (errno == ETIMEDOUT) + { + HDEBUG(D_verify) debug_printf("SMTP timeout\n"); + } + else if (errno == 0) + { + if (*resp == 0) + Ustrcpy(resp, US"connection dropped"); + + addr->message = + string_sprintf("response to \"%s\" from %s [%s] was: %s", + big_buffer, host->name, host->address, + string_printing(resp)); + + addr->user_message = + string_sprintf("Callout verification failed:\n%s", resp); + + /* Hard rejection ends the process */ + + if (resp[0] == '5') /* Address rejected */ + { + yield = FAIL; + done = TRUE; + } + } + } + } + break; + } + if (!done) + cancel_cutthrough_connection("incompatible connection"); + } + /* Now make connections to the hosts and do real callouts. The list of hosts is passed in as an argument. */ @@ -428,6 +536,7 @@ else uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) BOOL dane = FALSE; + BOOL dane_required; dns_answer tlsa_dnsa; #endif uschar inbuffer[4096]; @@ -470,9 +579,10 @@ else deliver_domain = addr->domain; transport_name = addr->transport->name; - if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, - US"callout") || - !smtp_get_port(tf->port, addr, &port, US"callout")) + if ( !smtp_get_interface(tf->interface, host_af, addr, &interface, + US"callout") + || !smtp_get_port(tf->port, addr, &port, US"callout") + ) log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, addr->message); @@ -483,36 +593,6 @@ else HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port); -#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) - { - BOOL dane_required; - int rc; - - tls_out.dane_verified = FALSE; - tls_out.tlsa_usage = 0; - - dane_required = - verify_check_given_host(&ob->hosts_require_dane, host) == OK; - - if (host->dnssec == DS_YES) - { - if( dane_required - || verify_check_given_host(&ob->hosts_try_dane, host) == OK - ) - if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK) - return rc; - } - else if (dane_required) - { - log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name); - return FAIL; - } - - if (dane) - ob->tls_tempfail_tryclear = FALSE; - } -#endif /*DANE*/ - /* Set up the buffer for reading SMTP response packets. */ inblock.buffer = inbuffer; @@ -528,22 +608,17 @@ else outblock.cmd_count = 0; outblock.authenticating = FALSE; - /* Reset the parameters of a TLS session */ - tls_out.cipher = tls_out.peerdn = NULL; - /* Connect to the host; on failure, just loop for the next one, but we set the error for the last one. Use the callout_connect timeout. */ tls_retry_connection: + /* Reset the parameters of a TLS session */ + tls_out.cipher = tls_out.peerdn = tls_out.peercert = NULL; + inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL -#ifdef EXPERIMENTAL_EVENT - /*XXX event action? NULL for now. */ - , NULL -#endif - ); - /* reconsider DSCP here */ + smtp_connect(host, host_af, port, interface, callout_connect, + addr->transport); if (inblock.sock < 0) { addr->message = string_sprintf("could not connect to %s [%s]: %s", @@ -554,6 +629,36 @@ else continue; } +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { + int rc; + + tls_out.dane_verified = FALSE; + tls_out.tlsa_usage = 0; + + dane_required = + verify_check_given_host(&ob->hosts_require_dane, host) == OK; + + if (host->dnssec == DS_YES) + { + if( ( dane_required + || verify_check_given_host(&ob->hosts_try_dane, host) == OK + ) + && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK + ) + return rc; + } + else if (dane_required) + { + log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name); + return FAIL; + } + + if (dane) + ob->tls_tempfail_tryclear = FALSE; + } +#endif /*DANE*/ + /* Expand the helo_data string to find the host name to use. */ if (tf->helo_data != NULL) @@ -582,7 +687,7 @@ else if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout))) goto RESPONSE_FAILED; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes" : host->dnssec==DS_NO ? US"no" : NULL; if (event_raise(addr->transport->event_action, @@ -597,7 +702,7 @@ else } /* Not worth checking greeting line for ESMTP support */ - if (!(esmtp = verify_check_given_host(&(ob->hosts_avoid_esmtp), host) != OK)) + if (!(esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK)) DEBUG(D_transport) debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n"); @@ -690,34 +795,53 @@ else int oldtimeout = ob->command_timeout; int rc; + tls_negotiate: ob->command_timeout = callout; rc = tls_client_start(inblock.sock, host, addr, addr->transport -#ifdef EXPERIMENTAL_DANE +# ifdef EXPERIMENTAL_DANE , dane ? &tlsa_dnsa : NULL -#endif +# endif ); ob->command_timeout = oldtimeout; - /* TLS negotiation failed; give an error. Try in clear on a new connection, - if the options permit it for this host. */ + /* TLS negotiation failed; give an error. Try in clear on a new + connection, if the options permit it for this host. */ if (rc != OK) { - if ( rc == DEFER - && ob->tls_tempfail_tryclear - && !smtps - && verify_check_given_host(&ob->hosts_require_tls, host) != OK - ) + if (rc == DEFER) { (void)close(inblock.sock); -#ifdef EXPERIMENTAL_EVENT +# ifndef DISABLE_EVENT (void) event_raise(addr->transport->event_action, US"tcp:close", NULL); -#endif - log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " - "to %s [%s] (not in hosts_require_tls)", host->name, host->address); - suppress_tls = TRUE; - goto tls_retry_connection; +# endif +# ifdef EXPERIMENTAL_DANE + if (dane) + { + if (!dane_required) + { + log_write(0, LOG_MAIN, "DANE attempt failed;" + " trying CA-root TLS to %s [%s] (not in hosts_require_dane)", + host->name, host->address); + dane = FALSE; + goto tls_negotiate; + } + } + else +# endif + if ( ob->tls_tempfail_tryclear + && !smtps + && verify_check_given_host(&ob->hosts_require_tls, host) != OK + ) + { + log_write(0, LOG_MAIN, "TLS session failure:" + " delivering unencrypted to %s [%s] (not in hosts_require_tls)", + host->name, host->address); + suppress_tls = TRUE; + goto tls_retry_connection; + } } + /*save_errno = ERRNO_TLSFAILURE;*/ /*message = US"failure while setting up TLS session";*/ send_quit = FALSE; @@ -741,9 +865,9 @@ else /* If the host is required to use a secure channel, ensure that we have one. */ if (tls_out.active < 0) if ( -#ifdef EXPERIMENTAL_DANE +# ifdef EXPERIMENTAL_DANE dane || -#endif +# endif verify_check_given_host(&ob->hosts_require_tls, host) == OK ) { @@ -757,7 +881,7 @@ else goto TLS_FAILED; } - #endif /*SUPPORT_TLS*/ +#endif /*SUPPORT_TLS*/ done = TRUE; /* so far so good; have response to HELO */ @@ -765,17 +889,17 @@ else /* For now, transport_filter by cutthrough-delivery is not supported */ /* Need proper integration with the proper transport mechanism. */ - if (cutthrough_delivery) + if (cutthrough.delivery) { if (addr->transport->filter_command) { - cutthrough_delivery= FALSE; + cutthrough.delivery = FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n"); } #ifndef DISABLE_DKIM if (ob->dkim_domain) { - cutthrough_delivery= FALSE; + cutthrough.delivery = FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n"); } #endif @@ -800,6 +924,40 @@ else } } +#ifdef SUPPORT_I18N + else if ( addr->prop.utf8_msg + && !addr->prop.utf8_downcvt + && !( esmtp + && ( regex_UTF8 + || ( (regex_UTF8 = regex_must_compile( + US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE)), + TRUE + ) ) + && ( (utf8_offered = pcre_exec(regex_UTF8, NULL, + CS responsebuffer, Ustrlen(responsebuffer), + 0, PCRE_EOPT, NULL, 0) >= 0) + || addr->prop.utf8_downcvt_maybe + ) ) ) + { + HDEBUG(D_acl|D_v) debug_printf("utf8 required but not offered\n"); + errno = ERRNO_UTF8_FWD; + setflag(addr, af_verify_nsfail); + done = FALSE; + } + else if ( addr->prop.utf8_msg + && (addr->prop.utf8_downcvt || !utf8_offered) + && (setflag(addr, af_utf8_downcvt), + from_address = string_address_utf8_to_alabel(from_address, + &addr->message), + addr->message + ) ) + { + errno = ERRNO_EXPANDFAIL; + setflag(addr, af_verify_nsfail); + done = FALSE; + } +#endif + /* If we haven't authenticated, but are required to, give up. */ /* Try to AUTH */ @@ -817,7 +975,13 @@ else ( (addr->auth_sndr = client_authenticated_sender), /* Send the MAIL command */ - (smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>%s\r\n", + (smtp_write_command(&outblock, FALSE, +#ifdef SUPPORT_I18N + addr->prop.utf8_msg && !addr->prop.utf8_downcvt + ? "MAIL FROM:<%s>%s SMTPUTF8\r\n" + : +#endif + "MAIL FROM:<%s>%s\r\n", from_address, responsebuffer) >= 0) ) && @@ -856,6 +1020,23 @@ else else { + const uschar * rcpt_domain = addr->domain; + +#ifdef SUPPORT_I18N + uschar * errstr = NULL; + if ( testflag(addr, af_utf8_downcvt) + && (rcpt_domain = string_domain_utf8_to_alabel(rcpt_domain, + &errstr), errstr) + ) + { + addr->message = errstr; + errno = ERRNO_EXPANDFAIL; + setflag(addr, af_verify_nsfail); + done = FALSE; + rcpt_domain = US""; /*XXX errorhandling! */ + } +#endif + new_domain_record.result = (old_domain_cache_result == ccache_reject_mfnull)? ccache_reject_mfnull: ccache_accept; @@ -868,7 +1049,7 @@ else BOOL random_ok = smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s@%.1000s>\r\n", random_local_part, - addr->domain) >= 0 && + rcpt_domain) >= 0 && smtp_read_response(&inblock, randombuffer, sizeof(randombuffer), '2', callout); @@ -883,10 +1064,17 @@ else /* Otherwise, cache a real negative response, and get back to the right state to send RCPT. Unless there's some problem such as a dropped - connection, we expect to succeed, because the commands succeeded above. */ + connection, we expect to succeed, because the commands succeeded above. + However, some servers drop the connection after responding to an + invalid recipient, so on (any) error we drop and remake the connection. + */ else if (errno == 0) { + /* This would be ok for 1st rcpt a cutthrough, but no way to + handle a subsequent. So refuse to support any */ + cancel_cutthrough_connection("random-recipient"); + if (randombuffer[0] == '5') new_domain_record.random_result = ccache_reject; @@ -895,10 +1083,32 @@ else smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout) && - smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>\r\n", + smtp_write_command(&outblock, FALSE, +#ifdef SUPPORT_I18N + addr->prop.utf8_msg && !addr->prop.utf8_downcvt + ? "MAIL FROM:<%s> SMTPUTF8\r\n" + : +#endif + "MAIL FROM:<%s>\r\n", from_address) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout); + + if (!done) + { + HDEBUG(D_acl|D_v) + debug_printf("problem after random/rset/mfrom; reopen conn\n"); + random_local_part = NULL; +#ifdef SUPPORT_TLS + tls_close(FALSE, TRUE); +#endif + (void)close(inblock.sock); +#ifndef DISABLE_EVENT + (void) event_raise(addr->transport->event_action, + US"tcp:close", NULL); +#endif + goto tls_retry_connection; + } } else done = FALSE; /* Some timeout/connection problem */ } /* Random check */ @@ -911,11 +1121,27 @@ else /* Get the rcpt_include_affixes flag from the transport if there is one, but assume FALSE if there is not. */ + uschar * rcpt = transport_rcpt_address(addr, + addr->transport ? addr->transport->rcpt_include_affixes : FALSE); + +#ifdef SUPPORT_I18N + /*XXX should the conversion be moved into transport_rcpt_address() ? */ + uschar * dummy_errstr = NULL; + if ( testflag(addr, af_utf8_downcvt) + && (rcpt = string_address_utf8_to_alabel(rcpt, &dummy_errstr), + dummy_errstr + ) ) + { + errno = ERRNO_EXPANDFAIL; + *failure_ptr = US"recipient"; + done = FALSE; + } + else +#endif + done = smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s>\r\n", - transport_rcpt_address(addr, - (addr->transport == NULL)? FALSE : - addr->transport->rcpt_include_affixes)) >= 0 && + rcpt) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout); @@ -932,8 +1158,10 @@ else if (done && pm_mailfrom != NULL) { - /*XXX not suitable for cutthrough - sequencing problems */ - cutthrough_delivery= FALSE; + /* Could possibly shift before main verify, just above, and be ok + for cutthrough. But no way to handle a subsequent rcpt, so just + refuse any */ + cancel_cutthrough_connection("postmaster verify"); HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n"); done = @@ -950,7 +1178,7 @@ else (( smtp_write_command(&outblock, FALSE, - "RCPT TO:<postmaster@%.1000s>\r\n", addr->domain) >= 0 && + "RCPT TO:<postmaster@%.1000s>\r\n", rcpt_domain) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout) ) @@ -1001,6 +1229,21 @@ else HDEBUG(D_verify) debug_printf("SMTP timeout\n"); send_quit = FALSE; } +#ifdef SUPPORT_I18N + else if (errno == ERRNO_UTF8_FWD) + { + extern int acl_where; /* src/acl.c */ + errno = 0; + addr->message = string_sprintf( + "response to \"%s\" from %s [%s] did not include SMTPUTF8", + big_buffer, host->name, host->address); + addr->user_message = acl_where == ACL_WHERE_RCPT + ? US"533 mailbox name not allowed" + : US"550 mailbox unavailable"; + yield = FAIL; + done = TRUE; + } +#endif else if (errno == 0) { if (*responsebuffer == 0) Ustrcpy(responsebuffer, US"connection dropped"); @@ -1028,30 +1271,35 @@ else /* End the SMTP conversation and close the connection. */ - /* Cutthrough - on a successfull connect and recipient-verify with use-sender - and we have no cutthrough conn so far + /* Cutthrough - on a successfull connect and recipient-verify with + use-sender and we are 1st rcpt and have no cutthrough conn so far here is where we want to leave the conn open */ - if ( cutthrough_delivery + if ( cutthrough.delivery + && rcpt_count == 1 && done && yield == OK && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender && !random_local_part && !pm_mailfrom - && cutthrough_fd < 0 + && cutthrough.fd < 0 + && !lmtp ) { - cutthrough_fd= outblock.sock; /* We assume no buffer in use in the outblock */ - cutthrough_addr = *addr; /* Save the address_item for later logging */ - cutthrough_addr.next = NULL; - cutthrough_addr.host_used = store_get(sizeof(host_item)); - *(cutthrough_addr.host_used) = *host; + cutthrough.fd = outblock.sock; /* We assume no buffer in use in the outblock */ + cutthrough.nrcpt = 1; + cutthrough.interface = interface; + cutthrough.host = *host; + cutthrough.addr = *addr; /* Save the address_item for later logging */ + cutthrough.addr.next = NULL; + cutthrough.addr.host_used = &cutthrough.host; if (addr->parent) - *(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent; + *(cutthrough.addr.parent = store_get(sizeof(address_item))) = + *addr->parent; ctblock.buffer = ctbuffer; ctblock.buffersize = sizeof(ctbuffer); ctblock.ptr = ctbuffer; /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */ - ctblock.sock = cutthrough_fd; + ctblock.sock = cutthrough.fd; } else { @@ -1064,9 +1312,8 @@ else tls_close(FALSE, TRUE); #endif (void)close(inblock.sock); -#ifdef EXPERIMENTAL_EVENT - (void) event_raise(addr->transport->event_action, - US"tcp:close", NULL); +#ifndef DISABLE_EVENT + (void) event_raise(addr->transport->event_action, US"tcp:close", NULL); #endif } @@ -1176,7 +1423,8 @@ address_item addr2; get rewritten. */ addr2 = *addr; -HDEBUG(D_acl) debug_printf("----------- start cutthrough setup ------------\n"); +HDEBUG(D_acl) debug_printf("----------- %s cutthrough setup ------------\n", + rcpt_count > 1 ? "more" : "start"); (void) verify_address(&addr2, NULL, vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache, CUTTHROUGH_CMD_TIMEOUT, -1, -1, @@ -1191,14 +1439,14 @@ return; static BOOL cutthrough_send(int n) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return TRUE; if( #ifdef SUPPORT_TLS - (tls_out.active == cutthrough_fd) ? tls_write(FALSE, ctblock.buffer, n) : + (tls_out.active == cutthrough.fd) ? tls_write(FALSE, ctblock.buffer, n) : #endif - send(cutthrough_fd, ctblock.buffer, n, 0) > 0 + send(cutthrough.fd, ctblock.buffer, n, 0) > 0 ) { transport_count += n; @@ -1230,7 +1478,7 @@ return TRUE; BOOL cutthrough_puts(uschar * cp, int n) { -if (cutthrough_fd < 0) return TRUE; +if (cutthrough.fd < 0) return TRUE; if (_cutthrough_puts(cp, n)) return TRUE; cancel_cutthrough_connection("transmit failed"); return FALSE; @@ -1238,7 +1486,7 @@ return FALSE; static BOOL -_cutthrough_flush_send( void ) +_cutthrough_flush_send(void) { int n= ctblock.ptr-ctblock.buffer; @@ -1251,7 +1499,7 @@ return TRUE; /* Send out any bufferred output. Return boolean success. */ BOOL -cutthrough_flush_send( void ) +cutthrough_flush_send(void) { if (_cutthrough_flush_send()) return TRUE; cancel_cutthrough_connection("transmit failed"); @@ -1260,7 +1508,7 @@ return FALSE; BOOL -cutthrough_put_nl( void ) +cutthrough_put_nl(void) { return cutthrough_puts(US"\r\n", 2); } @@ -1278,7 +1526,7 @@ inblock.buffer = inbuffer; inblock.buffersize = sizeof(inbuffer); inblock.ptr = inbuffer; inblock.ptrend = inbuffer; -inblock.sock = cutthrough_fd; +inblock.sock = cutthrough.fd; /* this relies on (inblock.sock == tls_out.active) */ if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT)) cancel_cutthrough_connection("target timeout on read"); @@ -1286,7 +1534,7 @@ if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, if(copy != NULL) { uschar * cp; - *copy= cp= string_copy(responsebuffer); + *copy = cp = string_copy(responsebuffer); /* Trim the trailing end of line */ cp += Ustrlen(responsebuffer); if(cp > *copy && cp[-1] == '\n') *--cp = '\0'; @@ -1299,9 +1547,9 @@ return responsebuffer[0]; /* Negotiate dataphase with the cutthrough target, returning success boolean */ BOOL -cutthrough_predata( void ) +cutthrough_predata(void) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return FALSE; HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> DATA\n"); @@ -1332,9 +1580,9 @@ return TRUE; /* Expands newlines to wire format (CR,NL). */ /* Also sends header-terminating blank line. */ BOOL -cutthrough_headers_send( void ) +cutthrough_headers_send(void) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return FALSE; /* We share a routine with the mainline transport to handle header add/remove/rewrites, @@ -1342,10 +1590,12 @@ if(cutthrough_fd < 0) */ HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n"); -if (!transport_headers_send(&cutthrough_addr, cutthrough_fd, - cutthrough_addr.transport->add_headers, cutthrough_addr.transport->remove_headers, +if (!transport_headers_send(&cutthrough.addr, cutthrough.fd, + cutthrough.addr.transport->add_headers, + cutthrough.addr.transport->remove_headers, &cutthrough_write_chunk, TRUE, - cutthrough_addr.transport->rewrite_rules, cutthrough_addr.transport->rewrite_existflags)) + cutthrough.addr.transport->rewrite_rules, + cutthrough.addr.transport->rewrite_existflags)) return FALSE; HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n"); @@ -1354,9 +1604,9 @@ return TRUE; static void -close_cutthrough_connection( const char * why ) +close_cutthrough_connection(const char * why) { -if(cutthrough_fd >= 0) +if(cutthrough.fd >= 0) { /* We could be sending this after a bunch of data, but that is ok as the only way to cancel the transfer in dataphase is to drop the tcp @@ -1371,18 +1621,18 @@ if(cutthrough_fd >= 0) #ifdef SUPPORT_TLS tls_close(FALSE, TRUE); #endif - (void)close(cutthrough_fd); - cutthrough_fd= -1; + (void)close(cutthrough.fd); + cutthrough.fd = -1; HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown (%s) ------------\n", why); } ctblock.ptr = ctbuffer; } void -cancel_cutthrough_connection( const char * why ) +cancel_cutthrough_connection(const char * why) { close_cutthrough_connection(why); -cutthrough_delivery= FALSE; +cutthrough.delivery = FALSE; } @@ -1394,33 +1644,45 @@ cutthrough_delivery= FALSE; Return smtp response-class digit. */ uschar * -cutthrough_finaldot( void ) +cutthrough_finaldot(void) { +uschar res; +address_item * addr; HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> .\n"); /* Assume data finshed with new-line */ -if(!cutthrough_puts(US".", 1) || !cutthrough_put_nl() || !cutthrough_flush_send()) - return cutthrough_addr.message; +if( !cutthrough_puts(US".", 1) + || !cutthrough_put_nl() + || !cutthrough_flush_send() + ) + return cutthrough.addr.message; -switch(cutthrough_response('2', &cutthrough_addr.message)) +res = cutthrough_response('2', &cutthrough.addr.message); +for (addr = &cutthrough.addr; addr; addr = addr->next) { - case '2': - delivery_log(LOG_MAIN, &cutthrough_addr, (int)'>', NULL); - close_cutthrough_connection("delivered"); - break; + addr->message = cutthrough.addr.message; + switch(res) + { + case '2': + delivery_log(LOG_MAIN, addr, (int)'>', NULL); + close_cutthrough_connection("delivered"); + break; - case '4': - delivery_log(LOG_MAIN, &cutthrough_addr, 0, US"tmp-reject from cutthrough after DATA:"); - break; + case '4': + delivery_log(LOG_MAIN, addr, 0, + US"tmp-reject from cutthrough after DATA:"); + break; - case '5': - delivery_log(LOG_MAIN|LOG_REJECT, &cutthrough_addr, 0, US"rejected after DATA:"); - break; + case '5': + delivery_log(LOG_MAIN|LOG_REJECT, addr, 0, + US"rejected after DATA:"); + break; - default: - break; + default: + break; + } } - return cutthrough_addr.message; +return cutthrough.addr.message; } @@ -1451,7 +1713,7 @@ if (addr != vaddr) vaddr->user_message = addr->user_message; vaddr->basic_errno = addr->basic_errno; vaddr->more_errno = addr->more_errno; - vaddr->p.address_data = addr->p.address_data; + vaddr->prop.address_data = addr->prop.address_data; copyflag(vaddr, addr, af_pass_message); } return yield; @@ -1712,8 +1974,8 @@ while (addr_new != NULL) /* Just in case some router parameter refers to it. */ - return_path = (addr->p.errors_address != NULL)? - addr->p.errors_address : sender_address; + return_path = (addr->prop.errors_address != NULL)? + addr->prop.errors_address : sender_address; /* Split the address into domain and local part, handling the %-hack if necessary, and then route it. While routing a sender address, set @@ -1741,6 +2003,7 @@ while (addr_new != NULL) if (callout > 0) { host_item *host_list = addr->host_list; + transport_instance * tp; /* Make up some data for use in the case where there is no remote transport. */ @@ -1762,9 +2025,9 @@ while (addr_new != NULL) transport's options, so as to mimic what would happen if we were really sending a message to this address. */ - if (addr->transport != NULL && !addr->transport->info->local) + if ((tp = addr->transport) && !tp->info->local) { - (void)(addr->transport->setup)(addr->transport, addr, &tf, 0, 0, NULL); + (void)(tp->setup)(tp, addr, &tf, 0, 0, NULL); /* If the transport has hosts and the router does not, or if the transport is configured to override the router's hosts, we must build a @@ -1773,7 +2036,7 @@ while (addr_new != NULL) if (tf.hosts != NULL && (host_list == NULL || tf.hosts_override)) { uschar *s; - uschar *save_deliver_domain = deliver_domain; + const uschar *save_deliver_domain = deliver_domain; uschar *save_deliver_localpart = deliver_localpart; host_list = NULL; /* Ignore the router's hosts */ @@ -1788,12 +2051,11 @@ while (addr_new != NULL) { log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand list of hosts " "\"%s\" in %s transport for callout: %s", tf.hosts, - addr->transport->name, expand_string_message); + tp->name, expand_string_message); } else { int flags; - uschar *canonical_name; host_item *host, *nexthost; host_build_hostlist(&host_list, s, tf.hosts_randomize); @@ -1812,21 +2074,19 @@ while (addr_new != NULL) nexthost = host->next; if (tf.gethostbyname || string_is_ip_address(host->name, NULL) != 0) - (void)host_find_byname(host, NULL, flags, &canonical_name, TRUE); + (void)host_find_byname(host, NULL, flags, NULL, TRUE); else { - uschar * d_request = NULL, * d_require = NULL; - if (Ustrcmp(addr->transport->driver_name, "smtp") == 0) + dnssec_domains * dnssec_domains = NULL; + if (Ustrcmp(tp->driver_name, "smtp") == 0) { smtp_transport_options_block * ob = - (smtp_transport_options_block *) - addr->transport->options_block; - d_request = ob->dnssec_request_domains; - d_require = ob->dnssec_require_domains; + (smtp_transport_options_block *) tp->options_block; + dnssec_domains = &ob->dnssec; } (void)host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - d_request, d_require, &canonical_name, NULL); + dnssec_domains, NULL, NULL); } } } @@ -2007,7 +2267,7 @@ while (addr_new != NULL) /* If we have carried on to verify a child address, we want the value of $address_data to be that of the child */ - vaddr->p.address_data = addr->p.address_data; + vaddr->prop.address_data = addr->prop.address_data; yield = OK; goto out; } @@ -2030,17 +2290,18 @@ if (allok && addr_local == NULL && addr_remote == NULL) } for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++) - { - while (addr_list != NULL) + while (addr_list) { address_item *addr = addr_list; address_item *p = addr->parent; + transport_instance * tp = addr->transport; + addr_list = addr->next; fprintf(f, "%s", CS addr->address); #ifdef EXPERIMENTAL_SRS - if(addr->p.srs_sender) - fprintf(f, " [srs = %s]", addr->p.srs_sender); + if(addr->prop.srs_sender) + fprintf(f, " [srs = %s]", addr->prop.srs_sender); #endif /* If the address is a duplicate, show something about it. */ @@ -2048,67 +2309,56 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++) if (!testflag(addr, af_pfr)) { tree_node *tnode; - if ((tnode = tree_search(tree_duplicates, addr->unique)) != NULL) + if ((tnode = tree_search(tree_duplicates, addr->unique))) fprintf(f, " [duplicate, would not be delivered]"); else tree_add_duplicate(addr->unique, addr); } /* Now show its parents */ - while (p != NULL) - { + for (p = addr->parent; p; p = p->parent) fprintf(f, "\n <-- %s", p->address); - p = p->parent; - } fprintf(f, "\n "); /* Show router, and transport */ - fprintf(f, "router = %s, ", addr->router->name); - fprintf(f, "transport = %s\n", (addr->transport == NULL)? US"unset" : - addr->transport->name); + fprintf(f, "router = %s, transport = %s\n", + addr->router->name, tp ? tp->name : US"unset"); /* Show any hosts that are set up by a router unless the transport is going to override them; fiddle a bit to get a nice format. */ - if (addr->host_list != NULL && addr->transport != NULL && - !addr->transport->overrides_hosts) + if (addr->host_list && tp && !tp->overrides_hosts) { host_item *h; int maxlen = 0; int maxaddlen = 0; - for (h = addr->host_list; h != NULL; h = h->next) - { + for (h = addr->host_list; h; h = h->next) + { /* get max lengths of host names, addrs */ int len = Ustrlen(h->name); if (len > maxlen) maxlen = len; - len = (h->address != NULL)? Ustrlen(h->address) : 7; + len = h->address ? Ustrlen(h->address) : 7; if (len > maxaddlen) maxaddlen = len; } - for (h = addr->host_list; h != NULL; h = h->next) - { - int len = Ustrlen(h->name); - fprintf(f, " host %s ", h->name); - while (len++ < maxlen) fprintf(f, " "); - if (h->address != NULL) - { - fprintf(f, "[%s] ", h->address); - len = Ustrlen(h->address); - } - else if (!addr->transport->info->local) /* Omit [unknown] for local */ - { - fprintf(f, "[unknown] "); - len = 7; - } - else len = -3; - while (len++ < maxaddlen) fprintf(f," "); - if (h->mx >= 0) fprintf(f, "MX=%d", h->mx); + for (h = addr->host_list; h; h = h->next) + { + fprintf(f, " host %-*s ", maxlen, h->name); + + if (h->address) + fprintf(f, "[%s%-*c", h->address, maxaddlen+1 - Ustrlen(h->address), ']'); + else if (tp->info->local) + fprintf(f, " %-*s ", maxaddlen, ""); /* Omit [unknown] for local */ + else + fprintf(f, "[%s%-*c", "unknown", maxaddlen+1 - 7, ']'); + + if (h->mx >= 0) fprintf(f, " MX=%d", h->mx); if (h->port != PORT_NONE) fprintf(f, " port=%d", h->port); - if (h->status == hstatus_unusable) fprintf(f, " ** unusable **"); - fprintf(f, "\n"); + if (running_in_test_harness && h->dnssec == DS_YES) fputs(" AD", f); + if (h->status == hstatus_unusable) fputs(" ** unusable **", f); + fputc('\n', f); } } } - } /* Yield will be DEFER or FAIL if any one address has, only for full_info (which is the -bv or -bt case). */ @@ -2222,7 +2472,8 @@ for (h = header_list; h != NULL && yield == OK; h = h->next) verb = US"begins"; } - *msgptr = string_printing( + /* deconst cast ok as we're passing a non-const to string_printing() */ + *msgptr = US string_printing( string_sprintf("%s: failing address in \"%.*s:\" header %s: %.*s", errmess, tt - h->text, h->text, verb, len, s)); @@ -2649,7 +2900,7 @@ if (ip_bind(sock, host_af, interface_address, 0) < 0) if (ip_connect(sock, host_af, sender_host_address, port, rfc1413_query_timeout) < 0) { - if (errno == ETIMEDOUT && (log_extra_selector & LX_ident_timeout) != 0) + if (errno == ETIMEDOUT && LOGGING(ident_timeout)) { log_write(0, LOG_MAIN, "ident connection to %s timed out", sender_host_address); @@ -2743,9 +2994,9 @@ if (*p == 0) goto END_OFF; /* The rest of the line is the data we want. We turn it into printing characters when we save it, so that it cannot mess up the format of any logging or Received: lines into which it gets inserted. We keep a maximum of 127 -characters. */ +characters. The deconst cast is ok as we fed a nonconst to string_printing() */ -sender_ident = string_printing(string_copyn(p, 127)); +sender_ident = US string_printing(string_copyn(p, 127)); DEBUG(D_ident) debug_printf("sender_ident = %s\n", sender_ident); END_OFF: @@ -2790,7 +3041,7 @@ Returns: OK matched */ int -check_host(void *arg, uschar *ss, uschar **valueptr, uschar **error) +check_host(void *arg, const uschar *ss, const uschar **valueptr, uschar **error) { check_host_block *cb = (check_host_block *)arg; int mlen = -1; @@ -2798,7 +3049,7 @@ int maskoffset; BOOL iplookup = FALSE; BOOL isquery = FALSE; BOOL isiponly = cb->host_name != NULL && cb->host_name[0] == 0; -uschar *t; +const uschar *t; uschar *semicolon; uschar **aliases; @@ -2979,6 +3230,10 @@ if (*t == 0) h.address = NULL; h.mx = MX_NONE; + /* Using byname rather than bydns here means we cannot determine dnssec + status. On the other hand it is unclear how that could be either + propagated up or enforced. */ + rc = host_find_byname(&h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, FALSE); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) { @@ -3011,7 +3266,7 @@ on spec. */ if ((semicolon = Ustrchr(ss, ';')) != NULL) { - uschar *affix; + const uschar *affix; int partial, affixlen, starflags, id; *semicolon = 0; @@ -3112,12 +3367,12 @@ determined from the IP address, the result is FAIL unless the item "+allow_unknown" was met earlier in the list, in which case OK is returned. */ int -verify_check_this_host(uschar **listptr, unsigned int *cache_bits, - uschar *host_name, uschar *host_address, uschar **valueptr) +verify_check_this_host(const uschar **listptr, unsigned int *cache_bits, + const uschar *host_name, const uschar *host_address, const uschar **valueptr) { int rc; unsigned int *local_cache_bits = cache_bits; -uschar *save_host_address = deliver_host_address; +const uschar *save_host_address = deliver_host_address; check_host_block cb; cb.host_name = host_name; cb.host_address = host_address; @@ -3162,7 +3417,7 @@ return rc; int verify_check_given_host(uschar **listptr, host_item *host) { -return verify_check_this_host(listptr, NULL, host->name, host->address, NULL); +return verify_check_this_host(CUSS listptr, NULL, host->name, host->address, NULL); } /************************************************* @@ -3184,7 +3439,7 @@ Returns: the yield of verify_check_this_host(), int verify_check_host(uschar **listptr) { -return verify_check_this_host(listptr, sender_host_cache, NULL, +return verify_check_this_host(CUSS listptr, sender_host_cache, NULL, (sender_host_address == NULL)? US"" : sender_host_address, NULL); } @@ -3313,21 +3568,37 @@ if (!string_format(query, sizeof(query), "%s.%s", prepend, domain)) /* Look for this query in the cache. */ -t = tree_search(dnsbl_cache, query); +if ( (t = tree_search(dnsbl_cache, query)) + && (cb = t->data.ptr)->expiry > time(NULL) + ) + +/* Previous lookup was cached */ + + { + HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n"); + } /* If not cached from a previous lookup, we must do a DNS lookup, and cache the result in permanent memory. */ -if (t == NULL) +else { + uint ttl = 3600; + store_pool = POOL_PERM; - /* Set up a tree entry to cache the lookup */ + if (t) + { + HDEBUG(D_dnsbl) debug_printf("cached data found but past valid time; "); + } - t = store_get(sizeof(tree_node) + Ustrlen(query)); - Ustrcpy(t->name, query); - t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block)); - (void)tree_insertnode(&dnsbl_cache, t); + else + { /* Set up a tree entry to cache the lookup */ + t = store_get(sizeof(tree_node) + Ustrlen(query)); + Ustrcpy(t->name, query); + t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block)); + (void)tree_insertnode(&dnsbl_cache, t); + } /* Do the DNS loopup . */ @@ -3345,24 +3616,28 @@ if (t == NULL) Quite apart from one A6 RR generating multiple addresses, there are DNS lists that return more than one A record, so we must handle multiple - addresses generated in that way as well. */ + addresses generated in that way as well. + + Mark the cache entry with the "now" plus the minimum of the address TTLs, + or some suitably far-future time if none were found. */ if (cb->rc == DNS_SUCCEED) { dns_record *rr; dns_address **addrp = &(cb->rhs); for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { if (rr->type == T_A) { dns_address *da = dns_address_from_rr(&dnsa, rr); - if (da != NULL) + if (da) { *addrp = da; while (da->next != NULL) da = da->next; addrp = &(da->next); + if (ttl > rr->ttl) ttl = rr->ttl; } } } @@ -3374,17 +3649,10 @@ if (t == NULL) if (cb->rhs == NULL) cb->rc = DNS_NODATA; } + cb->expiry = time(NULL)+ttl; store_pool = old_pool; } -/* Previous lookup was cached */ - -else - { - HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n"); - cb = t->data.ptr; - } - /* We now have the result of the DNS lookup, either newly done, or cached from a previous call. If the lookup succeeded, check against the address list if there is one. This may be a positive equality list (introduced by @@ -3415,7 +3683,7 @@ if (cb->rc == DNS_SUCCEED) { int ipsep = ','; uschar ip[46]; - uschar *ptr = iplist; + const uschar *ptr = iplist; uschar *res; /* Handle exact matching */ @@ -3608,7 +3876,9 @@ Note: an address for testing DUL is 192.203.178.4 Note: a domain for testing RFCI is example.tld.dsn.rfc-ignorant.org Arguments: + where the acl type listptr the domain/address/data list + log_msgptr log message on error Returns: OK successful lookup (i.e. the address is on the list), or lookup deferred after +include_unknown @@ -3618,11 +3888,11 @@ Returns: OK successful lookup (i.e. the address is on the list), or */ int -verify_check_dnsbl(uschar **listptr) +verify_check_dnsbl(int where, const uschar ** listptr, uschar ** log_msgptr) { int sep = 0; int defer_return = FAIL; -uschar *list = *listptr; +const uschar *list = *listptr; uschar *domain; uschar *s; uschar buffer[1024]; @@ -3665,21 +3935,19 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL /* See if there's explicit data to be looked up */ - key = Ustrchr(domain, '/'); - if (key != NULL) *key++ = 0; + if ((key = Ustrchr(domain, '/'))) *key++ = 0; /* See if there's a list of addresses supplied after the domain name. This is introduced by an = or a & character; if preceded by = we require all matches and if preceded by ! we invert the result. */ - iplist = Ustrchr(domain, '='); - if (iplist == NULL) + if (!(iplist = Ustrchr(domain, '='))) { bitmask = TRUE; iplist = Ustrchr(domain, '&'); } - if (iplist != NULL) /* Found either = or & */ + if (iplist) /* Found either = or & */ { if (iplist > domain && iplist[-1] == '!') /* Handle preceding ! */ { @@ -3698,6 +3966,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL } } + /* If there is a comma in the domain, it indicates that a second domain for looking up TXT records is provided, before the main domain. Otherwise we must set domain_txt == domain. */ @@ -3743,6 +4012,13 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL if (key == NULL) { + if (where == ACL_WHERE_NOTSMTP_START || where == ACL_WHERE_NOTSMTP) + { + *log_msgptr = string_sprintf + ("cannot test auto-keyed dnslists condition in %s ACL", + acl_wherenames[where]); + return ERROR; + } if (sender_host_address == NULL) return FAIL; /* can never match */ if (revadd[0] == 0) invert_address(revadd, sender_host_address); rc = one_check_dnsbl(domain, domain_txt, sender_host_address, revadd, @@ -3768,7 +4044,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL uschar keybuffer[256]; uschar keyrevadd[128]; - while ((keydomain = string_nextinlist(&key, &keysep, keybuffer, + while ((keydomain = string_nextinlist(CUSS &key, &keysep, keybuffer, sizeof(keybuffer))) != NULL) { uschar *prepend = keydomain; |