summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2021-06-28 00:35:57 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2021-06-28 00:35:57 +0100
commit753739fdef6d9753ee4a7e89afd959a4034d2ad9 (patch)
tree52d58ee0ad7d577f82b15f9c2140dbf754ccb66a
parentd8c9f31a3ec7a424ac9465604c397f1882b05567 (diff)
parenta746f186fdd8b3b6561919177b6dd011c2b177e4 (diff)
Merge branch 'readonly_config'
-rw-r--r--doc/doc-txt/ChangeLog3
-rw-r--r--src/src/auths/gsasl_exim.c2
-rw-r--r--src/src/deliver.c13
-rw-r--r--src/src/exim.c51
-rw-r--r--src/src/functions.h5
-rw-r--r--src/src/parse.c68
-rw-r--r--src/src/readconf.c161
-rw-r--r--src/src/route.c7
-rw-r--r--src/src/routers/manualroute.c4
-rw-r--r--src/src/smtp_in.c2
-rw-r--r--src/src/store.c41
-rw-r--r--src/src/store.h4
-rw-r--r--src/src/tls-openssl.c8
-rw-r--r--src/src/transports/appendfile.c5
-rw-r--r--src/src/transports/autoreply.c47
-rw-r--r--src/src/transports/smtp.c5
-rw-r--r--test/log/02244
17 files changed, 251 insertions, 179 deletions
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 90645e005..3311ae8f5 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -316,6 +316,9 @@ JH/54 DMARC: recent versions of the OpenDMARC library appear to have broken
JH/55 TLS: as server, reject connections with ALPN indicating non-smtp use.
+JH/56 Make the majority of info read from config files readonly, for defence-in-
+ depth against exploits. Suggestion by Qualsy.
+
Exim version 4.94
-----------------
diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c
index 7f9cc3295..479d01a29 100644
--- a/src/src/auths/gsasl_exim.c
+++ b/src/src/auths/gsasl_exim.c
@@ -757,7 +757,7 @@ switch (prop)
for memory wiping, so expanding strings will leave stuff laying around.
But no need to compound the problem, so get rid of the one we can. */
- memset(tmps, '\0', strlen(tmps));
+ if (US tmps != s) memset(tmps, '\0', strlen(tmps));
cbrc = GSASL_OK;
break;
diff --git a/src/src/deliver.c b/src/src/deliver.c
index f9f674643..e931d22e9 100644
--- a/src/src/deliver.c
+++ b/src/src/deliver.c
@@ -6554,14 +6554,19 @@ while (addr_new) /* Loop until all addresses dealt with */
/* Treat /dev/null as a special case and abandon the delivery. This
avoids having to specify a uid on the transport just for this case.
- Arrange for the transport name to be logged as "**bypassed**". */
+ Arrange for the transport name to be logged as "**bypassed**".
+ Copy the transport for this fairly unusual case rather than having
+ to make all transports mutable. */
if (Ustrcmp(addr->address, "/dev/null") == 0)
{
- uschar *save = addr->transport->name;
- addr->transport->name = US"**bypassed**";
+ transport_instance * save_t = addr->transport;
+ transport_instance * t = store_get(sizeof(*t), is_tainted(save_t));
+ *t = *save_t;
+ t->name = US"**bypassed**";
+ addr->transport = t;
(void)post_process_one(addr, OK, LOG_MAIN, EXIM_DTYPE_TRANSPORT, '=');
- addr->transport->name = save;
+ addr->transport= save_t;
continue; /* with the next new address */
}
diff --git a/src/src/exim.c b/src/src/exim.c
index 6a9a1477f..a42c48b2c 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -211,6 +211,19 @@ exit(1);
}
+/***********************************************
+* Handler for SIGSEGV *
+***********************************************/
+
+static void
+segv_handler(int sig)
+{
+log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
+signal(SIGSEGV, SIG_DFL);
+kill(getpid(), sig);
+}
+
+
/*************************************************
* Handler for SIGUSR1 *
*************************************************/
@@ -1805,7 +1818,8 @@ descriptive text. */
process_info = store_get(PROCESS_INFO_SIZE, TRUE); /* tainted */
set_process_info("initializing");
-os_restarting_signal(SIGUSR1, usr1_handler);
+os_restarting_signal(SIGUSR1, usr1_handler); /* exiwhat */
+signal(SIGSEGV, segv_handler); /* log faults */
/* If running in a dockerized environment, the TERM signal is only
delegated to the PID 1 if we request it by setting an signal handler */
@@ -3688,7 +3702,7 @@ else
{
struct rlimit rlp;
- #ifdef RLIMIT_NOFILE
+#ifdef RLIMIT_NOFILE
if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
{
log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NOFILE) failed: %s",
@@ -3711,9 +3725,9 @@ else
strerror(errno));
}
}
- #endif
+#endif
- #ifdef RLIMIT_NPROC
+#ifdef RLIMIT_NPROC
if (getrlimit(RLIMIT_NPROC, &rlp) < 0)
{
log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NPROC) failed: %s",
@@ -3721,20 +3735,20 @@ else
rlp.rlim_cur = rlp.rlim_max = 0;
}
- #ifdef RLIM_INFINITY
+# ifdef RLIM_INFINITY
if (rlp.rlim_cur != RLIM_INFINITY && rlp.rlim_cur < 1000)
{
rlp.rlim_cur = rlp.rlim_max = RLIM_INFINITY;
- #else
+# else
if (rlp.rlim_cur < 1000)
{
rlp.rlim_cur = rlp.rlim_max = 1000;
- #endif
+# endif
if (setrlimit(RLIMIT_NPROC, &rlp) < 0)
log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NPROC) failed: %s",
strerror(errno));
}
- #endif
+#endif
}
/* Exim is normally entered as root (but some special configurations are
@@ -3857,6 +3871,7 @@ is equivalent to the ability to modify a setuid binary!
This needs to happen before we read the main configuration. */
init_lookup_list();
+/*XXX this excrescence could move to the testsuite standard config setup file */
#ifdef SUPPORT_I18N
if (f.running_in_test_harness) smtputf8_advertise_hosts = NULL;
#endif
@@ -3895,19 +3910,21 @@ issues (currently about tls_advertise_hosts and keep_environment not being
defined) */
{
+ int old_pool = store_pool;
#ifdef MEASURE_TIMING
struct timeval t0, diff;
(void)gettimeofday(&t0, NULL);
#endif
+ store_pool = POOL_CONFIG;
readconf_main(checking || list_options);
+ store_pool = old_pool;
#ifdef MEASURE_TIMING
report_time_since(&t0, US"readconf_main (delta)");
#endif
}
-
/* Now in directory "/" */
if (cleanup_environment() == FALSE)
@@ -4494,7 +4511,13 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
event_action gets expanded */
if (msg_action == MSG_REMOVE)
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_CONFIG;
readconf_rest();
+ store_pool = old_pool;
+ store_writeprotect(POOL_CONFIG);
+ }
if (!one_msg_action)
{
@@ -4519,12 +4542,16 @@ Now, since the intro of the ${acl } expansion, ACL definitions may be
needed in transports so we lost the optimisation. */
{
+ int old_pool = store_pool;
#ifdef MEASURE_TIMING
struct timeval t0, diff;
(void)gettimeofday(&t0, NULL);
#endif
+ store_pool = POOL_CONFIG;
readconf_rest();
+ store_pool = old_pool;
+ store_writeprotect(POOL_CONFIG);
#ifdef MEASURE_TIMING
report_time_since(&t0, US"readconf_rest (delta)");
@@ -5451,15 +5478,15 @@ that SIG_IGN works. */
if (!f.synchronous_delivery)
{
- #ifdef SA_NOCLDWAIT
+#ifdef SA_NOCLDWAIT
struct sigaction act;
act.sa_handler = SIG_IGN;
sigemptyset(&(act.sa_mask));
act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, NULL);
- #else
+#else
signal(SIGCHLD, SIG_IGN);
- #endif
+#endif
}
/* Save the current store pool point, for resetting at the start of
diff --git a/src/src/functions.h b/src/src/functions.h
index e34972170..0744697f9 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -372,9 +372,9 @@ extern int open_cutthrough_connection( address_item * addr );
extern uschar *parse_extract_address(const uschar *, uschar **, int *, int *, int *,
BOOL);
-extern int parse_forward_list(uschar *, int, address_item **, uschar **,
+extern int parse_forward_list(const uschar *, int, address_item **, uschar **,
const uschar *, uschar *, error_block **);
-extern uschar *parse_find_address_end(uschar *, BOOL);
+extern uschar *parse_find_address_end(const uschar *, BOOL);
extern const uschar *parse_find_at(const uschar *);
extern const uschar *parse_fix_phrase(const uschar *, int);
extern const uschar *parse_message_id(const uschar *, uschar **, uschar **);
@@ -527,6 +527,7 @@ extern int stdin_ungetc(int);
extern void store_exit(void);
extern void store_init(void);
+extern void store_writeprotect(int);
extern gstring *string_append(gstring *, int, ...) WARN_UNUSED_RESULT;
extern gstring *string_append_listele(gstring *, uschar, const uschar *) WARN_UNUSED_RESULT;
diff --git a/src/src/parse.c b/src/src/parse.c
index 896d00f30..58f894199 100644
--- a/src/src/parse.c
+++ b/src/src/parse.c
@@ -65,7 +65,7 @@ Returns: pointer past the end of the address
*/
uschar *
-parse_find_address_end(uschar *s, BOOL nl_ends)
+parse_find_address_end(const uschar *s, BOOL nl_ends)
{
BOOL source_routing = *s == '@';
int no_term = source_routing? 1 : 0;
@@ -121,7 +121,7 @@ while (*s != 0 && (*s != ',' || no_term > 0) && (*s != '\n' || !nl_ends))
}
}
-return s;
+return US s;
}
@@ -1233,7 +1233,7 @@ Returns: FF_DELIVERED addresses extracted
*/
int
-parse_forward_list(uschar *s, int options, address_item **anchor,
+parse_forward_list(const uschar *s, int options, address_item **anchor,
uschar **error, const uschar *incoming_domain, uschar *directory,
error_block **syntax_errors)
{
@@ -1247,7 +1247,7 @@ for (;;)
int special = 0;
int specopt = 0;
int specbit = 0;
- uschar *ss, *nexts;
+ const uschar *ss, *nexts;
address_item *addr;
BOOL inquote = FALSE;
@@ -1275,7 +1275,7 @@ for (;;)
syntax error has been skipped. I now think it is the wrong approach, but
have left this here just in case, and for the record. */
- #ifdef NEVER
+#ifdef NEVER
if (count > 0) return FF_DELIVERED; /* Something was generated */
if (syntax_errors == NULL || /* Not skipping syntax errors, or */
@@ -1285,8 +1285,7 @@ for (;;)
*error = string_sprintf("no addresses generated: syntax error in %s: %s",
(*syntax_errors)->text2, (*syntax_errors)->text1);
return FF_ERROR;
- #endif
-
+#endif
}
/* Find the end of the next address. Quoted strings in addresses may contain
@@ -1323,13 +1322,7 @@ for (;;)
len = ss - s;
- DEBUG(D_route)
- {
- int save = s[len];
- s[len] = 0;
- debug_printf("extract item: %s\n", s);
- s[len] = save;
- }
+ DEBUG(D_route) debug_printf("extract item: %.*s\n", len, s);
/* Handle special addresses if permitted. If the address is :unknown:
ignore it - this is for backward compatibility with old alias files. You
@@ -1350,7 +1343,7 @@ for (;;)
else if (Ustrncmp(s, ":fail:", 6) == 0)
{ special = FF_FAIL; specopt = RDO_FAIL; } /* specbit is 0 */
- if (special != 0)
+ if (special)
{
uschar *ss = Ustrchr(s+1, ':') + 1;
if ((options & specopt) == specbit)
@@ -1358,10 +1351,9 @@ for (;;)
*error = string_sprintf("\"%.*s\" is not permitted", len, s);
return FF_ERROR;
}
- while (*ss != 0 && isspace(*ss)) ss++;
- while (s[len] != 0 && s[len] != '\n') len++;
- s[len] = 0;
- *error = string_copy(ss);
+ while (*ss && isspace(*ss)) ss++;
+ while (s[len] && s[len] != '\n') len++;
+ *error = string_copyn(ss, s + len - ss);
return special;
}
@@ -1374,7 +1366,7 @@ for (;;)
{
uschar *filebuf;
uschar filename[256];
- uschar *t = s+9;
+ const uschar * t = s+9;
int flen = len - 9;
int frc;
struct stat statbuf;
@@ -1587,18 +1579,17 @@ for (;;)
{
int start, end, domain;
const uschar *recipient = NULL;
- int save = s[len];
- s[len] = 0;
+ uschar * s_ltd = string_copyn(s, len);
/* If it starts with \ and the rest of it parses as a valid mail address
without a domain, carry on with that address, but qualify it with the
incoming domain. Otherwise arrange for the address to fall through,
causing an error message on the re-parse. */
- if (*s == '\\')
+ if (*s_ltd == '\\')
{
recipient =
- parse_extract_address(s+1, error, &start, &end, &domain, FALSE);
+ parse_extract_address(s_ltd+1, error, &start, &end, &domain, FALSE);
if (recipient)
recipient = domain != 0 ? NULL :
string_sprintf("%s@%s", recipient, incoming_domain);
@@ -1607,17 +1598,17 @@ for (;;)
/* Try parsing the item as an address. */
if (!recipient) recipient =
- parse_extract_address(s, error, &start, &end, &domain, FALSE);
+ parse_extract_address(s_ltd, error, &start, &end, &domain, FALSE);
/* If item starts with / or | and is not a valid address, or there
is no domain, treat it as a file or pipe. If it was a quoted item,
remove the quoting occurrences of \ within it. */
- if ((*s == '|' || *s == '/') && (recipient == NULL || domain == 0))
+ if ((*s_ltd == '|' || *s_ltd == '/') && (recipient == NULL || domain == 0))
{
- uschar *t = store_get(Ustrlen(s) + 1, is_tainted(s));
+ uschar *t = store_get(Ustrlen(s_ltd) + 1, is_tainted(s_ltd));
uschar *p = t;
- uschar *q = s;
+ uschar *q = s_ltd;
while (*q != 0)
{
if (inquote)
@@ -1630,7 +1621,7 @@ for (;;)
*p = 0;
addr = deliver_make_addr(t, TRUE);
setflag(addr, af_pfr); /* indicates pipe/file/reply */
- if (*s != '|') setflag(addr, af_file); /* indicates file */
+ if (*s_ltd != '|') setflag(addr, af_file); /* indicates file */
}
/* Item must be an address. Complain if not, else qualify, rewrite and set
@@ -1642,36 +1633,33 @@ for (;;)
else
{
- if (recipient == NULL)
+ if (!recipient)
{
if (Ustrcmp(*error, "empty address") == 0)
{
*error = NULL;
- s[len] = save;
s = nexts;
continue;
}
- if (syntax_errors != NULL)
+ if (syntax_errors)
{
error_block *e = store_get(sizeof(error_block), FALSE);
error_block *last = *syntax_errors;
- if (last == NULL) *syntax_errors = e; else
+ if (!last) *syntax_errors = e; else
{
- while (last->next != NULL) last = last->next;
+ while (last->next) last = last->next;
last->next = e;
}
e->next = NULL;
e->text1 = *error;
- e->text2 = string_copy(s);
- s[len] = save;
+ e->text2 = s_ltd;
s = nexts;
continue;
}
else
{
- *error = string_sprintf("%s in \"%s\"", *error, s);
- s[len] = save; /* _after_ using it for *error */
+ *error = string_sprintf("%s in \"%s\"", *error, s_ltd);
return FF_ERROR;
}
}
@@ -1686,10 +1674,8 @@ for (;;)
addr = deliver_make_addr(US recipient, TRUE); /* TRUE => copy recipient, so deconst ok */
}
- /* Restore the final character in the original data, and add to the
- output chain. */
+ /* Add the original data to the output chain. */
- s[len] = save;
addr->next = *anchor;
*anchor = addr;
count++;
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 34ebf8769..01e85a329 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -3011,7 +3011,7 @@ read_named_list(tree_node **anchorp, int *numberp, int max, uschar *s,
BOOL forcecache = FALSE;
uschar *ss;
tree_node *t;
-namedlist_block * nb = store_get(sizeof(namedlist_block), FALSE);
+namedlist_block * nb = store_get_perm(sizeof(namedlist_block), FALSE);
if (Ustrncmp(s, "_cache", 6) == 0)
{
@@ -3142,55 +3142,54 @@ while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
/* Cut out all the fancy processing unless specifically wanted */
- #if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
+#if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
uschar *suffix = filename + Ustrlen(filename);
/* Try for the node-specific file if a node name exists */
- #ifdef CONFIGURE_FILE_USE_NODE
+# ifdef CONFIGURE_FILE_USE_NODE
struct utsname uts;
if (uname(&uts) >= 0)
{
- #ifdef CONFIGURE_FILE_USE_EUID
+# ifdef CONFIGURE_FILE_USE_EUID
sprintf(CS suffix, ".%ld.%.256s", (long int)original_euid, uts.nodename);
- config_file = Ufopen(filename, "rb");
- if (config_file == NULL)
- #endif /* CONFIGURE_FILE_USE_EUID */
+ if (!(config_file = Ufopen(filename, "rb")))
+# endif /* CONFIGURE_FILE_USE_EUID */
{
sprintf(CS suffix, ".%.256s", uts.nodename);
config_file = Ufopen(filename, "rb");
}
}
- #endif /* CONFIGURE_FILE_USE_NODE */
+# endif /* CONFIGURE_FILE_USE_NODE */
/* Otherwise, try the generic name, possibly with the euid added */
- #ifdef CONFIGURE_FILE_USE_EUID
- if (config_file == NULL)
+# ifdef CONFIGURE_FILE_USE_EUID
+ if (!config_file)
{
sprintf(CS suffix, ".%ld", (long int)original_euid);
config_file = Ufopen(filename, "rb");
}
- #endif /* CONFIGURE_FILE_USE_EUID */
+# endif /* CONFIGURE_FILE_USE_EUID */
/* Finally, try the unadorned name */
- if (config_file == NULL)
+ if (!config_file)
{
*suffix = 0;
config_file = Ufopen(filename, "rb");
}
- #else /* if neither defined */
+#else /* if neither defined */
/* This is the common case when the fancy processing is not included. */
config_file = Ufopen(filename, "rb");
- #endif
+#endif
/* If the file does not exist, continue to try any others. For any other
error, break out (and die). */
- if (config_file != NULL || errno != ENOENT) break;
+ if (config_file || errno != ENOENT) break;
}
/* On success, save the name for verification; config_filename is used when
@@ -3213,39 +3212,37 @@ if (config_file)
config_main_directory = last_slash == filename ? US"/" : string_copyn(filename, last_slash - filename);
else
{
- /* relative configuration file name: working dir + / + basename(filename) */
+ /* relative configuration file name: working dir + / + basename(filename) */
- uschar buf[PATH_MAX];
- gstring * g;
+ uschar buf[PATH_MAX];
+ gstring * g;
- if (os_getcwd(buf, PATH_MAX) == NULL)
- {
- perror("exim: getcwd");
- exit(EXIT_FAILURE);
- }
- g = string_cat(NULL, buf);
+ if (os_getcwd(buf, PATH_MAX) == NULL)
+ {
+ perror("exim: getcwd");
+ exit(EXIT_FAILURE);
+ }
+ g = string_cat(NULL, buf);
- /* If the dir does not end with a "/", append one */
- if (g->s[g->ptr-1] != '/')
- g = string_catn(g, US"/", 1);
+ /* If the dir does not end with a "/", append one */
+ if (g->s[g->ptr-1] != '/')
+ g = string_catn(g, US"/", 1);
- /* If the config file contains a "/", extract the directory part */
- if (last_slash)
- g = string_catn(g, filename, last_slash - filename);
+ /* If the config file contains a "/", extract the directory part */
+ if (last_slash)
+ g = string_catn(g, filename, last_slash - filename);
- config_main_directory = string_from_gstring(g);
+ config_main_directory = string_from_gstring(g);
}
config_directory = config_main_directory;
}
else
- {
if (!filename)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "non-existent configuration file(s): "
"%s", config_main_filelist);
else
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
string_open_failed("configuration file %s", filename));
- }
/* Now, once we found and opened our configuration file, we change the directory
to a safe place. Later we change to $spool_directory. */
@@ -3265,19 +3262,19 @@ if (f.trusted_config && Ustrcmp(filename, US"/dev/null"))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to stat configuration file %s",
big_buffer);
- if ((statbuf.st_uid != root_uid /* owner not root */
- #ifdef CONFIGURE_OWNER
- && statbuf.st_uid != config_uid /* owner not the special one */
- #endif
- ) || /* or */
- (statbuf.st_gid != root_gid /* group not root & */
- #ifdef CONFIGURE_GROUP
- && statbuf.st_gid != config_gid /* group not the special one */
- #endif
- && (statbuf.st_mode & 020) != 0) || /* group writeable */
- /* or */
- ((statbuf.st_mode & 2) != 0)) /* world writeable */
-
+ if ( statbuf.st_uid != root_uid /* owner not root */
+#ifdef CONFIGURE_OWNER
+ && statbuf.st_uid != config_uid /* owner not the special one */
+#endif
+ || /* or */
+ statbuf.st_gid != root_gid /* group not root & */
+#ifdef CONFIGURE_GROUP
+ && statbuf.st_gid != config_gid /* group not the special one */
+#endif
+ && (statbuf.st_mode & 020) != 0 /* group writeable */
+ || /* or */
+ (statbuf.st_mode & 2) != 0 /* world writeable */
+ )
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Exim configuration file %s has the "
"wrong owner, group, or mode", big_buffer);
@@ -3324,11 +3321,11 @@ while ((s = get_config_line()))
read_named_list(&hostlist_anchor, &hostlist_count,
MAX_NAMED_LIST, t+8, US"host list", hide);
- else if (Ustrncmp(t, US"addresslist", 11) == 0)
+ else if (Ustrncmp(t, "addresslist", 11) == 0)
read_named_list(&addresslist_anchor, &addresslist_count,
MAX_NAMED_LIST, t+11, US"address list", hide);
- else if (Ustrncmp(t, US"localpartlist", 13) == 0)
+ else if (Ustrncmp(t, "localpartlist", 13) == 0)
read_named_list(&localpartlist_anchor, &localpartlist_count,
MAX_NAMED_LIST, t+13, US"local part list", hide);
@@ -3347,7 +3344,7 @@ if (local_sender_retain && local_from_check)
/* If the timezone string is empty, set it to NULL, implying no TZ variable
wanted. */
-if (timezone_string != NULL && *timezone_string == 0) timezone_string = NULL;
+if (timezone_string && !*timezone_string) timezone_string = NULL;
/* The max retry interval must not be greater than 24 hours. */
@@ -3492,7 +3489,7 @@ if (syslog_facility_str)
/* Expand pid_file_path */
-if (*pid_file_path != 0)
+if (*pid_file_path)
{
if (!(s = expand_string(pid_file_path)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand pid_file_path "
@@ -3502,7 +3499,7 @@ if (*pid_file_path != 0)
/* Set default value of process_log_path */
-if (!process_log_path || *process_log_path =='\0')
+if (!process_log_path || !*process_log_path)
process_log_path = string_sprintf("%s/exim-process.info", spool_directory);
/* Compile the regex for matching a UUCP-style "From_" line in an incoming
@@ -3554,7 +3551,7 @@ if (errors_reply_to)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"error in errors_reply_to (%s): %s", errors_reply_to, errmess);
- if (domain == 0)
+ if (!domain)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"errors_reply_to (%s) does not contain a domain", errors_reply_to);
}
@@ -3562,8 +3559,7 @@ if (errors_reply_to)
/* If smtp_accept_queue or smtp_accept_max_per_host is set, then
smtp_accept_max must also be set. */
-if (smtp_accept_max == 0 &&
- (smtp_accept_queue > 0 || smtp_accept_max_per_host != NULL))
+if (smtp_accept_max == 0 && (smtp_accept_queue > 0 || smtp_accept_max_per_host))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"smtp_accept_max must be set if smtp_accept_queue or "
"smtp_accept_max_per_host is set");
@@ -3584,7 +3580,7 @@ if (host_number_string)
host_number_string, expand_string_message);
n = Ustrtol(s, &end, 0);
while (isspace(*end)) end++;
- if (*end != 0)
+ if (*end)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"localhost_number value is not a number: %s", s);
if (n > LOCALHOST_MAX)
@@ -3661,7 +3657,7 @@ for (driver_info * dd = drivers_available; dd->driver_name[0] != 0;
{
int len = dd->options_len;
d->info = dd;
- d->options_block = store_get(len, FALSE);
+ d->options_block = store_get_perm(len, FALSE);
memcpy(d->options_block, dd->options_block, len);
for (int i = 0; i < *(dd->options_count); i++)
dd->options[i].type &= ~opt_set;
@@ -3677,6 +3673,16 @@ return NULL; /* never obeyed */
+static void
+driver_init_fini(driver_instance * d, const uschar * class)
+{
+if (!d->driver_name)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+ "no driver defined for %s \"%s\"", class, d->name);
+(d->info->init)(d);
+}
+
+
/*************************************************
* Initialize driver list *
*************************************************/
@@ -3737,11 +3743,8 @@ while ((buffer = get_config_line()))
{
if (d)
{
- if (!d->driver_name)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "no driver defined for %s \"%s\"", class, d->name);
/* s is using big_buffer, so this call had better not */
- (d->info->init)(d);
+ driver_init_fini(d, class);
d = NULL;
}
if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE);
@@ -3757,12 +3760,7 @@ while ((buffer = get_config_line()))
/* Finish off initializing the previous driver. */
if (d)
- {
- if (!d->driver_name)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "no driver defined for %s \"%s\"", class, d->name);
- (d->info->init)(d);
- }
+ driver_init_fini(d, class);
/* Check that we haven't already got a driver of this name */
@@ -3774,7 +3772,7 @@ while ((buffer = get_config_line()))
/* Set up a new driver instance data block on the chain, with
its default values installed. */
- d = store_get(instance_size, FALSE);
+ d = store_get_perm(instance_size, FALSE);
memcpy(d, instance_default, instance_size);
*p = d;
p = &d->next;
@@ -3826,12 +3824,7 @@ while ((buffer = get_config_line()))
/* Run the initialization function for the final driver. */
if (d)
- {
- if (!d->driver_name)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "no driver defined for %s \"%s\"", class, d->name);
- (d->info->init)(d);
- }
+ driver_init_fini(d, class);
}
@@ -4264,7 +4257,7 @@ while(acl_line)
if (*p != ':' || name[0] == 0)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name");
- node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
+ node = store_get_perm(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
Ustrcpy(node->name, name);
if (!tree_insertnode(&acl_anchor, node))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
@@ -4426,25 +4419,28 @@ print_config(BOOL admin, BOOL terse)
{
const int TS = terse ? 0 : 2;
int indent = 0;
+rmark r = NULL;
-for (config_line_item * i = config_lines; i; i = i->next)
+for (const config_line_item * i = config_lines; i; i = i->next)
{
- uschar *current;
- uschar *p;
+ uschar * current, * p;
+
+ if (r) store_reset(r);
+ r = store_mark();
/* skip over to the first non-space */
- for (current = i->line; *current && isspace(*current); ++current)
+ for (current = string_copy(i->line); *current && isspace(*current); ++current)
;
- if (*current == '\0')
+ if (!*current)
continue;
/* Collapse runs of spaces. We stop this if we encounter one of the
- * following characters: "'$, as this may indicate careful formatting */
- for (p = current; *p; ++p)
+ following characters: "'$, as this may indicate careful formatting */
+
+ for (p = current; *p; p++) if (isspace(*p))
{
uschar *next;
- if (!isspace(*p)) continue;
if (*p != ' ') *p = ' ';
for (next = p; isspace(*next); ++next)
@@ -4498,6 +4494,7 @@ for (config_line_item * i = config_lines; i; i = i->next)
/* rest is public */
printf("%*s%s\n", indent, "", current);
}
+if (r) store_reset(r);
}
#endif /*!MACRO_PREDEF*/
diff --git a/src/src/route.c b/src/src/route.c
index 1d87b079a..5aed06b4f 100644
--- a/src/src/route.c
+++ b/src/src/route.c
@@ -288,7 +288,12 @@ for (router_instance * r = routers; r; r = r->next)
/* Build a host list if fallback hosts is set. */
- host_build_hostlist(&(r->fallback_hostlist), r->fallback_hosts, FALSE);
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ host_build_hostlist(&r->fallback_hostlist, r->fallback_hosts, FALSE);
+ store_pool = old_pool;
+ }
/* Check redirect_router and pass_router are valid */
diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c
index 471b38566..01802714f 100644
--- a/src/src/routers/manualroute.c
+++ b/src/src/routers/manualroute.c
@@ -259,7 +259,7 @@ if (ob->route_list)
int sep = -(';'); /* Default is semicolon */
listptr = ob->route_list;
- while ((route_item = string_nextinlist(&listptr, &sep, NULL, 0)) != NULL)
+ while ((route_item = string_nextinlist(&listptr, &sep, NULL, 0)))
{
int rc;
@@ -468,7 +468,7 @@ if (!addr->host_list)
defined for these hosts. It will be a remote one, as a local transport is
dealt with above. However, we don't need one if verifying only. */
-if (transport == NULL && verify == v_none)
+if (!transport && verify == v_none)
{
log_write(0, LOG_MAIN, "Error in %s router: no transport defined",
rblock->name);
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 1d7b22254..78c86e7c9 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -3015,7 +3015,7 @@ else
p = s + Ustrlen(s);
while (p > s && isspace(p[-1])) p--;
-*p = 0;
+s = string_copyn(s, p-s);
/* It seems that CC:Mail is braindead, and assumes that the greeting message
is all contained in a single IP packet. The original code wrote out the
diff --git a/src/src/store.c b/src/src/store.c
index 2a32e9b5c..ad4da3c2e 100644
--- a/src/src/store.c
+++ b/src/src/store.c
@@ -41,6 +41,9 @@ The following different types of store are recognized:
a single message transaction but needed for longer than the use of the main
pool permits. Currently this means only receive-time DKIM information.
+- There is a dedicated pool for configuration data read from the config file(s).
+ Once complete, it is made readonly.
+
. Orthogonal to the three pool types, there are two classes of memory: untainted
and tainted. The latter is used for values derived from untrusted input, and
the string-expansion mechanism refuses to operate on such values (obviously,
@@ -165,21 +168,24 @@ static int max_nonpool_malloc; /* max value for nonpool_malloc */
static const uschar * pooluse[NPOOLS] = {
[POOL_MAIN] = US"main",
[POOL_PERM] = US"perm",
+[POOL_CONFIG] = US"config",
[POOL_SEARCH] = US"search",
[POOL_MESSAGE] = US"message",
[POOL_TAINT_MAIN] = US"main",
[POOL_TAINT_PERM] = US"perm",
-[POOL_TAINT_SEARCH] = US"search",
+[POOL_TAINT_CONFIG] = US"config",
[POOL_TAINT_SEARCH] = US"search",
[POOL_TAINT_MESSAGE] = US"message",
};
static const uschar * poolclass[NPOOLS] = {
[POOL_MAIN] = US"untainted",
[POOL_PERM] = US"untainted",
+[POOL_CONFIG] = US"untainted",
[POOL_SEARCH] = US"untainted",
[POOL_MESSAGE] = US"untainted",
[POOL_TAINT_MAIN] = US"tainted",
[POOL_TAINT_PERM] = US"tainted",
+[POOL_TAINT_CONFIG] = US"tainted",
[POOL_TAINT_SEARCH] = US"tainted",
[POOL_TAINT_MESSAGE] = US"tainted",
};
@@ -245,6 +251,22 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n",
+/******************************************************************************/
+void
+store_writeprotect(int pool)
+{
+for (storeblock * b = chainbase[pool]; b; b = b->next)
+ {
+#ifndef COMPILE_UTILITY
+ if (mprotect(b, ALIGNED_SIZEOF_STOREBLOCK + b->length, PROT_READ) != 0)
+ DEBUG(D_any) debug_printf("config block mprotect: (%d) %s\n", errno, strerror(errno))
+#endif
+ ;
+ }
+}
+
+/******************************************************************************/
+
/*************************************************
* Get a block from the current pool *
*************************************************/
@@ -324,7 +346,13 @@ if (size > yield_length[pool])
if (++nblocks[pool] > maxblocks[pool])
maxblocks[pool] = nblocks[pool];
- newblock = internal_store_malloc(mlength, func, linenumber);
+ if (pool == POOL_CONFIG)
+ {
+ long pgsize = sysconf(_SC_PAGESIZE);
+ posix_memalign((void **)&newblock, pgsize, (mlength + pgsize - 1) & ~(pgsize - 1));
+ }
+ else
+ newblock = internal_store_malloc(mlength, func, linenumber);
newblock->next = NULL;
newblock->length = length;
#ifndef RESTRICTED_MEMORY
@@ -483,7 +511,8 @@ not call with a pointer returned by store_get(). Both the untainted and tainted
pools corresposding to store_pool are reset.
Arguments:
- r place to back up to
+ ptr place to back up to
+ pool pool holding the pointer
func function from which called
linenumber line number in source file
@@ -561,7 +590,8 @@ if ( yield_length[pool] < STOREPOOL_MIN_SIZE
}
bb = b->next;
-b->next = NULL;
+if (pool != POOL_CONFIG)
+ b->next = NULL;
while ((b = bb))
{
@@ -576,7 +606,8 @@ while ((b = bb))
nbytes[pool] -= siz;
pool_malloc -= siz;
nblocks[pool]--;
- internal_store_free(b, func, linenumber);
+ if (pool != POOL_CONFIG)
+ internal_store_free(b, func, linenumber);
#ifndef RESTRICTED_MEMORY
if (store_block_order[pool] > 13) store_block_order[pool]--;
diff --git a/src/src/store.h b/src/src/store.h
index 92deabf9b..58561ac28 100644
--- a/src/src/store.h
+++ b/src/src/store.h
@@ -13,9 +13,10 @@
/* Define symbols for identifying the store pools. */
-#define NPOOLS 8
+#define NPOOLS 10
enum { POOL_MAIN,
POOL_PERM,
+ POOL_CONFIG,
POOL_SEARCH,
POOL_MESSAGE,
@@ -23,6 +24,7 @@ enum { POOL_MAIN,
POOL_TAINT_MAIN = POOL_TAINT_BASE,
POOL_TAINT_PERM,
+ POOL_TAINT_CONFIG,
POOL_TAINT_SEARCH,
POOL_TAINT_MESSAGE };
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 9692033f0..4636f3c32 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -4529,7 +4529,6 @@ tls_openssl_options_parse(uschar *option_spec, long *results)
{
long result, item;
uschar * exp, * end;
-uschar keep_c;
BOOL adding, item_parsed;
/* Server: send no (<= TLS1.2) session tickets */
@@ -4571,11 +4570,8 @@ for (uschar * s = exp; *s; /**/)
return FALSE;
}
adding = *s++ == '+';
- for (end = s; (*end != '\0') && !isspace(*end); ++end) /**/ ;
- keep_c = *end;
- *end = '\0';
- item_parsed = tls_openssl_one_option_parse(s, &item);
- *end = keep_c;
+ for (end = s; *end && !isspace(*end); ) end++;
+ item_parsed = tls_openssl_one_option_parse(string_copyn(s, end-s), &item);
if (!item_parsed)
{
DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c
index 5d957b62e..2d008d97d 100644
--- a/src/src/transports/appendfile.c
+++ b/src/src/transports/appendfile.c
@@ -1368,8 +1368,8 @@ if (!isdirectory)
if (ob->create_directory && allow_creation_here)
{
uschar *p = Ustrrchr(path, '/');
- *p = '\0';
- if (!directory_make(NULL, path, ob->dirmode, FALSE))
+ p = string_copyn(path, p - path);
+ if (!directory_make(NULL, p, ob->dirmode, FALSE))
{
addr->basic_errno = errno;
addr->message =
@@ -1378,7 +1378,6 @@ if (!isdirectory)
DEBUG(D_transport) debug_printf("%s transport: %s\n", tblock->name, path);
return FALSE;
}
- *p = '/';
}
/* If file_format is set we must check that any existing file matches one of
diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c
index 80c7c0db0..864257b46 100644
--- a/src/src/transports/autoreply.c
+++ b/src/src/transports/autoreply.c
@@ -175,18 +175,21 @@ return ss;
list. Any that are found are removed.
Arguments:
- listptr points to the list of addresses
+ list list of addresses to be checked
never_mail an address list, already expanded
-Returns: nothing
+Returns: edited replacement address list, or NULL, or original
*/
-static void
-check_never_mail(uschar **listptr, const uschar *never_mail)
+static uschar *
+check_never_mail(uschar * list, const uschar * never_mail)
{
-uschar *s = *listptr;
+rmark reset_point = store_mark();
+uschar * newlist = string_copy(list);
+uschar * s = newlist;
+BOOL hit = FALSE;
-while (*s != 0)
+while (*s)
{
uschar *error, *next;
uschar *e = parse_find_address_end(s, FALSE);
@@ -220,6 +223,7 @@ while (*s != 0)
{
DEBUG(D_transport)
debug_printf("discarding recipient %s (matched never_mail)\n", next);
+ hit = TRUE;
if (terminator == ',') e++;
memmove(s, e, Ustrlen(e) + 1);
}
@@ -230,18 +234,31 @@ while (*s != 0)
}
}
+/* If no addresses were removed, retrieve the memory used and return
+the original. */
+
+if (!hit)
+ {
+ store_reset(reset_point);
+ return list;
+ }
+
/* Check to see if we removed the last address, leaving a terminating comma
that needs to be removed */
-s = *listptr + Ustrlen(*listptr);
-while (s > *listptr && (isspace(s[-1]) || s[-1] == ',')) s--;
+s = newlist + Ustrlen(newlist);
+while (s > newlist && (isspace(s[-1]) || s[-1] == ',')) s--;
*s = 0;
-/* Check to see if there any addresses left; if not, set NULL */
+/* Check to see if there any addresses left; if not, return NULL */
+
+s = newlist;
+while (s && isspace(*s)) s++;
+if (*s)
+ return newlist;
-s = *listptr;
-while (s != 0 && isspace(*s)) s++;
-if (*s == 0) *listptr = NULL;
+store_reset(reset_point);
+return NULL;
}
@@ -372,9 +389,9 @@ if (ob->never_mail)
return FALSE;
}
- if (to) check_never_mail(&to, never_mail);
- if (cc) check_never_mail(&cc, never_mail);
- if (bcc) check_never_mail(&bcc, never_mail);
+ if (to) to = check_never_mail(to, never_mail);
+ if (cc) cc = check_never_mail(cc, never_mail);
+ if (bcc) bcc = check_never_mail(bcc, never_mail);
if (!to && !cc && !bcc)
{
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index e2b2250ad..dfb4a9284 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -355,6 +355,7 @@ void
smtp_transport_init(transport_instance *tblock)
{
smtp_transport_options_block *ob = SOB tblock->options_block;
+int old_pool = store_pool;
/* Retry_use_local_part defaults FALSE if unset */
@@ -390,7 +391,9 @@ if (ob->hosts_override && ob->hosts) tblock->overrides_hosts = TRUE;
/* If there are any fallback hosts listed, build a chain of host items
for them, but do not do any lookups at this time. */
-host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts, FALSE);
+store_pool = POOL_PERM;
+host_build_hostlist(&ob->fallback_hostlist, ob->fallback_hosts, FALSE);
+store_pool = old_pool;
}
diff --git a/test/log/0224 b/test/log/0224
index a03b4cd9b..317f22cb6 100644
--- a/test/log/0224
+++ b/test/log/0224
@@ -8,11 +8,11 @@
1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
1999-03-02 09:44:33 10HmaZ-0005vi-00 == hdefer@test.ex <useryy@test.ex> R=halias defer (-1): not just yet
1999-03-02 09:44:33 10HmaZ-0005vi-00 == defer@test.ex <userxy@test.ex> R=alias defer (-1): not just yet
-1999-03-02 09:44:33 10HmaZ-0005vi-00 == /no/such/file <file@test.ex> R=alias T=address_file defer (EEE): Permission denied: failed to create directories for /no/such: Permission denied
+1999-03-02 09:44:33 10HmaZ-0005vi-00 == /no/such/file <file@test.ex> R=alias T=address_file defer (EEE): Permission denied: failed to create directories for /no/such/file: Permission denied
1999-03-02 09:44:33 Start queue run: pid=pppp -qf
1999-03-02 09:44:33 10HmaZ-0005vi-00 == hdefer@test.ex <useryy@test.ex> R=halias defer (-1): not just yet
1999-03-02 09:44:33 10HmaZ-0005vi-00 == defer@test.ex <userxy@test.ex> R=alias defer (-1): not just yet
-1999-03-02 09:44:33 10HmaZ-0005vi-00 == /no/such/file <file@test.ex> R=alias T=address_file defer (EEE): Permission denied: failed to create directories for /no/such: Permission denied
+1999-03-02 09:44:33 10HmaZ-0005vi-00 == /no/such/file <file@test.ex> R=alias T=address_file defer (EEE): Permission denied: failed to create directories for /no/such/file: Permission denied
1999-03-02 09:44:33 10HmbA-0005vi-00 <= <> R=10HmaZ-0005vi-00 U=EXIMUSER P=local S=sss
1999-03-02 09:44:33 10HmbA-0005vi-00 => CALLER <CALLER@test.ex> R=localuser T=local_delivery
1999-03-02 09:44:33 10HmbA-0005vi-00 Completed