X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4938e65b323e0cbc06c7e0fd06575b7eb7780ee4..da2e4883f3efa4d86f35c15d110877e95c7d77d9:/src/src/exim.c diff --git a/src/src/exim.c b/src/src/exim.c index 49f7e5f36..37e1b756a 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -211,6 +211,19 @@ exit(1); } +/*********************************************** +* Handler for SIGSEGV * +***********************************************/ + +static void +segv_handler(int sig) +{ +log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)"); +signal(SIGSEGV, SIG_DFL); +kill(getpid(), sig); +} + + /************************************************* * Handler for SIGUSR1 * *************************************************/ @@ -233,18 +246,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 @@ -761,18 +764,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); @@ -1559,10 +1562,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 +1580,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 +1644,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 +1663,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; @@ -1759,7 +1762,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 @@ -1814,7 +1817,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 +3259,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 +3292,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 +3429,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 +3443,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 +3474,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 +3488,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 +3701,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 +3724,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 +3734,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 +3870,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 +3886,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 +3909,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 +4180,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 +4510,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 +4541,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 +4579,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 +4608,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) @@ -5062,11 +5092,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 +5477,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 +5497,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 +5539,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 +5619,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 +5708,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 +5722,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 +5901,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;