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-05-05 22:55:15.836518000 +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-05-05 22:55:15.837991000 +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/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-05-05 22:55:15.845332000 +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-05-05 22:55:15.849335000 +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-05-05 22:55:15.852461000 +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-05-05 22:55:15.852570000 +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/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-05-05 22:55:15.863779000 +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-05-05 22:55:15.864927000 +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-05-05 22:55:15.865487000 +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-05-05 22:55:15.866261000 +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/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-05-05 22:55:15.870144000 +0300 @@ -492,62 +492,55 @@ 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. */ + Ustrcpy(mainlog_name, buffer); + if (string_datestamp_offset > 0) + mainlog_datestamp = mainlog_name + string_datestamp_offset; + case lt_reject: + /* Ditto for the reject log */ + Ustrcpy(rejectlog_name, buffer); + if (string_datestamp_offset > 0) + rejectlog_datestamp = rejectlog_name + string_datestamp_offset; + case lt_debug: + /* and deal with the debug log (which keeps the datestamp, but does not + update it) */ + Ustrcpy(debuglog_name, buffer); + if (tag) + { + /* this won't change the offset of the datestamp */ + ok2 = string_format(buffer, sizeof(buffer), "%s%s", + debuglog_name, tag); + if (ok2) + Ustrcpy(debuglog_name, buffer); + } + default: + /* Remove any datestamp if this is the panic log. This is rare, so there's no + need to optimize getting the datestamp length. We remove one non-alphanumeric + char afterwards if at the start, otherwise one before. */ + if (string_datestamp_offset >= 0) + { + uschar * from = buffer + string_datestamp_offset; + uschar * to = from + string_datestamp_length; -/* Ditto for the reject log */ + if (from == buffer || from[-1] == '/') + { + if (!isalnum(*to)) to++; + } + else + if (!isalnum(from[-1])) from--; -else if (type == lt_reject && string_datestamp_offset >= 0) - { - Ustrcpy(rejectlog_name, buffer); - rejectlog_datestamp = rejectlog_name + string_datestamp_offset; + /* 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); + } } -/* and deal with the debug log (which keeps the datestamp, but does not -update it) */ - -else if (type == lt_debug) - { - Ustrcpy(debuglog_name, buffer); - if (tag) - { - /* this won't change the offset of the datestamp */ - ok2 = string_format(buffer, sizeof(buffer), "%s%s", - debuglog_name, tag); - if (ok2) - Ustrcpy(debuglog_name, buffer); - } - } - -/* Remove any datestamp if this is the panic log. This is rare, so there's no -need to optimize getting the datestamp length. We remove one non-alphanumeric -char afterwards if at the start, otherwise one before. */ - -else if (string_datestamp_offset >= 0) - { - uschar * from = buffer + string_datestamp_offset; - uschar * to = from + string_datestamp_length; - - 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); - } - /* If the file name is too long, it is an unrecoverable disaster */ if (!ok) @@ -713,18 +706,36 @@ } - -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))) +const uschar *ss = *log_file_path ? log_file_path : US LOG_FILE_PATH; + +logging_mode = 0; +while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) { - if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; - file_path = string_copy(t); - break; + if (Ustrcmp(s, "syslog") == 0) + logging_mode |= LOG_MODE_SYSLOG; + else if (logging_mode & LOG_MODE_FILE) /* we know a file already */ + { + if (multiple) *multiple = TRUE; + } + else + { + logging_mode |= LOG_MODE_FILE; + + /* If a non-empty path is given, use it */ + + if (*s) + file_path = string_copy(s); + + /* If the path is empty, we want to use the first non-empty, non- + syslog item in LOG_FILE_PATH, if there is one, since the value of + log_file_path may have been set at runtime. If there is no such item, + use the ultimate default in the spool directory. */ + } } } @@ -732,7 +743,11 @@ 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; @@ -847,39 +862,8 @@ /* If nothing has been set, don't waste effort... the default values for the statics are file_path="" and logging_mode = LOG_MODE_FILE. */ - if (*log_file_path) - { - int sep = ':'; /* Fixed separator - outside use */ - uschar *s; - const uschar *ss = log_file_path; + if (*log_file_path) set_file_path(&multiple); - 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) @@ -1499,7 +1483,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 +1505,12 @@ unlink_log(lt_debug); } +void +open_logs(void) +{ +set_file_path(NULL); +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-05-05 22:55:15.873237000 +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-05-05 22:55:15.877947000 +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-05-05 22:55:15.881462000 +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-05-05 22:55:15.884179000 +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-05-05 22:55:15.885392000 +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-05-05 22:55:15.892137000 +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-05-05 22:55:15.893035000 +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-05-05 22:55:15.896207000 +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-05-05 22:55:15.904606000 +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-05-05 22:55:15.904988000 +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-05-05 22:55:15.905766000 +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-05-05 22:55:15.908367000 +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-05-05 22:55:16.838703000 +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-31-503e55a2c" #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-05-05 22:55:16.838167000 +0300 @@ -1,3 +1,3 @@ # automatically generated file - see ../scripts/reversion -EXIM_RELEASE_VERSION="4.94.2" +EXIM_RELEASE_VERSION="4.94.2-31-503e55a2c" EXIM_COMPILE_NUMBER="1"