X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/dd19ce4f24eec64177cdcfcf294b8efbb631a24b..1d28cc061677bd07d9bed48dd84bd5c590247043:/src/src/exim.c diff --git a/src/src/exim.c b/src/src/exim.c index 42db457c0..35f4ae4f7 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2,9 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* The main function: entry point, initialization, and high-level control. @@ -17,6 +18,13 @@ Also a few functions that don't naturally fit elsewhere. */ # include #endif +#ifndef _TIME_H +# include +#endif +#ifndef NO_EXECINFO +# include +#endif + #ifdef USE_GNUTLS # include # if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP) @@ -24,10 +32,6 @@ Also a few functions that don't naturally fit elsewhere. */ # endif #endif -#ifndef _TIME_H -# include -#endif - extern void init_lookup_list(void); @@ -56,78 +60,40 @@ if (block) store_free(block); } +static void * +function_store_get(PCRE2_SIZE size, void * tag) +{ +return store_get((int)size, GET_UNTAINTED); /* loses track of taint */ +} - -/************************************************* -* Enums for cmdline interface * -*************************************************/ - -enum commandline_info { CMDINFO_NONE=0, - CMDINFO_HELP, CMDINFO_SIEVE, CMDINFO_DSCP }; +static void +function_store_nullfree(void * block, void * tag) +{ +} /************************************************* -* Compile regular expression and panic on fail * +* Enums for cmdline interface * *************************************************/ -/* This function is called when failure to compile a regular expression leads -to a panic exit. In other cases, pcre_compile() is called directly. In many -cases where this function is used, the results of the compilation are to be -placed in long-lived store, so we temporarily reset the store management -functions that PCRE uses if the use_malloc flag is set. - -Argument: - pattern the pattern to compile - caseless TRUE if caseless matching is required - use_malloc TRUE if compile into malloc store - -Returns: pointer to the compiled pattern -*/ - -const pcre2_code * -regex_must_compile(const uschar * pattern, BOOL caseless, BOOL use_malloc) -{ -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) - { - gctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL); - cctx = pcre2_compile_context_create(gctx); - } -else - cctx = pcre_cmp_ctx; +enum commandline_info { CMDINFO_NONE=0, + CMDINFO_HELP, CMDINFO_SIEVE, CMDINFO_DSCP }; -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 %ld 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); +pcre_mlc_ctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL); +pcre_gen_ctx = pcre2_general_context_create(function_store_get, function_store_nullfree, NULL); + +pcre_mlc_cmp_ctx = pcre2_compile_context_create(pcre_mlc_ctx); +pcre_gen_cmp_ctx = pcre2_compile_context_create(pcre_gen_ctx); + +pcre_gen_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx); } @@ -139,7 +105,9 @@ pcre_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx); /* This function runs a regular expression match, and sets up the pointers to the matched substrings. The matched strings are copied so the lifetime of -the subject is not a problem. +the subject is not a problem. Matched strings will have the same taint status +as the subject string (this is not a de-taint method, and must not be made so +given the support for wildcards in REs). Arguments: re the compiled expression @@ -157,19 +125,25 @@ regex_match_and_setup(const pcre2_code * re, const uschar * subject, int options { 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); + PCRE_EOPT | options, md, pcre_gen_mtc_ctx); BOOL yield; if ((yield = (res >= 0))) { + PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); res = pcre2_get_ovector_count(md); expand_nmax = setup < 0 ? 0 : setup + 1; for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++) { - PCRE2_SIZE len; - pcre2_substring_get_bynumber(md, matchnum, - (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len); - expand_nlength[expand_nmax++] = (int)len; + /* Although PCRE2 has a pcre2_substring_get_bynumber() conveneience, it + seems to return a bad pointer when a capture group had no data, eg. (.*) + matching zero letters. So use the underlying ovec and hope (!) that the + offsets are sane (including that case). Should we go further and range- + check each one vs. the subject string length? */ + int off = matchnum * 2; + int len = ovec[off + 1] - ovec[off]; + expand_nstring[expand_nmax] = string_copyn(subject + ovec[off], len); + expand_nlength[expand_nmax++] = len; } expand_nmax--; } @@ -179,7 +153,7 @@ else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any) pcre2_get_error_message(res, errbuf, sizeof(errbuf)); debug_printf_indent("pcre2: %s\n", errbuf); } -pcre2_match_data_free(md); +/* pcre2_match_data_free(md); gen ctx needs no free */ return yield; } @@ -201,13 +175,18 @@ regex_match(const pcre2_code * re, const uschar * subject, int slen, uschar ** r 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); + 0, PCRE_EOPT, md, pcre_gen_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; +BOOL ret = FALSE; + +if (rc >= 0) + { + if (rptr) + *rptr = string_copyn(subject + ovec[0], ovec[1] - ovec[0]); + ret = TRUE; + } +/* pcre2_match_data_free(md); gen ctx needs no free */ +return ret; } @@ -261,13 +240,67 @@ exit(1); * Handler for SIGSEGV * ***********************************************/ +#define STACKDUMP_MAX 24 +void +stackdump(void) +{ +#ifndef NO_EXECINFO +void * buf[STACKDUMP_MAX]; +char ** ss; +int nptrs = backtrace(buf, STACKDUMP_MAX); + +log_write(0, LOG_MAIN|LOG_PANIC, "backtrace"); +log_write(0, LOG_MAIN|LOG_PANIC, "---"); +if ((ss = backtrace_symbols(buf, nptrs))) + { + for (int i = 0; i < nptrs; i++) + log_write(0, LOG_MAIN|LOG_PANIC, "\t%s", ss[i]); + free(ss); + } +else + log_write(0, LOG_MAIN|LOG_PANIC, "backtrace_symbols: %s", strerror(errno)); +log_write(0, LOG_MAIN|LOG_PANIC, "---"); +#endif +} +#undef STACKDUMP_MAX + + static void +#ifdef SA_SIGINFO +segv_handler(int sig, siginfo_t * info, void * uctx) +{ +log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (fault address: %p)", info->si_addr); +# if defined(SEGV_MAPERR) && defined(SEGV_ACCERR) && defined(SEGV_BNDERR) && defined(SEGV_PKUERR) +switch (info->si_code) + { + case SEGV_MAPERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_MAPERR"); break; + case SEGV_ACCERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_ACCERR"); break; + case SEGV_BNDERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_BNDERR"); break; + case SEGV_PKUERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_PKUERR"); break; + } +# endif +if (US info->si_addr < US 4096) + log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (null pointer indirection)"); +else + log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)"); +if (process_info_len > 0) + log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%.*s)", process_info_len, process_info); +stackdump(); +signal(SIGSEGV, SIG_DFL); +kill(getpid(), sig); +} + +#else segv_handler(int sig) { log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)"); +if (process_info_len > 0) + log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%.*s)", process_info_len, process_info); +stackdump(); signal(SIGSEGV, SIG_DFL); kill(getpid(), sig); } +#endif /************************************************* @@ -712,7 +745,7 @@ Returns: nothing; bombs out on failure */ void -exim_setugid(uid_t uid, gid_t gid, BOOL igflag, uschar *msg) +exim_setugid(uid_t uid, gid_t gid, BOOL igflag, const uschar * msg) { uid_t euid = geteuid(); gid_t egid = getegid(); @@ -921,7 +954,7 @@ else int rc = verify_address(deliver_make_addr(address,TRUE), stdout, flags, -1, -1, -1, NULL, NULL, NULL); if (rc == FAIL) *exit_value = 2; - else if (rc == DEFER && *exit_value == 0) *exit_value = 1; + else if (rc == DEFER && *exit_value == 0) *exit_value = 1; } } @@ -932,54 +965,66 @@ else *************************************************/ static void -show_db_version(FILE * f) +show_string(BOOL is_stdout, gstring * g) +{ +const uschar * s = string_from_gstring(g); +if (s) + if (is_stdout) fputs(CCS s, stdout); + else debug_printf("%s", s); +} + + +static gstring * +show_db_version(gstring * g) { #ifdef DB_VERSION_STRING DEBUG(D_any) { - fprintf(f, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING); - fprintf(f, " Runtime: %s\n", + g = string_fmt_append(g, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING); + g = string_fmt_append(g, " Runtime: %s\n", db_version(NULL, NULL, NULL)); } else - fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING); + g = string_fmt_append(g, "Berkeley DB: %s\n", DB_VERSION_STRING); #elif defined(BTREEVERSION) && defined(HASHVERSION) - #ifdef USE_DB - fprintf(f, "Probably Berkeley DB version 1.8x (native mode)\n"); - #else - fprintf(f, "Probably Berkeley DB version 1.8x (compatibility mode)\n"); - #endif +# ifdef USE_DB + g = string_cat(g, US"Probably Berkeley DB version 1.8x (native mode)\n"); +# else + g = string_cat(g, US"Probably Berkeley DB version 1.8x (compatibility mode)\n"); +# endif #elif defined(_DBM_RDONLY) || defined(dbm_dirfno) -fprintf(f, "Probably ndbm\n"); +g = string_cat(g, US"Probably ndbm\n"); #elif defined(USE_TDB) -fprintf(f, "Using tdb\n"); +g = string_cat(g, US"Using tdb\n"); #else - #ifdef USE_GDBM - fprintf(f, "Probably GDBM (native mode)\n"); - #else - fprintf(f, "Probably GDBM (compatibility mode)\n"); - #endif +# ifdef USE_GDBM + g = string_cat(g, US"Probably GDBM (native mode)\n"); +# else + g = string_cat(g, US"Probably GDBM (compatibility mode)\n"); +# endif #endif +return g; } /* This function is called for -bV/--version and for -d to output the optional features of the current Exim binary. -Arguments: a FILE for printing +Arguments: BOOL, true for stdout else debug channel Returns: nothing */ static void -show_whats_supported(FILE * fp) +show_whats_supported(BOOL is_stdout) { rmark reset_point = store_mark(); -gstring * g; -DEBUG(D_any) {} else show_db_version(fp); +gstring * g = NULL; + +DEBUG(D_any) {} else g = show_db_version(g); -g = string_cat(NULL, US"Support for:"); +g = string_cat(g, US"Support for:"); #ifdef SUPPORT_CRYPTEQ g = string_cat(g, US" crypteq"); #endif @@ -1086,9 +1131,6 @@ g = string_cat(NULL, US"Support for:"); #ifdef EXPERIMENTAL_QUEUEFILE g = string_cat(g, US" Experimental_QUEUEFILE"); #endif -#if defined(EXPERIMENTAL_SRS_ALT) - g = string_cat(g, US" Experimental_SRS"); -#endif g = string_cat(g, US"\n"); g = string_cat(g, US"Lookups (built-in):"); @@ -1158,6 +1200,7 @@ g = transport_show_supported(g); #ifdef WITH_CONTENT_SCAN g = malware_show_supported(g); #endif +show_string(is_stdout, g); g = NULL; if (fixed_never_users[0] > 0) { @@ -1169,19 +1212,19 @@ if (fixed_never_users[0] > 0) } g = string_fmt_append(g, "Configure owner: %d:%d\n", config_uid, config_gid); -fputs(CS string_from_gstring(g), fp); -fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); +g = string_fmt_append(g, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); /* Everything else is details which are only worth reporting when debugging. Perhaps the tls_version_report should move into this too. */ -DEBUG(D_any) do { +DEBUG(D_any) + { /* clang defines __GNUC__ (at least, for me) so test for it first */ #if defined(__clang__) - fprintf(fp, "Compiler: CLang [%s]\n", __clang_version__); + g = string_fmt_append(g, "Compiler: CLang [%s]\n", __clang_version__); #elif defined(__GNUC__) - fprintf(fp, "Compiler: GCC [%s]\n", + g = string_fmt_append(g, "Compiler: GCC [%s]\n", # ifdef __VERSION__ __VERSION__ # else @@ -1189,35 +1232,38 @@ DEBUG(D_any) do { # endif ); #else - fprintf(fp, "Compiler: \n"); + g = string_cat(g, US"Compiler: \n"); #endif #if defined(__GLIBC__) && !defined(__UCLIBC__) - fprintf(fp, "Library version: Glibc: Compile: %d.%d\n", + g = string_fmt_append(g, "Library version: Glibc: Compile: %d.%d\n", __GLIBC__, __GLIBC_MINOR__); if (__GLIBC_PREREQ(2, 1)) - fprintf(fp, " Runtime: %s\n", + g = string_fmt_append(g, " Runtime: %s\n", gnu_get_libc_version()); #endif -show_db_version(fp); +g = show_db_version(g); #ifndef DISABLE_TLS - tls_version_report(fp); + g = tls_version_report(g); #endif #ifdef SUPPORT_I18N - utf8_version_report(fp); + g = utf8_version_report(g); #endif #ifdef SUPPORT_DMARC - dmarc_version_report(fp); + g = dmarc_version_report(g); #endif #ifdef SUPPORT_SPF - spf_lib_version_report(fp); + g = spf_lib_version_report(g); #endif - for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi) - if (authi->version_report) - (*authi->version_report)(fp); +show_string(is_stdout, g); +g = NULL; + +for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi) + if (authi->version_report) + g = (*authi->version_report)(g); /* PCRE_PRERELEASE is either defined and empty or a bare sequence of characters; unless it's an ancient version of PCRE in which case it @@ -1230,7 +1276,7 @@ show_db_version(fp); { uschar buf[24]; pcre2_config(PCRE2_CONFIG_VERSION, buf); - fprintf(fp, "Library version: PCRE2: Compile: %d.%d%s\n" + g = string_fmt_append(g, "Library version: PCRE2: Compile: %d.%d%s\n" " Runtime: %s\n", PCRE2_MAJOR, PCRE2_MINOR, EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "", @@ -1239,23 +1285,29 @@ show_db_version(fp); #undef QUOTE #undef EXPAND_AND_QUOTE - init_lookup_list(); - for (int i = 0; i < lookup_list_count; i++) - if (lookup_list[i]->version_report) - lookup_list[i]->version_report(fp); +show_string(is_stdout, g); +g = NULL; + +init_lookup_list(); +for (int i = 0; i < lookup_list_count; i++) + if (lookup_list[i]->version_report) + g = lookup_list[i]->version_report(g); +show_string(is_stdout, g); +g = NULL; #ifdef WHITELIST_D_MACROS - fprintf(fp, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); + g = string_fmt_append(g, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); #else - fprintf(fp, "WHITELIST_D_MACROS unset\n"); + g = string_cat(g, US"WHITELIST_D_MACROS unset\n"); #endif #ifdef TRUSTED_CONFIG_LIST - fprintf(fp, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); + g = string_fmt_append(g, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); #else - fprintf(fp, "TRUSTED_CONFIG_LIST unset\n"); + g = string_cat(g, US"TRUSTED_CONFIG_LIST unset\n"); #endif + } -} while (0); +show_string(is_stdout, g); store_reset(reset_point); } @@ -1407,56 +1459,58 @@ static uschar * get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *)) { gstring * g = NULL; +BOOL had_input = FALSE; if (!fn_readline) { printf("> "); fflush(stdout); } for (int i = 0;; i++) { uschar buffer[1024]; - uschar *p, *ss; + uschar * p, * ss; - #ifdef USE_READLINE +#ifdef USE_READLINE char *readline_line = NULL; if (fn_readline) { if (!(readline_line = fn_readline((i > 0)? "":"> "))) break; - if (*readline_line != 0 && fn_addhist) fn_addhist(readline_line); + if (*readline_line && fn_addhist) fn_addhist(readline_line); p = US readline_line; } else - #endif +#endif /* readline() not in use */ { - if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break; + if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break; /*EOF*/ p = buffer; } /* Handle the line */ - ss = p + (int)Ustrlen(p); - while (ss > p && isspace(ss[-1])) ss--; + had_input = TRUE; + ss = p + Ustrlen(p); + while (ss > p && isspace(ss[-1])) ss--; /* strip trailing newline (and spaces) */ if (i > 0) - while (p < ss && isspace(*p)) p++; /* leading space after cont */ + while (p < ss && isspace(*p)) p++; /* strip leading space after cont */ g = string_catn(g, p, ss - p); - #ifdef USE_READLINE +#ifdef USE_READLINE if (fn_readline) free(readline_line); - #endif +#endif /* g can only be NULL if ss==p */ - if (ss == p || g->s[g->ptr-1] != '\\') + if (ss == p || g->s[g->ptr-1] != '\\') /* not continuation; done */ break; - --g->ptr; - (void) string_from_gstring(g); + --g->ptr; /* drop the \ */ } -if (!g) printf("\n"); -return string_from_gstring(g); +if (had_input) return g ? string_from_gstring(g) : US""; +printf("\n"); +return NULL; } @@ -1625,6 +1679,8 @@ if (isupper(big_buffer[0])) if (macro_read_assignment(big_buffer)) printf("Defined macro '%s'\n", mlast->name); } +else if (Ustrncmp(big_buffer, "set ", 4) == 0) + printf("%s\n", acl_standalone_setvar(big_buffer+4)); else if ((s = expand_string(big_buffer))) printf("%s\n", CS s); else printf("Failed: %s\n", expand_string_message); @@ -1665,7 +1721,7 @@ int i, rv; int list_queue_option = 0; int msg_action = 0; int msg_action_arg = -1; -int namelen = (argv[0] == NULL)? 0 : Ustrlen(argv[0]); +int namelen = argv[0] ? Ustrlen(argv[0]) : 0; int queue_only_reason = 0; #ifdef EXIM_PERL int perl_start_option = 0; @@ -1809,6 +1865,7 @@ if (f.running_in_test_harness) debug_store = TRUE; /* Protect against abusive argv[0] */ +if (!argv[0] || !argc) exim_fail("exim: executable name required\n"); exim_str_fail_toolong(argv[0], PATH_MAX, "argv[0]"); /* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed @@ -1853,10 +1910,17 @@ big_buffer = store_malloc(big_buffer_size); /* Set up the handler for the data request signal, and set the initial descriptive text. */ -process_info = store_get(PROCESS_INFO_SIZE, TRUE); /* tainted */ +process_info = store_get(PROCESS_INFO_SIZE, GET_TAINTED); set_process_info("initializing"); os_restarting_signal(SIGUSR1, usr1_handler); /* exiwhat */ +#ifdef SA_SIGINFO + { + struct sigaction act = { .sa_sigaction = segv_handler, .sa_flags = SA_RESETHAND | SA_SIGINFO }; + sigaction(SIGSEGV, &act, NULL); + } +#else signal(SIGSEGV, segv_handler); /* log faults */ +#endif /* 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 */ @@ -1927,7 +1991,7 @@ this here, because the -M options check their arguments for syntactic validity using mac_ismsgid, which uses this. */ regex_ismsgid = - regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE); + regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", MCS_NOFLAGS, TRUE); /* Precompile the regular expression that is used for matching an SMTP error code, possibly extended, at the start of an error message. Note that the @@ -1935,18 +1999,16 @@ terminating whitespace character is included. */ regex_smtp_code = regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?", - FALSE, TRUE); + MCS_NOFLAGS, TRUE); #ifdef WHITELIST_D_MACROS /* Precompile the regular expression used to filter the content of macros given to -D for permissibility. */ regex_whitelisted_macro = - regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); + regex_must_compile(US"^[A-Za-z0-9_/.-]*$", MCS_NOFLAGS, TRUE); #endif -for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; - /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic links called "mailq" in standard OS configurations. */ @@ -2160,7 +2222,7 @@ on the second character (the one after '-'), to save some effort. */ -bdf: Ditto, but in the foreground. */ case 'd': - f.daemon_listen = TRUE; + f.daemon_listen = f.daemon_scion = TRUE; if (*argrest == 'f') f.background_daemon = FALSE; else if (*argrest) badarg = TRUE; break; @@ -2217,7 +2279,9 @@ on the second character (the one after '-'), to save some effort. */ if (!*argrest || Ustrcmp(argrest, "c") == 0) { if (++i >= argc) { badarg = TRUE; break; } - sender_host_address = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-bh"), TRUE); + sender_host_address = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-bh"), + GET_TAINTED); host_checking = checking = f.log_testing_mode = TRUE; f.host_checking_callout = *argrest == 'c'; message_logs = FALSE; @@ -2408,7 +2472,7 @@ on the second character (the one after '-'), to save some effort. */ version_cnumber, version_date); printf("%s\n", CS version_copyright); version_printed = TRUE; - show_whats_supported(stdout); + show_whats_supported(TRUE); f.log_testing_mode = TRUE; } else badarg = TRUE; @@ -2418,7 +2482,7 @@ on the second character (the one after '-'), to save some effort. */ case 'w': f.inetd_wait_mode = TRUE; f.background_daemon = FALSE; - f.daemon_listen = TRUE; + f.daemon_listen = f.daemon_scion = TRUE; if (*argrest) if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0) exim_fail("exim: bad time value %s: abandoned\n", argv[i]); @@ -2603,21 +2667,36 @@ on the second character (the one after '-'), to save some effort. */ #endif break; - /* -d: Set debug level (see also -v below) or set the drop_cr option. - The latter is now a no-op, retained for compatibility only. If -dd is used, - debugging subprocesses of the daemon is disabled. */ - case 'd': + + /* -dropcr: Set this option. Now a no-op, retained for compatibility only. */ + if (Ustrcmp(argrest, "ropcr") == 0) { /* drop_cr = TRUE; */ } - /* Use an intermediate variable so that we don't set debugging while - decoding the debugging bits. */ + /* -dp: Set up a debug pretrigger buffer with given size. */ + + else if (Ustrcmp(argrest, "p") == 0) + if (++i >= argc) + badarg = TRUE; + else + debug_pretrigger_setup(argv[i]); + + /* -dt: Set a debug trigger selector */ + + else if (Ustrncmp(argrest, "t=", 2) == 0) + dtrigger_selector = (unsigned int) Ustrtol(argrest + 2, NULL, 0); + + /* -d: Set debug level (see also -v below). + If -dd is used, debugging subprocesses of the daemon is disabled. */ else { + /* Use an intermediate variable so that we don't set debugging while + decoding the debugging bits. */ + unsigned int selector = D_default; debug_selector = 0; debug_file = NULL; @@ -2675,7 +2754,9 @@ on the second character (the one after '-'), to save some effort. */ case 'F': if (!*argrest) if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } - originator_name = string_copy_taint(exim_str_fail_toolong(argrest, EXIM_HUMANNAME_MAX, "-F"), TRUE); + originator_name = string_copy_taint( + exim_str_fail_toolong(argrest, EXIM_HUMANNAME_MAX, "-F"), + GET_TAINTED); f.sender_name_forced = TRUE; break; @@ -2703,7 +2784,7 @@ on the second character (the one after '-'), to save some effort. */ if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } (void) exim_str_fail_toolong(argrest, EXIM_DISPLAYMAIL_MAX, "-f"); if (!*argrest) - *(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */ + *(sender_address = store_get(1, GET_UNTAINTED)) = '\0'; /* Ensure writeable memory */ else { uschar * temp = argrest + Ustrlen(argrest) - 1; @@ -2718,7 +2799,7 @@ on the second character (the one after '-'), to save some effort. */ &dummy_start, &dummy_end, &sender_address_domain, TRUE))) exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); - sender_address = string_copy_taint(sender_address, TRUE); + sender_address = string_copy_taint(sender_address, GET_TAINTED); #ifdef SUPPORT_I18N message_smtputf8 = string_is_utf8(sender_address); allow_utf8_domains = FALSE; @@ -2768,7 +2849,7 @@ on the second character (the one after '-'), to save some effort. */ exim_fail("exim: the -L syslog name is too long: \"%s\"\n", argrest); if (sz < 1) exim_fail("exim: the -L syslog name is too short\n"); - cmdline_syslog_name = string_copy_taint(argrest, TRUE); + cmdline_syslog_name = string_copy_taint(argrest, GET_TAINTED); break; case 'M': @@ -2798,9 +2879,15 @@ on the second character (the one after '-'), to save some effort. */ if (msg_action_arg >= 0) exim_fail("exim: incompatible arguments\n"); - continue_transport = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-C internal transport"), TRUE); - continue_hostname = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-C internal hostname"), TRUE); - continue_host_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-C internal hostaddr"), TRUE); + continue_transport = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-C internal transport"), + GET_TAINTED); + continue_hostname = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-C internal hostname"), + GET_TAINTED); + continue_host_address = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-C internal hostaddr"), + GET_TAINTED); continue_sequence = Uatoi(argv[++i]); msg_action = MSG_DELIVER; msg_action_arg = ++i; @@ -2845,7 +2932,9 @@ on the second character (the one after '-'), to save some effort. */ /* -MCd: for debug, set a process-purpose string */ case 'd': if (++i < argc) - process_purpose = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCd"), TRUE); + process_purpose = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCd"), + GET_TAINTED); else badarg = TRUE; break; @@ -2853,7 +2942,9 @@ on the second character (the one after '-'), to save some effort. */ from the commandline should be tainted - but we will need an untainted value for the spoolfile when doing a -odi delivery process. */ - case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), FALSE); + case 'G': if (++i < argc) queue_name = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), + GET_UNTAINTED); else badarg = TRUE; break; @@ -2882,13 +2973,13 @@ on the second character (the one after '-'), to save some effort. */ case 'p': proxy_session = TRUE; if (++i < argc) { - proxy_local_address = string_copy_taint(argv[i], TRUE); + proxy_local_address = string_copy_taint(argv[i], GET_TAINTED); if (++i < argc) { proxy_local_port = Uatoi(argv[i]); if (++i < argc) { - proxy_external_address = string_copy_taint(argv[i], TRUE); + proxy_external_address = string_copy_taint(argv[i], GET_TAINTED); if (++i < argc) { proxy_external_port = Uatoi(argv[i]); @@ -2926,7 +3017,9 @@ on the second character (the one after '-'), to save some effort. */ case 'r': case 's': if (++i < argc) { - continue_proxy_sni = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_HOSTNAME_MAX, "-MCr/-MCs"), TRUE); + continue_proxy_sni = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_HOSTNAME_MAX, "-MCr/-MCs"), + GET_TAINTED); if (argrest[1] == 'r') continue_proxy_dane = TRUE; } else badarg = TRUE; @@ -2938,13 +3031,17 @@ on the second character (the one after '-'), to save some effort. */ and the TLS cipher. */ case 't': if (++i < argc) - sending_ip_address = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-MCt IP"), TRUE); + sending_ip_address = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-MCt IP"), + GET_TAINTED); else badarg = TRUE; if (++i < argc) sending_port = (int)(Uatol(argv[i])); else badarg = TRUE; if (++i < argc) - continue_proxy_cipher = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_CIPHERNAME_MAX, "-MCt cipher"), TRUE); + continue_proxy_cipher = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_CIPHERNAME_MAX, "-MCt cipher"), + GET_TAINTED); else badarg = TRUE; /*FALLTHROUGH*/ @@ -3007,12 +3104,11 @@ on the second character (the one after '-'), to save some effort. */ else if (Ustrcmp(argrest, "G") == 0) { msg_action = MSG_SETQUEUE; - queue_name_dest = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-MG"), TRUE); - } - else if (Ustrcmp(argrest, "mad") == 0) - { - msg_action = MSG_MARK_ALL_DELIVERED; + queue_name_dest = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-MG"), + GET_TAINTED); } + else if (Ustrcmp(argrest, "mad") == 0) msg_action = MSG_MARK_ALL_DELIVERED; else if (Ustrcmp(argrest, "md") == 0) { msg_action = MSG_MARK_DELIVERED; @@ -3220,27 +3316,37 @@ on the second character (the one after '-'), to save some effort. */ /* -oMa: Set sender host address */ if (Ustrcmp(argrest, "a") == 0) - sender_host_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMa"), TRUE); + sender_host_address = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMa"), + GET_TAINTED); /* -oMaa: Set authenticator name */ else if (Ustrcmp(argrest, "aa") == 0) - sender_host_authenticated = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMaa"), TRUE); + sender_host_authenticated = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMaa"), + GET_TAINTED); /* -oMas: setting authenticated sender */ else if (Ustrcmp(argrest, "as") == 0) - authenticated_sender = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), TRUE); + authenticated_sender = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), + GET_TAINTED); /* -oMai: setting authenticated id */ else if (Ustrcmp(argrest, "ai") == 0) - authenticated_id = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), TRUE); + authenticated_id = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMai"), + GET_TAINTED); /* -oMi: Set incoming interface address */ else if (Ustrcmp(argrest, "i") == 0) - interface_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMi"), TRUE); + interface_address = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMi"), + GET_TAINTED); /* -oMm: Message reference */ @@ -3260,19 +3366,25 @@ on the second character (the one after '-'), to save some effort. */ if (received_protocol) exim_fail("received_protocol is set already\n"); else - received_protocol = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMr"), TRUE); + received_protocol = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMr"), + GET_TAINTED); /* -oMs: Set sender host name */ else if (Ustrcmp(argrest, "s") == 0) - sender_host_name = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-oMs"), TRUE); + sender_host_name = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-oMs"), + GET_TAINTED); /* -oMt: Set sender ident */ else if (Ustrcmp(argrest, "t") == 0) { sender_ident_set = TRUE; - sender_ident = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IDENTUSER_MAX, "-oMt"), TRUE); + sender_ident = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IDENTUSER_MAX, "-oMt"), + GET_TAINTED); } /* Else a bad argument */ @@ -3330,7 +3442,9 @@ 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"), + GET_TAINTED); break; /* -oY: Override creation of daemon notifier socket */ @@ -3378,12 +3492,14 @@ on the second character (the one after '-'), to save some effort. */ exim_fail("received_protocol is set already\n"); if (!hn) - received_protocol = string_copy_taint(exim_str_fail_toolong(argrest, EXIM_DRIVERNAME_MAX, "-p"), TRUE); + received_protocol = string_copy_taint( + exim_str_fail_toolong(argrest, EXIM_DRIVERNAME_MAX, "-p"), + GET_TAINTED); else { (void) exim_str_fail_toolong(argrest, (EXIM_DRIVERNAME_MAX+1+EXIM_HOSTNAME_MAX), "-p:"); - received_protocol = string_copyn_taint(argrest, hn - argrest, TRUE); - sender_host_name = string_copy_taint(hn + 1, TRUE); + received_protocol = string_copyn_taint(argrest, hn - argrest, GET_TAINTED); + sender_host_name = string_copy_taint(hn + 1, GET_TAINTED); } } break; @@ -3452,9 +3568,9 @@ on the second character (the one after '-'), to save some effort. */ { queue_interval = 0; if (i+1 < argc && mac_ismsgid(argv[i+1])) - start_queue_run_id = string_copy_taint(argv[++i], TRUE); + start_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); if (i+1 < argc && mac_ismsgid(argv[i+1])) - stop_queue_run_id = string_copy_taint(argv[++i], TRUE); + stop_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); } /* -q[f][f][l][G/]: Run the queue at regular intervals, optionally @@ -3502,7 +3618,9 @@ on the second character (the one after '-'), to save some effort. */ 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); + deliver_selectstring = string_copy_taint( + exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-R"), + GET_TAINTED); } break; @@ -3545,7 +3663,9 @@ on the second character (the one after '-'), to save some effort. */ 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); + deliver_selectstring_sender = string_copy_taint( + exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-S"), + GET_TAINTED); } break; @@ -3556,7 +3676,7 @@ on the second character (the one after '-'), to save some effort. */ case 'T': if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0) - fudged_queue_times = string_copy_taint(argv[++i], TRUE); + fudged_queue_times = string_copy_taint(argv[++i], GET_TAINTED); else badarg = TRUE; break; @@ -3633,7 +3753,9 @@ on the second character (the one after '-'), to save some effort. */ case 'z': if (!*argrest) if (++i < argc) - log_oneline = string_copy_taint(exim_str_fail_toolong(argv[i], 2048, "-z logtext"), TRUE); + log_oneline = string_copy_taint( + exim_str_fail_toolong(argv[i], 2048, "-z logtext"), + GET_TAINTED); else exim_fail("exim: file name expected after %s\n", argv[i-1]); break; @@ -3721,7 +3843,7 @@ if (debug_selector != 0) version_string, (long int)real_uid, (long int)real_gid, (int)getpid(), debug_selector); if (!version_printed) - show_whats_supported(stderr); + show_whats_supported(FALSE); } } @@ -3949,7 +4071,7 @@ defined) */ { int old_pool = store_pool; #ifdef MEASURE_TIMING - struct timeval t0, diff; + struct timeval t0; (void)gettimeofday(&t0, NULL); #endif @@ -4281,7 +4403,7 @@ if (bi_option) { int i = 0; uschar *argv[3]; - argv[i++] = bi_command; + argv[i++] = bi_command; /* nonexpanded option so assume untainted */ if (alias_arg) argv[i++] = alias_arg; argv[i++] = NULL; @@ -4581,7 +4703,7 @@ needed in transports so we lost the optimisation. */ { int old_pool = store_pool; #ifdef MEASURE_TIMING - struct timeval t0, diff; + struct timeval t0; (void)gettimeofday(&t0, NULL); #endif @@ -4887,7 +5009,7 @@ for (i = 0;;) if (gecos_pattern && gecos_name) { const pcre2_code *re; - re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */ + re = regex_must_compile(gecos_pattern, MCS_NOFLAGS, TRUE); /* Use malloc */ if (regex_match_and_setup(re, name, 0, -1)) { @@ -4975,7 +5097,7 @@ if (f.daemon_listen || f.inetd_wait_mode || queue_interval > 0) routines in it, so call even if tls_require_ciphers is unset */ { # ifdef MEASURE_TIMING - struct timeval t0, diff; + struct timeval t0; (void)gettimeofday(&t0, NULL); # endif if (!tls_dropprivs_validate_require_cipher(FALSE)) @@ -5101,7 +5223,9 @@ if (verify_address_mode || f.address_test_mode) while (recipients_arg < argc) { /* Supplied addresses are tainted since they come from a user */ - uschar * s = string_copy_taint(exim_str_fail_toolong(argv[recipients_arg++], EXIM_DISPLAYMAIL_MAX, "address verification"), TRUE); + uschar * s = string_copy_taint( + exim_str_fail_toolong(argv[recipients_arg++], EXIM_DISPLAYMAIL_MAX, "address verification"), + GET_TAINTED); while (*s) { BOOL finished = FALSE; @@ -5118,7 +5242,10 @@ if (verify_address_mode || f.address_test_mode) { uschar * s = get_stdinput(NULL, NULL); if (!s) break; - test_address(string_copy_taint(exim_str_fail_toolong(s, EXIM_DISPLAYMAIL_MAX, "address verification (stdin)"), TRUE), flags, &exit_value); + test_address(string_copy_taint( + exim_str_fail_toolong(s, EXIM_DISPLAYMAIL_MAX, "address verification (stdin)"), + GET_TAINTED), + flags, &exit_value); } route_tidyup(); @@ -5141,7 +5268,7 @@ if (expansion_test) 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); + message_id = string_copy_taint(message_id, GET_UNTAINTED); spoolname = string_sprintf("%s-H", message_id); if ((deliver_datafile = spool_open_datafile(message_id)) < 0) @@ -5258,7 +5385,7 @@ if (host_checking) it. The code works for both IPv4 and IPv6, as it happens. */ size = host_aton(sender_host_address, x); - sender_host_address = store_get(48, FALSE); /* large enough for full IPv6 */ + sender_host_address = store_get(48, GET_UNTAINTED); /* large enough for full IPv6 */ (void)host_nmtoa(size, x, -1, sender_host_address, ':'); /* Now set up for testing */ @@ -5278,7 +5405,10 @@ if (host_checking) memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (verify_check_host(&hosts_connection_nolog) == OK) + { BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_no_mail); + } log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails, @@ -5467,7 +5597,10 @@ if (smtp_input) smtp_out = stdout; memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (verify_check_host(&hosts_connection_nolog) == OK) + { BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_no_mail); + } log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); if (!smtp_start_session()) { @@ -5630,11 +5763,13 @@ for (BOOL more = TRUE; more; ) uschar * errmess; /* There can be multiple addresses, so EXIM_DISPLAYMAIL_MAX (tuned for 1) is too short. * We'll still want to cap it to something, just in case. */ - uschar * s = string_copy_taint(exim_str_fail_toolong(list[i], BIG_BUFFER_SIZE, "address argument"), TRUE); + uschar * s = string_copy_taint( + exim_str_fail_toolong(list[i], BIG_BUFFER_SIZE, "address argument"), + GET_TAINTED); /* Loop for each comma-separated address */ - while (*s != 0) + while (*s) { BOOL finished = FALSE; uschar *recipient; @@ -5696,7 +5831,7 @@ for (BOOL more = TRUE; more; ) errors_sender_rc : EXIT_FAILURE; } - receive_add_recipient(string_copy_taint(recipient, TRUE), -1); + receive_add_recipient(string_copy_taint(recipient, GET_TAINTED), -1); s = ss; if (!finished) while (*(++s) != 0 && (*s == ',' || isspace(*s))); @@ -5955,13 +6090,13 @@ MORELOOP: dnslist_domain = dnslist_matched = NULL; #ifdef WITH_CONTENT_SCAN malware_name = NULL; + regex_vars_clear(); #endif callout_address = NULL; sending_ip_address = NULL; deliver_localpart_data = deliver_domain_data = recipient_data = sender_data = NULL; acl_var_m = NULL; - for(int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; store_reset(reset_point); }