X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4938e65b323e0cbc06c7e0fd06575b7eb7780ee4..e2e0f812212b81ac029f90b57c63ee88455f6a12:/src/src/exim.c diff --git a/src/src/exim.c b/src/src/exim.c index 49f7e5f36..833045018 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -45,7 +45,7 @@ are two sets of functions; one for use when we want to retain the compiled regular expression for a long time; the other for short-term use. */ static void * -function_store_get(size_t size) +function_store_get(PCRE2_SIZE size, void * tag) { /* For now, regard all RE results as potentially tainted. We might need more intelligence on this point. */ @@ -53,16 +53,16 @@ return store_get((int)size, TRUE); } static void -function_dummy_free(void * block) {} +function_dummy_free(void * block, void * tag) {} static void * -function_store_malloc(size_t size) +function_store_malloc(PCRE2_SIZE size, void * tag) { return store_malloc((int)size); } static void -function_store_free(void * block) +function_store_free(void * block, void * tag) { store_free(block); } @@ -98,29 +98,51 @@ Argument: Returns: pointer to the compiled pattern */ -const pcre * -regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc) +const pcre2_code * +regex_must_compile(const uschar * pattern, BOOL caseless, BOOL use_malloc) { -int offset; -int options = PCRE_COPT; -const pcre *yield; -const uschar *error; +size_t offset; +int options = caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT; +const pcre2_code * yield; +int err; +pcre2_general_context * gctx; +pcre2_compile_context * cctx; + if (use_malloc) { - pcre_malloc = function_store_malloc; - pcre_free = function_store_free; + gctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL); + cctx = pcre2_compile_context_create(gctx); } -if (caseless) options |= PCRE_CASELESS; -yield = pcre_compile(CCS pattern, options, CCSS &error, &offset, NULL); -pcre_malloc = function_store_get; -pcre_free = function_dummy_free; -if (yield == NULL) +else + cctx = pcre_cmp_ctx; + +if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options, + &err, &offset, cctx))) + { + uschar errbuf[128]; + pcre2_get_error_message(err, errbuf, sizeof(errbuf)); log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: " - "%s at offset %d while compiling %s", error, offset, pattern); + "%s at offset %d while compiling %s", errbuf, (long)offset, pattern); + } + +if (use_malloc) + { + pcre2_compile_context_free(cctx); + pcre2_general_context_free(gctx); + } return yield; } +static void +pcre_init(void) +{ +pcre_gen_ctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL); +pcre_cmp_ctx = pcre2_compile_context_create(pcre_gen_ctx); +pcre_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx); +} + + /************************************************* @@ -128,7 +150,8 @@ return yield; *************************************************/ /* This function runs a regular expression match, and sets up the pointers to -the matched substrings. +the matched substrings. The matched strings are copied so the lifetime of +the subject is not a problem. Arguments: re the compiled expression @@ -138,32 +161,67 @@ Arguments: if >= 0 setup from setup+1 onwards, excluding the full matched string -Returns: TRUE or FALSE +Returns: TRUE if matched, or FALSE */ BOOL -regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup) +regex_match_and_setup(const pcre2_code * re, const uschar * subject, int options, int setup) { -int ovector[3*(EXPAND_MAXN+1)]; -uschar * s = string_copy(subject); /* de-constifying */ -int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0, - PCRE_EOPT | options, ovector, nelem(ovector)); -BOOL yield = n >= 0; -if (n == 0) n = EXPAND_MAXN + 1; -if (yield) +pcre2_match_data * md = pcre2_match_data_create_from_pattern(re, pcre_gen_ctx); +int res = pcre2_match(re, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0, + PCRE_EOPT | options, md, pcre_mtc_ctx); +BOOL yield; + +if ((yield = (res >= 0))) { + res = pcre2_get_ovector_count(md); expand_nmax = setup < 0 ? 0 : setup + 1; - for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2) + for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++) { - expand_nstring[expand_nmax] = s + ovector[nn]; - expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn]; + PCRE2_SIZE len; + pcre2_substring_get_bynumber(md, matchnum, + (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len); + expand_nlength[expand_nmax++] = (int)len; } expand_nmax--; } +else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any) + { + uschar errbuf[128]; + pcre2_get_error_message(res, errbuf, sizeof(errbuf)); + debug_printf_indent("pcre2: %s\n", errbuf); + } +pcre2_match_data_free(md); return yield; } +/* Check just for match with regex. Uses the common memory-handling. + +Arguments: + re compiled regex + subject string to be checked + slen length of subject; -1 for nul-terminated + rptr pointer for matched string, copied, or NULL + +Return: TRUE for a match. +*/ + +BOOL +regex_match(const pcre2_code * re, const uschar * subject, int slen, uschar ** rptr) +{ +pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx); +int rc = pcre2_match(re, (PCRE2_SPTR)subject, + slen >= 0 ? slen : PCRE2_ZERO_TERMINATED, + 0, PCRE_EOPT, md, pcre_mtc_ctx); +PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); +if (rc < 0) + return FALSE; +if (rptr) + *rptr = string_copyn(subject + ovec[0], ovec[1] - ovec[0]); +return TRUE; +} + /************************************************* @@ -211,6 +269,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 * *************************************************/ @@ -233,18 +304,8 @@ int fd; os_restarting_signal(sig, usr1_handler); -if ((fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE)) < 0) - { - /* If we are already running as the Exim user, try to create it in the - current process (assuming spool_directory exists). Otherwise, if we are - root, do the creation in an exim:exim subprocess. */ - - int euid = geteuid(); - if (euid == exim_uid) - fd = Uopen(process_log_path, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE); - else if (euid == root_uid) - fd = log_create_as_exim(process_log_path); - } +if (!process_log_path) return; +fd = log_open_as_exim(process_log_path); /* If we are neither exim nor root, or if we failed to create the log file, give up. There is not much useful we can do with errors, since we don't want @@ -441,9 +502,10 @@ function prepares for the time when things are faster - and it also copes with clocks that go backwards. Arguments: - tgt_tv A timeval which was used to create uniqueness; its usec field + prev_tv A timeval which was used to create uniqueness; its usec field has been rounded down to the value of the resolution. We want to be sure the current time is greater than this. + On return, updated to current (rounded down). resolution The resolution that was used to divide the microseconds (1 for maildir, larger for message ids) @@ -451,7 +513,7 @@ Returns: nothing */ void -exim_wait_tick(struct timeval * tgt_tv, int resolution) +exim_wait_tick(struct timeval * prev_tv, int resolution) { struct timeval now_tv; long int now_true_usec; @@ -460,13 +522,13 @@ exim_gettime(&now_tv); now_true_usec = now_tv.tv_usec; now_tv.tv_usec = (now_true_usec/resolution) * resolution; -while (exim_tvcmp(&now_tv, tgt_tv) <= 0) +while (exim_tvcmp(&now_tv, prev_tv) <= 0) { struct itimerval itval; itval.it_interval.tv_sec = 0; itval.it_interval.tv_usec = 0; - itval.it_value.tv_sec = tgt_tv->tv_sec - now_tv.tv_sec; - itval.it_value.tv_usec = tgt_tv->tv_usec + resolution - now_true_usec; + itval.it_value.tv_sec = prev_tv->tv_sec - now_tv.tv_sec; + itval.it_value.tv_usec = prev_tv->tv_usec + resolution - now_true_usec; /* We know that, overall, "now" is less than or equal to "then". Therefore, a negative value for the microseconds is possible only in the case when "now" @@ -484,7 +546,7 @@ while (exim_tvcmp(&now_tv, tgt_tv) <= 0) if (!f.running_in_test_harness) { debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n", - tgt_tv->tv_sec, (long) tgt_tv->tv_usec, + prev_tv->tv_sec, (long) prev_tv->tv_usec, now_tv.tv_sec, (long) now_tv.tv_usec); debug_printf("waiting " TIME_T_FMT ".%06lu sec\n", itval.it_value.tv_sec, (long) itval.it_value.tv_usec); @@ -500,6 +562,7 @@ while (exim_tvcmp(&now_tv, tgt_tv) <= 0) now_true_usec = now_tv.tv_usec; now_tv.tv_usec = (now_true_usec/resolution) * resolution; } +*prev_tv = now_tv; } @@ -761,18 +824,18 @@ exit(EXIT_FAILURE); } /* fail if a length is too long */ -static void +static inline void exim_len_fail_toolong(int itemlen, int maxlen, const char *description) { if (itemlen <= maxlen) return; fprintf(stderr, "exim: length limit exceeded (%d > %d) for: %s\n", - len, maxlen, description) + itemlen, maxlen, description); exit(EXIT_FAILURE); } /* only pass through the string item back to the caller if it's short enough */ -static const uschar * +static inline const uschar * exim_str_fail_toolong(const uschar *item, int maxlen, const char *description) { exim_len_fail_toolong(Ustrlen(item), maxlen, description); @@ -1176,11 +1239,15 @@ show_db_version(fp); #endif #define QUOTE(X) #X #define EXPAND_AND_QUOTE(X) QUOTE(X) - fprintf(fp, "Library version: PCRE: Compile: %d.%d%s\n" - " Runtime: %s\n", - PCRE_MAJOR, PCRE_MINOR, - EXPAND_AND_QUOTE(PCRE_PRERELEASE) "", - pcre_version()); + { + uschar buf[24]; + pcre2_config(PCRE2_CONFIG_VERSION, buf); + fprintf(fp, "Library version: PCRE2: Compile: %d.%d%s\n" + " Runtime: %s\n", + PCRE2_MAJOR, PCRE2_MINOR, + EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "", + buf); + } #undef QUOTE #undef EXPAND_AND_QUOTE @@ -1533,14 +1600,8 @@ for (macro_item * m = macros_user; m; m = m->next) if (m->command_line) continue; if ((len = m->replen) == 0) continue; - n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len, - 0, PCRE_EOPT, NULL, 0); - if (n < 0) - { - if (n != PCRE_ERROR_NOMATCH) - debug_printf("macros_trusted checking %s returned %d\n", m->name, n); + if (!regex_match(regex_whitelisted_macro, m->replacement, len, NULL)) return FALSE; - } } DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n"); return TRUE; @@ -1559,10 +1620,11 @@ Arguments: */ static void -expansion_test_line(uschar * line) +expansion_test_line(const uschar * line) { int len; BOOL dummy_macexp; +uschar * s; Ustrncpy(big_buffer, line, big_buffer_size); big_buffer[big_buffer_size-1] = '\0'; @@ -1576,7 +1638,7 @@ if (isupper(big_buffer[0])) printf("Defined macro '%s'\n", mlast->name); } else - if ((line = expand_string(big_buffer))) printf("%s\n", CS line); + if ((s = expand_string(big_buffer))) printf("%s\n", CS s); else printf("Failed: %s\n", expand_string_message); } @@ -1640,7 +1702,6 @@ BOOL list_queue = FALSE; BOOL list_options = FALSE; BOOL list_config = FALSE; BOOL local_queue_only; -BOOL more = TRUE; BOOL one_msg_action = FALSE; BOOL opt_D_used = FALSE; BOOL queue_only_set = FALSE; @@ -1660,10 +1721,10 @@ uschar *cmdline_syslog_name = NULL; uschar *start_queue_run_id = NULL; uschar *stop_queue_run_id = NULL; uschar *expansion_test_message = NULL; -uschar *ftest_domain = NULL; -uschar *ftest_localpart = NULL; -uschar *ftest_prefix = NULL; -uschar *ftest_suffix = NULL; +const uschar *ftest_domain = NULL; +const uschar *ftest_localpart = NULL; +const uschar *ftest_prefix = NULL; +const uschar *ftest_suffix = NULL; uschar *log_oneline = NULL; uschar *malware_test_file = NULL; uschar *real_sender_address; @@ -1695,6 +1756,7 @@ extern char **environ; #endif store_init(); /* Initialise the memory allocation susbsystem */ +pcre_init(); /* Set up memory handling for pcre */ /* If the Exim user and/or group and/or the configuration file owner/group were defined by ref:name at build time, we must now find the actual uid/gid values. @@ -1759,7 +1821,7 @@ if (f.running_in_test_harness) debug_store = TRUE; /* Protect against abusive argv[0] */ -exim_len_fail_toolong(argv[0], PATH_MAX, "argv[0]"); +exim_str_fail_toolong(argv[0], PATH_MAX, "argv[0]"); /* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed at the start of a program; however, it seems that some environments do not @@ -1795,15 +1857,6 @@ indirection, because some systems don't allow writing to the variable "stderr". if (fstat(fileno(stderr), &statbuf) >= 0) log_stderr = stderr; -/* Arrange for the PCRE regex library to use our store functions. Note that -the normal calls are actually macros that add additional arguments for -debugging purposes so we have to assign specially constructed functions here. -The default is to use store in the stacking pool, but this is overridden in the -regex_must_compile() function. */ - -pcre_malloc = function_store_get; -pcre_free = function_dummy_free; - /* Ensure there is a big buffer for temporary use in several places. It is put in malloc store so that it can be freed for enlargement if necessary. */ @@ -1814,7 +1867,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 */ @@ -3255,6 +3309,10 @@ on the second character (the one after '-'), to save some effort. */ -oPX: delete pid file of daemon */ case 'P': + if (!f.running_in_test_harness && real_uid != root_uid && real_uid != exim_uid) + exim_fail("exim: only uid=%d or uid=%d can use -oP and -oPX " + "(uid=%d euid=%d | %d)\n", + root_uid, exim_uid, getuid(), geteuid(), real_uid); if (!*argrest) override_pid_file_path = argv[++i]; else if (Ustrcmp(argrest, "X") == 0) delete_pid_file(); else badarg = TRUE; @@ -3284,7 +3342,7 @@ on the second character (the one after '-'), to save some effort. */ case 'X': if (*argrest) badarg = TRUE; - else override_local_interfaces = string_copy_taint(exim_str_fail_toolong(argv[++i], 1024, "-oX", TRUE); + else override_local_interfaces = string_copy_taint(exim_str_fail_toolong(argv[++i], 1024, "-oX"), TRUE); break; /* -oY: Override creation of daemon notifier socket */ @@ -3421,7 +3479,10 @@ on the second character (the one after '-'), to save some effort. */ case 'R': /* Synonymous with -qR... */ - receiving_message = FALSE; + { + const uschar *tainted_selectstr; + + receiving_message = FALSE; /* -Rf: As -R (below) but force all deliveries, -Rff: Ditto, but also thaw all frozen messages, @@ -3432,29 +3493,29 @@ on the second character (the one after '-'), to save some effort. */ in all cases provided there are no further characters in this argument. */ - if (*argrest) - for (int i = 0; i < nelem(rsopts); i++) - if (Ustrcmp(argrest, rsopts[i]) == 0) - { - if (i != 2) f.queue_run_force = TRUE; - if (i >= 2) f.deliver_selectstring_regex = TRUE; - if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; - argrest += Ustrlen(rsopts[i]); - } + if (*argrest) + for (int i = 0; i < nelem(rsopts); i++) + if (Ustrcmp(argrest, rsopts[i]) == 0) + { + if (i != 2) f.queue_run_force = TRUE; + if (i >= 2) f.deliver_selectstring_regex = TRUE; + if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; + argrest += Ustrlen(rsopts[i]); + } /* -R: Set string to match in addresses for forced queue run to pick out particular messages. */ - /* Avoid attacks from people providing very long strings, and do so before - we make copies. */ - const char *tainted_selectstr; - if (*argrest) - tainted_selectstr = argrest; - else if (i+1 < argc) - tainted_selectstr = argv[++i]; - else - exim_fail("exim: string expected after -R\n"); - deliver_selectstring = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-R"), TRUE); + /* Avoid attacks from people providing very long strings, and do so before + we make copies. */ + if (*argrest) + tainted_selectstr = argrest; + else if (i+1 < argc) + tainted_selectstr = argv[++i]; + else + exim_fail("exim: string expected after -R\n"); + deliver_selectstring = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-R"), TRUE); + } break; /* -r: an obsolete synonym for -f (see above) */ @@ -3463,7 +3524,10 @@ on the second character (the one after '-'), to save some effort. */ /* -S: Like -R but works on sender. */ case 'S': /* Synonymous with -qS... */ - receiving_message = FALSE; + { + const uschar *tainted_selectstr; + + receiving_message = FALSE; /* -Sf: As -S (below) but force all deliveries, -Sff: Ditto, but also thaw all frozen messages, @@ -3474,27 +3538,27 @@ on the second character (the one after '-'), to save some effort. */ in all cases provided there are no further characters in this argument. */ - if (*argrest) - for (int i = 0; i < nelem(rsopts); i++) - if (Ustrcmp(argrest, rsopts[i]) == 0) - { - if (i != 2) f.queue_run_force = TRUE; - if (i >= 2) f.deliver_selectstring_sender_regex = TRUE; - if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; - argrest += Ustrlen(rsopts[i]); - } + if (*argrest) + for (int i = 0; i < nelem(rsopts); i++) + if (Ustrcmp(argrest, rsopts[i]) == 0) + { + if (i != 2) f.queue_run_force = TRUE; + if (i >= 2) f.deliver_selectstring_sender_regex = TRUE; + if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; + argrest += Ustrlen(rsopts[i]); + } /* -S: Set string to match in addresses for forced queue run to pick out particular messages. */ - const char *tainted_selectstr; - if (*argrest) - tainted_selectstr = argrest; - else if (i+1 < argc) - tainted_selectstr = argv[++i]; - else - exim_fail("exim: string expected after -S\n"); - deliver_selectstring_sender = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-S"), TRUE); + if (*argrest) + tainted_selectstr = argrest; + else if (i+1 < argc) + tainted_selectstr = argv[++i]; + else + exim_fail("exim: string expected after -S\n"); + deliver_selectstring_sender = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-S"), TRUE); + } break; /* -Tqt is an option that is exclusively for use by the testing suite. @@ -3687,7 +3751,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", @@ -3710,9 +3774,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", @@ -3720,20 +3784,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 @@ -3856,6 +3920,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 @@ -3871,6 +3936,11 @@ during readconf_main() some expansion takes place already. */ /* Store the initial cwd before we change directories. Can be NULL if the dir has already been unlinked. */ initial_cwd = os_getcwd(NULL, 0); +if (!initial_cwd && errno) + exim_fail("exim: getting initial cwd failed: %s\n", strerror(errno)); + +if (initial_cwd && (strlen(CCS initial_cwd) >= BIG_BUFFER_SIZE)) + exim_fail("exim: initial cwd is far too long (%d)\n", Ustrlen(CCS initial_cwd)); /* checking: -be[m] expansion test - @@ -3889,19 +3959,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) @@ -4158,11 +4230,9 @@ if ( (debug_selector & D_any || LOGGING(arguments)) p += 13; else { - Ustrncpy(p + 4, initial_cwd, big_buffer_size-5); - p += 4 + Ustrlen(initial_cwd); - /* in case p is near the end and we don't provide enough space for - * string_format to be willing to write. */ - *p = '\0'; + p += 4; + snprintf(CS p, big_buffer_size - (p - big_buffer), "%s", CCS initial_cwd); + p += Ustrlen(CCS p); } (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc); @@ -4490,7 +4560,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) { @@ -4515,12 +4591,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)"); @@ -4549,7 +4629,7 @@ if (test_retry_arg >= 0) retry_config *yield; int basic_errno = 0; int more_errno = 0; - uschar *s1, *s2; + const uschar *s1, *s2; if (test_retry_arg >= argc) { @@ -4578,7 +4658,7 @@ if (test_retry_arg >= 0) if (test_retry_arg < argc) { - uschar *ss = exim_str_fail_toolong(argv[test_retry_arg], EXIM_DRIVERNAME_MAX, "-brt 3rd"); + const uschar *ss = exim_str_fail_toolong(argv[test_retry_arg], EXIM_DRIVERNAME_MAX, "-brt 3rd"); uschar *error = readconf_retry_error(ss, ss + Ustrlen(ss), &basic_errno, &more_errno); if (error != NULL) @@ -4813,7 +4893,7 @@ for (i = 0;;) if (gecos_pattern && gecos_name) { - const pcre *re; + const pcre2_code *re; re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */ if (regex_match_and_setup(re, name, 0, -1)) @@ -5062,11 +5142,15 @@ if (expansion_test) dns_init(FALSE, FALSE, FALSE); if (msg_action_arg > 0 && msg_action == MSG_LOAD) { - uschar spoolname[SPOOL_NAME_LENGTH]; /* Not big_buffer; used in spool_read_header() */ + uschar * spoolname; if (!f.admin_user) exim_fail("exim: permission denied\n"); - message_id = exim_str_fail_toolong(argv[msg_action_arg], MESSAGE_ID_LENGTH, "message-id"); - (void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id); + message_id = US exim_str_fail_toolong(argv[msg_action_arg], MESSAGE_ID_LENGTH, "message-id"); + /* Checking the length of the ID is sufficient to validate it. + Get an untainted version so file opens can be done. */ + message_id = string_copy_taint(message_id, FALSE); + + spoolname = string_sprintf("%s-H", message_id); if ((deliver_datafile = spool_open_datafile(message_id)) < 0) printf ("Failed to load message datafile %s\n", message_id); if (spool_read_header(spoolname, TRUE, FALSE) != spool_read_OK) @@ -5443,15 +5527,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 @@ -5463,7 +5547,7 @@ real_sender_address = sender_address; messages to be read (SMTP input), or FALSE otherwise (not SMTP, or SMTP channel collapsed). */ -while (more) +for (BOOL more = TRUE; more; ) { rmark reset_point = store_mark(); message_id[0] = 0; @@ -5505,10 +5589,10 @@ while (more) /* Now get the data for the message */ more = receive_msg(extract_recipients); - if (message_id[0] == 0) + if (!message_id[0]) { cancel_cutthrough_connection(TRUE, US"receive dropped"); - if (more) goto moreloop; + if (more) goto MORELOOP; smtp_log_no_mail(); /* Log no mail if configured */ exim_exit(EXIT_FAILURE); } @@ -5585,11 +5669,12 @@ while (more) parse_extract_address(s, &errmess, &start, &end, &domain, FALSE); #ifdef SUPPORT_I18N - if (string_is_utf8(recipient)) - message_smtputf8 = TRUE; - else - allow_utf8_domains = b; + if (recipient) + if (string_is_utf8(recipient)) message_smtputf8 = TRUE; + else allow_utf8_domains = b; } +#else + ; #endif if (domain == 0 && !f.allow_unqualified_recipient) { @@ -5673,7 +5758,7 @@ while (more) for real; when reading the headers of a message for filter testing, it is TRUE if the headers were terminated by '.' and FALSE otherwise. */ - if (message_id[0] == 0) exim_exit(EXIT_FAILURE); + if (!message_id[0]) exim_exit(EXIT_FAILURE); } /* Non-SMTP message reception */ /* If this is a filter testing run, there are headers in store, but @@ -5687,10 +5772,10 @@ while (more) { deliver_domain = ftest_domain ? ftest_domain : qualify_domain_recipient; deliver_domain_orig = deliver_domain; - deliver_localpart = ftest_localpart ? ftest_localpart : originator_login; + deliver_localpart = ftest_localpart ? US ftest_localpart : originator_login; deliver_localpart_orig = deliver_localpart; - deliver_localpart_prefix = ftest_prefix; - deliver_localpart_suffix = ftest_suffix; + deliver_localpart_prefix = US ftest_prefix; + deliver_localpart_suffix = US ftest_suffix; deliver_home = originator_home; if (!return_path) @@ -5866,11 +5951,11 @@ while (more) finished subprocesses here, in case there are lots of messages coming in from the same source. */ - #ifndef SIG_IGN_WORKS +#ifndef SIG_IGN_WORKS while (waitpid(-1, NULL, WNOHANG) > 0); - #endif +#endif -moreloop: +MORELOOP: return_path = sender_address = NULL; authenticated_sender = NULL; deliver_localpart_orig = NULL;