diff -urN ../exim-4.94.2.orig/doc/ChangeLog ./doc/ChangeLog --- ../exim-4.94.2.orig/doc/ChangeLog 2021-04-30 15:08:21.000000000 +0300 +++ ./doc/ChangeLog 2021-06-14 01:52:12.760820000 +0300 @@ -220,6 +220,13 @@ QS/04 Always die if requested from internal logging, even is logging is disabled. +JH/52 Fix ${ip6norm:} operator. Previously, any trailing line text was dropped, + making it unusable in complex expressions. + +JH/53 Bug 2743: fix immediate-delivery via named queue. Previously this would + fail with a taint-check on the spoolfile name, and leave the message + queued. + Exim version 4.94 ----------------- diff -urN ../exim-4.94.2.orig/doc/NewStuff ./doc/NewStuff --- ../exim-4.94.2.orig/doc/NewStuff 2021-04-30 15:08:21.000000000 +0300 +++ ./doc/NewStuff 2021-06-14 01:52:12.763457000 +0300 @@ -6,6 +6,51 @@ test from the snapshots or the Git before the documentation is updated. Once the documentation is updated, this file is reduced to a short list. +Version 4.95 +------------ + + 1. The fast-ramp two phase queue run support, previously experimental, is + now supported by default. + + 2. The native SRS support, previously experimental, is now supported. It is + not built unless specified in the Local/Makefile. + + 3. TLS resumption support, previously experimental, is now supported and + included in default builds. + + 4. Single-key LMDB lookups, previously experimental, are now supported. + The support is not built unless specified in the Local/Makefile. + + 5. Option "message_linelength_limit" on the smtp transport to enforce (by + default) the RFC 998 character limit. + + 6. An option to ignore the cache on a lookup. + + 7. Quota checking during reception (i.e. at SMTP time) for appendfile- + transport-managed quotas. + + 8. Sqlite lookups accept a "file=" option to specify a per-operation + db file, replacing the previous prefix to the SQL string (which had + issues when the SQL used tainted values). + + 9. Lsearch lookups accept a "ret=full" option, to return both the portion + of the line matching the key, and the remainder. + +10. A command-line option to have a daemon not create a notifier socket. + +11. Faster TLS startup. When various configuration options contain no + expandable elements, the information can be preloaded and cached rather + than the provious behaviour of always loading at startup time for every + connection. This helps particularly for the CA bundle. + +12. Proxy Protocol Timeout is configurable via "proxy_protocol_timeout" + main config option. + +13. Option "smtp_accept_msx_per_connection" is now expanded. + +13. A main config option "allow_insecure_tainted_data" allows to turn + taint errors into warnings. + Version 4.94 ------------ diff -urN ../exim-4.94.2.orig/src/EDITME ./src/EDITME --- ../exim-4.94.2.orig/src/EDITME 2021-04-30 15:08:21.000000000 +0300 +++ ./src/EDITME 2021-06-14 01:52:12.780661000 +0300 @@ -749,6 +749,13 @@ # WHITELIST_D_MACROS=TLS:SPOOL +# The next setting enables a main config option +# "allow_insecure_tainted_data" to turn taint failures into warnings. +# Though this option is new, it is deprecated already now, and will be +# ignored in future releases of Exim. It is meant as mitigation for +# upgrading old (possibly insecure) configurations to more secure ones. +ALLOW_INSECURE_TAINTED_DATA=yes + #------------------------------------------------------------------------------ # Exim has support for the AUTH (authentication) extension of the SMTP # protocol, as defined by RFC 2554. If you don't know what SMTP authentication diff -urN ../exim-4.94.2.orig/src/acl.c ./src/acl.c --- ../exim-4.94.2.orig/src/acl.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/acl.c 2021-06-14 01:52:12.781958000 +0300 @@ -3598,20 +3598,22 @@ #endif case ACLC_QUEUE: - if (is_tainted(arg)) { - *log_msgptr = string_sprintf("Tainted name '%s' for queue not permitted", - arg); - return ERROR; + uschar *m; + if ((m = is_tainted2(arg, 0, "Tainted name '%s' for queue not permitted", arg))) + { + *log_msgptr = m; + return ERROR; + } + if (Ustrchr(arg, '/')) + { + *log_msgptr = string_sprintf( + "Directory separator not permitted in queue name: '%s'", arg); + return ERROR; + } + queue_name = string_copy_perm(arg, FALSE); + break; } - if (Ustrchr(arg, '/')) - { - *log_msgptr = string_sprintf( - "Directory separator not permitted in queue name: '%s'", arg); - return ERROR; - } - queue_name = string_copy_perm(arg, FALSE); - break; case ACLC_RATELIMIT: rc = acl_ratelimit(arg, where, log_msgptr); @@ -4007,10 +4009,8 @@ else if (*ss == '/') { struct stat statbuf; - if (is_tainted(ss)) + if (is_tainted2(ss, LOG_MAIN|LOG_PANIC, "Tainted ACL file name '%s'", ss)) { - log_write(0, LOG_MAIN|LOG_PANIC, - "attempt to open tainted ACL file name \"%s\"", ss); /* Avoid leaking info to an attacker */ *log_msgptr = US"internal configuration error"; return ERROR; diff -urN ../exim-4.94.2.orig/src/cnumber.h ./src/cnumber.h --- ../exim-4.94.2.orig/src/cnumber.h 2021-04-30 15:11:38.000000000 +0300 +++ ./src/cnumber.h 2021-06-14 01:52:52.109710000 +0300 @@ -1 +1 @@ -1 +2 diff -urN ../exim-4.94.2.orig/src/config.h.defaults ./src/config.h.defaults --- ../exim-4.94.2.orig/src/config.h.defaults 2021-04-30 15:08:21.000000000 +0300 +++ ./src/config.h.defaults 2021-06-14 01:52:12.787491000 +0300 @@ -17,6 +17,8 @@ #define ALT_CONFIG_PREFIX #define TRUSTED_CONFIG_LIST +#define ALLOW_INSECURE_TAINTED_DATA + #define APPENDFILE_MODE 0600 #define APPENDFILE_DIRECTORY_MODE 0700 #define APPENDFILE_LOCKFILE_MODE 0600 diff -urN ../exim-4.94.2.orig/src/dbstuff.h ./src/dbstuff.h --- ../exim-4.94.2.orig/src/dbstuff.h 2021-04-30 15:08:21.000000000 +0300 +++ ./src/dbstuff.h 2021-06-14 01:52:12.790783000 +0300 @@ -643,11 +643,9 @@ : (flags) == O_RDWR ? "O_RDWR" \ : (flags) == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" \ : "??"); \ - if (is_tainted(name) || is_tainted(dirname)) \ - { \ - log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted"); \ + if (is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted name '%s' for DB file not permitted", name) \ + || is_tainted2(dirname, LOG_MAIN|LOG_PANIC, "Tainted name '%s' for DB directory not permitted", dirname)) \ *dbpp = NULL; \ - } \ else \ { EXIM_DBOPEN__(name, dirname, flags, mode, dbpp); } \ DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", *dbpp); \ diff -urN ../exim-4.94.2.orig/src/deliver.c ./src/deliver.c --- ../exim-4.94.2.orig/src/deliver.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/deliver.c 2021-06-14 01:52:12.793231000 +0300 @@ -5538,10 +5538,11 @@ if (!s || !*s) log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand %s: '%s'\n", varname, filename); -else if (*s != '/' || is_tainted(s)) - log_write(0, LOG_MAIN|LOG_PANIC, - "%s is not %s after expansion: '%s'\n", - varname, *s == '/' ? "untainted" : "absolute", s); +else if (*s != '/') + log_write(0, LOG_MAIN|LOG_PANIC, "%s is not absolute after expansion: '%s'\n", + varname, s); +else if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted %s after expansion: '%s'\n", varname, s)) + ; else if (!(fp = Ufopen(s, "rb"))) log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for %s " "message texts: %s", s, reason, strerror(errno)); @@ -6151,9 +6152,10 @@ if (!tmp) p->message = string_sprintf("failed to expand \"%s\" as a " "system filter transport name", tpname); - if (is_tainted(tmp)) - p->message = string_sprintf("attempt to used tainted value '%s' for" - "transport '%s' as a system filter", tmp, tpname); + { uschar *m; + if ((m = is_tainted2(tmp, 0, "Tainted values '%s' " "for transport '%s' as a system filter", tmp, tpname))) + p->message = m; + } tpname = tmp; } else diff -urN ../exim-4.94.2.orig/src/directory.c ./src/directory.c --- ../exim-4.94.2.orig/src/directory.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/directory.c 2021-06-14 01:52:12.793310000 +0300 @@ -44,6 +44,11 @@ struct stat statbuf; uschar * path; +/* does not work with 4.94 +if (is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted path '%s' for new directory", name)) + { p = US"create"; path = US name; errno = EACCES; goto bad; } +*/ + if (parent) { path = string_sprintf("%s%s%s", parent, US"/", name); diff -urN ../exim-4.94.2.orig/src/exim.c ./src/exim.c --- ../exim-4.94.2.orig/src/exim.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/exim.c 2021-06-14 01:52:12.796873000 +0300 @@ -2789,9 +2789,11 @@ else badarg = TRUE; break; - /* -MCG: set the queue name, to a non-default value */ + /* -MCG: set the queue name, to a non-default value. Arguably, anything + from the commandline should be tainted - but we will need an untainted + value for the spoolfile when doing a -odi delivery process. */ - case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), TRUE); + case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), FALSE); else badarg = TRUE; break; diff -urN ../exim-4.94.2.orig/src/expand.c ./src/expand.c --- ../exim-4.94.2.orig/src/expand.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/expand.c 2021-06-14 01:52:12.802112000 +0300 @@ -4383,13 +4383,13 @@ f.expand_string_forcedfail = FALSE; expand_string_message = US""; -if (is_tainted(string)) +{ uschar *m; +if ((m = is_tainted2(string, LOG_MAIN|LOG_PANIC, "Tainted string '%s' in expansion", s))) { - expand_string_message = - string_sprintf("attempt to expand tainted string '%s'", s); - log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); + expand_string_message = m; goto EXPAND_FAILED; } +} while (*s != 0) { @@ -7629,10 +7629,12 @@ /* Manually track tainting, as we deal in individual chars below */ if (is_tainted(sub)) + { if (yield->s && yield->ptr) gstring_rebuffer(yield); else yield->s = store_get(yield->size = Ustrlen(sub), TRUE); + } /* Check the UTF-8, byte-by-byte */ @@ -8193,6 +8195,7 @@ EXPAND_FAILED: if (left) *left = s; DEBUG(D_expand) + { DEBUG(D_noutf8) { debug_printf_indent("|failed to expand: %s\n", string); @@ -8212,6 +8215,7 @@ if (f.expand_string_forcedfail) debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n"); } + } if (resetok_p && !resetok) *resetok_p = FALSE; expand_level--; return NULL; diff -urN ../exim-4.94.2.orig/src/functions.h ./src/functions.h --- ../exim-4.94.2.orig/src/functions.h 2021-04-30 15:08:21.000000000 +0300 +++ ./src/functions.h 2021-06-14 01:52:12.803027000 +0300 @@ -1084,36 +1084,66 @@ /******************************************************************************/ /* Taint-checked file opens */ +static inline uschar * +is_tainted2(const void *p, int lflags, const char* fmt, ...) +{ +va_list ap; +uschar *msg; +rmark mark; +if (!is_tainted(p)) + return NULL; + +mark = store_mark(); +va_start(ap, fmt); +msg = string_from_gstring(string_vformat(NULL, SVFMT_TAINT_NOCHK|SVFMT_EXTEND, fmt, ap)); +va_end(ap); + +#ifdef ALLOW_INSECURE_TAINTED_DATA +if (allow_insecure_tainted_data) + { + if LOGGING(tainted) log_write(0, LOG_MAIN, "Warning: %s", msg); + store_reset(mark); + return NULL; + } +#endif + +if (lflags) log_write(0, lflags, "%s", msg); +return msg; /* no store_reset(), as the message might be used afterwards and Exim + is expected to exit anyway, so we do not care about the leaked + storage */ +} + static inline int exim_open2(const char *pathname, int flags) { -if (!is_tainted(pathname)) return open(pathname, flags); -log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); +if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) + return open(pathname, flags); errno = EACCES; return -1; } + static inline int exim_open(const char *pathname, int flags, mode_t mode) { -if (!is_tainted(pathname)) return open(pathname, flags, mode); -log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); +if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) + return open(pathname, flags, mode); errno = EACCES; return -1; } static inline int exim_openat(int dirfd, const char *pathname, int flags) { -if (!is_tainted(pathname)) return openat(dirfd, pathname, flags); -log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); +if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) + return openat(dirfd, pathname, flags); errno = EACCES; return -1; } static inline int exim_openat4(int dirfd, const char *pathname, int flags, mode_t mode) { -if (!is_tainted(pathname)) return openat(dirfd, pathname, flags, mode); -log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); +if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) + return openat(dirfd, pathname, flags, mode); errno = EACCES; return -1; } @@ -1121,8 +1151,8 @@ static inline FILE * exim_fopen(const char *pathname, const char *mode) { -if (!is_tainted(pathname)) return fopen(pathname, mode); -log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname); +if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname)) + return fopen(pathname, mode); errno = EACCES; return NULL; } @@ -1130,8 +1160,8 @@ static inline DIR * exim_opendir(const uschar * name) { -if (!is_tainted(name)) return opendir(CCS name); -log_write(0, LOG_MAIN|LOG_PANIC, "Tainted dirname '%s'", name); +if (!is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted dirname '%s'", name)) + return opendir(CCS name); errno = EACCES; return NULL; } diff -urN ../exim-4.94.2.orig/src/globals.c ./src/globals.c --- ../exim-4.94.2.orig/src/globals.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/globals.c 2021-06-14 01:52:12.803387000 +0300 @@ -98,6 +98,10 @@ BOOL move_frozen_messages = FALSE; #endif +#ifdef ALLOW_INSECURE_TAINTED_DATA +BOOL allow_insecure_tainted_data = FALSE; +#endif + /* These variables are outside the #ifdef because it keeps the code less cluttered in several places (e.g. during logging) if we can always refer to them. Also, the tls_ variables are now always visible. Note that these are @@ -1034,6 +1038,9 @@ Li_size_reject, Li_skip_delivery, Li_smtp_confirmation, +#ifdef ALLOW_INSECURE_TAINTED_DATA + Li_tainted, +#endif Li_tls_certificate_verified, Li_tls_cipher, -1 @@ -1101,6 +1108,9 @@ BIT_TABLE(L, smtp_protocol_error), BIT_TABLE(L, smtp_syntax_error), BIT_TABLE(L, subject), +#ifdef ALLOW_INSECURE_TAINTED_DATA + BIT_TABLE(L, tainted), +#endif BIT_TABLE(L, tls_certificate_verified), BIT_TABLE(L, tls_cipher), BIT_TABLE(L, tls_peerdn), diff -urN ../exim-4.94.2.orig/src/globals.h ./src/globals.h --- ../exim-4.94.2.orig/src/globals.h 2021-04-30 15:08:21.000000000 +0300 +++ ./src/globals.h 2021-06-14 01:52:12.803884000 +0300 @@ -77,6 +77,10 @@ extern BOOL move_frozen_messages; /* Get them out of the normal directory */ #endif +#ifdef ALLOW_INSECURE_TAINTED_DATA +extern BOOL allow_insecure_tainted_data; +#endif + /* These variables are outside the #ifdef because it keeps the code less cluttered in several places (e.g. during logging) if we can always refer to them. Also, the tls_ variables are now always visible. */ diff -urN ../exim-4.94.2.orig/src/host.c ./src/host.c --- ../exim-4.94.2.orig/src/host.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/host.c 2021-06-14 01:52:12.805394000 +0300 @@ -1197,9 +1197,9 @@ c++; } -c[-1] = '\0'; /* drop trailing colon */ +*--c = '\0'; /* drop trailing colon */ -/* debug_printf("%s: D k %d <%s> <%s>\n", __FUNCTION__, k, d, d + 2*(k+1)); */ +/* debug_printf("%s: D k %d <%s> <%s>\n", __FUNCTION__, k, buffer, buffer + 2*(k+1)); */ if (k >= 0) { /* collapse */ c = d + 2*(k+1); @@ -1581,7 +1581,7 @@ if (hosts->h_aliases) { - int count = 1; + int count = 1; /* need 1 more for terminating NULL */ uschar **ptr; for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++; @@ -1690,7 +1690,7 @@ { uschar **aptr = NULL; int ssize = 264; - int count = 0; + int count = 1; /* need 1 more for terminating NULL */ int old_pool = store_pool; sender_host_dnssec = dns_is_secure(dnsa); diff -urN ../exim-4.94.2.orig/src/log.c ./src/log.c --- ../exim-4.94.2.orig/src/log.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/log.c 2021-06-14 01:52:12.806414000 +0300 @@ -287,8 +287,11 @@ uschar *lastslash = Ustrrchr(name, '/'); *lastslash = 0; created = directory_make(NULL, name, LOG_DIRECTORY_MODE, FALSE); - DEBUG(D_any) debug_printf("%s log directory %s\n", - created ? "created" : "failed to create", name); + DEBUG(D_any) + if (created) + debug_printf("created log directory %s\n", name); + else + debug_printf("failed to create log directory %s: %s\n", name, strerror(errno)); *lastslash = '/'; if (created) fd = Uopen(name, flags, LOG_MODE); } @@ -394,9 +397,7 @@ const uid_t euid = geteuid(); if (euid == exim_uid) - { fd = log_open_already_exim(name); - } else if (euid == root_uid) { int sock[2]; @@ -457,7 +458,7 @@ it does not exist. This may be called recursively on failure, in order to open the panic log. -The directory is in the static variable file_path. This is static so that it +The directory is in the static variable file_path. This is static so that the work of sorting out the path is done just once per Exim process. Exim is normally configured to avoid running as root wherever possible, the log @@ -492,60 +493,64 @@ ok = string_format(buffer, sizeof(buffer), CS file_path, log_names[type]); -/* Save the name of the mainlog for rollover processing. Without a datestamp, -it gets statted to see if it has been cycled. With a datestamp, the datestamp -will be compared. The static slot for saving it is the same size as buffer, -and the text has been checked above to fit, so this use of strcpy() is OK. */ - -if (type == lt_main && string_datestamp_offset >= 0) +switch (type) { - Ustrcpy(mainlog_name, buffer); - mainlog_datestamp = mainlog_name + string_datestamp_offset; - } + case lt_main: + /* Save the name of the mainlog for rollover processing. Without a datestamp, + it gets statted to see if it has been cycled. With a datestamp, the datestamp + will be compared. The static slot for saving it is the same size as buffer, + and the text has been checked above to fit, so this use of strcpy() is OK. */ -/* Ditto for the reject log */ + Ustrcpy(mainlog_name, buffer); + if (string_datestamp_offset > 0) + mainlog_datestamp = mainlog_name + string_datestamp_offset; + break; -else if (type == lt_reject && string_datestamp_offset >= 0) - { - Ustrcpy(rejectlog_name, buffer); - rejectlog_datestamp = rejectlog_name + string_datestamp_offset; - } + case lt_reject: + /* Ditto for the reject log */ -/* and deal with the debug log (which keeps the datestamp, but does not -update it) */ + Ustrcpy(rejectlog_name, buffer); + if (string_datestamp_offset > 0) + rejectlog_datestamp = rejectlog_name + string_datestamp_offset; + break; -else if (type == lt_debug) - { - Ustrcpy(debuglog_name, buffer); - if (tag) - { - /* this won't change the offset of the datestamp */ - ok2 = string_format(buffer, sizeof(buffer), "%s%s", - debuglog_name, tag); - if (ok2) - Ustrcpy(debuglog_name, buffer); - } - } + case lt_debug: + /* and deal with the debug log (which keeps the datestamp, but does not + update it) */ -/* Remove any datestamp if this is the panic log. This is rare, so there's no -need to optimize getting the datestamp length. We remove one non-alphanumeric -char afterwards if at the start, otherwise one before. */ + Ustrcpy(debuglog_name, buffer); + if (tag) + { + /* this won't change the offset of the datestamp */ + ok2 = string_format(buffer, sizeof(buffer), "%s%s", + debuglog_name, tag); + if (ok2) + Ustrcpy(debuglog_name, buffer); + } + break; -else if (string_datestamp_offset >= 0) - { - uschar * from = buffer + string_datestamp_offset; - uschar * to = from + string_datestamp_length; + default: + /* Remove any datestamp if this is the panic log. This is rare, so there's no + need to optimize getting the datestamp length. We remove one non-alphanumeric + char afterwards if at the start, otherwise one before. */ - if (from == buffer || from[-1] == '/') - { - if (!isalnum(*to)) to++; - } - else - if (!isalnum(from[-1])) from--; + if (string_datestamp_offset >= 0) + { + uschar * from = buffer + string_datestamp_offset; + uschar * to = from + string_datestamp_length; - /* This copy is ok, because we know that to is a substring of from. But - due to overlap we must use memmove() not Ustrcpy(). */ - memmove(from, to, Ustrlen(to)+1); + if (from == buffer || from[-1] == '/') + { + if (!isalnum(*to)) to++; + } + else + if (!isalnum(from[-1])) from--; + + /* This copy is ok, because we know that to is a substring of from. But + due to overlap we must use memmove() not Ustrcpy(). */ + memmove(from, to, Ustrlen(to)+1); + } + break; } /* If the file name is too long, it is an unrecoverable disaster */ @@ -559,9 +564,7 @@ *fd = log_open_as_exim(buffer); if (*fd >= 0) - { return; - } euid = geteuid(); @@ -713,26 +716,62 @@ } +/* Pull the file out of the configured or the compiled-in list. +Called for an empty log_file_path element, for debug logging activation +when file_path has not previously been set, and from the appenfile transport setup. */ -static void -set_file_path(void) +void +set_file_path(BOOL *multiple) { +uschar *s; int sep = ':'; /* Fixed separator - outside use */ -uschar *t; -const uschar *tt = US LOG_FILE_PATH; -while ((t = string_nextinlist(&tt, &sep, log_buffer, LOG_BUFFER_SIZE))) - { - if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; - file_path = string_copy(t); - break; - } +const uschar *ss = *log_file_path ? log_file_path : US LOG_FILE_PATH; + +if (*ss) + for (logging_mode = 0; + s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE); ) + { + if (Ustrcmp(s, "syslog") == 0) + logging_mode |= LOG_MODE_SYSLOG; + else if (!(logging_mode & LOG_MODE_FILE)) /* no file yet */ + { + logging_mode |= LOG_MODE_FILE; + if (*s) file_path = string_copy(s); /* If a non-empty path is given, use it */ + } + else if (multiple) *multiple = TRUE; + } +else + logging_mode = LOG_MODE_FILE; + +/* Set up the ultimate default if necessary. */ + +if (logging_mode & LOG_MODE_FILE && !*file_path) + if (LOG_FILE_PATH[0]) + { + /* If we still do not have a file_path, we take + the first non-empty, non-syslog item in LOG_FILE_PATH, if there is + one. If there is no such item, use the ultimate default in the + spool directory. */ + + for (ss = US LOG_FILE_PATH; + s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE);) + { + if (*s != '/') continue; + file_path = string_copy(s); + } + } + else file_path = string_sprintf("%s/log/%%slog", spool_directory); } void mainlog_close(void) { -if (mainlogfd < 0) return; +/* avoid closing it if it is closed already or if we do not see a chance +to open the file mainlog later again */ +if (mainlogfd < 0 /* already closed */ + || !(geteuid() == 0 || geteuid() == exim_uid)) + return; (void)close(mainlogfd); mainlogfd = -1; mainlog_inode = 0; @@ -844,53 +883,18 @@ store_pool = POOL_PERM; - /* If nothing has been set, don't waste effort... the default values for the - statics are file_path="" and logging_mode = LOG_MODE_FILE. */ + /* make sure that we have a valid log file path in "file_path", + the open_log() later relies on it */ + set_file_path(&multiple); - if (*log_file_path) - { - int sep = ':'; /* Fixed separator - outside use */ - uschar *s; - const uschar *ss = log_file_path; - - logging_mode = 0; - while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) - { - if (Ustrcmp(s, "syslog") == 0) - logging_mode |= LOG_MODE_SYSLOG; - else if (logging_mode & LOG_MODE_FILE) - multiple = TRUE; - else - { - logging_mode |= LOG_MODE_FILE; - - /* If a non-empty path is given, use it */ - - if (*s) - file_path = string_copy(s); - - /* If the path is empty, we want to use the first non-empty, non- - syslog item in LOG_FILE_PATH, if there is one, since the value of - log_file_path may have been set at runtime. If there is no such item, - use the ultimate default in the spool directory. */ - - else - set_file_path(); /* Empty item in log_file_path */ - } /* First non-syslog item in log_file_path */ - } /* Scan of log_file_path */ - } - /* If no modes have been selected, it is a major disaster */ if (logging_mode == 0) die(US"Neither syslog nor file logging set in log_file_path", US"Unexpected logging failure"); - /* Set up the ultimate default if necessary. Then revert to the old store - pool, and record that we've sorted out the path. */ + /* Revert to the old store pool, and record that we've sorted out the path. */ - if (logging_mode & LOG_MODE_FILE && !file_path[0]) - file_path = string_sprintf("%s/log/%%slog", spool_directory); store_pool = old_pool; path_inspected = TRUE; @@ -1244,6 +1248,7 @@ if (logging_mode & LOG_MODE_FILE) { + if (!*file_path) set_file_path(NULL); panic_recurseflag = TRUE; open_log(&paniclogfd, lt_panic, NULL); /* Won't return on failure */ panic_recurseflag = FALSE; @@ -1499,7 +1504,7 @@ resulting in certain setup not having been done. Hack this for now so we do not segfault; note that nondefault log locations will not work */ -if (!*file_path) set_file_path(); +if (!*file_path) set_file_path(NULL); open_log(&fd, lt_debug, tag_name); @@ -1521,5 +1526,14 @@ unlink_log(lt_debug); } +/* Called from the appendfile transport setup. */ +void +open_logs(void) +{ +set_file_path(NULL); +if (!(logging_mode & LOG_MODE_FILE)) return; +open_log(&mainlogfd, lt_main, 0); +open_log(&rejectlogfd, lt_reject, 0); +} /* End of log.c */ diff -urN ../exim-4.94.2.orig/src/lookups/lf_sqlperform.c ./src/lookups/lf_sqlperform.c --- ../exim-4.94.2.orig/src/lookups/lf_sqlperform.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/lookups/lf_sqlperform.c 2021-06-14 01:52:12.808762000 +0300 @@ -102,11 +102,13 @@ } } - if (is_tainted(server)) - { - *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); + { uschar *m; + if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server))) + { + *errmsg = m; return DEFER; } + } rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts); if (rc != DEFER || defer_break) return rc; @@ -158,11 +160,13 @@ server = ele; } - if (is_tainted(server)) + { uschar *m; + if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server))) { - *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); + *errmsg = m; return DEFER; } + } rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts); if (rc != DEFER || defer_break) return rc; diff -urN ../exim-4.94.2.orig/src/macros.h ./src/macros.h --- ../exim-4.94.2.orig/src/macros.h 2021-04-30 15:08:21.000000000 +0300 +++ ./src/macros.h 2021-06-14 01:52:12.811619000 +0300 @@ -497,6 +497,9 @@ Li_smtp_mailauth, Li_smtp_no_mail, Li_subject, +#ifdef ALLOW_INSECURE_TAINTED_DATA + Li_tainted, +#endif Li_tls_certificate_verified, Li_tls_cipher, Li_tls_peerdn, diff -urN ../exim-4.94.2.orig/src/parse.c ./src/parse.c --- ../exim-4.94.2.orig/src/parse.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/parse.c 2021-06-14 01:52:12.814450000 +0300 @@ -1410,12 +1410,8 @@ return FF_ERROR; } - if (is_tainted(filename)) - { - *error = string_sprintf("Tainted name '%s' for included file not permitted\n", - filename); + if ((*error = is_tainted2(filename, 0, "Tainted name '%s' for included file not permitted\n", filename))) return FF_ERROR; - } /* Check file name if required */ diff -urN ../exim-4.94.2.orig/src/rda.c ./src/rda.c --- ../exim-4.94.2.orig/src/rda.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/rda.c 2021-06-14 01:52:12.816438000 +0300 @@ -179,10 +179,8 @@ /* Reading a file is a form of expansion; we wish to deny attackers the capability to specify the file name. */ -if (is_tainted(filename)) +if ((*error = is_tainted2(filename, 0, "Tainted name '%s' for file read not permitted\n", filename))) { - *error = string_sprintf("Tainted name '%s' for file read not permitted\n", - filename); *yield = FF_ERROR; return NULL; } diff -urN ../exim-4.94.2.orig/src/readconf.c ./src/readconf.c --- ../exim-4.94.2.orig/src/readconf.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/readconf.c 2021-06-14 01:52:12.817420000 +0300 @@ -68,6 +68,9 @@ { "add_environment", opt_stringptr, {&add_environment} }, { "admin_groups", opt_gidlist, {&admin_groups} }, { "allow_domain_literals", opt_bool, {&allow_domain_literals} }, +#ifdef ALLOW_INSECURE_TAINTED_DATA + { "allow_insecure_tainted_data", opt_bool, {&allow_insecure_tainted_data} }, +#endif { "allow_mx_to_ip", opt_bool, {&allow_mx_to_ip} }, { "allow_utf8_domains", opt_bool, {&allow_utf8_domains} }, { "auth_advertise_hosts", opt_stringptr, {&auth_advertise_hosts} }, diff -urN ../exim-4.94.2.orig/src/routers/rf_get_transport.c ./src/routers/rf_get_transport.c --- ../exim-4.94.2.orig/src/routers/rf_get_transport.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/routers/rf_get_transport.c 2021-06-14 01:52:12.822364000 +0300 @@ -66,10 +66,8 @@ "\"%s\" in %s router: %s", tpname, router_name, expand_string_message); return FALSE; } - if (is_tainted(ss)) + if (is_tainted2(ss, LOG_MAIN|LOG_PANIC, "Tainted tainted value '%s' from '%s' for transport", ss, tpname)) { - log_write(0, LOG_MAIN|LOG_PANIC, - "attempt to use tainted value '%s' from '%s' for transport", ss, tpname); addr->basic_errno = ERRNO_BADTRANSPORT; /* Avoid leaking info to an attacker */ addr->message = US"internal configuration error"; diff -urN ../exim-4.94.2.orig/src/search.c ./src/search.c --- ../exim-4.94.2.orig/src/search.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/search.c 2021-06-14 01:52:12.822998000 +0300 @@ -343,12 +343,8 @@ uschar keybuffer[256]; int old_pool = store_pool; -if (filename && is_tainted(filename)) - { - log_write(0, LOG_MAIN|LOG_PANIC, - "Tainted filename for search: '%s'", filename); +if (filename && is_tainted2(filename, LOG_MAIN|LOG_PANIC, "Tainted filename for search '%s'", filename)) return NULL; - } /* Change to the search store pool and remember our reset point */ @@ -639,7 +635,7 @@ /* Arrange to put this database at the top of the LRU chain if it is a type that opens real files. */ -if ( open_top != (tree_node *)handle +if ( open_top != (tree_node *)handle && lookup_list[t->name[0]-'0']->type == lookup_absfile) { search_cache *c = (search_cache *)(t->data.ptr); diff -urN ../exim-4.94.2.orig/src/smtp_out.c ./src/smtp_out.c --- ../exim-4.94.2.orig/src/smtp_out.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/smtp_out.c 2021-06-14 01:52:12.825538000 +0300 @@ -53,11 +53,8 @@ return FALSE; } -if (is_tainted(expint)) +if (is_tainted2(expint, LOG_MAIN|LOG_PANIC, "Tainted value '%s' from '%s' for interface", expint, istring)) { - log_write(0, LOG_MAIN|LOG_PANIC, - "attempt to use tainted value '%s' from '%s' for interface", - expint, istring); addr->transport_return = PANIC; addr->message = string_sprintf("failed to expand \"interface\" " "option for %s: configuration error", msg); @@ -425,7 +422,7 @@ { int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface, sc->tblock, ob->connect_timeout); - + if (sock >= 0) { if (early_data && early_data->data && early_data->len) diff -urN ../exim-4.94.2.orig/src/transports/appendfile.c ./src/transports/appendfile.c --- ../exim-4.94.2.orig/src/transports/appendfile.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/transports/appendfile.c 2021-06-14 01:52:12.832423000 +0300 @@ -217,6 +217,9 @@ Returns: OK, FAIL, or DEFER */ +void +open_logs(void); + static int appendfile_transport_setup(transport_instance *tblock, address_item *addrlist, transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg) @@ -231,6 +234,9 @@ uid = uid; gid = gid; +/* we can't wait until we're not privileged anymore */ +open_logs(); + if (ob->expand_maildir_use_size_file) ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file, US"`maildir_use_size_file` in transport", tblock->name); @@ -1286,12 +1292,14 @@ expand_string_message); goto ret_panic; } -if (is_tainted(path)) +{ uschar *m; +if ((m = is_tainted2(path, 0, "Tainted '%s' (file or directory " + "name for %s transport) not permitted", path, tblock->name))) { - addr->message = string_sprintf("Tainted '%s' (file or directory " - "name for %s transport) not permitted", path, tblock->name); + addr->message = m; goto ret_panic; } +} if (path[0] != '/') { diff -urN ../exim-4.94.2.orig/src/transports/autoreply.c ./src/transports/autoreply.c --- ../exim-4.94.2.orig/src/transports/autoreply.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/transports/autoreply.c 2021-06-14 01:52:12.832709000 +0300 @@ -404,14 +404,15 @@ if (oncelog && *oncelog && to) { + uschar *m; time_t then = 0; - if (is_tainted(oncelog)) + if ((m = is_tainted2(oncelog, 0, "Tainted '%s' (once file for %s transport)" + " not permitted", oncelog, tblock->name))) { addr->transport_return = DEFER; addr->basic_errno = EACCES; - addr->message = string_sprintf("Tainted '%s' (once file for %s transport)" - " not permitted", oncelog, tblock->name); + addr->message = m; goto END_OFF; } @@ -515,13 +516,14 @@ if (then != 0 && (once_repeat_sec <= 0 || now - then < once_repeat_sec)) { + uschar *m; int log_fd; - if (is_tainted(logfile)) + if ((m = is_tainted2(logfile, 0, "Tainted '%s' (logfile for %s transport)" + " not permitted", logfile, tblock->name))) { addr->transport_return = DEFER; addr->basic_errno = EACCES; - addr->message = string_sprintf("Tainted '%s' (logfile for %s transport)" - " not permitted", logfile, tblock->name); + addr->message = m; goto END_OFF; } @@ -548,12 +550,13 @@ /* We are going to send a message. Ensure any requested file is available. */ if (file) { - if (is_tainted(file)) + uschar *m; + if ((m = is_tainted2(file, 0, "Tainted '%s' (file for %s transport)" + " not permitted", file, tblock->name))) { addr->transport_return = DEFER; addr->basic_errno = EACCES; - addr->message = string_sprintf("Tainted '%s' (file for %s transport)" - " not permitted", file, tblock->name); + addr->message = m; return FALSE; } if (!(ff = Ufopen(file, "rb")) && !ob->file_optional) diff -urN ../exim-4.94.2.orig/src/transports/pipe.c ./src/transports/pipe.c --- ../exim-4.94.2.orig/src/transports/pipe.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/transports/pipe.c 2021-06-14 01:52:12.833325000 +0300 @@ -599,13 +599,16 @@ tblock->name); return FALSE; } -if (is_tainted(cmd)) + +{ uschar *m; +if ((m = is_tainted2(cmd, 0, "Tainted '%s' (command " + "for %s transport) not permitted", cmd, tblock->name))) { - addr->message = string_sprintf("Tainted '%s' (command " - "for %s transport) not permitted", cmd, tblock->name); addr->transport_return = PANIC; + addr->message = m; return FALSE; } +} /* When a pipe is set up by a filter file, there may be values for $thisaddress and numerical the variables in existence. These are passed in diff -urN ../exim-4.94.2.orig/src/transports/smtp.c ./src/transports/smtp.c --- ../exim-4.94.2.orig/src/transports/smtp.c 2021-04-30 15:08:21.000000000 +0300 +++ ./src/transports/smtp.c 2021-06-14 01:52:12.835456000 +0300 @@ -2015,7 +2015,7 @@ { case OK: sx->conn_args.dane = TRUE; ob->tls_tempfail_tryclear = FALSE; /* force TLS */ - ob->tls_sni = sx->first_addr->domain; /* force SNI */ + ob->tls_sni = sx->conn_args.host->name; /* force SNI */ break; case FAIL_FORCED: break; default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, @@ -2097,7 +2097,7 @@ { case OK: sx->conn_args.dane = TRUE; ob->tls_tempfail_tryclear = FALSE; /* force TLS */ - ob->tls_sni = sx->first_addr->domain; /* force SNI */ + ob->tls_sni = sx->conn_args.host->name; /* force SNI */ break; case FAIL_FORCED: break; default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, @@ -4715,11 +4715,8 @@ else if (ob->hosts_randomize) s = expanded_hosts = string_copy(s); - if (is_tainted(s)) + if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted host list '%s' from '%s' in transport %s", s, ob->hosts, tblock->name)) { - log_write(0, LOG_MAIN|LOG_PANIC, - "attempt to use tainted host list '%s' from '%s' in transport %s", - s, ob->hosts, tblock->name); /* Avoid leaking info to an attacker */ addrlist->message = US"internal configuration error"; addrlist->transport_return = PANIC; diff -urN ../exim-4.94.2.orig/src/version.h ./src/version.h --- ../exim-4.94.2.orig/src/version.h 2021-04-30 15:11:38.000000000 +0300 +++ ./src/version.h 2021-06-14 01:52:13.393946000 +0300 @@ -1,5 +1,5 @@ /* automatically generated file - see ../scripts/reversion */ -#define EXIM_RELEASE_VERSION "4.94.2" +#define EXIM_RELEASE_VERSION "4.94.2-41-3ce94d3b9" #ifdef EXIM_VARIANT_VERSION #define EXIM_VERSION_STR EXIM_RELEASE_VERSION "-" EXIM_VARIANT_VERSION #else diff -urN ../exim-4.94.2.orig/src/version.sh ./src/version.sh --- ../exim-4.94.2.orig/src/version.sh 2021-04-30 15:11:38.000000000 +0300 +++ ./src/version.sh 2021-06-14 01:52:52.110150000 +0300 @@ -1,3 +1,3 @@ # automatically generated file - see ../scripts/reversion -EXIM_RELEASE_VERSION="4.94.2" -EXIM_COMPILE_NUMBER="1" +EXIM_RELEASE_VERSION="4.94.2-41-3ce94d3b9" +EXIM_COMPILE_NUMBER="2"