From d56e798eb66ac044ff22f0daa2185549f5d49632 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Mon, 10 May 2021 22:47:01 +0100 Subject: [PATCH] first go. crashes in 0003 --- src/src/exim.c | 29 ++++++++---- src/src/functions.h | 1 + src/src/readconf.c | 104 +++++++++++++++++++++----------------------- src/src/store.c | 41 ++++++++++++++--- src/src/store.h | 4 +- 5 files changed, 111 insertions(+), 68 deletions(-) diff --git a/src/src/exim.c b/src/src/exim.c index 6a9a1477f..eb1d83416 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3688,7 +3688,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 +3711,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 +3721,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 +3857,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 +3896,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 +4497,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 +4528,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)"); diff --git a/src/src/functions.h b/src/src/functions.h index e34972170..38c8e40a0 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -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/readconf.c b/src/src/readconf.c index 34ebf8769..4602dd262 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -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) 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 }; -- 2.30.2