X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/204a7a2c2e8601558905dc34c576a627045a9f21..a85c067ba6c6940512cf57ec213277a370d87e70:/src/src/exim.c diff --git a/src/src/exim.c b/src/src/exim.c index 6fde16a9c..62413e367 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* The main function: entry point, initialization, and high-level control. @@ -20,7 +21,9 @@ Also a few functions that don't naturally fit elsewhere. */ #ifndef _TIME_H # include #endif -#include /*XXX maybe glibc-only? */ +#ifndef NO_EXECINFO +# include +#endif #ifdef USE_GNUTLS # include @@ -57,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); } @@ -140,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 @@ -158,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--; } @@ -180,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; } @@ -202,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; } @@ -264,23 +242,25 @@ exit(1); #define STACKDUMP_MAX 24 void -stackdump(ucontext_t * ucontext) +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\n"); -log_write(0, LOG_MAIN|LOG_PANIC, "---\n"); +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\n", ss[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\n", strerror(errno)); -log_write(0, LOG_MAIN|LOG_PANIC, "---\n"); + log_write(0, LOG_MAIN|LOG_PANIC, "backtrace_symbols: %s", strerror(errno)); +log_write(0, LOG_MAIN|LOG_PANIC, "---"); +#endif } #undef STACKDUMP_MAX @@ -305,7 +285,7 @@ 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(uctx); +stackdump(); signal(SIGSEGV, SIG_DFL); kill(getpid(), sig); } @@ -1699,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); @@ -2009,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 @@ -2017,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. */ @@ -2242,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; @@ -2502,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]); @@ -3358,7 +3338,7 @@ on the second character (the one after '-'), to save some effort. */ else if (Ustrcmp(argrest, "ai") == 0) authenticated_id = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), + exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMai"), GET_TAINTED); /* -oMi: Set incoming interface address */ @@ -4091,7 +4071,7 @@ defined) */ { int old_pool = store_pool; #ifdef MEASURE_TIMING - struct timeval t0, diff; + struct timeval t0; (void)gettimeofday(&t0, NULL); #endif @@ -4723,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 @@ -5029,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)) { @@ -5117,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)) @@ -5425,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, @@ -5614,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()) { @@ -6104,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); }