<secondary>failure report</secondary>
<see><emphasis>bounce message</emphasis></see>
</indexterm>
-<indexterm role="concept">
- <primary>de-tainting</primary>
- <see><emphasis>tainting, de-tainting</emphasis></see>
-</indexterm>
-<indexterm role="concept">
- <primary>detainting</primary>
- <see><emphasis>tainting, de-tainting</emphasis></see>
-</indexterm>
<indexterm role="concept">
<primary>dialup</primary>
<see><emphasis>intermittently connected hosts</emphasis></see>
.cindex expansion "tainted data"
and expansion of data deriving from the sender (&"tainted data"&)
is not permitted (including acessing a file using a tainted name).
-The main config option &%allow_insecure_tainted_data%& can be used as
-mitigation during uprades to more secure configurations.
Common ways of obtaining untainted equivalents of variables with
tainted values
.section "Miscellaneous" "SECID96"
.table2
.row &%add_environment%& "environment variables"
-.row &%allow_insecure_tainted_data%& "turn taint errors into warnings"
.row &%bi_command%& "to run for &%-bi%& command line option"
.row &%debug_store%& "do extra internal checks"
.row &%disable_ipv6%& "do no IPv6 processing"
configuration). This &"magic string"& matches the domain literal form of all
the local host's IP addresses.
-.option allow_insecure_tainted_data main boolean false
-.cindex "de-tainting"
-.oindex "allow_insecure_tainted_data"
-The handling of tainted data may break older (pre 4.94) configurations.
-Setting this option to "true" turns taint errors (which result in a temporary
-message rejection) into warnings. This option is meant as mitigation only
-and deprecated already today. Future releases of Exim may ignore it.
-The &%taint%& log selector can be used to suppress even the warnings.
-
-
-
.option allow_mx_to_ip main boolean false
.cindex "MX record" "pointing to IP address"
It appears that more and more DNS zone administrators are breaking the rules
&` smtp_protocol_error `& SMTP protocol errors
&` smtp_syntax_error `& SMTP syntax errors
&` subject `& contents of &'Subject:'& on <= lines
-&`*taint `& taint errors or warnings
&`*tls_certificate_verified `& certificate verification status
&`*tls_cipher `& TLS cipher suite on <= and => lines
&` tls_peerdn `& TLS peer DN on <= and => lines
&`CV=dane`& if using a DNS trust anchor,
and &`CV=no`& if not.
.next
-.cindex "log" "Taint warnings"
-&%taint%&: Log warnings about tainted data. This selector can't be
-turned of if &%allow_insecure_tainted_data%& is false (which is the
-default).
-.next
.cindex "log" "TLS cipher"
.cindex "TLS" "logging cipher"
&%tls_cipher%&: When a message is sent or received over an encrypted
file.pag and file.dir files would be created in that directory's
parent.
+JH/21 Remove the "allow_insecure_tainted_data" main config option and the
+ "taint" log_selector. These were previously deprecated.
+
Exim version 4.95
-----------------
# 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
#endif
case ACLC_QUEUE:
+ if (is_tainted(arg))
{
- 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;
+ *log_msgptr = string_sprintf("Tainted name '%s' for queue not permitted",
+ arg);
+ 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;
case ACLC_RATELIMIT:
rc = acl_ratelimit(arg, where, log_msgptr);
acl_text = ss;
-if ( !f.running_in_test_harness
- && is_tainted2(acl_text, LOG_MAIN|LOG_PANIC,
- "Tainted ACL text \"%s\"", acl_text))
+if (is_tainted(acl_text) && !f.running_in_test_harness)
{
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted ACL text \"%s\"", acl_text);
/* Avoid leaking info to an attacker */
*log_msgptr = US"internal configuration error";
return ERROR;
else if (*ss == '/')
{
struct stat statbuf;
- if (is_tainted2(ss, LOG_MAIN|LOG_PANIC, "Tainted ACL file name '%s'", ss))
- {
- /* Avoid leaking info to an attacker */
- *log_msgptr = US"internal configuration error";
- return ERROR;
- }
if ((fd = Uopen(ss, O_RDONLY, 0)) < 0)
{
*log_msgptr = string_sprintf("failed to open ACL file \"%s\": %s", ss,
#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
: (flags) == O_RDWR ? "O_RDWR" \
: (flags) == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" \
: "??"); \
- 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)) \
+ if (is_tainted(name) || is_tainted(dirname)) \
+ { \
+ log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted"); \
*dbpp = NULL; \
+ } \
else \
{ EXIM_DBOPEN__(name, dirname, flags, mode, dbpp); } \
DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", *dbpp); \
if (!s || !*s)
log_write(0, LOG_MAIN|LOG_PANIC,
"Failed to expand %s: '%s'\n", varname, filename);
-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 (*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 (!(fp = Ufopen(s, "rb")))
log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for %s "
"message texts: %s", s, reason, strerror(errno));
if (!tmp)
p->message = string_sprintf("failed to expand \"%s\" as a "
"system filter transport name", tpname);
- { uschar *m;
- if ((m = is_tainted2(tmp, 0, "Tainted values '%s' " "for transport '%s' as a system filter", tmp, tpname)))
- p->message = m;
- }
+ if (is_tainted(tmp))
+ p->message = string_sprintf("attempt to used tainted value '%s' for"
+ "transport '%s' as a system filter", tmp, tpname);
tpname = tmp;
}
else
struct stat statbuf;
uschar * path;
-if (is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted path '%s' for new directory", name))
- { p = US"create"; path = US name; errno = EACCES; goto bad; }
+if (is_tainted(name))
+ { p = US"create"; path = US name; errno = ERRNO_TAINT; goto bad; }
if (parent)
{
vtype_pspace, /* partition space; value is T/F for spool/log */
vtype_pinodes, /* partition inodes; value is T/F for spool/log */
vtype_cert /* SSL certificate */
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
,vtype_dkim /* Lookup of value in DKIM signature */
- #endif
+#endif
};
/* Type for main variable table */
{ "interface_address", vtype_stringptr, &interface_address },
{ "interface_port", vtype_int, &interface_port },
{ "item", vtype_stringptr, &iterate_item },
- #ifdef LOOKUP_LDAP
+#ifdef LOOKUP_LDAP
{ "ldap_dn", vtype_stringptr, &eldap_dn },
- #endif
+#endif
{ "load_average", vtype_load_avg, NULL },
{ "local_part", vtype_stringptr, &deliver_localpart },
{ "local_part_data", vtype_stringptr, &deliver_localpart_data },
f.expand_string_forcedfail = FALSE;
expand_string_message = US"";
-{ uschar *m;
-if ((m = is_tainted2(string, LOG_MAIN|LOG_PANIC, "Tainted string '%s' in expansion", s)))
+if (is_tainted(string))
{
- expand_string_message = m;
+ expand_string_message =
+ string_sprintf("attempt to expand tainted string '%s'", s);
+ log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message);
goto EXPAND_FAILED;
}
-}
while (*s)
{
}
case EOP_MD5:
- #ifndef DISABLE_TLS
+#ifndef DISABLE_TLS
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_md5(*(void **)vp->value);
yield = string_cat(yield, cp);
}
else
- #endif
+#endif
{
md5 base;
uschar digest[16];
break;
case EOP_SHA1:
- #ifndef DISABLE_TLS
+#ifndef DISABLE_TLS
if (vp && *(void **)vp->value)
{
uschar * cp = tls_cert_fprt_sha1(*(void **)vp->value);
yield = string_cat(yield, cp);
}
else
- #endif
+#endif
{
hctx h;
uschar digest[20];
case EOP_SHA2:
case EOP_SHA256:
- #ifdef EXIM_HAVE_SHA2
+#ifdef EXIM_HAVE_SHA2
if (vp && *(void **)vp->value)
if (c == EOP_SHA256)
yield = string_cat(yield, tls_cert_fprt_sha256(*(void **)vp->value));
while (b.len-- > 0)
yield = string_fmt_append(yield, "%02X", *b.data++);
}
- #else
+#else
expand_string_message = US"sha256 only supported with TLS";
- #endif
+#endif
break;
case EOP_SHA3:
- #ifdef EXIM_HAVE_SHA3
+#ifdef EXIM_HAVE_SHA3
{
hctx h;
blob b;
yield = string_fmt_append(yield, "%02X", *b.data++);
}
break;
- #else
+#else
expand_string_message = US"sha3 only supported with GnuTLS 3.5.0 + or OpenSSL 1.1.1 +";
goto EXPAND_FAILED;
- #endif
+#endif
/* Convert hex encoding to base64 encoding */
else if (opt)
sub = NULL;
- if (!sub)
- {
- expand_string_message = string_sprintf(
- "\"%s\" unrecognized after \"${quote_%s\"", /*}*/
- opt, arg);
- goto EXPAND_FAILED;
- }
+ if (!sub)
+ {
+ expand_string_message = string_sprintf(
+ "\"%s\" unrecognized after \"${quote_%s\"", /*}*/
+ opt, arg);
+ goto EXPAND_FAILED;
+ }
- yield = string_cat(yield, sub);
- break;
- }
+ yield = string_cat(yield, sub);
+ break;
+ }
- /* rx quote sticks in \ before any non-alphameric character so that
- the insertion works in a regular expression. */
+ /* rx quote sticks in \ before any non-alphameric character so that
+ the insertion works in a regular expression. */
- case EOP_RXQUOTE:
- {
- uschar *t = sub - 1;
- while (*(++t) != 0)
- {
- if (!isalnum(*t))
- yield = string_catn(yield, US"\\", 1);
- yield = string_catn(yield, t, 1);
- }
- break;
- }
+ case EOP_RXQUOTE:
+ {
+ uschar *t = sub - 1;
+ while (*(++t) != 0)
+ {
+ if (!isalnum(*t))
+ yield = string_catn(yield, US"\\", 1);
+ yield = string_catn(yield, t, 1);
+ }
+ break;
+ }
- /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as
- prescribed by the RFC, if there are characters that need to be encoded */
+ /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as
+ prescribed by the RFC, if there are characters that need to be encoded */
- case EOP_RFC2047:
- yield = string_cat(yield,
- parse_quote_2047(sub, Ustrlen(sub), headers_charset,
- FALSE));
- break;
+ case EOP_RFC2047:
+ yield = string_cat(yield,
+ parse_quote_2047(sub, Ustrlen(sub), headers_charset,
+ FALSE));
+ break;
- /* RFC 2047 decode */
+ /* RFC 2047 decode */
- case EOP_RFC2047D:
- {
- int len;
- uschar *error;
- uschar *decoded = rfc2047_decode(sub, check_rfc2047_length,
- headers_charset, '?', &len, &error);
- if (error)
- {
- expand_string_message = error;
- goto EXPAND_FAILED;
- }
- yield = string_catn(yield, decoded, len);
- break;
- }
+ case EOP_RFC2047D:
+ {
+ int len;
+ uschar *error;
+ uschar *decoded = rfc2047_decode(sub, check_rfc2047_length,
+ headers_charset, '?', &len, &error);
+ if (error)
+ {
+ expand_string_message = error;
+ goto EXPAND_FAILED;
+ }
+ yield = string_catn(yield, decoded, len);
+ break;
+ }
- /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into
- underscores */
+ /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into
+ underscores */
- case EOP_FROM_UTF8:
- {
- uschar * buff = store_get(4, is_tainted(sub));
- while (*sub)
- {
- int c;
- GETUTF8INC(c, sub);
- if (c > 255) c = '_';
- buff[0] = c;
- yield = string_catn(yield, buff, 1);
- }
- break;
- }
+ case EOP_FROM_UTF8:
+ {
+ uschar * buff = store_get(4, is_tainted(sub));
+ while (*sub)
+ {
+ int c;
+ GETUTF8INC(c, sub);
+ if (c > 255) c = '_';
+ buff[0] = c;
+ yield = string_catn(yield, buff, 1);
+ }
+ break;
+ }
- /* replace illegal UTF-8 sequences by replacement character */
+ /* replace illegal UTF-8 sequences by replacement character */
- #define UTF8_REPLACEMENT_CHAR US"?"
+ #define UTF8_REPLACEMENT_CHAR US"?"
- case EOP_UTF8CLEAN:
- {
- int seq_len = 0, index = 0;
- int bytes_left = 0;
- long codepoint = -1;
- int complete;
- uschar seq_buff[4]; /* accumulate utf-8 here */
+ case EOP_UTF8CLEAN:
+ {
+ int seq_len = 0, index = 0;
+ int bytes_left = 0;
+ long codepoint = -1;
+ int complete;
+ uschar seq_buff[4]; /* accumulate utf-8 here */
- /* Manually track tainting, as we deal in individual chars below */
+ /* 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), is_tainted(sub));
- }
+ 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 */
+ /* Check the UTF-8, byte-by-byte */
- while (*sub)
- {
- complete = 0;
- uschar c = *sub++;
+ while (*sub)
+ {
+ complete = 0;
+ uschar c = *sub++;
- if (bytes_left)
+ if (bytes_left)
{
if ((c & 0xc0) != 0x80)
/* wrong continuation byte; invalidate all bytes */
break;
}
- #ifdef SUPPORT_I18N
+#ifdef SUPPORT_I18N
case EOP_UTF8_DOMAIN_TO_ALABEL:
{
uschar * error = NULL;
yield = string_cat(yield, s);
break;
}
- #endif /* EXPERIMENTAL_INTERNATIONAL */
+#endif /* EXPERIMENTAL_INTERNATIONAL */
/* escape turns all non-printing characters into escape sequences. */
case EOP_STR2B64:
case EOP_BASE64:
{
- #ifndef DISABLE_TLS
+#ifndef DISABLE_TLS
uschar * s = vp && *(void **)vp->value
? tls_cert_der_b64(*(void **)vp->value)
: b64encode(CUS sub, Ustrlen(sub));
- #else
+#else
uschar * s = b64encode(CUS sub, Ustrlen(sub));
- #endif
+#endif
yield = string_cat(yield, s);
break;
}
EXPAND_FAILED:
if (left) *left = s;
DEBUG(D_expand)
- {
DEBUG(D_noutf8)
{
debug_printf_indent("|failed to expand: %s\n", string);
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;
/******************************************************************************/
/* 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_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname))
- return open(pathname, flags);
+if (!is_tainted(pathname)) return open(pathname, flags);
+log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname);
errno = EACCES;
return -1;
}
-
static inline int
exim_open(const char *pathname, int flags, mode_t mode)
{
-if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname))
- return open(pathname, flags, mode);
+if (!is_tainted(pathname)) return open(pathname, flags, mode);
+log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname);
errno = EACCES;
return -1;
}
static inline int
exim_openat(int dirfd, const char *pathname, int flags)
{
-if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname))
- return openat(dirfd, pathname, flags);
+if (!is_tainted(pathname)) return openat(dirfd, pathname, flags);
+log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname);
errno = EACCES;
return -1;
}
static inline int
exim_openat4(int dirfd, const char *pathname, int flags, mode_t mode)
{
-if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname))
- return openat(dirfd, pathname, flags, mode);
+if (!is_tainted(pathname)) return openat(dirfd, pathname, flags, mode);
+log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname);
errno = EACCES;
return -1;
}
static inline FILE *
exim_fopen(const char *pathname, const char *mode)
{
-if (!is_tainted2(pathname, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname))
- return fopen(pathname, mode);
+if (!is_tainted(pathname)) return fopen(pathname, mode);
+log_write(0, LOG_MAIN|LOG_PANIC, "Tainted filename '%s'", pathname);
errno = EACCES;
return NULL;
}
static inline DIR *
exim_opendir(const uschar * name)
{
-if (!is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted dirname '%s'", name))
- return opendir(CCS name);
+if (!is_tainted(name)) return opendir(CCS name);
+log_write(0, LOG_MAIN|LOG_PANIC, "Tainted dirname '%s'", name);
errno = EACCES;
return NULL;
}
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
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
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),
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. */
uschar *lastslash = Ustrrchr(name, '/');
*lastslash = 0;
created = directory_make(NULL, name, LOG_DIRECTORY_MODE, FALSE);
- 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));
+ DEBUG(D_any) debug_printf("%s log directory %s\n",
+ created ? "created" : "failed to create", name);
*lastslash = '/';
if (created) fd = Uopen(name, flags, LOG_MODE);
}
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
+The directory is in the static variable file_path. This is static so that it
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
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)
{
- if (is_tainted(tag))
- die(US"exim: tainted tag for debug log filename",
- US"Logging failure; please try later");
-
/* this won't change the offset of the datestamp */
ok2 = string_format(buffer, sizeof(buffer), "%s%s",
debuglog_name, tag);
/* 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;
}
-/* Pull the file out of the configured or the compiled-in list.
-Called for an empty log_file_path element, for debug logging activation
-when file_path has not previously been set, and from the appenfile transport setup. */
-void
-set_file_path(BOOL *multiple)
+static void
+set_file_path(void)
{
-uschar *s;
int sep = ':'; /* Fixed separator - outside use */
-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);
+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;
+ }
}
void
mainlog_close(void)
{
-/* 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;
+if (mainlogfd < 0) return;
(void)close(mainlogfd);
mainlogfd = -1;
mainlog_inode = 0;
store_pool = POOL_PERM;
- /* 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 nothing has been set, don't waste effort... the default values for the
+ statics are file_path="" and logging_mode = LOG_MODE_FILE. */
+
+ if (*log_file_path)
+ {
+ int sep = ':'; /* Fixed separator - outside use */
+ uschar *s;
+ const uschar *ss = log_file_path;
+
+ logging_mode = 0;
+ while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE)))
+ {
+ if (Ustrcmp(s, "syslog") == 0)
+ logging_mode |= LOG_MODE_SYSLOG;
+ else if (logging_mode & LOG_MODE_FILE)
+ multiple = TRUE;
+ else
+ {
+ logging_mode |= LOG_MODE_FILE;
+
+ /* If a non-empty path is given, use it */
+
+ if (*s)
+ file_path = string_copy(s);
+
+ /* If the path is empty, we want to use the first non-empty, non-
+ syslog item in LOG_FILE_PATH, if there is one, since the value of
+ log_file_path may have been set at runtime. If there is no such item,
+ use the ultimate default in the spool directory. */
+
+ else
+ set_file_path(); /* Empty item in log_file_path */
+ } /* First non-syslog item in log_file_path */
+ } /* Scan of log_file_path */
+ }
/* If no modes have been selected, it is a major disaster */
die(US"Neither syslog nor file logging set in log_file_path",
US"Unexpected logging failure");
- /* Revert to the old store pool, and record that we've sorted out the path. */
+ /* Set up the ultimate default if necessary. Then revert to the old store
+ pool, and record that we've sorted out the path. */
+ if (logging_mode & LOG_MODE_FILE && !file_path[0])
+ file_path = string_sprintf("%s/log/%%slog", spool_directory);
store_pool = old_pool;
path_inspected = TRUE;
if (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;
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(NULL);
+if (!*file_path) set_file_path();
open_log(&debug_fd, lt_debug, tag_name);
if (kill) 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 */
}
}
- { uschar *m;
- if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server)))
- {
- *errmsg = m;
+ if (is_tainted(server))
+ {
+ *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
return DEFER;
}
- }
rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts);
if (rc != DEFER || defer_break) return rc;
server = ele;
}
- { uschar *m;
- if ((m = is_tainted2(server, 0, "Tainted %s server '%s'", name, server)))
+ if (is_tainted(server))
{
- *errmsg = m;
+ *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
return DEFER;
}
- }
rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
if (rc != DEFER || defer_break) return rc;
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,
return FF_ERROR;
}
- if ((*error = is_tainted2(filename, 0, "Tainted name '%s' for included file not permitted\n", filename)))
+ if (is_tainted(filename))
+ {
+ *error = string_sprintf("Tainted name '%s' for included file not permitted\n",
+ filename);
return FF_ERROR;
+ }
/* Check file name if required */
/* Reading a file is a form of expansion; we wish to deny attackers the
capability to specify the file name. */
-if ((*error = is_tainted2(filename, 0, "Tainted name '%s' for file read not permitted\n", filename)))
+if (is_tainted(filename))
{
+ *error = string_sprintf("Tainted name '%s' for file read not permitted\n",
+ filename);
*yield = FF_ERROR;
return NULL;
}
{ "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} },
"\"%s\" in %s router: %s", tpname, router_name, expand_string_message);
return FALSE;
}
- if (is_tainted2(ss, LOG_MAIN|LOG_PANIC, "Tainted tainted value '%s' from '%s' for transport", ss, tpname))
+ if (is_tainted(ss))
{
+ 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";
uschar keybuffer[256];
int old_pool = store_pool;
-if (filename && is_tainted2(filename, LOG_MAIN|LOG_PANIC, "Tainted filename for search: '%s'", filename))
+if (filename && is_tainted(filename))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "Tainted filename for search: '%s'", filename);
return NULL;
+ }
/* Change to the search store pool and remember our reset point */
/* 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);
return FALSE;
}
-if (is_tainted2(expint, LOG_MAIN|LOG_PANIC, "Tainted value '%s' from '%s' for interface", expint, istring))
+if (is_tainted(expint))
{
+ 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);
{
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)
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)
uschar *q = ob->quota;
double default_value = 0.0;
-addrlist = addrlist; /* Keep picky compilers happy */
-dummy = dummy;
-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);
if (oncelog && *oncelog && to)
{
- uschar *m;
time_t then = 0;
- if ((m = is_tainted2(oncelog, 0, "Tainted '%s' (once file for %s transport)"
- " not permitted", oncelog, tblock->name)))
+ if (is_tainted(oncelog))
{
addr->transport_return = DEFER;
addr->basic_errno = EACCES;
- addr->message = m;
+ addr->message = string_sprintf("Tainted '%s' (once file for %s transport)"
+ " not permitted", oncelog, tblock->name);
goto END_OFF;
}
if (then != 0 && (once_repeat_sec <= 0 || now - then < once_repeat_sec))
{
- uschar *m;
int log_fd;
- if ((m = is_tainted2(logfile, 0, "Tainted '%s' (logfile for %s transport)"
- " not permitted", logfile, tblock->name)))
+ if (is_tainted(logfile))
{
addr->transport_return = DEFER;
addr->basic_errno = EACCES;
- addr->message = m;
+ addr->message = string_sprintf("Tainted '%s' (logfile for %s transport)"
+ " not permitted", logfile, tblock->name);
goto END_OFF;
}
/* We are going to send a message. Ensure any requested file is available. */
if (file)
{
- uschar *m;
- if ((m = is_tainted2(file, 0, "Tainted '%s' (file for %s transport)"
- " not permitted", file, tblock->name)))
+ if (is_tainted(file))
{
addr->transport_return = DEFER;
addr->basic_errno = EACCES;
- addr->message = m;
+ addr->message = string_sprintf("Tainted '%s' (file for %s transport)"
+ " not permitted", file, tblock->name);
return FALSE;
}
if (!(ff = Ufopen(file, "rb")) && !ob->file_optional)
tblock->name);
return FALSE;
}
-
-{ uschar *m;
-if ((m = is_tainted2(cmd, 0, "Tainted '%s' (command "
- "for %s transport) not permitted", cmd, tblock->name)))
+if (is_tainted(cmd))
{
+ 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
else
if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
- if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted host list '%s' from '%s' in transport %s", s, ob->hosts, tblock->name))
+ if (is_tainted(s))
{
+ 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;
+++ /dev/null
-# this is the default
-allow_insecure_tainted_data = ALLOW_TAINTED
2017-07-30 18:51:05.712 10HmaZ-0005vi-00 Completed
2017-07-30 18:51:05.712 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for f@test.ex
2017-07-30 18:51:05.712 10HmbA-0005vi-00 ** f@test.ex: Unrouteable address
-2017-07-30 18:51:05.712 10HmbA-0005vi-00 Tainted bounce_message_file after expansion: 'TESTSUITE/aux-fixed/0608.CALLER@myhost.test.ex'
+2017-07-30 18:51:05.712 10HmbA-0005vi-00 bounce_message_file is not untainted after expansion: 'TESTSUITE/aux-fixed/0608.CALLER@myhost.test.ex'
2017-07-30 18:51:05.712 10HmbJ-0005vi-00 <= <> R=10HmbA-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@myhost.test.ex
2017-07-30 18:51:05.712 10HmbJ-0005vi-00 => CALLER <CALLER@myhost.test.ex> R=bounces T=savebounce
2017-07-30 18:51:05.712 10HmaZ-0005vi-00 Failed to expand bounce_message_file: '$acl_m_unset'
-2017-07-30 18:51:05.712 10HmbA-0005vi-00 Tainted bounce_message_file after expansion: 'TESTSUITE/aux-fixed/0608.CALLER@myhost.test.ex'
+2017-07-30 18:51:05.712 10HmbA-0005vi-00 bounce_message_file is not untainted after expansion: 'TESTSUITE/aux-fixed/0608.CALLER@myhost.test.ex'
2017-07-30 18:51:05.712 10HmbB-0005vi-00 Failed to open TESTSUITE/aux-fixed/0608.nonexist.tmpl for warning message texts: No such file or directory
+++ /dev/null
-# Allow insecure tainted data
-exim -DALLOW_TAINTED=no -f hans@example.com -be
-${lookup{$sender_address_local_part}lsearch{DIR/aux-fixed/0990/$sender_address_domain}{yes}{no}}
-****
-exim -DALLOW_TAINTED=yes -f hans@example.com -be
-${lookup{$sender_address_local_part}lsearch{DIR/aux-fixed/0990/$sender_address_domain}{yes}{no}}
-****
+++ /dev/null
-feature _OPT_MAIN_ALLOW_INSECURE_TAINTED_DATA
2017-07-30 18:51:05.712 10HmaZ-0005vi-00 Failed to expand bounce_message_file: '$acl_m_unset'
-2017-07-30 18:51:05.712 10HmbA-0005vi-00 Tainted bounce_message_file after expansion: 'TESTSUITE/aux-fixed/0608.CALLER@myhost.test.ex'
+2017-07-30 18:51:05.712 10HmbA-0005vi-00 bounce_message_file is not untainted after expansion: 'TESTSUITE/aux-fixed/0608.CALLER@myhost.test.ex'
2017-07-30 18:51:05.712 10HmbB-0005vi-00 Failed to open TESTSUITE/aux-fixed/0608.nonexist.tmpl for warning message texts: No such file or directory
+++ /dev/null
-1999-03-02 09:44:33 Tainted filename for search: 'TESTSUITE/aux-fixed/0990/example.com'
-1999-03-02 09:44:33 Warning: Tainted filename for search: 'TESTSUITE/aux-fixed/0990/example.com'
-1999-03-02 09:44:33 Warning: Tainted filename 'TESTSUITE/aux-fixed/0990/example.com'
type=pgsql key="servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts=NULL
database lookup required for servers=localhost::1223/test/CALLER/; select name from them where id = 'c'
PostgreSQL query: "servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts 'NULL'
- lookup deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
warn: condition test deferred in ACL "check_recipient"
LOG: MAIN
- H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
processing "warn" (TESTSUITE/test-config 38)
check set acl_m0 = ok: hostlist
check hosts = net-pgsql;select * from them where id='$local_part'
type=pgsql key="servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts=NULL
database lookup required for servers=localhost::1223/test/CALLER/; select * from them where id='c'
PostgreSQL query: "servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts 'NULL'
-lookup deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
host in "<& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c'
warn: condition test deferred in ACL "check_recipient"
LOG: MAIN
- H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
processing "warn" (TESTSUITE/test-config 46)
check set acl_m0 = FAIL: hostlist
check hosts = <& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='$local_part'
type=pgsql key=" select * from them where id='c'" opts="servers=localhost::1223/test/CALLER/"
database lookup required for select * from them where id='c'
PostgreSQL query: " select * from them where id='c'" opts 'servers=localhost::1223/test/CALLER/'
-lookup deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
host in "<& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c'
warn: condition test deferred in ACL "check_recipient"
LOG: MAIN
- H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
processing "accept" (TESTSUITE/test-config 49)
check domains = +local_domains
d in "@"? no (end of list)
type=pgsql key="servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts=NULL
database lookup required for servers=localhost::1223/test/CALLER/; select name from them where id = 'c'
PostgreSQL query: "servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts 'NULL'
- lookup deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
warn: condition test deferred in ACL "check_recipient"
LOG: MAIN
- H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
processing "warn" (TESTSUITE/test-config 38)
check set acl_m0 = ok: hostlist
check hosts = net-pgsql;select * from them where id='$local_part'
type=pgsql key="servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts=NULL
database lookup required for servers=localhost::1223/test/CALLER/; select * from them where id='c'
PostgreSQL query: "servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts 'NULL'
-lookup deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
host in "<& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c'
warn: condition test deferred in ACL "check_recipient"
LOG: MAIN
- H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
processing "warn" (TESTSUITE/test-config 46)
check set acl_m0 = FAIL: hostlist
check hosts = <& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='$local_part'
type=pgsql key=" select * from them where id='c'" opts="servers=localhost::1223/test/CALLER/"
database lookup required for select * from them where id='c'
PostgreSQL query: " select * from them where id='c'" opts 'servers=localhost::1223/test/CALLER/'
-lookup deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
host in "<& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c'
warn: condition test deferred in ACL "check_recipient"
LOG: MAIN
- H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: Tainted PostgreSQL server 'localhost:1223/test/CALLER/'
+ H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted
processing "accept" (TESTSUITE/test-config 49)
check domains = +local_domains
d in "@"? no (end of list)
+++ /dev/null
-> Failed: (null)
->
-> yes
->