X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexim.c;h=cd42a88ad51cb9c822fa6db2d4b06706ef9971de;hp=f0feaf5bd43eb8575113b21c177f63ae29498abe;hb=dce58c04af4439fec7269f83886e22b503756a8f;hpb=25ef0f44a4d150d8a3571906475ed3d567ee7f4a diff --git a/src/src/exim.c b/src/src/exim.c index f0feaf5bd..cd42a88ad 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -110,7 +110,7 @@ if (use_malloc) pcre_free = function_store_free; } if (caseless) options |= PCRE_CASELESS; -yield = pcre_compile(CCS pattern, options, (const char **)&error, &offset, NULL); +yield = pcre_compile(CCS pattern, options, CCSS &error, &offset, NULL); pcre_malloc = function_store_get; pcre_free = function_dummy_free; if (yield == NULL) @@ -309,6 +309,7 @@ milliwait(struct itimerval *itval) { sigset_t sigmask; sigset_t old_sigmask; +int save_errno = errno; if (itval->it_value.tv_usec < 50 && itval->it_value.tv_sec == 0) return; @@ -322,6 +323,8 @@ if (setitimer(ITIMER_REAL, itval, NULL) < 0) /* Start timer */ (void)sigdelset(&sigmask, SIGALRM); /* Remove SIGALRM */ (void)sigsuspend(&sigmask); /* Until SIGALRM */ (void)sigprocmask(SIG_SETMASK, &old_sigmask, NULL); /* Restore mask */ +errno = save_errno; +sigalrm_seen = FALSE; } @@ -342,11 +345,9 @@ Returns: nothing void millisleep(int msec) { -struct itimerval itval; -itval.it_interval.tv_sec = 0; -itval.it_interval.tv_usec = 0; -itval.it_value.tv_sec = msec/1000; -itval.it_value.tv_usec = (msec % 1000) * 1000; +struct itimerval itval = {.it_interval = {.tv_sec = 0, .tv_usec = 0}, + .it_value = {.tv_sec = msec/1000, + .tv_usec = (msec % 1000) * 1000}}; milliwait(&itval); } @@ -714,14 +715,14 @@ Returns: does not return */ void -exim_exit(int rc, const uschar * process) +exim_exit(int rc) { search_tidyup(); store_exit(); DEBUG(D_any) - debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d " - ">>>>>>>>>>>>>>>>\n", (int)getpid(), - process ? "(" : "", process, process ? ") " : "", rc); + debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d (%s) terminating with rc=%d " + ">>>>>>>>>>>>>>>>\n", + (int)getpid(), process_purpose, rc); exit(rc); } @@ -730,6 +731,10 @@ void exim_underbar_exit(int rc) { store_exit(); +DEBUG(D_any) + debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d (%s) terminating with rc=%d " + ">>>>>>>>>>>>>>>>\n", + (int)getpid(), process_purpose, rc); _exit(rc); } @@ -826,7 +831,7 @@ int start, end, domain; uschar *parse_error = NULL; uschar *address = parse_extract_address(s, &parse_error, &start, &end, &domain, FALSE); -if (address == NULL) +if (!address) { fprintf(stdout, "syntax error: %s\n", parse_error); *exit_value = 2; @@ -890,192 +895,198 @@ Returns: nothing static void show_whats_supported(FILE * fp) { +rmark reset_point = store_mark(); +gstring * g; DEBUG(D_any) {} else show_db_version(fp); -fprintf(fp, "Support for:"); +g = string_cat(NULL, US"Support for:"); #ifdef SUPPORT_CRYPTEQ - fprintf(fp, " crypteq"); + g = string_cat(g, US" crypteq"); #endif #if HAVE_ICONV - fprintf(fp, " iconv()"); + g = string_cat(g, US" iconv()"); #endif #if HAVE_IPV6 - fprintf(fp, " IPv6"); + g = string_cat(g, US" IPv6"); #endif #ifdef HAVE_SETCLASSRESOURCES - fprintf(fp, " use_setclassresources"); + g = string_cat(g, US" use_setclassresources"); #endif #ifdef SUPPORT_PAM - fprintf(fp, " PAM"); + g = string_cat(g, US" PAM"); #endif #ifdef EXIM_PERL - fprintf(fp, " Perl"); + g = string_cat(g, US" Perl"); #endif #ifdef EXPAND_DLFUNC - fprintf(fp, " Expand_dlfunc"); + g = string_cat(g, US" Expand_dlfunc"); #endif #ifdef USE_TCP_WRAPPERS - fprintf(fp, " TCPwrappers"); + g = string_cat(g, US" TCPwrappers"); #endif #ifdef USE_GNUTLS - fprintf(fp, " GnuTLS"); + g = string_cat(g, US" GnuTLS"); #endif #ifdef USE_OPENSSL - fprintf(fp, " OpenSSL"); + g = string_cat(g, US" OpenSSL"); #endif #ifdef SUPPORT_TRANSLATE_IP_ADDRESS - fprintf(fp, " translate_ip_address"); + g = string_cat(g, US" translate_ip_address"); #endif #ifdef SUPPORT_MOVE_FROZEN_MESSAGES - fprintf(fp, " move_frozen_messages"); + g = string_cat(g, US" move_frozen_messages"); #endif #ifdef WITH_CONTENT_SCAN - fprintf(fp, " Content_Scanning"); + g = string_cat(g, US" Content_Scanning"); #endif #ifdef SUPPORT_DANE - fprintf(fp, " DANE"); + g = string_cat(g, US" DANE"); #endif #ifndef DISABLE_DKIM - fprintf(fp, " DKIM"); + g = string_cat(g, US" DKIM"); #endif #ifndef DISABLE_DNSSEC - fprintf(fp, " DNSSEC"); + g = string_cat(g, US" DNSSEC"); #endif #ifndef DISABLE_EVENT - fprintf(fp, " Event"); + g = string_cat(g, US" Event"); #endif #ifdef SUPPORT_I18N - fprintf(fp, " I18N"); + g = string_cat(g, US" I18N"); #endif #ifndef DISABLE_OCSP - fprintf(fp, " OCSP"); + g = string_cat(g, US" OCSP"); #endif #ifndef DISABLE_PIPE_CONNECT - fprintf(fp, " PIPE_CONNECT"); + g = string_cat(g, US" PIPE_CONNECT"); #endif #ifndef DISABLE_PRDR - fprintf(fp, " PRDR"); + g = string_cat(g, US" PRDR"); #endif #ifdef SUPPORT_PROXY - fprintf(fp, " PROXY"); + g = string_cat(g, US" PROXY"); #endif #ifdef SUPPORT_SOCKS - fprintf(fp, " SOCKS"); + g = string_cat(g, US" SOCKS"); #endif #ifdef SUPPORT_SPF - fprintf(fp, " SPF"); + g = string_cat(g, US" SPF"); #endif #ifdef SUPPORT_DMARC - fprintf(fp, " DMARC"); + g = string_cat(g, US" DMARC"); #endif #ifdef TCP_FASTOPEN tcp_init(); - if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open"); -#endif -#ifdef EXPERIMENTAL_LMDB - fprintf(fp, " Experimental_LMDB"); -#endif -#ifdef EXPERIMENTAL_QUEUEFILE - fprintf(fp, " Experimental_QUEUEFILE"); -#endif -#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) - fprintf(fp, " Experimental_SRS"); + if (f.tcp_fastopen_ok) g = string_cat(g, US" TCP_Fast_Open"); #endif #ifdef EXPERIMENTAL_ARC - fprintf(fp, " Experimental_ARC"); + g = string_cat(g, US" Experimental_ARC"); #endif #ifdef EXPERIMENTAL_BRIGHTMAIL - fprintf(fp, " Experimental_Brightmail"); + g = string_cat(g, US" Experimental_Brightmail"); #endif #ifdef EXPERIMENTAL_DCC - fprintf(fp, " Experimental_DCC"); + g = string_cat(g, US" Experimental_DCC"); #endif #ifdef EXPERIMENTAL_DSN_INFO - fprintf(fp, " Experimental_DSN_info"); + g = string_cat(g, US" Experimental_DSN_info"); +#endif +#ifdef EXPERIMENTAL_LMDB + g = string_cat(g, US" Experimental_LMDB"); +#endif +#ifdef EXPERIMENTAL_QUEUE_RAMP + g = string_cat(g, US" Experimental_Queue_Ramp"); +#endif +#ifdef EXPERIMENTAL_QUEUEFILE + g = string_cat(g, US" Experimental_QUEUEFILE"); +#endif +#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) + g = string_cat(g, US" Experimental_SRS"); #endif #ifdef EXPERIMENTAL_TLS_RESUME - fprintf(fp, " Experimental_TLS_resume"); + g = string_cat(g, US" Experimental_TLS_resume"); #endif -fprintf(fp, "\n"); +g = string_cat(g, US"\n"); -fprintf(fp, "Lookups (built-in):"); +g = string_cat(g, US"Lookups (built-in):"); #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2 - fprintf(fp, " lsearch wildlsearch nwildlsearch iplsearch"); + g = string_cat(g, US" lsearch wildlsearch nwildlsearch iplsearch"); #endif #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2 - fprintf(fp, " cdb"); + g = string_cat(g, US" cdb"); #endif #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2 - fprintf(fp, " dbm dbmjz dbmnz"); + g = string_cat(g, US" dbm dbmjz dbmnz"); #endif #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2 - fprintf(fp, " dnsdb"); + g = string_cat(g, US" dnsdb"); #endif #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2 - fprintf(fp, " dsearch"); + g = string_cat(g, US" dsearch"); #endif #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2 - fprintf(fp, " ibase"); + g = string_cat(g, US" ibase"); #endif #if defined(LOOKUP_JSON) && LOOKUP_JSON!=2 - fprintf(fp, " json"); + g = string_cat(g, US" json"); #endif #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2 - fprintf(fp, " ldap ldapdn ldapm"); + g = string_cat(g, US" ldap ldapdn ldapm"); #endif #ifdef EXPERIMENTAL_LMDB - fprintf(fp, " lmdb"); + g = string_cat(g, US" lmdb"); #endif #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2 - fprintf(fp, " mysql"); + g = string_cat(g, US" mysql"); #endif #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2 - fprintf(fp, " nis nis0"); + g = string_cat(g, US" nis nis0"); #endif #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2 - fprintf(fp, " nisplus"); + g = string_cat(g, US" nisplus"); #endif #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2 - fprintf(fp, " oracle"); + g = string_cat(g, US" oracle"); #endif #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2 - fprintf(fp, " passwd"); + g = string_cat(g, US" passwd"); #endif #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2 - fprintf(fp, " pgsql"); + g = string_cat(g, US" pgsql"); #endif #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 - fprintf(fp, " redis"); + g = string_cat(g, US" redis"); #endif #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2 - fprintf(fp, " sqlite"); + g = string_cat(g, US" sqlite"); #endif #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2 - fprintf(fp, " testdb"); + g = string_cat(g, US" testdb"); #endif #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2 - fprintf(fp, " whoson"); + g = string_cat(g, US" whoson"); #endif -fprintf(fp, "\n"); +g = string_cat(g, US"\n"); -auth_show_supported(fp); -route_show_supported(fp); -transport_show_supported(fp); +g = auth_show_supported(g); +g = route_show_supported(g); +g = transport_show_supported(g); #ifdef WITH_CONTENT_SCAN -malware_show_supported(fp); +g = malware_show_supported(g); #endif if (fixed_never_users[0] > 0) { int i; - fprintf(fp, "Fixed never_users: "); + g = string_cat(g, US"Fixed never_users: "); for (i = 1; i <= (int)fixed_never_users[0] - 1; i++) - fprintf(fp, "%d:", (unsigned int)fixed_never_users[i]); - fprintf(fp, "%d\n", (unsigned int)fixed_never_users[i]); + string_fmt_append(g, "%u:", (unsigned)fixed_never_users[i]); + g = string_fmt_append(g, "%u\n", (unsigned)fixed_never_users[i]); } -fprintf(fp, "Configure owner: %d:%d\n", config_uid, config_gid); +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)); @@ -1114,6 +1125,9 @@ show_db_version(fp); #ifdef SUPPORT_I18N utf8_version_report(fp); #endif +#ifdef SUPPORT_SPF + spf_lib_version_report(fp); +#endif for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi) if (authi->version_report) @@ -1152,6 +1166,7 @@ show_db_version(fp); #endif } while (0); +store_reset(reset_point); } @@ -1262,9 +1277,9 @@ void *dlhandle; void *dlhandle_curses = dlopen("libcurses." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_LAZY); dlhandle = dlopen("libreadline." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_NOW); -if (dlhandle_curses != NULL) dlclose(dlhandle_curses); +if (dlhandle_curses) dlclose(dlhandle_curses); -if (dlhandle != NULL) +if (dlhandle) { /* Checked manual pages; at least in GNU Readline 6.1, the prototypes are: * char * readline (const char *prompt); @@ -1274,9 +1289,7 @@ if (dlhandle != NULL) *fn_addhist_ptr = (void(*)(const char*))dlsym(dlhandle, "add_history"); } else - { DEBUG(D_any) debug_printf("failed to load readline: %s\n", dlerror()); - } return dlhandle; } @@ -1620,7 +1633,6 @@ uschar *malware_test_file = NULL; uschar *real_sender_address; uschar *originator_home = US"/"; size_t sz; -rmark reset_point; struct passwd *pw; struct stat statbuf; @@ -1939,15 +1951,20 @@ running in an unprivileged state. */ unprivileged = (real_uid != root_uid && original_euid != root_uid); +/* For most of the args-parsing we need to use permanent pool memory */ + { + int old_pool = store_pool; + store_pool = POOL_PERM; + /* Scan the program's arguments. Some can be dealt with right away; others are simply recorded for checking and handling afterwards. Do a high-level switch on the second character (the one after '-'), to save some effort. */ -for (i = 1; i < argc; i++) + for (i = 1; i < argc; i++) { BOOL badarg = FALSE; - uschar *arg = argv[i]; - uschar *argrest; + uschar * arg = argv[i]; + uschar * argrest; int switchchar; /* An argument not starting with '-' is the start of a recipients list; @@ -2026,7 +2043,7 @@ for (i = 1; i < argc; i++) /* sendmail uses -Ac and -Am to control which .cf file is used; we ignore them. */ case 'A': - if (*argrest == '\0') { badarg = TRUE; break; } + if (!*argrest) { badarg = TRUE; break; } else { BOOL ignore = FALSE; @@ -2038,7 +2055,7 @@ for (i = 1; i < argc; i++) ignore = TRUE; break; } - if (!ignore) { badarg = TRUE; break; } + if (!ignore) badarg = TRUE; } break; @@ -2046,293 +2063,298 @@ for (i = 1; i < argc; i++) so has no need of it. */ case 'B': - if (*argrest == 0) i++; /* Skip over the type */ + if (!*argrest) i++; /* Skip over the type */ break; case 'b': - receiving_message = FALSE; /* Reset TRUE for -bm, -bS, -bs below */ - - /* -bd: Run in daemon mode, awaiting SMTP connections. - -bdf: Ditto, but in the foreground. - */ - - if (*argrest == 'd') { - f.daemon_listen = TRUE; - if (*(++argrest) == 'f') f.background_daemon = FALSE; - else if (*argrest != 0) { badarg = TRUE; break; } - } - - /* -be: Run in expansion test mode - -bem: Ditto, but read a message from a file first - */ + receiving_message = FALSE; /* Reset TRUE for -bm, -bS, -bs below */ - else if (*argrest == 'e') - { - expansion_test = checking = TRUE; - if (argrest[1] == 'm') - { - if (++i >= argc) { badarg = TRUE; break; } - expansion_test_message = argv[i]; - argrest++; - } - if (argrest[1] != 0) { badarg = TRUE; break; } - } - - /* -bF: Run system filter test */ - - else if (*argrest == 'F') - { - filter_test |= checking = FTEST_SYSTEM; - if (*(++argrest) != 0) { badarg = TRUE; break; } - if (++i < argc) filter_test_sfile = argv[i]; else - exim_fail("exim: file name expected after %s\n", argv[i-1]); - } - - /* -bf: Run user filter test - -bfd: Set domain for filter testing - -bfl: Set local part for filter testing - -bfp: Set prefix for filter testing - -bfs: Set suffix for filter testing - */ - - else if (*argrest == 'f') - { - if (*(++argrest) == 0) - { - filter_test |= checking = FTEST_USER; - if (++i < argc) filter_test_ufile = argv[i]; else - exim_fail("exim: file name expected after %s\n", argv[i-1]); - } - else - { - if (++i >= argc) - exim_fail("exim: string expected after %s\n", arg); - if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i]; - else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i]; - else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i]; - else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i]; - else { badarg = TRUE; break; } - } - } - - /* -bh: Host checking - an IP address must follow. */ - - else if (Ustrcmp(argrest, "h") == 0 || Ustrcmp(argrest, "hc") == 0) - { - if (++i >= argc) { badarg = TRUE; break; } - sender_host_address = argv[i]; - host_checking = checking = f.log_testing_mode = TRUE; - f.host_checking_callout = argrest[1] == 'c'; - message_logs = FALSE; - } - - /* -bi: This option is used by sendmail to initialize *the* alias file, - though it has the -oA option to specify a different file. Exim has no - concept of *the* alias file, but since Sun's YP make script calls - sendmail this way, some support must be provided. */ - - else if (Ustrcmp(argrest, "i") == 0) bi_option = TRUE; - - /* -bI: provide information, of the type to follow after a colon. - This is an Exim flag. */ - - else if (argrest[0] == 'I' && Ustrlen(argrest) >= 2 && argrest[1] == ':') - { - uschar *p = &argrest[2]; - info_flag = CMDINFO_HELP; - if (Ustrlen(p)) - { - if (strcmpic(p, CUS"sieve") == 0) - { - info_flag = CMDINFO_SIEVE; - info_stdout = TRUE; - } - else if (strcmpic(p, CUS"dscp") == 0) - { - info_flag = CMDINFO_DSCP; - info_stdout = TRUE; - } - else if (strcmpic(p, CUS"help") == 0) - { - info_stdout = TRUE; - } - } - } - - /* -bm: Accept and deliver message - the default option. Reinstate - receiving_message, which got turned off for all -b options. */ - - else if (Ustrcmp(argrest, "m") == 0) receiving_message = TRUE; - - /* -bmalware: test the filename given for malware */ - - else if (Ustrcmp(argrest, "malware") == 0) - { - if (++i >= argc) { badarg = TRUE; break; } - checking = TRUE; - malware_test_file = argv[i]; - } - - /* -bnq: For locally originating messages, do not qualify unqualified - addresses. In the envelope, this causes errors; in header lines they - just get left. */ - - else if (Ustrcmp(argrest, "nq") == 0) - { - f.allow_unqualified_sender = FALSE; - f.allow_unqualified_recipient = FALSE; - } - - /* -bpxx: List the contents of the mail queue, in various forms. If - the option is -bpc, just a queue count is needed. Otherwise, if the - first letter after p is r, then order is random. */ - - else if (*argrest == 'p') - { - if (*(++argrest) == 'c') - { - count_queue = TRUE; - if (*(++argrest) != 0) badarg = TRUE; - break; - } - - if (*argrest == 'r') - { - list_queue_option = 8; - argrest++; - } - else list_queue_option = 0; - - list_queue = TRUE; - - /* -bp: List the contents of the mail queue, top-level only */ - - if (*argrest == 0) {} - - /* -bpu: List the contents of the mail queue, top-level undelivered */ - - else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1; - - /* -bpa: List the contents of the mail queue, including all delivered */ - - else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2; - - /* Unknown after -bp[r] */ - - else - { - badarg = TRUE; - break; - } - } - - - /* -bP: List the configuration variables given as the address list. - Force -v, so configuration errors get displayed. */ - - else if (Ustrcmp(argrest, "P") == 0) - { - /* -bP config: we need to setup here, because later, - * when list_options is checked, the config is read already */ - if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0) - { - list_config = TRUE; - readconf_save_config(version_string); - } - else - { - list_options = TRUE; - debug_selector |= D_v; - debug_file = stderr; - } - } - - /* -brt: Test retry configuration lookup */ - - else if (Ustrcmp(argrest, "rt") == 0) - { - checking = TRUE; - test_retry_arg = i + 1; - goto END_ARG; - } - - /* -brw: Test rewrite configuration */ + switch (*argrest++) + { + /* -bd: Run in daemon mode, awaiting SMTP connections. + -bdf: Ditto, but in the foreground. + */ + case 'd': + f.daemon_listen = TRUE; + if (*argrest == 'f') f.background_daemon = FALSE; + else if (*argrest) badarg = TRUE; + break; + + /* -be: Run in expansion test mode + -bem: Ditto, but read a message from a file first + */ + case 'e': + expansion_test = checking = TRUE; + if (*argrest == 'm') + { + if (++i >= argc) { badarg = TRUE; break; } + expansion_test_message = argv[i]; + argrest++; + } + if (*argrest) badarg = TRUE; + break; + + /* -bF: Run system filter test */ + case 'F': + filter_test |= checking = FTEST_SYSTEM; + if (*argrest) badarg = TRUE; + else if (++i < argc) filter_test_sfile = argv[i]; + else exim_fail("exim: file name expected after %s\n", argv[i-1]); + break; + + /* -bf: Run user filter test + -bfd: Set domain for filter testing + -bfl: Set local part for filter testing + -bfp: Set prefix for filter testing + -bfs: Set suffix for filter testing + */ + case 'f': + if (!*argrest) + { + filter_test |= checking = FTEST_USER; + if (++i < argc) filter_test_ufile = argv[i]; + else exim_fail("exim: file name expected after %s\n", argv[i-1]); + } + else + { + if (++i >= argc) + exim_fail("exim: string expected after %s\n", arg); + if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i]; + else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i]; + else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i]; + else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i]; + else badarg = TRUE; + } + break; + + /* -bh: Host checking - an IP address must follow. */ + case 'h': + if (!*argrest || Ustrcmp(argrest, "c") == 0) + { + if (++i >= argc) { badarg = TRUE; break; } + sender_host_address = string_copy_taint(argv[i], TRUE); + host_checking = checking = f.log_testing_mode = TRUE; + f.host_checking_callout = *argrest == 'c'; + message_logs = FALSE; + } + else badarg = TRUE; + break; + + /* -bi: This option is used by sendmail to initialize *the* alias file, + though it has the -oA option to specify a different file. Exim has no + concept of *the* alias file, but since Sun's YP make script calls + sendmail this way, some support must be provided. */ + case 'i': + if (!*++argrest) bi_option = TRUE; + else badarg = TRUE; + break; + + /* -bI: provide information, of the type to follow after a colon. + This is an Exim flag. */ + case 'I': + if (Ustrlen(argrest) >= 1 && *argrest == ':') + { + uschar *p = argrest+1; + info_flag = CMDINFO_HELP; + if (Ustrlen(p)) + if (strcmpic(p, CUS"sieve") == 0) + { + info_flag = CMDINFO_SIEVE; + info_stdout = TRUE; + } + else if (strcmpic(p, CUS"dscp") == 0) + { + info_flag = CMDINFO_DSCP; + info_stdout = TRUE; + } + else if (strcmpic(p, CUS"help") == 0) + info_stdout = TRUE; + } + else badarg = TRUE; + break; + + /* -bm: Accept and deliver message - the default option. Reinstate + receiving_message, which got turned off for all -b options. + -bmalware: test the filename given for malware */ + case 'm': + if (!*argrest) receiving_message = TRUE; + else if (Ustrcmp(argrest, "alware") == 0) + { + if (++i >= argc) { badarg = TRUE; break; } + checking = TRUE; + malware_test_file = argv[i]; + } + else badarg = TRUE; + break; + + /* -bnq: For locally originating messages, do not qualify unqualified + addresses. In the envelope, this causes errors; in header lines they + just get left. */ + case 'n': + if (Ustrcmp(argrest, "q") == 0) + { + f.allow_unqualified_sender = FALSE; + f.allow_unqualified_recipient = FALSE; + } + else badarg = TRUE; + break; + + /* -bpxx: List the contents of the mail queue, in various forms. If + the option is -bpc, just a queue count is needed. Otherwise, if the + first letter after p is r, then order is random. */ + case 'p': + if (*argrest == 'c') + { + count_queue = TRUE; + if (*++argrest) badarg = TRUE; + break; + } - else if (Ustrcmp(argrest, "rw") == 0) - { - checking = TRUE; - test_rewrite_arg = i + 1; - goto END_ARG; - } + if (*argrest == 'r') + { + list_queue_option = 8; + argrest++; + } + else list_queue_option = 0; - /* -bS: Read SMTP commands on standard input, but produce no replies - - all errors are reported by sending messages. */ + list_queue = TRUE; - else if (Ustrcmp(argrest, "S") == 0) - smtp_input = smtp_batched_input = receiving_message = TRUE; + /* -bp: List the contents of the mail queue, top-level only */ - /* -bs: Read SMTP commands on standard input and produce SMTP replies - on standard output. */ + if (!*argrest) {} - else if (Ustrcmp(argrest, "s") == 0) smtp_input = receiving_message = TRUE; + /* -bpu: List the contents of the mail queue, top-level undelivered */ - /* -bt: address testing mode */ + else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1; - else if (Ustrcmp(argrest, "t") == 0) - f.address_test_mode = checking = f.log_testing_mode = TRUE; + /* -bpa: List the contents of the mail queue, including all delivered */ - /* -bv: verify addresses */ + else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2; - else if (Ustrcmp(argrest, "v") == 0) - verify_address_mode = checking = f.log_testing_mode = TRUE; + /* Unknown after -bp[r] */ - /* -bvs: verify sender addresses */ + else badarg = TRUE; + break; - else if (Ustrcmp(argrest, "vs") == 0) - { - verify_address_mode = checking = f.log_testing_mode = TRUE; - verify_as_sender = TRUE; - } - /* -bV: Print version string and support details */ + /* -bP: List the configuration variables given as the address list. + Force -v, so configuration errors get displayed. */ + case 'P': - else if (Ustrcmp(argrest, "V") == 0) - { - printf("Exim version %s #%s built %s\n", version_string, - version_cnumber, version_date); - printf("%s\n", CS version_copyright); - version_printed = TRUE; - show_whats_supported(stdout); - f.log_testing_mode = TRUE; - } + /* -bP config: we need to setup here, because later, + * when list_options is checked, the config is read already */ + if (*argrest) + badarg = TRUE; + else if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0) + { + list_config = TRUE; + readconf_save_config(version_string); + } + else + { + list_options = TRUE; + debug_selector |= D_v; + debug_file = stderr; + } + break; + + /* -brt: Test retry configuration lookup */ + case 'r': + if (Ustrcmp(argrest, "t") == 0) + { + checking = TRUE; + test_retry_arg = i + 1; + goto END_ARG; + } - /* -bw: inetd wait mode, accept a listening socket as stdin */ + /* -brw: Test rewrite configuration */ - else if (*argrest == 'w') - { - f.inetd_wait_mode = TRUE; - f.background_daemon = FALSE; - f.daemon_listen = TRUE; - if (*(++argrest) != '\0') - if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0) - exim_fail("exim: bad time value %s: abandoned\n", argv[i]); + else if (Ustrcmp(argrest, "w") == 0) + { + checking = TRUE; + test_rewrite_arg = i + 1; + goto END_ARG; + } + else badarg = TRUE; + break; + + /* -bS: Read SMTP commands on standard input, but produce no replies - + all errors are reported by sending messages. */ + case 'S': + if (!*argrest) + smtp_input = smtp_batched_input = receiving_message = TRUE; + else badarg = TRUE; + break; + + /* -bs: Read SMTP commands on standard input and produce SMTP replies + on standard output. */ + case 's': + if (!*argrest) smtp_input = receiving_message = TRUE; + else badarg = TRUE; + break; + + /* -bt: address testing mode */ + case 't': + if (!*argrest) + f.address_test_mode = checking = f.log_testing_mode = TRUE; + else badarg = TRUE; + break; + + /* -bv: verify addresses */ + case 'v': + if (!*argrest) + verify_address_mode = checking = f.log_testing_mode = TRUE; + + /* -bvs: verify sender addresses */ + + else if (Ustrcmp(argrest, "s") == 0) + { + verify_address_mode = checking = f.log_testing_mode = TRUE; + verify_as_sender = TRUE; + } + else badarg = TRUE; + break; + + /* -bV: Print version string and support details */ + case 'V': + if (!*argrest) + { + printf("Exim version %s #%s built %s\n", version_string, + version_cnumber, version_date); + printf("%s\n", CS version_copyright); + version_printed = TRUE; + show_whats_supported(stdout); + f.log_testing_mode = TRUE; + } + else badarg = TRUE; + break; + + /* -bw: inetd wait mode, accept a listening socket as stdin */ + case 'w': + f.inetd_wait_mode = TRUE; + f.background_daemon = FALSE; + f.daemon_listen = TRUE; + if (*argrest) + if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0) + exim_fail("exim: bad time value %s: abandoned\n", argv[i]); + break; + + default: + badarg = TRUE; + break; + } + break; } - else badarg = TRUE; - break; - /* -C: change configuration file list; ignore if it isn't really a change! Enforce a prefix check if required. */ case 'C': - if (*argrest == 0) - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } if (Ustrcmp(config_main_filelist, argrest) != 0) { #ifdef ALT_CONFIG_PREFIX @@ -2341,14 +2363,14 @@ for (i = 1; i < argc; i++) const uschar *list = argrest; uschar *filename; while((filename = string_nextinlist(&list, &sep, big_buffer, - big_buffer_size)) != NULL) - { - if ((Ustrlen(filename) < len || - Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 || - Ustrstr(filename, "/../") != NULL) && - (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid)) + big_buffer_size))) + if ( ( Ustrlen(filename) < len + || Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 + || Ustrstr(filename, "/../") != NULL + ) + && (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid) + ) exim_fail("-C Permission denied\n"); - } #endif if (real_uid != root_uid) { @@ -2387,11 +2409,14 @@ for (i = 1; i < argc; i++) else { /* Well, the trust list at least is up to scratch... */ - rmark reset_point = store_mark(); + rmark reset_point; uschar *trusted_configs[32]; int nr_configs = 0; int i = 0; + int old_pool = store_pool; + store_pool = POOL_MAIN; + reset_point = store_mark(); while (Ufgets(big_buffer, big_buffer_size, trust_list)) { uschar *start = big_buffer, *nl; @@ -2403,7 +2428,7 @@ for (i = 1; i < argc; i++) if (nl) *nl = 0; trusted_configs[nr_configs++] = string_copy(start); - if (nr_configs == 32) + if (nr_configs == nelem(trusted_configs)) break; } fclose(trust_list); @@ -2414,7 +2439,7 @@ for (i = 1; i < argc; i++) const uschar *list = argrest; uschar *filename; while (f.trusted_config && (filename = string_nextinlist(&list, - &sep, big_buffer, big_buffer_size)) != NULL) + &sep, big_buffer, big_buffer_size))) { for (i=0; i < nr_configs; i++) if (Ustrcmp(filename, trusted_configs[i]) == 0) @@ -2429,6 +2454,7 @@ for (i = 1; i < argc; i++) else /* No valid prefixes found in trust_list file. */ f.trusted_config = FALSE; store_reset(reset_point); + store_pool = old_pool; } } else /* Could not open trust_list file. */ @@ -2516,7 +2542,7 @@ for (i = 1; i < argc; i++) f.debug_daemon = TRUE; argrest++; } - if (*argrest != 0) + if (*argrest) decode_bits(&selector, 1, debug_notall, argrest, debug_options, debug_options_count, US"debug", 0); debug_selector = selector; @@ -2563,12 +2589,9 @@ for (i = 1; i < argc; i++) the -F or be in the next argument. */ case 'F': - if (*argrest == 0) - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } - originator_name = argrest; + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } + originator_name = string_copy_taint(argrest, TRUE); f.sender_name_forced = TRUE; break; @@ -2592,16 +2615,13 @@ for (i = 1; i < argc; i++) { int dummy_start, dummy_end; uschar *errmess; - if (*argrest == 0) - { - if (i+1 < argc) argrest = argv[++i]; else - { badarg = TRUE; break; } - } - if (*argrest == 0) + if (!*argrest) + if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } + if (!*argrest) *(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */ else { - uschar *temp = argrest + Ustrlen(argrest) - 1; + uschar * temp = argrest + Ustrlen(argrest) - 1; while (temp >= argrest && isspace(*temp)) temp--; if (temp >= argrest && *temp == '.') f_end_dot = TRUE; allow_domain_literals = TRUE; @@ -2609,8 +2629,10 @@ for (i = 1; i < argc; i++) #ifdef SUPPORT_I18N allow_utf8_domains = TRUE; #endif - sender_address = parse_extract_address(argrest, &errmess, - &dummy_start, &dummy_end, &sender_address_domain, TRUE); + if (!(sender_address = parse_extract_address(argrest, &errmess, + &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); #ifdef SUPPORT_I18N message_smtputf8 = string_is_utf8(sender_address); @@ -2618,8 +2640,6 @@ for (i = 1; i < argc; i++) #endif allow_domain_literals = FALSE; strip_trailing_dot = FALSE; - if (!sender_address) - exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); } f.sender_address_forced = TRUE; } @@ -2639,11 +2659,8 @@ for (i = 1; i < argc; i++) To put it in will require a change to the spool header file format. */ case 'h': - if (*argrest == 0) - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } if (!isdigit(*argrest)) badarg = TRUE; break; @@ -2652,7 +2669,7 @@ for (i = 1; i < argc; i++) not to be documented for sendmail but mailx (at least) uses it) */ case 'i': - if (*argrest == 0) f.dot_ends = FALSE; else badarg = TRUE; + if (!*argrest) f.dot_ends = FALSE; else badarg = TRUE; break; @@ -2660,16 +2677,13 @@ for (i = 1; i < argc; i++) syslog_processname in the config file, but needs to be an admin option. */ case 'L': - if (*argrest == '\0') - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } if ((sz = Ustrlen(argrest)) > 32) 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 = argrest; + cmdline_syslog_name = string_copy_taint(argrest, TRUE); break; case 'M': @@ -2699,9 +2713,9 @@ for (i = 1; i < argc; i++) if (msg_action_arg >= 0) exim_fail("exim: incompatible arguments\n"); - continue_transport = argv[++i]; - continue_hostname = argv[++i]; - continue_host_address = argv[++i]; + continue_transport = string_copy_taint(argv[++i], TRUE); + continue_hostname = string_copy_taint(argv[++i], TRUE); + continue_host_address = string_copy_taint(argv[++i], TRUE); continue_sequence = Uatoi(argv[++i]); msg_action = MSG_DELIVER; msg_action_arg = ++i; @@ -2743,9 +2757,16 @@ for (i = 1; i < argc; i++) case 'D': smtp_peer_options |= OPTION_DSN; break; + /* -MCd: for debug, set a process-purpose string */ + + case 'd': if (++i < argc) + process_purpose = string_copy_taint(argv[i], TRUE); + else badarg = TRUE; + break; + /* -MCG: set the queue name, to a non-default value */ - case 'G': if (++i < argc) queue_name = string_copy(argv[i]); + case 'G': if (++i < argc) queue_name = string_copy_taint(argv[i], TRUE); else badarg = TRUE; break; @@ -2779,11 +2800,14 @@ for (i = 1; i < argc; i++) Require three arguments for the proxied local address and port, and the TLS cipher. */ - case 't': if (++i < argc) sending_ip_address = argv[i]; + case 't': if (++i < argc) + sending_ip_address = string_copy_taint(argv[i], TRUE); else badarg = TRUE; - if (++i < argc) sending_port = (int)(Uatol(argv[i])); + if (++i < argc) + sending_port = (int)(Uatol(argv[i])); else badarg = TRUE; - if (++i < argc) continue_proxy_cipher = argv[i]; + if (++i < argc) + continue_proxy_cipher = string_copy_taint(argv[i], TRUE); else badarg = TRUE; /*FALLTHROUGH*/ @@ -2820,7 +2844,7 @@ for (i = 1; i < argc; i++) -Mvl show log */ - else if (*argrest == 0) + else if (!*argrest) { msg_action = MSG_DELIVER; forced_delivery = f.deliver_force_thaw = TRUE; @@ -2845,7 +2869,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "G") == 0) { msg_action = MSG_SETQUEUE; - queue_name_dest = argv[++i]; + queue_name_dest = string_copy_taint(argv[++i], TRUE); } else if (Ustrcmp(argrest, "mad") == 0) { @@ -2918,7 +2942,7 @@ for (i = 1; i < argc; i++) for sendmail it askes for "me too". Exim always does this. */ case 'm': - if (*argrest != 0) badarg = TRUE; + if (*argrest) badarg = TRUE; break; @@ -2926,7 +2950,7 @@ for (i = 1; i < argc; i++) their thing. It implies debugging at the D_v level. */ case 'N': - if (*argrest == 0) + if (!*argrest) { f.dont_deliver = TRUE; debug_selector |= D_v; @@ -2949,217 +2973,228 @@ for (i = 1; i < argc; i++) -O option=value and -Ooption=value. */ case 'O': - if (*argrest == 0) - { + if (!*argrest) if (++i >= argc) exim_fail("exim: string expected after -O\n"); - } break; case 'o': - - /* -oA: Set an argument for the bi command (sendmail's "alternate alias - file" option). */ - - if (*argrest == 'A') + switch (*argrest++) { - alias_arg = argrest + 1; - if (alias_arg[0] == 0) - { - if (i+1 < argc) alias_arg = argv[++i]; else - exim_fail("exim: string expected after -oA\n"); - } - } - - /* -oB: Set a connection message max value for remote deliveries */ - - else if (*argrest == 'B') - { - uschar *p = argrest + 1; - if (p[0] == 0) - { - if (i+1 < argc && isdigit((argv[i+1][0]))) p = argv[++i]; else - { - connection_max_messages = 1; - p = NULL; - } - } - - if (p != NULL) - { - if (!isdigit(*p)) - exim_fail("exim: number expected after -oB\n"); - connection_max_messages = Uatoi(p); - } - } - - /* -odb: background delivery */ - - else if (Ustrcmp(argrest, "db") == 0) - { - f.synchronous_delivery = FALSE; - arg_queue_only = FALSE; - queue_only_set = TRUE; - } - - /* -odf: foreground delivery (smail-compatible option); same effect as - -odi: interactive (synchronous) delivery (sendmail-compatible option) - */ - - else if (Ustrcmp(argrest, "df") == 0 || Ustrcmp(argrest, "di") == 0) - { - f.synchronous_delivery = TRUE; - arg_queue_only = FALSE; - queue_only_set = TRUE; - } - - /* -odq: queue only */ + /* -oA: Set an argument for the bi command (sendmail's "alternate alias + file" option). */ + case 'A': + if (!*(alias_arg = argrest)) + if (i+1 < argc) alias_arg = argv[++i]; + else exim_fail("exim: string expected after -oA\n"); + break; - else if (Ustrcmp(argrest, "dq") == 0) - { - f.synchronous_delivery = FALSE; - arg_queue_only = TRUE; - queue_only_set = TRUE; - } + /* -oB: Set a connection message max value for remote deliveries */ + case 'B': + { + uschar * p = argrest; + if (!*p) + if (i+1 < argc && isdigit((argv[i+1][0]))) + p = argv[++i]; + else + { + connection_max_messages = 1; + p = NULL; + } - /* -odqs: queue SMTP only - do local deliveries and remote routing, - but no remote delivery */ + if (p) + { + if (!isdigit(*p)) + exim_fail("exim: number expected after -oB\n"); + connection_max_messages = Uatoi(p); + } + } + break; - else if (Ustrcmp(argrest, "dqs") == 0) - { - f.queue_smtp = TRUE; - arg_queue_only = FALSE; - queue_only_set = TRUE; - } + /* -odb: background delivery */ + + case 'd': + if (Ustrcmp(argrest, "b") == 0) + { + f.synchronous_delivery = FALSE; + arg_queue_only = FALSE; + queue_only_set = TRUE; + } + + /* -odd: testsuite-only: add no inter-process delays */ + + else if (Ustrcmp(argrest, "d") == 0) + f.testsuite_delays = FALSE; + + /* -odf: foreground delivery (smail-compatible option); same effect as + -odi: interactive (synchronous) delivery (sendmail-compatible option) + */ + + else if (Ustrcmp(argrest, "f") == 0 || Ustrcmp(argrest, "i") == 0) + { + f.synchronous_delivery = TRUE; + arg_queue_only = FALSE; + queue_only_set = TRUE; + } + + /* -odq: queue only */ + + else if (Ustrcmp(argrest, "q") == 0) + { + f.synchronous_delivery = FALSE; + arg_queue_only = TRUE; + queue_only_set = TRUE; + } + + /* -odqs: queue SMTP only - do local deliveries and remote routing, + but no remote delivery */ + + else if (Ustrcmp(argrest, "qs") == 0) + { + f.queue_smtp = TRUE; + arg_queue_only = FALSE; + queue_only_set = TRUE; + } + else badarg = TRUE; + break; - /* -oex: Sendmail error flags. As these are also accepted without the - leading -o prefix, for compatibility with vacation and other callers, - they are handled with -e above. */ + /* -oex: Sendmail error flags. As these are also accepted without the + leading -o prefix, for compatibility with vacation and other callers, + they are handled with -e above. */ - /* -oi: Set flag so dot doesn't end non-SMTP input (same as -i) - -oitrue: Another sendmail syntax for the same */ + /* -oi: Set flag so dot doesn't end non-SMTP input (same as -i) + -oitrue: Another sendmail syntax for the same */ - else if (Ustrcmp(argrest, "i") == 0 || - Ustrcmp(argrest, "itrue") == 0) - f.dot_ends = FALSE; + case 'i': + if (!*argrest || Ustrcmp(argrest, "true") == 0) + f.dot_ends = FALSE; + else badarg = TRUE; + break; /* -oM*: Set various characteristics for an incoming message; actually acted on for trusted callers only. */ - else if (*argrest == 'M') - { - if (i+1 >= argc) - exim_fail("exim: data expected after -o%s\n", argrest); + case 'M': + { + if (i+1 >= argc) + exim_fail("exim: data expected after -oM%s\n", argrest); - /* -oMa: Set sender host address */ + /* -oMa: Set sender host address */ - if (Ustrcmp(argrest, "Ma") == 0) sender_host_address = argv[++i]; + if (Ustrcmp(argrest, "a") == 0) + sender_host_address = string_copy_taint(argv[++i], TRUE); - /* -oMaa: Set authenticator name */ + /* -oMaa: Set authenticator name */ - else if (Ustrcmp(argrest, "Maa") == 0) - sender_host_authenticated = argv[++i]; + else if (Ustrcmp(argrest, "aa") == 0) + sender_host_authenticated = string_copy_taint(argv[++i], TRUE); - /* -oMas: setting authenticated sender */ + /* -oMas: setting authenticated sender */ - else if (Ustrcmp(argrest, "Mas") == 0) - authenticated_sender = string_copy_taint(argv[++i], TRUE); + else if (Ustrcmp(argrest, "as") == 0) + authenticated_sender = string_copy_taint(argv[++i], TRUE); - /* -oMai: setting authenticated id */ + /* -oMai: setting authenticated id */ - else if (Ustrcmp(argrest, "Mai") == 0) - authenticated_id = string_copy_taint(argv[++i], TRUE); + else if (Ustrcmp(argrest, "ai") == 0) + authenticated_id = string_copy_taint(argv[++i], TRUE); - /* -oMi: Set incoming interface address */ + /* -oMi: Set incoming interface address */ - else if (Ustrcmp(argrest, "Mi") == 0) interface_address = argv[++i]; + else if (Ustrcmp(argrest, "i") == 0) + interface_address = string_copy_taint(argv[++i], TRUE); - /* -oMm: Message reference */ + /* -oMm: Message reference */ - else if (Ustrcmp(argrest, "Mm") == 0) - { - if (!mac_ismsgid(argv[i+1])) - exim_fail("-oMm must be a valid message ID\n"); - if (!f.trusted_config) - exim_fail("-oMm must be called by a trusted user/config\n"); - message_reference = argv[++i]; - } + else if (Ustrcmp(argrest, "m") == 0) + { + if (!mac_ismsgid(argv[i+1])) + exim_fail("-oMm must be a valid message ID\n"); + if (!f.trusted_config) + exim_fail("-oMm must be called by a trusted user/config\n"); + message_reference = argv[++i]; + } - /* -oMr: Received protocol */ - - else if (Ustrcmp(argrest, "Mr") == 0) - - if (received_protocol) - exim_fail("received_protocol is set already\n"); - else - received_protocol = argv[++i]; + /* -oMr: Received protocol */ - /* -oMs: Set sender host name */ + else if (Ustrcmp(argrest, "r") == 0) - else if (Ustrcmp(argrest, "Ms") == 0) - sender_host_name = string_copy_taint(argv[++i], TRUE); + if (received_protocol) + exim_fail("received_protocol is set already\n"); + else + received_protocol = string_copy_taint(argv[++i], TRUE); - /* -oMt: Set sender ident */ + /* -oMs: Set sender host name */ - else if (Ustrcmp(argrest, "Mt") == 0) - { - sender_ident_set = TRUE; - sender_ident = argv[++i]; - } + else if (Ustrcmp(argrest, "s") == 0) + sender_host_name = string_copy_taint(argv[++i], TRUE); - /* Else a bad argument */ + /* -oMt: Set sender ident */ - else - { - badarg = TRUE; - break; - } - } + else if (Ustrcmp(argrest, "t") == 0) + { + sender_ident_set = TRUE; + sender_ident = string_copy_taint(argv[++i], TRUE); + } - /* -om: Me-too flag for aliases. Exim always does this. Some programs - seem to call this as -m (undocumented), so that is also accepted (see - above). */ + /* Else a bad argument */ - else if (Ustrcmp(argrest, "m") == 0) {} + else + badarg = TRUE; + } + break; - /* -oo: An ancient flag for old-style addresses which still seems to - crop up in some calls (see in SCO). */ + /* -om: Me-too flag for aliases. Exim always does this. Some programs + seem to call this as -m (undocumented), so that is also accepted (see + above). */ + /* -oo: An ancient flag for old-style addresses which still seems to + crop up in some calls (see in SCO). */ - else if (Ustrcmp(argrest, "o") == 0) {} + case 'm': + case 'o': + if (*argrest) badarg = TRUE; + break; - /* -oP : set pid file path for daemon - -oPX: delete pid file of daemon */ + /* -oP : set pid file path for daemon + -oPX: delete pid file of daemon */ - else if (Ustrcmp(argrest, "P") == 0) - override_pid_file_path = argv[++i]; + case 'P': + if (!*argrest) override_pid_file_path = argv[++i]; + else if (Ustrcmp(argrest, "X") == 0) delete_pid_file(); + else badarg = TRUE; + break; - else if (Ustrcmp(argrest, "PX") == 0) - delete_pid_file(); - /* -or : set timeout for non-SMTP acceptance - -os : set timeout for SMTP acceptance */ + /* -or : set timeout for non-SMTP acceptance + -os : set timeout for SMTP acceptance */ - else if (*argrest == 'r' || *argrest == 's') - { - int *tp = (*argrest == 'r')? - &arg_receive_timeout : &arg_smtp_receive_timeout; - if (argrest[1] == 0) - { - if (i+1 < argc) *tp= readconf_readtime(argv[++i], 0, FALSE); - } - else *tp = readconf_readtime(argrest + 1, 0, FALSE); - if (*tp < 0) - exim_fail("exim: bad time value %s: abandoned\n", argv[i]); - } + case 'r': + case 's': + { + int * tp = argrest[-1] == 'r' + ? &arg_receive_timeout : &arg_smtp_receive_timeout; + if (*argrest) + *tp = readconf_readtime(argrest, 0, FALSE); + else if (i+1 < argc) + *tp = readconf_readtime(argv[++i], 0, FALSE); + + if (*tp < 0) + exim_fail("exim: bad time value %s: abandoned\n", argv[i]); + } + break; - /* -oX : Override local_interfaces and/or default daemon ports */ + /* -oX : Override local_interfaces and/or default daemon ports */ - else if (Ustrcmp(argrest, "X") == 0) - override_local_interfaces = argv[++i]; + case 'X': + if (*argrest) badarg = TRUE; + else override_local_interfaces = string_copy_taint(argv[++i], TRUE); + break; - /* Unknown -o argument */ + /* Unknown -o argument */ - else badarg = TRUE; + default: + badarg = TRUE; + } break; @@ -3182,29 +3217,22 @@ for (i = 1; i < argc; i++) /* -panythingelse is taken as the Sendmail-compatible argument -prval:sval, which sets the host protocol and host name */ - if (*argrest == 0) - if (i+1 < argc) - argrest = argv[++i]; - else - { badarg = TRUE; break; } + if (!*argrest) + if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } - if (*argrest != 0) + if (*argrest) { - uschar *hn; + uschar * hn = Ustrchr(argrest, ':'); if (received_protocol) exim_fail("received_protocol is set already\n"); - hn = Ustrchr(argrest, ':'); - if (hn == NULL) - received_protocol = argrest; + if (!hn) + received_protocol = string_copy_taint(argrest, TRUE); else { - int old_pool = store_pool; - store_pool = POOL_PERM; - received_protocol = string_copyn(argrest, hn - argrest); - store_pool = old_pool; - sender_host_name = hn + 1; + received_protocol = string_copyn_taint(argrest, hn - argrest, TRUE); + sender_host_name = string_copy_taint(hn + 1, TRUE); } } break; @@ -3267,14 +3295,14 @@ for (i = 1; i < argc; i++) only, optionally named, optionally starting from a given message id. */ if (!(list_queue || count_queue)) - if (*argrest == 0 + if ( !*argrest && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1]))) { queue_interval = 0; if (i+1 < argc && mac_ismsgid(argv[i+1])) - start_queue_run_id = argv[++i]; + start_queue_run_id = string_copy_taint(argv[++i], TRUE); if (i+1 < argc && mac_ismsgid(argv[i+1])) - stop_queue_run_id = argv[++i]; + stop_queue_run_id = string_copy_taint(argv[++i], TRUE); } /* -q[f][f][l][G/]: Run the queue at regular intervals, optionally @@ -3298,7 +3326,7 @@ for (i = 1; i < argc; i++) in all cases provided there are no further characters in this argument. */ - if (*argrest != 0) + if (*argrest) for (int i = 0; i < nelem(rsopts); i++) if (Ustrcmp(argrest, rsopts[i]) == 0) { @@ -3312,9 +3340,9 @@ for (i = 1; i < argc; i++) pick out particular messages. */ if (*argrest) - deliver_selectstring = argrest; + deliver_selectstring = string_copy_taint(argrest, TRUE); else if (i+1 < argc) - deliver_selectstring = argv[++i]; + deliver_selectstring = string_copy_taint(argv[++i], TRUE); else exim_fail("exim: string expected after -R\n"); break; @@ -3351,9 +3379,9 @@ for (i = 1; i < argc; i++) pick out particular messages. */ if (*argrest) - deliver_selectstring_sender = argrest; + deliver_selectstring_sender = string_copy_taint(argrest, TRUE); else if (i+1 < argc) - deliver_selectstring_sender = argv[++i]; + deliver_selectstring_sender = string_copy_taint(argv[++i], TRUE); else exim_fail("exim: string expected after -S\n"); break; @@ -3365,7 +3393,7 @@ for (i = 1; i < argc; i++) case 'T': if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0) - fudged_queue_times = argv[++i]; + fudged_queue_times = string_copy_taint(argv[++i], TRUE); else badarg = TRUE; break; @@ -3373,7 +3401,7 @@ for (i = 1; i < argc; i++) /* -t: Set flag to extract recipients from body of message. */ case 't': - if (*argrest == 0) extract_recipients = TRUE; + if (!*argrest) extract_recipients = TRUE; /* -ti: Set flag to extract recipients from body of message, and also specify that dot does not end the message. */ @@ -3405,7 +3433,7 @@ for (i = 1; i < argc; i++) /* -v: verify things - this is a very low-level debugging */ case 'v': - if (*argrest == 0) + if (!*argrest) { debug_selector |= D_v; debug_file = stderr; @@ -3425,22 +3453,22 @@ for (i = 1; i < argc; i++) As Exim is 8-bit clean, it just ignores this flag. */ case 'x': - if (*argrest != 0) badarg = TRUE; + if (*argrest) badarg = TRUE; break; /* -X: in sendmail: takes one parameter, logfile, and sends debugging logs to that file. We swallow the parameter and otherwise ignore it. */ case 'X': - if (*argrest == '\0') + if (!*argrest) if (++i >= argc) exim_fail("exim: string expected after -X\n"); break; case 'z': - if (*argrest == '\0') + if (!*argrest) if (++i < argc) - log_oneline = argv[i]; + log_oneline = string_copy_taint(argv[i], TRUE); else exim_fail("exim: file name expected after %s\n", argv[i-1]); break; @@ -3462,12 +3490,15 @@ for (i = 1; i < argc; i++) /* If -R or -S have been specified without -q, assume a single queue run. */ -if ( (deliver_selectstring || deliver_selectstring_sender) - && queue_interval < 0) - queue_interval = 0; + if ( (deliver_selectstring || deliver_selectstring_sender) + && queue_interval < 0) + queue_interval = 0; END_ARG: + store_pool = old_pool; + } + /* If usage_wanted is set we call the usage function - which never returns */ if (usage_wanted) exim_usage(called_as); @@ -3638,15 +3669,15 @@ an error return. The following code should cope with both types of system. in the call to exim_setugid(). However, if this process isn't running as root, setgroups() can't be used -since you have to be root to run it, even if throwing away groups. Not being -root here happens only in some unusual configurations. We just ignore the -error. */ +since you have to be root to run it, even if throwing away groups. +Except, sigh, for Hurd - where you can. +Not being root here happens only in some unusual configurations. */ -if ( +if ( !unprivileged #ifndef OS_SETGROUPS_ZERO_DROPS_ALL - setgroups(0, NULL) != 0 && + && setgroups(0, NULL) != 0 #endif - setgroups(1, group_list) != 0 && !unprivileged) + && setgroups(1, group_list) != 0) exim_fail("exim: setgroups() failed: %s\n", strerror(errno)); /* If the configuration file name has been altered by an argument on the @@ -4050,11 +4081,12 @@ if ( (debug_selector & D_any || LOGGING(arguments)) p = big_buffer + 3; } printing = string_printing(argv[i]); - if (printing[0] == 0) quote = US"\""; else + if (!*printing) quote = US"\""; + else { const uschar *pp = printing; quote = US""; - while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; } + while (*pp) if (isspace(*pp++)) { quote = US"\""; break; } } p += sprintf(CS p, " %s%.*s%s", quote, (int)(big_buffer_size - (p - big_buffer) - 4), printing, quote); @@ -4090,19 +4122,19 @@ script. */ if (bi_option) { (void)fclose(config_file); - if (bi_command != NULL) + if (bi_command) { int i = 0; uschar *argv[3]; argv[i++] = bi_command; - if (alias_arg != NULL) argv[i++] = alias_arg; + if (alias_arg) argv[i++] = alias_arg; argv[i++] = NULL; setgroups(group_count, group_list); exim_setugid(real_uid, real_gid, FALSE, US"running bi_command"); DEBUG(D_exec) debug_printf("exec %.256s %.256s\n", argv[0], - (argv[1] == NULL)? US"" : argv[1]); + argv[1] ? argv[1] : US""); execv(CS argv[0], (char *const *)argv); exim_fail("exim: exec failed: %s\n", strerror(errno)); @@ -4293,8 +4325,8 @@ else if (!(unprivileged || removed_privilege)) exim_fail("exim: changing group failed: %s\n", strerror(errno)); else - DEBUG(D_any) debug_printf("changing group to %ld failed: %d: %s\n", - (long int)exim_gid, errno, strerror(errno)); + DEBUG(D_any) debug_printf("changing group to %ld failed: %s\n", + (long int)exim_gid, strerror(errno)); } /* Handle a request to scan a file for malware */ @@ -4338,7 +4370,7 @@ if (list_queue) if (count_queue) { set_process_info("counting the queue"); - queue_count(); + fprintf(stdout, "%u\n", queue_count()); exit(EXIT_SUCCESS); } @@ -4365,7 +4397,7 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD) yield = EXIT_FAILURE; switch (msg_action) { - case MSG_REMOVE: MSG_DELETE: case MSG_FREEZE: case MSG_THAW: break; + case MSG_REMOVE: case MSG_FREEZE: case MSG_THAW: break; default: printf("\n"); break; } } @@ -4408,7 +4440,7 @@ if (test_retry_arg >= 0) if (test_retry_arg >= argc) { printf("-brt needs a domain or address argument\n"); - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); } s1 = argv[test_retry_arg++]; s2 = NULL; @@ -4513,7 +4545,7 @@ if (test_retry_arg >= 0) printf("\n"); } - exim_exit(EXIT_SUCCESS, US"main"); + exim_exit(EXIT_SUCCESS); } /* Handle a request to list one or more configuration options */ @@ -4540,14 +4572,14 @@ if (list_options) else fail = !readconf_print(argv[i], NULL, flag_n); } - exim_exit(fail ? EXIT_FAILURE : EXIT_SUCCESS, US"main"); + exim_exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); } if (list_config) { set_process_info("listing config"); exim_exit(readconf_print(US"config", NULL, flag_n) - ? EXIT_SUCCESS : EXIT_FAILURE, US"main"); + ? EXIT_SUCCESS : EXIT_FAILURE); } @@ -4573,7 +4605,7 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) if (prod_requires_admin && !f.admin_user) { fprintf(stderr, "exim: Permission denied\n"); - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); } set_process_info("delivering specified messages"); if (deliver_give_up) forced_delivery = f.deliver_force_thaw = TRUE; @@ -4581,9 +4613,11 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) { int status; pid_t pid; + /*XXX This use of argv[i] for msg_id should really be tainted, but doing + that runs into a later copy into the untainted global message_id[] */ if (i == argc - 1) (void)deliver_message(argv[i], forced_delivery, deliver_give_up); - else if ((pid = fork()) == 0) + else if ((pid = exim_fork(US"cmdline-delivery")) == 0) { (void)deliver_message(argv[i], forced_delivery, deliver_give_up); exim_underbar_exit(EXIT_SUCCESS); @@ -4592,11 +4626,11 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) { fprintf(stderr, "failed to fork delivery process for %s: %s\n", argv[i], strerror(errno)); - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); } else wait(&status); } - exim_exit(EXIT_SUCCESS, US"main"); + exim_exit(EXIT_SUCCESS); } @@ -4606,16 +4640,16 @@ turn into a queue runner, with an optional starting message id. */ if (queue_interval == 0 && !f.daemon_listen) { DEBUG(D_queue_run) debug_printf("Single queue run%s%s%s%s\n", - (start_queue_run_id == NULL)? US"" : US" starting at ", - (start_queue_run_id == NULL)? US"" : start_queue_run_id, - (stop_queue_run_id == NULL)? US"" : US" stopping at ", - (stop_queue_run_id == NULL)? US"" : stop_queue_run_id); + start_queue_run_id ? US" starting at " : US"", + start_queue_run_id ? start_queue_run_id: US"", + stop_queue_run_id ? US" stopping at " : US"", + stop_queue_run_id ? stop_queue_run_id : US""); if (*queue_name) set_process_info("running the '%s' queue (single queue run)", queue_name); else set_process_info("running the queue (single queue run)"); queue_run(start_queue_run_id, stop_queue_run_id, FALSE); - exim_exit(EXIT_SUCCESS, US"main"); + exim_exit(EXIT_SUCCESS); } @@ -4785,10 +4819,10 @@ if (test_rewrite_arg >= 0) if (test_rewrite_arg >= argc) { printf("-brw needs an address argument\n"); - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); } rewrite_test(argv[test_rewrite_arg]); - exim_exit(EXIT_SUCCESS, US"main"); + exim_exit(EXIT_SUCCESS); } /* A locally-supplied message is considered to be coming from a local user @@ -4903,7 +4937,7 @@ if (verify_address_mode || f.address_test_mode) } route_tidyup(); - exim_exit(exit_value, US"main"); + exim_exit(exit_value); } /* Handle expansion checking. Either expand items on the command line, or read @@ -4989,7 +5023,7 @@ if (expansion_test) deliver_datafile = -1; } - exim_exit(EXIT_SUCCESS, US"main: expansion test"); + exim_exit(EXIT_SUCCESS); } @@ -5026,9 +5060,9 @@ if (host_checking) if (!sender_ident_set) { sender_ident = NULL; - if (f.running_in_test_harness && sender_host_port != 0 && - interface_address != NULL && interface_port != 0) - verify_get_ident(1413); + if (f.running_in_test_harness && sender_host_port + && interface_address && interface_port) + verify_get_ident(1223); /* note hardwired port number */ } /* In case the given address is a non-canonical IPv6 address, canonicalize @@ -5065,6 +5099,7 @@ if (host_checking) if (smtp_start_session()) { + rmark reset_point; for (; (reset_point = store_mark()); store_reset(reset_point)) { if (smtp_setup_msg() <= 0) break; @@ -5083,7 +5118,7 @@ if (host_checking) } smtp_log_no_mail(); } - exim_exit(EXIT_SUCCESS, US"main"); + exim_exit(EXIT_SUCCESS); } @@ -5246,7 +5281,7 @@ if (smtp_input) if (!smtp_start_session()) { mac_smtp_fflush(); - exim_exit(EXIT_SUCCESS, US"smtp_start toplevel"); + exim_exit(EXIT_SUCCESS); } } @@ -5316,7 +5351,7 @@ collapsed). */ while (more) { - reset_point = store_mark(); + rmark reset_point = store_mark(); message_id[0] = 0; /* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP @@ -5361,14 +5396,14 @@ while (more) cancel_cutthrough_connection(TRUE, US"receive dropped"); if (more) goto moreloop; smtp_log_no_mail(); /* Log no mail if configured */ - exim_exit(EXIT_FAILURE, US"receive toplevel"); + exim_exit(EXIT_FAILURE); } } else { cancel_cutthrough_connection(TRUE, US"message setup dropped"); smtp_log_no_mail(); /* Log no mail if configured */ - exim_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS, US"msg setup toplevel"); + exim_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); } } @@ -5418,7 +5453,7 @@ while (more) if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: too many recipients\n"); - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); } else return @@ -5446,13 +5481,12 @@ while (more) errmess = US"unqualified recipient address not allowed"; } - if (recipient == NULL) - { + if (!recipient) if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: bad recipient address \"%s\": %s\n", string_printing(list[i]), errmess); - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); } else { @@ -5464,7 +5498,6 @@ while (more) moan_to_sender(ERRMESS_BADARGADDRESS, &eblock, NULL, stdin, TRUE)? errors_sender_rc : EXIT_FAILURE; } - } receive_add_recipient(string_copy_taint(recipient, TRUE), -1); s = ss; @@ -5477,8 +5510,8 @@ while (more) DEBUG(D_receive) { - if (sender_address != NULL) debug_printf("Sender: %s\n", sender_address); - if (recipients_list != NULL) + if (sender_address) debug_printf("Sender: %s\n", sender_address); + if (recipients_list) { debug_printf("Recipients:\n"); for (int i = 0; i < recipients_count; i++) @@ -5524,7 +5557,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, US"main"); + if (message_id[0] == 0) exim_exit(EXIT_FAILURE); } /* Non-SMTP message reception */ /* If this is a filter testing run, there are headers in store, but @@ -5569,7 +5602,7 @@ while (more) if (chdir("/")) /* Get away from wherever the user is running this from */ { DEBUG(D_receive) debug_printf("chdir(\"/\") failed\n"); - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); } /* Now we run either a system filter test, or a user filter test, or both. @@ -5579,15 +5612,15 @@ while (more) if ((filter_test & FTEST_SYSTEM) != 0) if (!filter_runtest(filter_sfd, filter_test_sfile, TRUE, more)) - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); memcpy(filter_sn, filter_n, sizeof(filter_sn)); if ((filter_test & FTEST_USER) != 0) if (!filter_runtest(filter_ufd, filter_test_ufile, FALSE, more)) - exim_exit(EXIT_FAILURE, US"main"); + exim_exit(EXIT_FAILURE); - exim_exit(EXIT_SUCCESS, US"main"); + exim_exit(EXIT_SUCCESS); } /* Else act on the result of message reception. We should not get here unless @@ -5669,7 +5702,7 @@ while (more) pid_t pid; search_tidyup(); - if ((pid = fork()) == 0) + if ((pid = exim_fork(US"local-accept-delivery")) == 0) { int rc; close_unwanted(); /* Close unwanted file descriptors and TLS */ @@ -5713,7 +5746,7 @@ while (more) log_write(0, LOG_MAIN|LOG_PANIC, "process %d crashed with signal %d while delivering %s", (int)pid, status & 0x00ff, message_id); - if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE, US"main"); + if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE); } } } @@ -5745,7 +5778,7 @@ moreloop: store_reset(reset_point); } -exim_exit(EXIT_SUCCESS, US"main"); /* Never returns */ +exim_exit(EXIT_SUCCESS); /* Never returns */ return 0; /* To stop compiler warning */ }