"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/exim.c" between
exim-4.91.tar.xz and exim-4.92.tar.xz

About: Exim is a message transfer agent (MTA).

exim.c  (exim-4.91.tar.xz):exim.c  (exim-4.92.tar.xz)
skipping to change at line 130 skipping to change at line 130
Returns: TRUE or FALSE Returns: TRUE or FALSE
*/ */
BOOL BOOL
regex_match_and_setup(const pcre *re, const uschar *subject, int options, int se tup) regex_match_and_setup(const pcre *re, const uschar *subject, int options, int se tup)
{ {
int ovector[3*(EXPAND_MAXN+1)]; int ovector[3*(EXPAND_MAXN+1)];
uschar * s = string_copy(subject); /* de-constifying */ uschar * s = string_copy(subject); /* de-constifying */
int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0, int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0,
PCRE_EOPT | options, ovector, sizeof(ovector)/sizeof(int)); PCRE_EOPT | options, ovector, nelem(ovector));
BOOL yield = n >= 0; BOOL yield = n >= 0;
if (n == 0) n = EXPAND_MAXN + 1; if (n == 0) n = EXPAND_MAXN + 1;
if (yield) if (yield)
{ {
int nn; int nn;
expand_nmax = (setup < 0)? 0 : setup + 1; expand_nmax = setup < 0 ? 0 : setup + 1;
for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2) for (nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
{ {
expand_nstring[expand_nmax] = s + ovector[nn]; expand_nstring[expand_nmax] = s + ovector[nn];
expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn]; expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
} }
expand_nmax--; expand_nmax--;
} }
return yield; return yield;
} }
/************************************************* /*************************************************
skipping to change at line 161 skipping to change at line 161
/* Save a text string for dumping when SIGUSR1 is received. /* Save a text string for dumping when SIGUSR1 is received.
Do checks for overruns. Do checks for overruns.
Arguments: format and arguments, as for printf() Arguments: format and arguments, as for printf()
Returns: nothing Returns: nothing
*/ */
void void
set_process_info(const char *format, ...) set_process_info(const char *format, ...)
{ {
int len = sprintf(CS process_info, "%5d ", (int)getpid()); gstring gs = { .size = PROCESS_INFO_SIZE - 2, .ptr = 0, .s = process_info };
gstring * g;
int len;
va_list ap; va_list ap;
g = string_fmt_append(&gs, "%5d ", (int)getpid());
len = g->ptr;
va_start(ap, format); va_start(ap, format);
if (!string_vformat(process_info + len, PROCESS_INFO_SIZE - len - 2, format, ap) if (!string_vformat(g, FALSE, format, ap))
) {
Ustrcpy(process_info + len, "**** string overflowed buffer ****"); gs.ptr = len;
len = Ustrlen(process_info); g = string_cat(&gs, US"**** string overflowed buffer ****");
process_info[len+0] = '\n'; }
process_info[len+1] = '\0'; g = string_catn(g, US"\n", 1);
process_info_len = len + 1; string_from_gstring(g);
process_info_len = g->ptr;
DEBUG(D_process_info) debug_printf("set_process_info: %s", process_info); DEBUG(D_process_info) debug_printf("set_process_info: %s", process_info);
va_end(ap); va_end(ap);
} }
/*********************************************** /***********************************************
* Handler for SIGTERM * * Handler for SIGTERM *
***********************************************/ ***********************************************/
static void static void
term_handler(int sig) term_handler(int sig)
skipping to change at line 394 skipping to change at line 401
is greater than zero. The following correction is therefore safe. */ is greater than zero. The following correction is therefore safe. */
if (itval.it_value.tv_usec < 0) if (itval.it_value.tv_usec < 0)
{ {
itval.it_value.tv_usec += 1000000; itval.it_value.tv_usec += 1000000;
itval.it_value.tv_sec -= 1; itval.it_value.tv_sec -= 1;
} }
DEBUG(D_transport|D_receive) DEBUG(D_transport|D_receive)
{ {
if (!running_in_test_harness) if (!f.running_in_test_harness)
{ {
debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n", debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
then_tv->tv_sec, (long) then_tv->tv_usec, then_tv->tv_sec, (long) then_tv->tv_usec,
now_tv.tv_sec, (long) now_tv.tv_usec); now_tv.tv_sec, (long) now_tv.tv_usec);
debug_printf("waiting " TIME_T_FMT ".%06lu\n", debug_printf("waiting " TIME_T_FMT ".%06lu\n",
itval.it_value.tv_sec, (long) itval.it_value.tv_usec); itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
} }
} }
milliwait(&itval); milliwait(&itval);
skipping to change at line 515 skipping to change at line 522
Arguments: None Arguments: None
Returns: Nothing Returns: Nothing
*/ */
static void static void
close_unwanted(void) close_unwanted(void)
{ {
if (smtp_input) if (smtp_input)
{ {
#ifdef SUPPORT_TLS #ifdef SUPPORT_TLS
tls_close(TRUE, TLS_NO_SHUTDOWN); /* Shut down the TLS library */ tls_close(NULL, TLS_NO_SHUTDOWN); /* Shut down the TLS library */
#endif #endif
(void)close(fileno(smtp_in)); (void)close(fileno(smtp_in));
(void)close(fileno(smtp_out)); (void)close(fileno(smtp_out));
smtp_in = NULL; smtp_in = NULL;
} }
else else
{ {
(void)close(0); /* stdin */ (void)close(0); /* stdin */
if ((debug_selector & D_resolver) == 0) (void)close(1); /* stdout */ if ((debug_selector & D_resolver) == 0) (void)close(1); /* stdout */
if (debug_selector == 0) /* stderr */ if (debug_selector == 0) /* stderr */
{ {
if (!synchronous_delivery) if (!f.synchronous_delivery)
{ {
(void)close(2); (void)close(2);
log_stderr = NULL; log_stderr = NULL;
} }
(void)setsid(); (void)setsid();
} }
} }
} }
/************************************************* /*************************************************
skipping to change at line 571 skipping to change at line 578
gid_t egid = getegid(); gid_t egid = getegid();
if (euid == root_uid || euid != uid || egid != gid || igflag) if (euid == root_uid || euid != uid || egid != gid || igflag)
{ {
/* At least one OS returns +1 for initgroups failure, so just check for /* At least one OS returns +1 for initgroups failure, so just check for
non-zero. */ non-zero. */
if (igflag) if (igflag)
{ {
struct passwd *pw = getpwuid(uid); struct passwd *pw = getpwuid(uid);
if (pw != NULL) if (!pw)
{ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "cannot run initgroups(): "
if (initgroups(pw->pw_name, gid) != 0) "no passwd entry for uid=%ld", (long int)uid);
log_write(0,LOG_MAIN|LOG_PANIC_DIE,"initgroups failed for uid=%ld: %s",
(long int)uid, strerror(errno)); if (initgroups(pw->pw_name, gid) != 0)
} log_write(0,LOG_MAIN|LOG_PANIC_DIE,"initgroups failed for uid=%ld: %s",
else log_write(0, LOG_MAIN|LOG_PANIC_DIE, "cannot run initgroups(): " (long int)uid, strerror(errno));
"no passwd entry for uid=%ld", (long int)uid);
} }
if (setgid(gid) < 0 || setuid(uid) < 0) if (setgid(gid) < 0 || setuid(uid) < 0)
{
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to set gid=%ld or uid=%ld " log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to set gid=%ld or uid=%ld "
"(euid=%ld): %s", (long int)gid, (long int)uid, (long int)euid, msg); "(euid=%ld): %s", (long int)gid, (long int)uid, (long int)euid, msg);
}
} }
/* Debugging output included uid/gid and all groups */ /* Debugging output included uid/gid and all groups */
DEBUG(D_uid) DEBUG(D_uid)
{ {
int group_count, save_errno; int group_count, save_errno;
gid_t group_list[NGROUPS_MAX]; gid_t group_list[EXIM_GROUPLIST_SIZE];
debug_printf("changed uid/gid: %s\n uid=%ld gid=%ld pid=%ld\n", msg, debug_printf("changed uid/gid: %s\n uid=%ld gid=%ld pid=%ld\n", msg,
(long int)geteuid(), (long int)getegid(), (long int)getpid()); (long int)geteuid(), (long int)getegid(), (long int)getpid());
group_count = getgroups(NGROUPS_MAX, group_list); group_count = getgroups(nelem(group_list), group_list);
save_errno = errno; save_errno = errno;
debug_printf(" auxiliary group list:"); debug_printf(" auxiliary group list:");
if (group_count > 0) if (group_count > 0)
{ {
int i; int i;
for (i = 0; i < group_count; i++) debug_printf(" %d", (int)group_list[i]); for (i = 0; i < group_count; i++) debug_printf(" %d", (int)group_list[i]);
} }
else if (group_count < 0) else if (group_count < 0)
debug_printf(" <error: %s>", strerror(save_errno)); debug_printf(" <error: %s>", strerror(save_errno));
else debug_printf(" <none>"); else debug_printf(" <none>");
skipping to change at line 635 skipping to change at line 639
exim_exit(int rc, const uschar * process) exim_exit(int rc, const uschar * process)
{ {
search_tidyup(); search_tidyup();
DEBUG(D_any) DEBUG(D_any)
debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d " debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d "
">>>>>>>>>>>>>>>>\n", (int)getpid(), ">>>>>>>>>>>>>>>>\n", (int)getpid(),
process ? "(" : "", process, process ? ") " : "", rc); process ? "(" : "", process, process ? ") " : "", rc);
exit(rc); exit(rc);
} }
/* Print error string, then die */
static void
exim_fail(const char * fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
exit(EXIT_FAILURE);
}
/************************************************* /*************************************************
* Extract port from host address * * Extract port from host address *
*************************************************/ *************************************************/
/* Called to extract the port from the values given to -oMa and -oMi. /* Called to extract the port from the values given to -oMa and -oMi.
It also checks the syntax of the address, and terminates it before the It also checks the syntax of the address, and terminates it before the
port data when a port is extracted. port data when a port is extracted.
Argument: Argument:
address the address, with possible port on the end address the address, with possible port on the end
Returns: the port, or zero if there isn't one Returns: the port, or zero if there isn't one
bombs out on a syntax error bombs out on a syntax error
*/ */
static int static int
check_port(uschar *address) check_port(uschar *address)
{ {
int port = host_address_extract_port(address); int port = host_address_extract_port(address);
if (string_is_ip_address(address, NULL) == 0) if (string_is_ip_address(address, NULL) == 0)
{ exim_fail("exim abandoned: \"%s\" is not an IP address\n", address);
fprintf(stderr, "exim abandoned: \"%s\" is not an IP address\n", address);
exit(EXIT_FAILURE);
}
return port; return port;
} }
/************************************************* /*************************************************
* Test/verify an address * * Test/verify an address *
*************************************************/ *************************************************/
/* This function is called by the -bv and -bt code. It extracts a working /* This function is called by the -bv and -bt code. It extracts a working
address from a full RFC 822 address. This isn't really necessary per se, but it address from a full RFC 822 address. This isn't really necessary per se, but it
has the effect of collapsing source routes. has the effect of collapsing source routes.
skipping to change at line 744 skipping to change at line 755
} }
/* This function is called for -bV/--version and for -d to output the optional /* This function is called for -bV/--version and for -d to output the optional
features of the current Exim binary. features of the current Exim binary.
Arguments: a FILE for printing Arguments: a FILE for printing
Returns: nothing Returns: nothing
*/ */
static void static void
show_whats_supported(FILE * f) show_whats_supported(FILE * fp)
{ {
auth_info * authi; auth_info * authi;
DEBUG(D_any) {} else show_db_version(f); DEBUG(D_any) {} else show_db_version(fp);
fprintf(f, "Support for:"); fprintf(fp, "Support for:");
#ifdef SUPPORT_CRYPTEQ #ifdef SUPPORT_CRYPTEQ
fprintf(f, " crypteq"); fprintf(fp, " crypteq");
#endif #endif
#if HAVE_ICONV #if HAVE_ICONV
fprintf(f, " iconv()"); fprintf(fp, " iconv()");
#endif #endif
#if HAVE_IPV6 #if HAVE_IPV6
fprintf(f, " IPv6"); fprintf(fp, " IPv6");
#endif #endif
#ifdef HAVE_SETCLASSRESOURCES #ifdef HAVE_SETCLASSRESOURCES
fprintf(f, " use_setclassresources"); fprintf(fp, " use_setclassresources");
#endif #endif
#ifdef SUPPORT_PAM #ifdef SUPPORT_PAM
fprintf(f, " PAM"); fprintf(fp, " PAM");
#endif #endif
#ifdef EXIM_PERL #ifdef EXIM_PERL
fprintf(f, " Perl"); fprintf(fp, " Perl");
#endif #endif
#ifdef EXPAND_DLFUNC #ifdef EXPAND_DLFUNC
fprintf(f, " Expand_dlfunc"); fprintf(fp, " Expand_dlfunc");
#endif #endif
#ifdef USE_TCP_WRAPPERS #ifdef USE_TCP_WRAPPERS
fprintf(f, " TCPwrappers"); fprintf(fp, " TCPwrappers");
#endif #endif
#ifdef SUPPORT_TLS #ifdef SUPPORT_TLS
# ifdef USE_GNUTLS # ifdef USE_GNUTLS
fprintf(f, " GnuTLS"); fprintf(fp, " GnuTLS");
# else # else
fprintf(f, " OpenSSL"); fprintf(fp, " OpenSSL");
# endif # endif
#endif #endif
#ifdef SUPPORT_TRANSLATE_IP_ADDRESS #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
fprintf(f, " translate_ip_address"); fprintf(fp, " translate_ip_address");
#endif #endif
#ifdef SUPPORT_MOVE_FROZEN_MESSAGES #ifdef SUPPORT_MOVE_FROZEN_MESSAGES
fprintf(f, " move_frozen_messages"); fprintf(fp, " move_frozen_messages");
#endif #endif
#ifdef WITH_CONTENT_SCAN #ifdef WITH_CONTENT_SCAN
fprintf(f, " Content_Scanning"); fprintf(fp, " Content_Scanning");
#endif #endif
#ifdef SUPPORT_DANE #ifdef SUPPORT_DANE
fprintf(f, " DANE"); fprintf(fp, " DANE");
#endif #endif
#ifndef DISABLE_DKIM #ifndef DISABLE_DKIM
fprintf(f, " DKIM"); fprintf(fp, " DKIM");
#endif #endif
#ifndef DISABLE_DNSSEC #ifndef DISABLE_DNSSEC
fprintf(f, " DNSSEC"); fprintf(fp, " DNSSEC");
#endif #endif
#ifndef DISABLE_EVENT #ifndef DISABLE_EVENT
fprintf(f, " Event"); fprintf(fp, " Event");
#endif #endif
#ifdef SUPPORT_I18N #ifdef SUPPORT_I18N
fprintf(f, " I18N"); fprintf(fp, " I18N");
#endif #endif
#ifndef DISABLE_OCSP #ifndef DISABLE_OCSP
fprintf(f, " OCSP"); fprintf(fp, " OCSP");
#endif #endif
#ifndef DISABLE_PRDR #ifndef DISABLE_PRDR
fprintf(f, " PRDR"); fprintf(fp, " PRDR");
#endif #endif
#ifdef SUPPORT_PROXY #ifdef SUPPORT_PROXY
fprintf(f, " PROXY"); fprintf(fp, " PROXY");
#endif #endif
#ifdef SUPPORT_SOCKS #ifdef SUPPORT_SOCKS
fprintf(f, " SOCKS"); fprintf(fp, " SOCKS");
#endif #endif
#ifdef SUPPORT_SPF #ifdef SUPPORT_SPF
fprintf(f, " SPF"); fprintf(fp, " SPF");
#endif #endif
#ifdef TCP_FASTOPEN #ifdef TCP_FASTOPEN
deliver_init(); deliver_init();
if (tcp_fastopen_ok) fprintf(f, " TCP_Fast_Open"); if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open");
#endif #endif
#ifdef EXPERIMENTAL_LMDB #ifdef EXPERIMENTAL_LMDB
fprintf(f, " Experimental_LMDB"); fprintf(fp, " Experimental_LMDB");
#endif #endif
#ifdef EXPERIMENTAL_QUEUEFILE #ifdef EXPERIMENTAL_QUEUEFILE
fprintf(f, " Experimental_QUEUEFILE"); fprintf(fp, " Experimental_QUEUEFILE");
#endif #endif
#ifdef EXPERIMENTAL_SRS #ifdef EXPERIMENTAL_SRS
fprintf(f, " Experimental_SRS"); fprintf(fp, " Experimental_SRS");
#endif #endif
#ifdef EXPERIMENTAL_ARC #ifdef EXPERIMENTAL_ARC
fprintf(f, " Experimental_ARC"); fprintf(fp, " Experimental_ARC");
#endif #endif
#ifdef EXPERIMENTAL_BRIGHTMAIL #ifdef EXPERIMENTAL_BRIGHTMAIL
fprintf(f, " Experimental_Brightmail"); fprintf(fp, " Experimental_Brightmail");
#endif #endif
#ifdef EXPERIMENTAL_DCC #ifdef EXPERIMENTAL_DCC
fprintf(f, " Experimental_DCC"); fprintf(fp, " Experimental_DCC");
#endif #endif
#ifdef EXPERIMENTAL_DMARC #ifdef EXPERIMENTAL_DMARC
fprintf(f, " Experimental_DMARC"); fprintf(fp, " Experimental_DMARC");
#endif #endif
#ifdef EXPERIMENTAL_DSN_INFO #ifdef EXPERIMENTAL_DSN_INFO
fprintf(f, " Experimental_DSN_info"); fprintf(fp, " Experimental_DSN_info");
#endif
#ifdef EXPERIMENTAL_REQUIRETLS
fprintf(fp, " Experimental_REQUIRETLS");
#endif
#ifdef EXPERIMENTAL_PIPE_CONNECT
fprintf(fp, " Experimental_PIPE_CONNECT");
#endif #endif
fprintf(f, "\n"); fprintf(fp, "\n");
fprintf(f, "Lookups (built-in):"); fprintf(fp, "Lookups (built-in):");
#if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
fprintf(f, " lsearch wildlsearch nwildlsearch iplsearch"); fprintf(fp, " lsearch wildlsearch nwildlsearch iplsearch");
#endif #endif
#if defined(LOOKUP_CDB) && LOOKUP_CDB!=2 #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
fprintf(f, " cdb"); fprintf(fp, " cdb");
#endif #endif
#if defined(LOOKUP_DBM) && LOOKUP_DBM!=2 #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
fprintf(f, " dbm dbmjz dbmnz"); fprintf(fp, " dbm dbmjz dbmnz");
#endif #endif
#if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2 #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
fprintf(f, " dnsdb"); fprintf(fp, " dnsdb");
#endif #endif
#if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2 #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
fprintf(f, " dsearch"); fprintf(fp, " dsearch");
#endif #endif
#if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2 #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
fprintf(f, " ibase"); fprintf(fp, " ibase");
#endif #endif
#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2 #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
fprintf(f, " ldap ldapdn ldapm"); fprintf(fp, " ldap ldapdn ldapm");
#endif #endif
#ifdef EXPERIMENTAL_LMDB #ifdef EXPERIMENTAL_LMDB
fprintf(f, " lmdb"); fprintf(fp, " lmdb");
#endif #endif
#if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2 #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
fprintf(f, " mysql"); fprintf(fp, " mysql");
#endif #endif
#if defined(LOOKUP_NIS) && LOOKUP_NIS!=2 #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
fprintf(f, " nis nis0"); fprintf(fp, " nis nis0");
#endif #endif
#if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2 #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
fprintf(f, " nisplus"); fprintf(fp, " nisplus");
#endif #endif
#if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2 #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
fprintf(f, " oracle"); fprintf(fp, " oracle");
#endif #endif
#if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2 #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
fprintf(f, " passwd"); fprintf(fp, " passwd");
#endif #endif
#if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2 #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
fprintf(f, " pgsql"); fprintf(fp, " pgsql");
#endif #endif
#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
fprintf(f, " redis"); fprintf(fp, " redis");
#endif #endif
#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2 #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
fprintf(f, " sqlite"); fprintf(fp, " sqlite");
#endif #endif
#if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2 #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
fprintf(f, " testdb"); fprintf(fp, " testdb");
#endif #endif
#if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2 #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
fprintf(f, " whoson"); fprintf(fp, " whoson");
#endif #endif
fprintf(f, "\n"); fprintf(fp, "\n");
auth_show_supported(f); auth_show_supported(fp);
route_show_supported(f); route_show_supported(fp);
transport_show_supported(f); transport_show_supported(fp);
#ifdef WITH_CONTENT_SCAN #ifdef WITH_CONTENT_SCAN
malware_show_supported(f); malware_show_supported(fp);
#endif #endif
if (fixed_never_users[0] > 0) if (fixed_never_users[0] > 0)
{ {
int i; int i;
fprintf(f, "Fixed never_users: "); fprintf(fp, "Fixed never_users: ");
for (i = 1; i <= (int)fixed_never_users[0] - 1; i++) for (i = 1; i <= (int)fixed_never_users[0] - 1; i++)
fprintf(f, "%d:", (unsigned int)fixed_never_users[i]); fprintf(fp, "%d:", (unsigned int)fixed_never_users[i]);
fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]); fprintf(fp, "%d\n", (unsigned int)fixed_never_users[i]);
} }
fprintf(f, "Configure owner: %d:%d\n", config_uid, config_gid); fprintf(fp, "Configure owner: %d:%d\n", config_uid, config_gid);
fprintf(f, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t)); fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
/* Everything else is details which are only worth reporting when debugging. /* Everything else is details which are only worth reporting when debugging.
Perhaps the tls_version_report should move into this too. */ Perhaps the tls_version_report should move into this too. */
DEBUG(D_any) do { DEBUG(D_any) do {
int i; int i;
/* clang defines __GNUC__ (at least, for me) so test for it first */ /* clang defines __GNUC__ (at least, for me) so test for it first */
#if defined(__clang__) #if defined(__clang__)
fprintf(f, "Compiler: CLang [%s]\n", __clang_version__); fprintf(fp, "Compiler: CLang [%s]\n", __clang_version__);
#elif defined(__GNUC__) #elif defined(__GNUC__)
fprintf(f, "Compiler: GCC [%s]\n", fprintf(fp, "Compiler: GCC [%s]\n",
# ifdef __VERSION__ # ifdef __VERSION__
__VERSION__ __VERSION__
# else # else
"? unknown version ?" "? unknown version ?"
# endif # endif
); );
#else #else
fprintf(f, "Compiler: <unknown>\n"); fprintf(fp, "Compiler: <unknown>\n");
#endif #endif
#if defined(__GLIBC__) && !defined(__UCLIBC__) #if defined(__GLIBC__) && !defined(__UCLIBC__)
fprintf(f, "Library version: Glibc: Compile: %d.%d\n", fprintf(fp, "Library version: Glibc: Compile: %d.%d\n",
__GLIBC__, __GLIBC_MINOR__); __GLIBC__, __GLIBC_MINOR__);
if (__GLIBC_PREREQ(2, 1)) if (__GLIBC_PREREQ(2, 1))
fprintf(f, " Runtime: %s\n", fprintf(fp, " Runtime: %s\n",
gnu_get_libc_version()); gnu_get_libc_version());
#endif #endif
show_db_version(f); show_db_version(fp);
#ifdef SUPPORT_TLS #ifdef SUPPORT_TLS
tls_version_report(f); tls_version_report(fp);
#endif #endif
#ifdef SUPPORT_I18N #ifdef SUPPORT_I18N
utf8_version_report(f); utf8_version_report(fp);
#endif #endif
for (authi = auths_available; *authi->driver_name != '\0'; ++authi) for (authi = auths_available; *authi->driver_name != '\0'; ++authi)
if (authi->version_report) if (authi->version_report)
(*authi->version_report)(f); (*authi->version_report)(fp);
/* PCRE_PRERELEASE is either defined and empty or a bare sequence of /* 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 characters; unless it's an ancient version of PCRE in which case it
is not defined. */ is not defined. */
#ifndef PCRE_PRERELEASE #ifndef PCRE_PRERELEASE
# define PCRE_PRERELEASE # define PCRE_PRERELEASE
#endif #endif
#define QUOTE(X) #X #define QUOTE(X) #X
#define EXPAND_AND_QUOTE(X) QUOTE(X) #define EXPAND_AND_QUOTE(X) QUOTE(X)
fprintf(f, "Library version: PCRE: Compile: %d.%d%s\n" fprintf(fp, "Library version: PCRE: Compile: %d.%d%s\n"
" Runtime: %s\n", " Runtime: %s\n",
PCRE_MAJOR, PCRE_MINOR, PCRE_MAJOR, PCRE_MINOR,
EXPAND_AND_QUOTE(PCRE_PRERELEASE) "", EXPAND_AND_QUOTE(PCRE_PRERELEASE) "",
pcre_version()); pcre_version());
#undef QUOTE #undef QUOTE
#undef EXPAND_AND_QUOTE #undef EXPAND_AND_QUOTE
init_lookup_list(); init_lookup_list();
for (i = 0; i < lookup_list_count; i++) for (i = 0; i < lookup_list_count; i++)
if (lookup_list[i]->version_report) if (lookup_list[i]->version_report)
lookup_list[i]->version_report(f); lookup_list[i]->version_report(fp);
#ifdef WHITELIST_D_MACROS #ifdef WHITELIST_D_MACROS
fprintf(f, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); fprintf(fp, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
#else #else
fprintf(f, "WHITELIST_D_MACROS unset\n"); fprintf(fp, "WHITELIST_D_MACROS unset\n");
#endif #endif
#ifdef TRUSTED_CONFIG_LIST #ifdef TRUSTED_CONFIG_LIST
fprintf(f, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST); fprintf(fp, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST);
#else #else
fprintf(f, "TRUSTED_CONFIG_LIST unset\n"); fprintf(fp, "TRUSTED_CONFIG_LIST unset\n");
#endif #endif
} while (0); } while (0);
} }
/************************************************* /*************************************************
* Show auxiliary information about Exim * * Show auxiliary information about Exim *
*************************************************/ *************************************************/
static void static void
skipping to change at line 1227 skipping to change at line 1244
Returns: DOES NOT RETURN Returns: DOES NOT RETURN
*/ */
static void static void
exim_usage(uschar *progname) exim_usage(uschar *progname)
{ {
/* Handle specific program invocation variants */ /* Handle specific program invocation variants */
if (Ustrcmp(progname, US"-mailq") == 0) if (Ustrcmp(progname, US"-mailq") == 0)
{ exim_fail(
fprintf(stderr,
"mailq - list the contents of the mail queue\n\n" "mailq - list the contents of the mail queue\n\n"
"For a list of options, see the Exim documentation.\n"); "For a list of options, see the Exim documentation.\n");
exit(EXIT_FAILURE);
}
/* Generic usage - we output this whatever happens */ /* Generic usage - we output this whatever happens */
fprintf(stderr, exim_fail(
"Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n" "Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n"
"not directly from a shell command line. Options and/or arguments control\n" "not directly from a shell command line. Options and/or arguments control\n"
"what it does when called. For a list of options, see the Exim documentation.\ n"); "what it does when called. For a list of options, see the Exim documentation.\ n");
exit(EXIT_FAILURE);
} }
/************************************************* /*************************************************
* Validate that the macros given are okay * * Validate that the macros given are okay *
*************************************************/ *************************************************/
/* Typically, Exim will drop privileges if macros are supplied. In some /* Typically, Exim will drop privileges if macros are supplied. In some
cases, we want to not do so. cases, we want to not do so.
Arguments: opt_D_used - true if the commandline had a "-D" option Arguments: opt_D_used - true if the commandline had a "-D" option
skipping to change at line 1427 skipping to change at line 1439
int msg_action_arg = -1; int msg_action_arg = -1;
int namelen = (argv[0] == NULL)? 0 : Ustrlen(argv[0]); int namelen = (argv[0] == NULL)? 0 : Ustrlen(argv[0]);
int queue_only_reason = 0; int queue_only_reason = 0;
#ifdef EXIM_PERL #ifdef EXIM_PERL
int perl_start_option = 0; int perl_start_option = 0;
#endif #endif
int recipients_arg = argc; int recipients_arg = argc;
int sender_address_domain = 0; int sender_address_domain = 0;
int test_retry_arg = -1; int test_retry_arg = -1;
int test_rewrite_arg = -1; int test_rewrite_arg = -1;
gid_t original_egid;
BOOL arg_queue_only = FALSE; BOOL arg_queue_only = FALSE;
BOOL bi_option = FALSE; BOOL bi_option = FALSE;
BOOL checking = FALSE; BOOL checking = FALSE;
BOOL count_queue = FALSE; BOOL count_queue = FALSE;
BOOL expansion_test = FALSE; BOOL expansion_test = FALSE;
BOOL extract_recipients = FALSE; BOOL extract_recipients = FALSE;
BOOL flag_G = FALSE; BOOL flag_G = FALSE;
BOOL flag_n = FALSE; BOOL flag_n = FALSE;
BOOL forced_delivery = FALSE; BOOL forced_delivery = FALSE;
BOOL f_end_dot = FALSE; BOOL f_end_dot = FALSE;
skipping to change at line 1476 skipping to change at line 1489
uschar *malware_test_file = NULL; uschar *malware_test_file = NULL;
uschar *real_sender_address; uschar *real_sender_address;
uschar *originator_home = US"/"; uschar *originator_home = US"/";
size_t sz; size_t sz;
void *reset_point; void *reset_point;
struct passwd *pw; struct passwd *pw;
struct stat statbuf; struct stat statbuf;
pid_t passed_qr_pid = (pid_t)0; pid_t passed_qr_pid = (pid_t)0;
int passed_qr_pipe = -1; int passed_qr_pipe = -1;
gid_t group_list[NGROUPS_MAX]; gid_t group_list[EXIM_GROUPLIST_SIZE];
/* For the -bI: flag */ /* For the -bI: flag */
enum commandline_info info_flag = CMDINFO_NONE; enum commandline_info info_flag = CMDINFO_NONE;
BOOL info_stdout = FALSE; BOOL info_stdout = FALSE;
/* Possible options for -R and -S */ /* Possible options for -R and -S */
static uschar *rsopts[] = { US"f", US"ff", US"r", US"rf", US"rff" }; static uschar *rsopts[] = { US"f", US"ff", US"r", US"rf", US"rff" };
/* Need to define this in case we need to change the environment in order /* Need to define this in case we need to change the environment in order
skipping to change at line 1500 skipping to change at line 1513
extern char **environ; extern char **environ;
/* If the Exim user and/or group and/or the configuration file owner/group were /* If the Exim user and/or group and/or the configuration file owner/group were
defined by ref:name at build time, we must now find the actual uid/gid values. defined by ref:name at build time, we must now find the actual uid/gid values.
This is a feature to make the lives of binary distributors easier. */ This is a feature to make the lives of binary distributors easier. */
#ifdef EXIM_USERNAME #ifdef EXIM_USERNAME
if (route_finduser(US EXIM_USERNAME, &pw, &exim_uid)) if (route_finduser(US EXIM_USERNAME, &pw, &exim_uid))
{ {
if (exim_uid == 0) if (exim_uid == 0)
{ exim_fail("exim: refusing to run with uid 0 for \"%s\"\n", EXIM_USERNAME);
fprintf(stderr, "exim: refusing to run with uid 0 for \"%s\"\n",
EXIM_USERNAME);
exit(EXIT_FAILURE);
}
/* If ref:name uses a number as the name, route_finduser() returns /* If ref:name uses a number as the name, route_finduser() returns
TRUE with exim_uid set and pw coerced to NULL. */ TRUE with exim_uid set and pw coerced to NULL. */
if (pw) if (pw)
exim_gid = pw->pw_gid; exim_gid = pw->pw_gid;
#ifndef EXIM_GROUPNAME #ifndef EXIM_GROUPNAME
else else
{ exim_fail(
fprintf(stderr,
"exim: ref:name should specify a usercode, not a group.\n" "exim: ref:name should specify a usercode, not a group.\n"
"exim: can't let you get away with it unless you also specify a group.\n "); "exim: can't let you get away with it unless you also specify a group.\n ");
exit(EXIT_FAILURE);
}
#endif #endif
} }
else else
{ exim_fail("exim: failed to find uid for user name \"%s\"\n", EXIM_USERNAME);
fprintf(stderr, "exim: failed to find uid for user name \"%s\"\n",
EXIM_USERNAME);
exit(EXIT_FAILURE);
}
#endif #endif
#ifdef EXIM_GROUPNAME #ifdef EXIM_GROUPNAME
if (!route_findgroup(US EXIM_GROUPNAME, &exim_gid)) if (!route_findgroup(US EXIM_GROUPNAME, &exim_gid))
{ exim_fail("exim: failed to find gid for group name \"%s\"\n", EXIM_GROUPNAME);
fprintf(stderr, "exim: failed to find gid for group name \"%s\"\n",
EXIM_GROUPNAME);
exit(EXIT_FAILURE);
}
#endif #endif
#ifdef CONFIGURE_OWNERNAME #ifdef CONFIGURE_OWNERNAME
if (!route_finduser(US CONFIGURE_OWNERNAME, NULL, &config_uid)) if (!route_finduser(US CONFIGURE_OWNERNAME, NULL, &config_uid))
{ exim_fail("exim: failed to find uid for user name \"%s\"\n",
fprintf(stderr, "exim: failed to find uid for user name \"%s\"\n",
CONFIGURE_OWNERNAME); CONFIGURE_OWNERNAME);
exit(EXIT_FAILURE);
}
#endif #endif
/* We default the system_filter_user to be the Exim run-time user, as a /* We default the system_filter_user to be the Exim run-time user, as a
sane non-root value. */ sane non-root value. */
system_filter_uid = exim_uid; system_filter_uid = exim_uid;
#ifdef CONFIGURE_GROUPNAME #ifdef CONFIGURE_GROUPNAME
if (!route_findgroup(US CONFIGURE_GROUPNAME, &config_gid)) if (!route_findgroup(US CONFIGURE_GROUPNAME, &config_gid))
{ exim_fail("exim: failed to find gid for group name \"%s\"\n",
fprintf(stderr, "exim: failed to find gid for group name \"%s\"\n",
CONFIGURE_GROUPNAME); CONFIGURE_GROUPNAME);
exit(EXIT_FAILURE);
}
#endif #endif
/* In the Cygwin environment, some initialization used to need doing. /* In the Cygwin environment, some initialization used to need doing.
It was fudged in by means of this macro; now no longer but we'll leave It was fudged in by means of this macro; now no longer but we'll leave
it in case of others. */ it in case of others. */
#ifdef OS_INIT #ifdef OS_INIT
OS_INIT OS_INIT
#endif #endif
/* Check a field which is patched when we are running Exim within its /* Check a field which is patched when we are running Exim within its
testing harness; do a fast initial check, and then the whole thing. */ testing harness; do a fast initial check, and then the whole thing. */
running_in_test_harness = f.running_in_test_harness =
*running_status == '<' && Ustrcmp(running_status, "<<<testing>>>") == 0; *running_status == '<' && Ustrcmp(running_status, "<<<testing>>>") == 0;
if (running_in_test_harness) if (f.running_in_test_harness)
debug_store = TRUE; debug_store = TRUE;
/* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed /* 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 at the start of a program; however, it seems that some environments do not
follow this. A "strange" locale can affect the formatting of timestamps, so we follow this. A "strange" locale can affect the formatting of timestamps, so we
make quite sure. */ make quite sure. */
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
/* Set up the default handler for timing using alarm(). */ /* Set up the default handler for timing using alarm(). */
os_non_restarting_signal(SIGALRM, sigalrm_handler); os_non_restarting_signal(SIGALRM, sigalrm_handler);
/* Ensure we have a buffer for constructing log entries. Use malloc directly, /* Ensure we have a buffer for constructing log entries. Use malloc directly,
because store_malloc writes a log entry on failure. */ because store_malloc writes a log entry on failure. */
if (!(log_buffer = US malloc(LOG_BUFFER_SIZE))) if (!(log_buffer = US malloc(LOG_BUFFER_SIZE)))
{ exim_fail("exim: failed to get store for log buffer\n");
fprintf(stderr, "exim: failed to get store for log buffer\n");
exit(EXIT_FAILURE);
}
/* Initialize the default log options. */ /* Initialize the default log options. */
bits_set(log_selector, log_selector_size, log_default); bits_set(log_selector, log_selector_size, log_default);
/* Set log_stderr to stderr, provided that stderr exists. This gets reset to /* Set log_stderr to stderr, provided that stderr exists. This gets reset to
NULL when the daemon is run and the file is closed. We have to use this NULL when the daemon is run and the file is closed. We have to use this
indirection, because some systems don't allow writing to the variable "stderr". indirection, because some systems don't allow writing to the variable "stderr".
*/ */
skipping to change at line 1735 skipping to change at line 1725
/* If the program is called as "rmail" treat it as equivalent to /* If the program is called as "rmail" treat it as equivalent to
"exim -i -oee", thus allowing UUCP messages to be input using non-SMTP mode, "exim -i -oee", thus allowing UUCP messages to be input using non-SMTP mode,
i.e. preventing a single dot on a line from terminating the message, and i.e. preventing a single dot on a line from terminating the message, and
returning with zero return code, even in cases of error (provided an error returning with zero return code, even in cases of error (provided an error
message has been sent). */ message has been sent). */
if ((namelen == 5 && Ustrcmp(argv[0], "rmail") == 0) || if ((namelen == 5 && Ustrcmp(argv[0], "rmail") == 0) ||
(namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rmail", 6) == 0)) (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rmail", 6) == 0))
{ {
dot_ends = FALSE; f.dot_ends = FALSE;
called_as = US"-rmail"; called_as = US"-rmail";
errors_sender_rc = EXIT_SUCCESS; errors_sender_rc = EXIT_SUCCESS;
} }
/* If the program is called as "rsmtp" treat it as equivalent to "exim -bS"; /* If the program is called as "rsmtp" treat it as equivalent to "exim -bS";
this is a smail convention. */ this is a smail convention. */
if ((namelen == 5 && Ustrcmp(argv[0], "rsmtp") == 0) || if ((namelen == 5 && Ustrcmp(argv[0], "rsmtp") == 0) ||
(namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rsmtp", 6) == 0)) (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rsmtp", 6) == 0))
{ {
skipping to change at line 1776 skipping to change at line 1766
{ {
bi_option = TRUE; bi_option = TRUE;
receiving_message = FALSE; receiving_message = FALSE;
called_as = US"-newaliases"; called_as = US"-newaliases";
} }
/* Save the original effective uid for a couple of uses later. It should /* Save the original effective uid for a couple of uses later. It should
normally be root, but in some esoteric environments it may not be. */ normally be root, but in some esoteric environments it may not be. */
original_euid = geteuid(); original_euid = geteuid();
original_egid = getegid();
/* Get the real uid and gid. If the caller is root, force the effective uid/gid /* Get the real uid and gid. If the caller is root, force the effective uid/gid
to be the same as the real ones. This makes a difference only if Exim is setuid to be the same as the real ones. This makes a difference only if Exim is setuid
(or setgid) to something other than root, which could be the case in some (or setgid) to something other than root, which could be the case in some
special configurations. */ special configurations. */
real_uid = getuid(); real_uid = getuid();
real_gid = getgid(); real_gid = getgid();
if (real_uid == root_uid) if (real_uid == root_uid)
{ {
rv = setgid(real_gid); if ((rv = setgid(real_gid)))
if (rv) exim_fail("exim: setgid(%ld) failed: %s\n",
{
fprintf(stderr, "exim: setgid(%ld) failed: %s\n",
(long int)real_gid, strerror(errno)); (long int)real_gid, strerror(errno));
exit(EXIT_FAILURE); if ((rv = setuid(real_uid)))
} exim_fail("exim: setuid(%ld) failed: %s\n",
rv = setuid(real_uid);
if (rv)
{
fprintf(stderr, "exim: setuid(%ld) failed: %s\n",
(long int)real_uid, strerror(errno)); (long int)real_uid, strerror(errno));
exit(EXIT_FAILURE);
}
} }
/* If neither the original real uid nor the original euid was root, Exim is /* If neither the original real uid nor the original euid was root, Exim is
running in an unprivileged state. */ running in an unprivileged state. */
unprivileged = (real_uid != root_uid && original_euid != root_uid); unprivileged = (real_uid != root_uid && original_euid != root_uid);
/* Scan the program's arguments. Some can be dealt with right away; others are /* 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 simply recorded for checking and handling afterwards. Do a high-level switch
on the second character (the one after '-'), to save some effort. */ on the second character (the one after '-'), to save some effort. */
skipping to change at line 1857 skipping to change at line 1840
Ustrncmp(arg+1, "qR", 2) == 0 || Ustrncmp(arg+1, "qR", 2) == 0 ||
Ustrncmp(arg+1, "qS", 2) == 0) Ustrncmp(arg+1, "qS", 2) == 0)
{ {
switchchar = arg[2]; switchchar = arg[2];
argrest++; argrest++;
} }
else if (Ustrncmp(arg+1, "qqR", 3) == 0 || Ustrncmp(arg+1, "qqS", 3) == 0) else if (Ustrncmp(arg+1, "qqR", 3) == 0 || Ustrncmp(arg+1, "qqS", 3) == 0)
{ {
switchchar = arg[3]; switchchar = arg[3];
argrest += 2; argrest += 2;
queue_2stage = TRUE; f.queue_2stage = TRUE;
} }
/* Make -r synonymous with -f, since it is a documented alias */ /* Make -r synonymous with -f, since it is a documented alias */
else if (arg[1] == 'r') switchchar = 'f'; else if (arg[1] == 'r') switchchar = 'f';
/* Make -ov synonymous with -v */ /* Make -ov synonymous with -v */
else if (Ustrcmp(arg, "-ov") == 0) else if (Ustrcmp(arg, "-ov") == 0)
{ {
skipping to change at line 1927 skipping to change at line 1910
case 'b': case 'b':
receiving_message = FALSE; /* Reset TRUE for -bm, -bS, -bs below */ receiving_message = FALSE; /* Reset TRUE for -bm, -bS, -bs below */
/* -bd: Run in daemon mode, awaiting SMTP connections. /* -bd: Run in daemon mode, awaiting SMTP connections.
-bdf: Ditto, but in the foreground. -bdf: Ditto, but in the foreground.
*/ */
if (*argrest == 'd') if (*argrest == 'd')
{ {
daemon_listen = TRUE; f.daemon_listen = TRUE;
if (*(++argrest) == 'f') background_daemon = FALSE; if (*(++argrest) == 'f') f.background_daemon = FALSE;
else if (*argrest != 0) { badarg = TRUE; break; } else if (*argrest != 0) { badarg = TRUE; break; }
} }
/* -be: Run in expansion test mode /* -be: Run in expansion test mode
-bem: Ditto, but read a message from a file first -bem: Ditto, but read a message from a file first
*/ */
else if (*argrest == 'e') else if (*argrest == 'e')
{ {
expansion_test = checking = TRUE; expansion_test = checking = TRUE;
skipping to change at line 1955 skipping to change at line 1938
if (argrest[1] != 0) { badarg = TRUE; break; } if (argrest[1] != 0) { badarg = TRUE; break; }
} }
/* -bF: Run system filter test */ /* -bF: Run system filter test */
else if (*argrest == 'F') else if (*argrest == 'F')
{ {
filter_test |= checking = FTEST_SYSTEM; filter_test |= checking = FTEST_SYSTEM;
if (*(++argrest) != 0) { badarg = TRUE; break; } if (*(++argrest) != 0) { badarg = TRUE; break; }
if (++i < argc) filter_test_sfile = argv[i]; else if (++i < argc) filter_test_sfile = argv[i]; else
{ exim_fail("exim: file name expected after %s\n", argv[i-1]);
fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
exit(EXIT_FAILURE);
}
} }
/* -bf: Run user filter test /* -bf: Run user filter test
-bfd: Set domain for filter testing -bfd: Set domain for filter testing
-bfl: Set local part for filter testing -bfl: Set local part for filter testing
-bfp: Set prefix for filter testing -bfp: Set prefix for filter testing
-bfs: Set suffix for filter testing -bfs: Set suffix for filter testing
*/ */
else if (*argrest == 'f') else if (*argrest == 'f')
{ {
if (*(++argrest) == 0) if (*(++argrest) == 0)
{ {
filter_test |= checking = FTEST_USER; filter_test |= checking = FTEST_USER;
if (++i < argc) filter_test_ufile = argv[i]; else if (++i < argc) filter_test_ufile = argv[i]; else
{ exim_fail("exim: file name expected after %s\n", argv[i-1]);
fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
exit(EXIT_FAILURE);
}
} }
else else
{ {
if (++i >= argc) if (++i >= argc)
{ exim_fail("exim: string expected after %s\n", arg);
fprintf(stderr, "exim: string expected after %s\n", arg);
exit(EXIT_FAILURE);
}
if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i]; if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i];
else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = 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, "p") == 0) ftest_prefix = argv[i];
else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i]; else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i];
else { badarg = TRUE; break; } else { badarg = TRUE; break; }
} }
} }
/* -bh: Host checking - an IP address must follow. */ /* -bh: Host checking - an IP address must follow. */
else if (Ustrcmp(argrest, "h") == 0 || Ustrcmp(argrest, "hc") == 0) else if (Ustrcmp(argrest, "h") == 0 || Ustrcmp(argrest, "hc") == 0)
{ {
if (++i >= argc) { badarg = TRUE; break; } if (++i >= argc) { badarg = TRUE; break; }
sender_host_address = argv[i]; sender_host_address = argv[i];
host_checking = checking = log_testing_mode = TRUE; host_checking = checking = f.log_testing_mode = TRUE;
host_checking_callout = argrest[1] == 'c'; f.host_checking_callout = argrest[1] == 'c';
message_logs = FALSE; message_logs = FALSE;
} }
/* -bi: This option is used by sendmail to initialize *the* alias file, /* -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 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 concept of *the* alias file, but since Sun's YP make script calls
sendmail this way, some support must be provided. */ sendmail this way, some support must be provided. */
else if (Ustrcmp(argrest, "i") == 0) bi_option = TRUE; else if (Ustrcmp(argrest, "i") == 0) bi_option = TRUE;
skipping to change at line 2058 skipping to change at line 2032
checking = TRUE; checking = TRUE;
malware_test_file = argv[i]; malware_test_file = argv[i];
} }
/* -bnq: For locally originating messages, do not qualify unqualified /* -bnq: For locally originating messages, do not qualify unqualified
addresses. In the envelope, this causes errors; in header lines they addresses. In the envelope, this causes errors; in header lines they
just get left. */ just get left. */
else if (Ustrcmp(argrest, "nq") == 0) else if (Ustrcmp(argrest, "nq") == 0)
{ {
allow_unqualified_sender = FALSE; f.allow_unqualified_sender = FALSE;
allow_unqualified_recipient = FALSE; f.allow_unqualified_recipient = FALSE;
} }
/* -bpxx: List the contents of the mail queue, in various forms. If /* -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 the option is -bpc, just a queue count is needed. Otherwise, if the
first letter after p is r, then order is random. */ first letter after p is r, then order is random. */
else if (*argrest == 'p') else if (*argrest == 'p')
{ {
if (*(++argrest) == 'c') if (*(++argrest) == 'c')
{ {
skipping to change at line 2157 skipping to change at line 2131
smtp_input = smtp_batched_input = receiving_message = TRUE; smtp_input = smtp_batched_input = receiving_message = TRUE;
/* -bs: Read SMTP commands on standard input and produce SMTP replies /* -bs: Read SMTP commands on standard input and produce SMTP replies
on standard output. */ on standard output. */
else if (Ustrcmp(argrest, "s") == 0) smtp_input = receiving_message = TRUE; else if (Ustrcmp(argrest, "s") == 0) smtp_input = receiving_message = TRUE;
/* -bt: address testing mode */ /* -bt: address testing mode */
else if (Ustrcmp(argrest, "t") == 0) else if (Ustrcmp(argrest, "t") == 0)
address_test_mode = checking = log_testing_mode = TRUE; f.address_test_mode = checking = f.log_testing_mode = TRUE;
/* -bv: verify addresses */ /* -bv: verify addresses */
else if (Ustrcmp(argrest, "v") == 0) else if (Ustrcmp(argrest, "v") == 0)
verify_address_mode = checking = log_testing_mode = TRUE; verify_address_mode = checking = f.log_testing_mode = TRUE;
/* -bvs: verify sender addresses */ /* -bvs: verify sender addresses */
else if (Ustrcmp(argrest, "vs") == 0) else if (Ustrcmp(argrest, "vs") == 0)
{ {
verify_address_mode = checking = log_testing_mode = TRUE; verify_address_mode = checking = f.log_testing_mode = TRUE;
verify_as_sender = TRUE; verify_as_sender = TRUE;
} }
/* -bV: Print version string and support details */ /* -bV: Print version string and support details */
else if (Ustrcmp(argrest, "V") == 0) else if (Ustrcmp(argrest, "V") == 0)
{ {
printf("Exim version %s #%s built %s\n", version_string, printf("Exim version %s #%s built %s\n", version_string,
version_cnumber, version_date); version_cnumber, version_date);
printf("%s\n", CS version_copyright); printf("%s\n", CS version_copyright);
version_printed = TRUE; version_printed = TRUE;
show_whats_supported(stdout); show_whats_supported(stdout);
log_testing_mode = TRUE; f.log_testing_mode = TRUE;
} }
/* -bw: inetd wait mode, accept a listening socket as stdin */ /* -bw: inetd wait mode, accept a listening socket as stdin */
else if (*argrest == 'w') else if (*argrest == 'w')
{ {
inetd_wait_mode = TRUE; f.inetd_wait_mode = TRUE;
background_daemon = FALSE; f.background_daemon = FALSE;
daemon_listen = TRUE; f.daemon_listen = TRUE;
if (*(++argrest) != '\0') if (*(++argrest) != '\0')
{ if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0)
inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE); exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
if (inetd_wait_timeout <= 0)
{
fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]);
exit(EXIT_FAILURE);
}
}
} }
else badarg = TRUE; else badarg = TRUE;
break; break;
/* -C: change configuration file list; ignore if it isn't really /* -C: change configuration file list; ignore if it isn't really
a change! Enforce a prefix check if required. */ a change! Enforce a prefix check if required. */
case 'C': case 'C':
if (*argrest == 0) if (*argrest == 0)
skipping to change at line 2228 skipping to change at line 2196
int len = Ustrlen(ALT_CONFIG_PREFIX); int len = Ustrlen(ALT_CONFIG_PREFIX);
const uschar *list = argrest; const uschar *list = argrest;
uschar *filename; uschar *filename;
while((filename = string_nextinlist(&list, &sep, big_buffer, while((filename = string_nextinlist(&list, &sep, big_buffer,
big_buffer_size)) != NULL) big_buffer_size)) != NULL)
{ {
if ((Ustrlen(filename) < len || if ((Ustrlen(filename) < len ||
Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 || Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 ||
Ustrstr(filename, "/../") != NULL) && Ustrstr(filename, "/../") != NULL) &&
(Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid)) (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid))
{ exim_fail("-C Permission denied\n");
fprintf(stderr, "-C Permission denied\n");
exit(EXIT_FAILURE);
}
} }
#endif #endif
if (real_uid != root_uid) if (real_uid != root_uid)
{ {
#ifdef TRUSTED_CONFIG_LIST #ifdef TRUSTED_CONFIG_LIST
if (real_uid != exim_uid if (real_uid != exim_uid
#ifdef CONFIGURE_OWNER #ifdef CONFIGURE_OWNER
&& real_uid != config_uid && real_uid != config_uid
#endif #endif
) )
trusted_config = FALSE; f.trusted_config = FALSE;
else else
{ {
FILE *trust_list = Ufopen(TRUSTED_CONFIG_LIST, "rb"); FILE *trust_list = Ufopen(TRUSTED_CONFIG_LIST, "rb");
if (trust_list) if (trust_list)
{ {
struct stat statbuf; struct stat statbuf;
if (fstat(fileno(trust_list), &statbuf) != 0 || if (fstat(fileno(trust_list), &statbuf) != 0 ||
(statbuf.st_uid != root_uid /* owner not root */ (statbuf.st_uid != root_uid /* owner not root */
#ifdef CONFIGURE_OWNER #ifdef CONFIGURE_OWNER
skipping to change at line 2265 skipping to change at line 2230
#endif #endif
) || /* or */ ) || /* or */
(statbuf.st_gid != root_gid /* group not root */ (statbuf.st_gid != root_gid /* group not root */
#ifdef CONFIGURE_GROUP #ifdef CONFIGURE_GROUP
&& statbuf.st_gid != config_gid /* group not the special one */ && statbuf.st_gid != config_gid /* group not the special one */
#endif #endif
&& (statbuf.st_mode & 020) != 0 /* group writeable */ && (statbuf.st_mode & 020) != 0 /* group writeable */
) || /* or */ ) || /* or */
(statbuf.st_mode & 2) != 0) /* world writeable */ (statbuf.st_mode & 2) != 0) /* world writeable */
{ {
trusted_config = FALSE; f.trusted_config = FALSE;
fclose(trust_list); fclose(trust_list);
} }
else else
{ {
/* Well, the trust list at least is up to scratch... */ /* Well, the trust list at least is up to scratch... */
void *reset_point = store_get(0); void *reset_point = store_get(0);
uschar *trusted_configs[32]; uschar *trusted_configs[32];
int nr_configs = 0; int nr_configs = 0;
int i = 0; int i = 0;
skipping to change at line 2297 skipping to change at line 2262
if (nr_configs == 32) if (nr_configs == 32)
break; break;
} }
fclose(trust_list); fclose(trust_list);
if (nr_configs) if (nr_configs)
{ {
int sep = 0; int sep = 0;
const uschar *list = argrest; const uschar *list = argrest;
uschar *filename; uschar *filename;
while (trusted_config && (filename = string_nextinlist(&list, while (f.trusted_config && (filename = string_nextinlist(&list,
&sep, big_buffer, big_buffer_size)) != NULL) &sep, big_buffer, big_buffer_size)) != NULL)
{ {
for (i=0; i < nr_configs; i++) for (i=0; i < nr_configs; i++)
{ {
if (Ustrcmp(filename, trusted_configs[i]) == 0) if (Ustrcmp(filename, trusted_configs[i]) == 0)
break; break;
} }
if (i == nr_configs) if (i == nr_configs)
{ {
trusted_config = FALSE; f.trusted_config = FALSE;
break; break;
} }
} }
store_reset(reset_point); store_reset(reset_point);
} }
else else
{ {
/* No valid prefixes found in trust_list file. */ /* No valid prefixes found in trust_list file. */
trusted_config = FALSE; f.trusted_config = FALSE;
} }
} }
} }
else else
{ {
/* Could not open trust_list file. */ /* Could not open trust_list file. */
trusted_config = FALSE; f.trusted_config = FALSE;
} }
} }
#else #else
/* Not root; don't trust config */ /* Not root; don't trust config */
trusted_config = FALSE; f.trusted_config = FALSE;
#endif #endif
} }
config_main_filelist = argrest; config_main_filelist = argrest;
config_changed = TRUE; f.config_changed = TRUE;
} }
break; break;
/* -D: set up a macro definition */ /* -D: set up a macro definition */
case 'D': case 'D':
#ifdef DISABLE_D_OPTION #ifdef DISABLE_D_OPTION
fprintf(stderr, "exim: -D is not available in this Exim binary\n"); exim_fail("exim: -D is not available in this Exim binary\n");
exit(EXIT_FAILURE); #else
#else
{ {
int ptr = 0; int ptr = 0;
macro_item *m; macro_item *m;
uschar name[24]; uschar name[24];
uschar *s = argrest; uschar *s = argrest;
opt_D_used = TRUE; opt_D_used = TRUE;
while (isspace(*s)) s++; while (isspace(*s)) s++;
if (*s < 'A' || *s > 'Z') if (*s < 'A' || *s > 'Z')
{ exim_fail("exim: macro name set by -D must start with "
fprintf(stderr, "exim: macro name set by -D must start with "
"an upper case letter\n"); "an upper case letter\n");
exit(EXIT_FAILURE);
}
while (isalnum(*s) || *s == '_') while (isalnum(*s) || *s == '_')
{ {
if (ptr < sizeof(name)-1) name[ptr++] = *s; if (ptr < sizeof(name)-1) name[ptr++] = *s;
s++; s++;
} }
name[ptr] = 0; name[ptr] = 0;
if (ptr == 0) { badarg = TRUE; break; } if (ptr == 0) { badarg = TRUE; break; }
while (isspace(*s)) s++; while (isspace(*s)) s++;
if (*s != 0) if (*s != 0)
{ {
if (*s++ != '=') { badarg = TRUE; break; } if (*s++ != '=') { badarg = TRUE; break; }
while (isspace(*s)) s++; while (isspace(*s)) s++;
} }
for (m = macros_user; m; m = m->next) for (m = macros_user; m; m = m->next)
if (Ustrcmp(m->name, name) == 0) if (Ustrcmp(m->name, name) == 0)
{ exim_fail("exim: duplicated -D in command line\n");
fprintf(stderr, "exim: duplicated -D in command line\n");
exit(EXIT_FAILURE);
}
m = macro_create(name, s, TRUE); m = macro_create(name, s, TRUE);
if (clmacro_count >= MAX_CLMACROS) if (clmacro_count >= MAX_CLMACROS)
{ exim_fail("exim: too many -D options on command line\n");
fprintf(stderr, "exim: too many -D options on command line\n");
exit(EXIT_FAILURE);
}
clmacros[clmacro_count++] = string_sprintf("-D%s=%s", m->name, clmacros[clmacro_count++] = string_sprintf("-D%s=%s", m->name,
m->replacement); m->replacement);
} }
#endif #endif
break; break;
/* -d: Set debug level (see also -v below) or set the drop_cr option. /* -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, The latter is now a no-op, retained for compatibility only. If -dd is used,
debugging subprocesses of the daemon is disabled. */ debugging subprocesses of the daemon is disabled. */
skipping to change at line 2414 skipping to change at line 2369
/* Use an intermediate variable so that we don't set debugging while /* Use an intermediate variable so that we don't set debugging while
decoding the debugging bits. */ decoding the debugging bits. */
else else
{ {
unsigned int selector = D_default; unsigned int selector = D_default;
debug_selector = 0; debug_selector = 0;
debug_file = NULL; debug_file = NULL;
if (*argrest == 'd') if (*argrest == 'd')
{ {
debug_daemon = TRUE; f.debug_daemon = TRUE;
argrest++; argrest++;
} }
if (*argrest != 0) if (*argrest != 0)
decode_bits(&selector, 1, debug_notall, argrest, decode_bits(&selector, 1, debug_notall, argrest,
debug_options, debug_options_count, US"debug", 0); debug_options, debug_options_count, US"debug", 0);
debug_selector = selector; debug_selector = selector;
} }
break; break;
/* -E: This is a local error message. This option is not intended for /* -E: This is a local error message. This option is not intended for
external use at all, but is not restricted to trusted callers because it external use at all, but is not restricted to trusted callers because it
does no harm (just suppresses certain error messages) and if Exim is run does no harm (just suppresses certain error messages) and if Exim is run
not setuid root it won't always be trusted when it generates error not setuid root it won't always be trusted when it generates error
messages using this option. If there is a message id following -E, point messages using this option. If there is a message id following -E, point
message_reference at it, for logging. */ message_reference at it, for logging. */
case 'E': case 'E':
local_error_message = TRUE; f.local_error_message = TRUE;
if (mac_ismsgid(argrest)) message_reference = argrest; if (mac_ismsgid(argrest)) message_reference = argrest;
break; break;
/* -ex: The vacation program calls sendmail with the undocumented "-eq" /* -ex: The vacation program calls sendmail with the undocumented "-eq"
option, so it looks as if historically the -oex options are also callable option, so it looks as if historically the -oex options are also callable
without the leading -o. So we have to accept them. Before the switch, without the leading -o. So we have to accept them. Before the switch,
anything starting -oe has been converted to -e. Exim does not support all anything starting -oe has been converted to -e. Exim does not support all
of the sendmail error options. */ of the sendmail error options. */
case 'e': case 'e':
skipping to change at line 2467 skipping to change at line 2422
there's no security involved in using this instead. The data can follow there's no security involved in using this instead. The data can follow
the -F or be in the next argument. */ the -F or be in the next argument. */
case 'F': case 'F':
if (*argrest == 0) if (*argrest == 0)
{ {
if(++i < argc) argrest = argv[i]; else if(++i < argc) argrest = argv[i]; else
{ badarg = TRUE; break; } { badarg = TRUE; break; }
} }
originator_name = argrest; originator_name = argrest;
sender_name_forced = TRUE; f.sender_name_forced = TRUE;
break; break;
/* -f: Set sender's address - this value is only actually used if Exim is /* -f: Set sender's address - this value is only actually used if Exim is
run by a trusted user, or if untrusted_set_sender is set and matches the run by a trusted user, or if untrusted_set_sender is set and matches the
address, except that the null address can always be set by any user. The address, except that the null address can always be set by any user. The
test for this happens later, when the value given here is ignored when not test for this happens later, when the value given here is ignored when not
permitted. For an untrusted user, the actual sender is still put in Sender: permitted. For an untrusted user, the actual sender is still put in Sender:
if it doesn't match the From: header (unless no_local_from_check is set). if it doesn't match the From: header (unless no_local_from_check is set).
The data can follow the -f or be in the next argument. The -r switch is an The data can follow the -f or be in the next argument. The -r switch is an
obsolete form of -f but since there appear to be programs out there that obsolete form of -f but since there appear to be programs out there that
skipping to change at line 2514 skipping to change at line 2469
allow_utf8_domains = TRUE; allow_utf8_domains = TRUE;
#endif #endif
sender_address = parse_extract_address(argrest, &errmess, sender_address = parse_extract_address(argrest, &errmess,
&dummy_start, &dummy_end, &sender_address_domain, TRUE); &dummy_start, &dummy_end, &sender_address_domain, TRUE);
#ifdef SUPPORT_I18N #ifdef SUPPORT_I18N
message_smtputf8 = string_is_utf8(sender_address); message_smtputf8 = string_is_utf8(sender_address);
allow_utf8_domains = FALSE; allow_utf8_domains = FALSE;
#endif #endif
allow_domain_literals = FALSE; allow_domain_literals = FALSE;
strip_trailing_dot = FALSE; strip_trailing_dot = FALSE;
if (sender_address == NULL) if (!sender_address)
{ exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess);
fprintf(stderr, "exim: bad -f address \"%s\": %s\n", argrest, errmess)
;
return EXIT_FAILURE;
}
} }
sender_address_forced = TRUE; f.sender_address_forced = TRUE;
} }
break; break;
/* -G: sendmail invocation to specify that it's a gateway submission and /* -G: sendmail invocation to specify that it's a gateway submission and
sendmail may complain about problems instead of fixing them. sendmail may complain about problems instead of fixing them.
We make it equivalent to an ACL "control = suppress_local_fixups" and do We make it equivalent to an ACL "control = suppress_local_fixups" and do
not at this time complain about problems. */ not at this time complain about problems. */
case 'G': case 'G':
flag_G = TRUE; flag_G = TRUE;
skipping to change at line 2550 skipping to change at line 2502
if(++i < argc) argrest = argv[i]; else if(++i < argc) argrest = argv[i]; else
{ badarg = TRUE; break; } { badarg = TRUE; break; }
} }
if (!isdigit(*argrest)) badarg = TRUE; if (!isdigit(*argrest)) badarg = TRUE;
break; break;
/* -i: Set flag so dot doesn't end non-SMTP input (same as -oi, seems /* -i: Set flag so dot doesn't end non-SMTP input (same as -oi, seems
not to be documented for sendmail but mailx (at least) uses it) */ not to be documented for sendmail but mailx (at least) uses it) */
case 'i': case 'i':
if (*argrest == 0) dot_ends = FALSE; else badarg = TRUE; if (*argrest == 0) f.dot_ends = FALSE; else badarg = TRUE;
break; break;
/* -L: set the identifier used for syslog; equivalent to setting /* -L: set the identifier used for syslog; equivalent to setting
syslog_processname in the config file, but needs to be an admin option. */ syslog_processname in the config file, but needs to be an admin option. */
case 'L': case 'L':
if (*argrest == '\0') if (*argrest == '\0')
{ {
if(++i < argc) argrest = argv[i]; else if(++i < argc) argrest = argv[i]; else
{ badarg = TRUE; break; } { badarg = TRUE; break; }
} }
sz = Ustrlen(argrest); if ((sz = Ustrlen(argrest)) > 32)
if (sz > 32) exim_fail("exim: the -L syslog name is too long: \"%s\"\n", argrest);
{
fprintf(stderr, "exim: the -L syslog name is too long: \"%s\"\n", argrest)
;
return EXIT_FAILURE;
}
if (sz < 1) if (sz < 1)
{ exim_fail("exim: the -L syslog name is too short\n");
fprintf(stderr, "exim: the -L syslog name is too short\n");
return EXIT_FAILURE;
}
cmdline_syslog_name = argrest; cmdline_syslog_name = argrest;
break; break;
case 'M': case 'M':
receiving_message = FALSE; receiving_message = FALSE;
/* -MC: continue delivery of another message via an existing open /* -MC: continue delivery of another message via an existing open
file descriptor. This option is used for an internal call by the file descriptor. This option is used for an internal call by the
smtp transport when there is a pending message waiting to go to an smtp transport when there is a pending message waiting to go to an
address to which it has got a connection. Five subsequent arguments are address to which it has got a connection. Five subsequent arguments are
skipping to change at line 2598 skipping to change at line 2543
If we are running in the test harness. delay for a bit, to let the process If we are running in the test harness. delay for a bit, to let the process
that set this one up complete. This makes for repeatability of the logging, that set this one up complete. This makes for repeatability of the logging,
etc. output. */ etc. output. */
if (Ustrcmp(argrest, "C") == 0) if (Ustrcmp(argrest, "C") == 0)
{ {
union sockaddr_46 interface_sock; union sockaddr_46 interface_sock;
EXIM_SOCKLEN_T size = sizeof(interface_sock); EXIM_SOCKLEN_T size = sizeof(interface_sock);
if (argc != i + 6) if (argc != i + 6)
{ exim_fail("exim: too many or too few arguments after -MC\n");
fprintf(stderr, "exim: too many or too few arguments after -MC\n");
return EXIT_FAILURE;
}
if (msg_action_arg >= 0) if (msg_action_arg >= 0)
{ exim_fail("exim: incompatible arguments\n");
fprintf(stderr, "exim: incompatible arguments\n");
return EXIT_FAILURE;
}
continue_transport = argv[++i]; continue_transport = argv[++i];
continue_hostname = argv[++i]; continue_hostname = argv[++i];
continue_host_address = argv[++i]; continue_host_address = argv[++i];
continue_sequence = Uatoi(argv[++i]); continue_sequence = Uatoi(argv[++i]);
msg_action = MSG_DELIVER; msg_action = MSG_DELIVER;
msg_action_arg = ++i; msg_action_arg = ++i;
forced_delivery = TRUE; forced_delivery = TRUE;
queue_run_pid = passed_qr_pid; queue_run_pid = passed_qr_pid;
queue_run_pipe = passed_qr_pipe; queue_run_pipe = passed_qr_pipe;
if (!mac_ismsgid(argv[i])) if (!mac_ismsgid(argv[i]))
{ exim_fail("exim: malformed message id %s after -MC option\n",
fprintf(stderr, "exim: malformed message id %s after -MC option\n",
argv[i]); argv[i]);
return EXIT_FAILURE;
}
/* Set up $sending_ip_address and $sending_port, unless proxied */ /* Set up $sending_ip_address and $sending_port, unless proxied */
if (!continue_proxy_cipher) if (!continue_proxy_cipher)
if (getsockname(fileno(stdin), (struct sockaddr *)(&interface_sock), if (getsockname(fileno(stdin), (struct sockaddr *)(&interface_sock),
&size) == 0) &size) == 0)
sending_ip_address = host_ntoa(-1, &interface_sock, NULL, sending_ip_address = host_ntoa(-1, &interface_sock, NULL,
&sending_port); &sending_port);
else else
{ exim_fail("exim: getsockname() failed after -MC option: %s\n",
fprintf(stderr, "exim: getsockname() failed after -MC option: %s\n",
strerror(errno)); strerror(errno));
return EXIT_FAILURE;
}
if (running_in_test_harness) millisleep(500); if (f.running_in_test_harness) millisleep(500);
break; break;
} }
else if (*argrest == 'C' && argrest[1] && !argrest[2]) else if (*argrest == 'C' && argrest[1] && !argrest[2])
{ {
switch(argrest[1]) switch(argrest[1])
{ {
/* -MCA: set the smtp_authenticated flag; this is useful only when it /* -MCA: set the smtp_authenticated flag; this is useful only when it
precedes -MC (see above). The flag indicates that the host to which precedes -MC (see above). The flag indicates that the host to which
Exim is connected has accepted an AUTH sequence. */ Exim is connected has accepted an AUTH sequence. */
case 'A': smtp_authenticated = TRUE; break; case 'A': f.smtp_authenticated = TRUE; break;
/* -MCD: set the smtp_use_dsn flag; this indicates that the host /* -MCD: set the smtp_use_dsn flag; this indicates that the host
that exim is connected to supports the esmtp extension DSN */ that exim is connected to supports the esmtp extension DSN */
case 'D': smtp_peer_options |= OPTION_DSN; break; case 'D': smtp_peer_options |= OPTION_DSN; break;
/* -MCG: set the queue name, to a non-default value */ /* -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(argv[i]);
else badarg = TRUE; else badarg = TRUE;
skipping to change at line 2691 skipping to change at line 2624
else badarg = TRUE; else badarg = TRUE;
break; break;
/* -MCS: set the smtp_use_size flag; this is useful only when it /* -MCS: set the smtp_use_size flag; this is useful only when it
precedes -MC (see above) */ precedes -MC (see above) */
case 'S': smtp_peer_options |= OPTION_SIZE; break; case 'S': smtp_peer_options |= OPTION_SIZE; break;
#ifdef SUPPORT_TLS #ifdef SUPPORT_TLS
/* -MCt: similar to -MCT below but the connection is still open /* -MCt: similar to -MCT below but the connection is still open
via a proxy proces which handles the TLS context and coding. via a proxy process which handles the TLS context and coding.
Require three arguments for the proxied local address and port, Require three arguments for the proxied local address and port,
and the TLS cipher. */ and the TLS cipher. */
case 't': if (++i < argc) sending_ip_address = argv[i]; case 't': if (++i < argc) sending_ip_address = argv[i];
else badarg = 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; else badarg = TRUE;
if (++i < argc) continue_proxy_cipher = argv[i]; if (++i < argc) continue_proxy_cipher = argv[i];
else badarg = TRUE; else badarg = TRUE;
/*FALLTHROUGH*/ /*FALLTHROUGH*/
/* -MCT: set the tls_offered flag; this is useful only when it /* -MCT: set the tls_offered flag; this is useful only when it
precedes -MC (see above). The flag indicates that the host to which precedes -MC (see above). The flag indicates that the host to which
Exim is connected has offered TLS support. */ Exim is connected has offered TLS support. */
case 'T': smtp_peer_options |= OPTION_TLS; break; case 'T': smtp_peer_options |= OPTION_TLS; break;
#endif #endif
default: badarg = TRUE; break; default: badarg = TRUE; break;
} }
break; break;
}
#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
/* -MS set REQUIRETLS on (new) message */
else if (*argrest == 'S')
{
tls_requiretls |= REQUIRETLS_MSG;
break;
} }
#endif
/* -M[x]: various operations on the following list of message ids: /* -M[x]: various operations on the following list of message ids:
-M deliver the messages, ignoring next retry times and thawing -M deliver the messages, ignoring next retry times and thawing
-Mc deliver the messages, checking next retry times, no thawing -Mc deliver the messages, checking next retry times, no thawing
-Mf freeze the messages -Mf freeze the messages
-Mg give up on the messages -Mg give up on the messages
-Mt thaw the messages -Mt thaw the messages
-Mrm remove the messages -Mrm remove the messages
In the above cases, this must be the last option. There are also the In the above cases, this must be the last option. There are also the
following options which are followed by a single message id, and which following options which are followed by a single message id, and which
skipping to change at line 2739 skipping to change at line 2682
-Mset load a message for use with -be -Mset load a message for use with -be
-Mvb show body -Mvb show body
-Mvc show copy (of whole message, in RFC 2822 format) -Mvc show copy (of whole message, in RFC 2822 format)
-Mvh show header -Mvh show header
-Mvl show log -Mvl show log
*/ */
else if (*argrest == 0) else if (*argrest == 0)
{ {
msg_action = MSG_DELIVER; msg_action = MSG_DELIVER;
forced_delivery = deliver_force_thaw = TRUE; forced_delivery = f.deliver_force_thaw = TRUE;
} }
else if (Ustrcmp(argrest, "ar") == 0) else if (Ustrcmp(argrest, "ar") == 0)
{ {
msg_action = MSG_ADD_RECIPIENT; msg_action = MSG_ADD_RECIPIENT;
one_msg_action = TRUE; one_msg_action = TRUE;
} }
else if (Ustrcmp(argrest, "c") == 0) msg_action = MSG_DELIVER; else if (Ustrcmp(argrest, "c") == 0) msg_action = MSG_DELIVER;
else if (Ustrcmp(argrest, "es") == 0) else if (Ustrcmp(argrest, "es") == 0)
{ {
msg_action = MSG_EDIT_SENDER; msg_action = MSG_EDIT_SENDER;
skipping to change at line 2800 skipping to change at line 2743
{ {
msg_action = MSG_SHOW_LOG; msg_action = MSG_SHOW_LOG;
one_msg_action = TRUE; one_msg_action = TRUE;
} }
else { badarg = TRUE; break; } else { badarg = TRUE; break; }
/* All the -Mxx options require at least one message id. */ /* All the -Mxx options require at least one message id. */
msg_action_arg = i + 1; msg_action_arg = i + 1;
if (msg_action_arg >= argc) if (msg_action_arg >= argc)
{ exim_fail("exim: no message ids given after %s option\n", arg);
fprintf(stderr, "exim: no message ids given after %s option\n", arg);
return EXIT_FAILURE;
}
/* Some require only message ids to follow */ /* Some require only message ids to follow */
if (!one_msg_action) if (!one_msg_action)
{ {
int j; int j;
for (j = msg_action_arg; j < argc; j++) if (!mac_ismsgid(argv[j])) for (j = msg_action_arg; j < argc; j++) if (!mac_ismsgid(argv[j]))
{ exim_fail("exim: malformed message id %s after %s option\n",
fprintf(stderr, "exim: malformed message id %s after %s option\n",
argv[j], arg); argv[j], arg);
return EXIT_FAILURE;
}
goto END_ARG; /* Remaining args are ids */ goto END_ARG; /* Remaining args are ids */
} }
/* Others require only one message id, possibly followed by addresses, /* Others require only one message id, possibly followed by addresses,
which will be handled as normal arguments. */ which will be handled as normal arguments. */
else else
{ {
if (!mac_ismsgid(argv[msg_action_arg])) if (!mac_ismsgid(argv[msg_action_arg]))
{ exim_fail("exim: malformed message id %s after %s option\n",
fprintf(stderr, "exim: malformed message id %s after %s option\n",
argv[msg_action_arg], arg); argv[msg_action_arg], arg);
return EXIT_FAILURE;
}
i++; i++;
} }
break; break;
/* Some programs seem to call the -om option without the leading o; /* Some programs seem to call the -om option without the leading o;
for sendmail it askes for "me too". Exim always does this. */ for sendmail it askes for "me too". Exim always does this. */
case 'm': case 'm':
if (*argrest != 0) badarg = TRUE; if (*argrest != 0) badarg = TRUE;
break; break;
/* -N: don't do delivery - a debugging option that stops transports doing /* -N: don't do delivery - a debugging option that stops transports doing
their thing. It implies debugging at the D_v level. */ their thing. It implies debugging at the D_v level. */
case 'N': case 'N':
if (*argrest == 0) if (*argrest == 0)
{ {
dont_deliver = TRUE; f.dont_deliver = TRUE;
debug_selector |= D_v; debug_selector |= D_v;
debug_file = stderr; debug_file = stderr;
} }
else badarg = TRUE; else badarg = TRUE;
break; break;
/* -n: This means "don't alias" in sendmail, apparently. /* -n: This means "don't alias" in sendmail, apparently.
For normal invocations, it has no effect. For normal invocations, it has no effect.
It may affect some other options. */ It may affect some other options. */
skipping to change at line 2870 skipping to change at line 2804
break; break;
/* -O: Just ignore it. In sendmail, apparently -O option=value means set /* -O: Just ignore it. In sendmail, apparently -O option=value means set
option to the specified value. This form uses long names. We need to handle option to the specified value. This form uses long names. We need to handle
-O option=value and -Ooption=value. */ -O option=value and -Ooption=value. */
case 'O': case 'O':
if (*argrest == 0) if (*argrest == 0)
{ {
if (++i >= argc) if (++i >= argc)
{ exim_fail("exim: string expected after -O\n");
fprintf(stderr, "exim: string expected after -O\n");
exit(EXIT_FAILURE);
}
} }
break; break;
case 'o': case 'o':
/* -oA: Set an argument for the bi command (sendmail's "alternate alias /* -oA: Set an argument for the bi command (sendmail's "alternate alias
file" option). */ file" option). */
if (*argrest == 'A') if (*argrest == 'A')
{ {
alias_arg = argrest + 1; alias_arg = argrest + 1;
if (alias_arg[0] == 0) if (alias_arg[0] == 0)
{ {
if (i+1 < argc) alias_arg = argv[++i]; else if (i+1 < argc) alias_arg = argv[++i]; else
{ exim_fail("exim: string expected after -oA\n");
fprintf(stderr, "exim: string expected after -oA\n");
exit(EXIT_FAILURE);
}
} }
} }
/* -oB: Set a connection message max value for remote deliveries */ /* -oB: Set a connection message max value for remote deliveries */
else if (*argrest == 'B') else if (*argrest == 'B')
{ {
uschar *p = argrest + 1; uschar *p = argrest + 1;
if (p[0] == 0) if (p[0] == 0)
{ {
if (i+1 < argc && isdigit((argv[i+1][0]))) p = argv[++i]; else if (i+1 < argc && isdigit((argv[i+1][0]))) p = argv[++i]; else
{ {
connection_max_messages = 1; connection_max_messages = 1;
p = NULL; p = NULL;
} }
} }
if (p != NULL) if (p != NULL)
{ {
if (!isdigit(*p)) if (!isdigit(*p))
{ exim_fail("exim: number expected after -oB\n");
fprintf(stderr, "exim: number expected after -oB\n");
exit(EXIT_FAILURE);
}
connection_max_messages = Uatoi(p); connection_max_messages = Uatoi(p);
} }
} }
/* -odb: background delivery */ /* -odb: background delivery */
else if (Ustrcmp(argrest, "db") == 0) else if (Ustrcmp(argrest, "db") == 0)
{ {
synchronous_delivery = FALSE; f.synchronous_delivery = FALSE;
arg_queue_only = FALSE; arg_queue_only = FALSE;
queue_only_set = TRUE; queue_only_set = TRUE;
} }
/* -odf: foreground delivery (smail-compatible option); same effect as /* -odf: foreground delivery (smail-compatible option); same effect as
-odi: interactive (synchronous) delivery (sendmail-compatible option) -odi: interactive (synchronous) delivery (sendmail-compatible option)
*/ */
else if (Ustrcmp(argrest, "df") == 0 || Ustrcmp(argrest, "di") == 0) else if (Ustrcmp(argrest, "df") == 0 || Ustrcmp(argrest, "di") == 0)
{ {
synchronous_delivery = TRUE; f.synchronous_delivery = TRUE;
arg_queue_only = FALSE; arg_queue_only = FALSE;
queue_only_set = TRUE; queue_only_set = TRUE;
} }
/* -odq: queue only */ /* -odq: queue only */
else if (Ustrcmp(argrest, "dq") == 0) else if (Ustrcmp(argrest, "dq") == 0)
{ {
synchronous_delivery = FALSE; f.synchronous_delivery = FALSE;
arg_queue_only = TRUE; arg_queue_only = TRUE;
queue_only_set = TRUE; queue_only_set = TRUE;
} }
/* -odqs: queue SMTP only - do local deliveries and remote routing, /* -odqs: queue SMTP only - do local deliveries and remote routing,
but no remote delivery */ but no remote delivery */
else if (Ustrcmp(argrest, "dqs") == 0) else if (Ustrcmp(argrest, "dqs") == 0)
{ {
queue_smtp = TRUE; f.queue_smtp = TRUE;
arg_queue_only = FALSE; arg_queue_only = FALSE;
queue_only_set = TRUE; queue_only_set = TRUE;
} }
/* -oex: Sendmail error flags. As these are also accepted without the /* -oex: Sendmail error flags. As these are also accepted without the
leading -o prefix, for compatibility with vacation and other callers, leading -o prefix, for compatibility with vacation and other callers,
they are handled with -e above. */ they are handled with -e above. */
/* -oi: Set flag so dot doesn't end non-SMTP input (same as -i) /* -oi: Set flag so dot doesn't end non-SMTP input (same as -i)
-oitrue: Another sendmail syntax for the same */ -oitrue: Another sendmail syntax for the same */
else if (Ustrcmp(argrest, "i") == 0 || else if (Ustrcmp(argrest, "i") == 0 ||
Ustrcmp(argrest, "itrue") == 0) Ustrcmp(argrest, "itrue") == 0)
dot_ends = FALSE; f.dot_ends = FALSE;
/* -oM*: Set various characteristics for an incoming message; actually /* -oM*: Set various characteristics for an incoming message; actually
acted on for trusted callers only. */ acted on for trusted callers only. */
else if (*argrest == 'M') else if (*argrest == 'M')
{ {
if (i+1 >= argc) if (i+1 >= argc)
{ exim_fail("exim: data expected after -o%s\n", argrest);
fprintf(stderr, "exim: data expected after -o%s\n", argrest);
exit(EXIT_FAILURE);
}
/* -oMa: Set sender host address */ /* -oMa: Set sender host address */
if (Ustrcmp(argrest, "Ma") == 0) sender_host_address = argv[++i]; if (Ustrcmp(argrest, "Ma") == 0) sender_host_address = argv[++i];
/* -oMaa: Set authenticator name */ /* -oMaa: Set authenticator name */
else if (Ustrcmp(argrest, "Maa") == 0) else if (Ustrcmp(argrest, "Maa") == 0)
sender_host_authenticated = argv[++i]; sender_host_authenticated = argv[++i];
skipping to change at line 3007 skipping to change at line 2929
/* -oMi: Set incoming interface address */ /* -oMi: Set incoming interface address */
else if (Ustrcmp(argrest, "Mi") == 0) interface_address = argv[++i]; else if (Ustrcmp(argrest, "Mi") == 0) interface_address = argv[++i];
/* -oMm: Message reference */ /* -oMm: Message reference */
else if (Ustrcmp(argrest, "Mm") == 0) else if (Ustrcmp(argrest, "Mm") == 0)
{ {
if (!mac_ismsgid(argv[i+1])) if (!mac_ismsgid(argv[i+1]))
{ exim_fail("-oMm must be a valid message ID\n");
fprintf(stderr,"-oMm must be a valid message ID\n"); if (!f.trusted_config)
exit(EXIT_FAILURE); exim_fail("-oMm must be called by a trusted user/config\n");
}
if (!trusted_config)
{
fprintf(stderr,"-oMm must be called by a trusted user/config\n");
exit(EXIT_FAILURE);
}
message_reference = argv[++i]; message_reference = argv[++i];
} }
/* -oMr: Received protocol */ /* -oMr: Received protocol */
else if (Ustrcmp(argrest, "Mr") == 0) else if (Ustrcmp(argrest, "Mr") == 0)
if (received_protocol) if (received_protocol)
{ exim_fail("received_protocol is set already\n");
fprintf(stderr, "received_protocol is set already\n"); else
exit(EXIT_FAILURE); received_protocol = argv[++i];
}
else received_protocol = argv[++i];
/* -oMs: Set sender host name */ /* -oMs: Set sender host name */
else if (Ustrcmp(argrest, "Ms") == 0) sender_host_name = argv[++i]; else if (Ustrcmp(argrest, "Ms") == 0) sender_host_name = argv[++i];
/* -oMt: Set sender ident */ /* -oMt: Set sender ident */
else if (Ustrcmp(argrest, "Mt") == 0) else if (Ustrcmp(argrest, "Mt") == 0)
{ {
sender_ident_set = TRUE; sender_ident_set = TRUE;
skipping to change at line 3080 skipping to change at line 2994
else if (*argrest == 'r' || *argrest == 's') else if (*argrest == 'r' || *argrest == 's')
{ {
int *tp = (*argrest == 'r')? int *tp = (*argrest == 'r')?
&arg_receive_timeout : &arg_smtp_receive_timeout; &arg_receive_timeout : &arg_smtp_receive_timeout;
if (argrest[1] == 0) if (argrest[1] == 0)
{ {
if (i+1 < argc) *tp= readconf_readtime(argv[++i], 0, FALSE); if (i+1 < argc) *tp= readconf_readtime(argv[++i], 0, FALSE);
} }
else *tp = readconf_readtime(argrest + 1, 0, FALSE); else *tp = readconf_readtime(argrest + 1, 0, FALSE);
if (*tp < 0) if (*tp < 0)
{ exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]);
exit(EXIT_FAILURE);
}
} }
/* -oX <list>: Override local_interfaces and/or default daemon ports */ /* -oX <list>: Override local_interfaces and/or default daemon ports */
else if (Ustrcmp(argrest, "X") == 0) else if (Ustrcmp(argrest, "X") == 0)
override_local_interfaces = argv[++i]; override_local_interfaces = argv[++i];
/* Unknown -o argument */ /* Unknown -o argument */
else badarg = TRUE; else badarg = TRUE;
skipping to change at line 3126 skipping to change at line 3037
if (i+1 < argc) if (i+1 < argc)
argrest = argv[++i]; argrest = argv[++i];
else else
{ badarg = TRUE; break; } { badarg = TRUE; break; }
if (*argrest != 0) if (*argrest != 0)
{ {
uschar *hn; uschar *hn;
if (received_protocol) if (received_protocol)
{ exim_fail("received_protocol is set already\n");
fprintf(stderr, "received_protocol is set already\n");
exit(EXIT_FAILURE);
}
hn = Ustrchr(argrest, ':'); hn = Ustrchr(argrest, ':');
if (hn == NULL) if (hn == NULL)
received_protocol = argrest; received_protocol = argrest;
else else
{ {
int old_pool = store_pool; int old_pool = store_pool;
store_pool = POOL_PERM; store_pool = POOL_PERM;
received_protocol = string_copyn(argrest, hn - argrest); received_protocol = string_copyn(argrest, hn - argrest);
store_pool = old_pool; store_pool = old_pool;
sender_host_name = hn + 1; sender_host_name = hn + 1;
} }
} }
break; break;
case 'q': case 'q':
receiving_message = FALSE; receiving_message = FALSE;
if (queue_interval >= 0) if (queue_interval >= 0)
{ exim_fail("exim: -q specified more than once\n");
fprintf(stderr, "exim: -q specified more than once\n");
exit(EXIT_FAILURE);
}
/* -qq...: Do queue runs in a 2-stage manner */ /* -qq...: Do queue runs in a 2-stage manner */
if (*argrest == 'q') if (*argrest == 'q')
{ {
queue_2stage = TRUE; f.queue_2stage = TRUE;
argrest++; argrest++;
} }
/* -qi...: Do only first (initial) deliveries */ /* -qi...: Do only first (initial) deliveries */
if (*argrest == 'i') if (*argrest == 'i')
{ {
queue_run_first_delivery = TRUE; f.queue_run_first_delivery = TRUE;
argrest++; argrest++;
} }
/* -qf...: Run the queue, forcing deliveries /* -qf...: Run the queue, forcing deliveries
-qff..: Ditto, forcing thawing as well */ -qff..: Ditto, forcing thawing as well */
if (*argrest == 'f') if (*argrest == 'f')
{ {
queue_run_force = TRUE; f.queue_run_force = TRUE;
if (*++argrest == 'f') if (*++argrest == 'f')
{ {
deliver_force_thaw = TRUE; f.deliver_force_thaw = TRUE;
argrest++; argrest++;
} }
} }
/* -q[f][f]l...: Run the queue only on local deliveries */ /* -q[f][f]l...: Run the queue only on local deliveries */
if (*argrest == 'l') if (*argrest == 'l')
{ {
queue_run_local = TRUE; f.queue_run_local = TRUE;
argrest++; argrest++;
} }
/* -q[f][f][l][G<name>]... Work on the named queue */ /* -q[f][f][l][G<name>]... Work on the named queue */
if (*argrest == 'G') if (*argrest == 'G')
{ {
int i; int i;
for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++; for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++;
queue_name = string_copyn(argrest, i); queue_name = string_copyn(argrest, i);
skipping to change at line 3219 skipping to change at line 3124
start_queue_run_id = argv[++i]; start_queue_run_id = argv[++i];
if (i+1 < argc && mac_ismsgid(argv[i+1])) if (i+1 < argc && mac_ismsgid(argv[i+1]))
stop_queue_run_id = argv[++i]; stop_queue_run_id = argv[++i];
} }
/* -q[f][f][l][G<name>/]<n>: Run the queue at regular intervals, optionally /* -q[f][f][l][G<name>/]<n>: Run the queue at regular intervals, optionally
forced, optionally local only, optionally named. */ forced, optionally local only, optionally named. */
else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i], else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i],
0, FALSE)) <= 0) 0, FALSE)) <= 0)
{ exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]);
exit(EXIT_FAILURE);
}
break; break;
case 'R': /* Synonymous with -qR... */ case 'R': /* Synonymous with -qR... */
receiving_message = FALSE; receiving_message = FALSE;
/* -Rf: As -R (below) but force all deliveries, /* -Rf: As -R (below) but force all deliveries,
-Rff: Ditto, but also thaw all frozen messages, -Rff: Ditto, but also thaw all frozen messages,
-Rr: String is regex -Rr: String is regex
-Rrf: Regex and force -Rrf: Regex and force
-Rrff: Regex and force and thaw -Rrff: Regex and force and thaw
in all cases provided there are no further characters in this in all cases provided there are no further characters in this
argument. */ argument. */
if (*argrest != 0) if (*argrest != 0)
{ {
int i; int i;
for (i = 0; i < nelem(rsopts); i++) for (i = 0; i < nelem(rsopts); i++)
if (Ustrcmp(argrest, rsopts[i]) == 0) if (Ustrcmp(argrest, rsopts[i]) == 0)
{ {
if (i != 2) queue_run_force = TRUE; if (i != 2) f.queue_run_force = TRUE;
if (i >= 2) deliver_selectstring_regex = TRUE; if (i >= 2) f.deliver_selectstring_regex = TRUE;
if (i == 1 || i == 4) deliver_force_thaw = TRUE; if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
argrest += Ustrlen(rsopts[i]); argrest += Ustrlen(rsopts[i]);
} }
} }
/* -R: Set string to match in addresses for forced queue run to /* -R: Set string to match in addresses for forced queue run to
pick out particular messages. */ pick out particular messages. */
if (*argrest) if (*argrest)
deliver_selectstring = argrest; deliver_selectstring = argrest;
else if (i+1 < argc) else if (i+1 < argc)
deliver_selectstring = argv[++i]; deliver_selectstring = argv[++i];
else else
{ exim_fail("exim: string expected after -R\n");
fprintf(stderr, "exim: string expected after -R\n");
exit(EXIT_FAILURE);
}
break; break;
/* -r: an obsolete synonym for -f (see above) */ /* -r: an obsolete synonym for -f (see above) */
/* -S: Like -R but works on sender. */ /* -S: Like -R but works on sender. */
case 'S': /* Synonymous with -qS... */ case 'S': /* Synonymous with -qS... */
receiving_message = FALSE; receiving_message = FALSE;
/* -Sf: As -S (below) but force all deliveries, /* -Sf: As -S (below) but force all deliveries,
skipping to change at line 3286 skipping to change at line 3185
in all cases provided there are no further characters in this in all cases provided there are no further characters in this
argument. */ argument. */
if (*argrest) if (*argrest)
{ {
int i; int i;
for (i = 0; i < nelem(rsopts); i++) for (i = 0; i < nelem(rsopts); i++)
if (Ustrcmp(argrest, rsopts[i]) == 0) if (Ustrcmp(argrest, rsopts[i]) == 0)
{ {
if (i != 2) queue_run_force = TRUE; if (i != 2) f.queue_run_force = TRUE;
if (i >= 2) deliver_selectstring_sender_regex = TRUE; if (i >= 2) f.deliver_selectstring_sender_regex = TRUE;
if (i == 1 || i == 4) deliver_force_thaw = TRUE; if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
argrest += Ustrlen(rsopts[i]); argrest += Ustrlen(rsopts[i]);
} }
} }
/* -S: Set string to match in addresses for forced queue run to /* -S: Set string to match in addresses for forced queue run to
pick out particular messages. */ pick out particular messages. */
if (*argrest) if (*argrest)
deliver_selectstring_sender = argrest; deliver_selectstring_sender = argrest;
else if (i+1 < argc) else if (i+1 < argc)
deliver_selectstring_sender = argv[++i]; deliver_selectstring_sender = argv[++i];
else else
{ exim_fail("exim: string expected after -S\n");
fprintf(stderr, "exim: string expected after -S\n");
exit(EXIT_FAILURE);
}
break; break;
/* -Tqt is an option that is exclusively for use by the testing suite. /* -Tqt is an option that is exclusively for use by the testing suite.
It is not recognized in other circumstances. It allows for the setting up It is not recognized in other circumstances. It allows for the setting up
of explicit "queue times" so that various warning/retry things can be of explicit "queue times" so that various warning/retry things can be
tested. Otherwise variability of clock ticks etc. cause problems. */ tested. Otherwise variability of clock ticks etc. cause problems. */
case 'T': case 'T':
if (running_in_test_harness && Ustrcmp(argrest, "qt") == 0) if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0)
fudged_queue_times = argv[++i]; fudged_queue_times = argv[++i];
else badarg = TRUE; else badarg = TRUE;
break; break;
/* -t: Set flag to extract recipients from body of message. */ /* -t: Set flag to extract recipients from body of message. */
case 't': case 't':
if (*argrest == 0) extract_recipients = TRUE; if (*argrest == 0) extract_recipients = TRUE;
/* -ti: Set flag to extract recipients from body of message, and also /* -ti: Set flag to extract recipients from body of message, and also
specify that dot does not end the message. */ specify that dot does not end the message. */
else if (Ustrcmp(argrest, "i") == 0) else if (Ustrcmp(argrest, "i") == 0)
{ {
extract_recipients = TRUE; extract_recipients = TRUE;
dot_ends = FALSE; f.dot_ends = FALSE;
} }
/* -tls-on-connect: don't wait for STARTTLS (for old clients) */ /* -tls-on-connect: don't wait for STARTTLS (for old clients) */
#ifdef SUPPORT_TLS #ifdef SUPPORT_TLS
else if (Ustrcmp(argrest, "ls-on-connect") == 0) tls_in.on_connect = TRUE; else if (Ustrcmp(argrest, "ls-on-connect") == 0) tls_in.on_connect = TRUE;
#endif #endif
else badarg = TRUE; else badarg = TRUE;
break; break;
skipping to change at line 3379 skipping to change at line 3275
case 'x': case 'x':
if (*argrest != 0) badarg = TRUE; if (*argrest != 0) badarg = TRUE;
break; break;
/* -X: in sendmail: takes one parameter, logfile, and sends debugging /* -X: in sendmail: takes one parameter, logfile, and sends debugging
logs to that file. We swallow the parameter and otherwise ignore it. */ logs to that file. We swallow the parameter and otherwise ignore it. */
case 'X': case 'X':
if (*argrest == '\0') if (*argrest == '\0')
if (++i >= argc) if (++i >= argc)
{ exim_fail("exim: string expected after -X\n");
fprintf(stderr, "exim: string expected after -X\n");
exit(EXIT_FAILURE);
}
break; break;
case 'z': case 'z':
if (*argrest == '\0') if (*argrest == '\0')
if (++i < argc) log_oneline = argv[i]; else if (++i < argc)
{ log_oneline = argv[i];
fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]); else
exit(EXIT_FAILURE); exim_fail("exim: file name expected after %s\n", argv[i-1]);
}
break; break;
/* All other initial characters are errors */ /* All other initial characters are errors */
default: default:
badarg = TRUE; badarg = TRUE;
break; break;
} /* End of high-level switch statement */ } /* End of high-level switch statement */
/* Failed to recognize the option, or syntax error */ /* Failed to recognize the option, or syntax error */
if (badarg) if (badarg)
{ exim_fail("exim abandoned: unknown, malformed, or incomplete "
fprintf(stderr, "exim abandoned: unknown, malformed, or incomplete "
"option %s\n", arg); "option %s\n", arg);
exit(EXIT_FAILURE);
}
} }
/* If -R or -S have been specified without -q, assume a single queue run. */ /* If -R or -S have been specified without -q, assume a single queue run. */
if ( (deliver_selectstring || deliver_selectstring_sender) if ( (deliver_selectstring || deliver_selectstring_sender)
&& queue_interval < 0) && queue_interval < 0)
queue_interval = 0; queue_interval = 0;
END_ARG: END_ARG:
/* If usage_wanted is set we call the usage function - which never returns */ /* If usage_wanted is set we call the usage function - which never returns */
if (usage_wanted) exim_usage(called_as); if (usage_wanted) exim_usage(called_as);
/* Arguments have been processed. Check for incompatibilities. */ /* Arguments have been processed. Check for incompatibilities. */
if (( if ((
(smtp_input || extract_recipients || recipients_arg < argc) && (smtp_input || extract_recipients || recipients_arg < argc) &&
(daemon_listen || queue_interval >= 0 || bi_option || (f.daemon_listen || queue_interval >= 0 || bi_option ||
test_retry_arg >= 0 || test_rewrite_arg >= 0 || test_retry_arg >= 0 || test_rewrite_arg >= 0 ||
filter_test != FTEST_NONE || (msg_action_arg > 0 && !one_msg_action)) filter_test != FTEST_NONE || (msg_action_arg > 0 && !one_msg_action))
) || ) ||
( (
msg_action_arg > 0 && msg_action_arg > 0 &&
(daemon_listen || queue_interval > 0 || list_options || (f.daemon_listen || queue_interval > 0 || list_options ||
(checking && msg_action != MSG_LOAD) || (checking && msg_action != MSG_LOAD) ||
bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0) bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0)
) || ) ||
( (
(daemon_listen || queue_interval > 0) && (f.daemon_listen || queue_interval > 0) &&
(sender_address != NULL || list_options || list_queue || checking || (sender_address != NULL || list_options || list_queue || checking ||
bi_option) bi_option)
) || ) ||
( (
daemon_listen && queue_interval == 0 f.daemon_listen && queue_interval == 0
) || ) ||
( (
inetd_wait_mode && queue_interval >= 0 f.inetd_wait_mode && queue_interval >= 0
) || ) ||
( (
list_options && list_options &&
(checking || smtp_input || extract_recipients || (checking || smtp_input || extract_recipients ||
filter_test != FTEST_NONE || bi_option) filter_test != FTEST_NONE || bi_option)
) || ) ||
( (
verify_address_mode && verify_address_mode &&
(address_test_mode || smtp_input || extract_recipients || (f.address_test_mode || smtp_input || extract_recipients ||
filter_test != FTEST_NONE || bi_option) filter_test != FTEST_NONE || bi_option)
) || ) ||
( (
address_test_mode && (smtp_input || extract_recipients || f.address_test_mode && (smtp_input || extract_recipients ||
filter_test != FTEST_NONE || bi_option) filter_test != FTEST_NONE || bi_option)
) || ) ||
( (
smtp_input && (sender_address != NULL || filter_test != FTEST_NONE || smtp_input && (sender_address != NULL || filter_test != FTEST_NONE ||
extract_recipients) extract_recipients)
) || ) ||
( (
deliver_selectstring != NULL && queue_interval < 0 deliver_selectstring != NULL && queue_interval < 0
) || ) ||
( (
msg_action == MSG_LOAD && msg_action == MSG_LOAD &&
(!expansion_test || expansion_test_message != NULL) (!expansion_test || expansion_test_message != NULL)
) )
) )
{ exim_fail("exim: incompatible command-line options or arguments\n");
fprintf(stderr, "exim: incompatible command-line options or arguments\n");
exit(EXIT_FAILURE);
}
/* If debugging is set up, set the file and the file descriptor to pass on to /* If debugging is set up, set the file and the file descriptor to pass on to
child processes. It should, of course, be 2 for stderr. Also, force the daemon child processes. It should, of course, be 2 for stderr. Also, force the daemon
to run in the foreground. */ to run in the foreground. */
if (debug_selector != 0) if (debug_selector != 0)
{ {
debug_file = stderr; debug_file = stderr;
debug_fd = fileno(debug_file); debug_fd = fileno(debug_file);
background_daemon = FALSE; f.background_daemon = FALSE;
if (running_in_test_harness) millisleep(100); /* lets caller finish */ if (f.running_in_test_harness) millisleep(100); /* lets caller finish */
if (debug_selector != D_v) /* -v only doesn't show this */ if (debug_selector != D_v) /* -v only doesn't show this */
{ {
debug_printf("Exim version %s uid=%ld gid=%ld pid=%d D=%x\n", debug_printf("Exim version %s uid=%ld gid=%ld pid=%d D=%x\n",
version_string, (long int)real_uid, (long int)real_gid, (int)getpid(), version_string, (long int)real_uid, (long int)real_gid, (int)getpid(),
debug_selector); debug_selector);
if (!version_printed) if (!version_printed)
show_whats_supported(stderr); show_whats_supported(stderr);
} }
} }
skipping to change at line 3571 skipping to change at line 3457
set their uid and gid as required for local delivery. We don't want to pass on set their uid and gid as required for local delivery. We don't want to pass on
any extra groups that root may belong to, so we want to get rid of them all at any extra groups that root may belong to, so we want to get rid of them all at
this point. this point.
We need to obey setgroups() at this stage, before possibly giving up root We need to obey setgroups() at this stage, before possibly giving up root
privilege for a changed configuration file, but later on we might need to privilege for a changed configuration file, but later on we might need to
check on the additional groups for the admin user privilege - can't do that check on the additional groups for the admin user privilege - can't do that
till after reading the config, which might specify the exim gid. Therefore, till after reading the config, which might specify the exim gid. Therefore,
save the group list here first. */ save the group list here first. */
group_count = getgroups(NGROUPS_MAX, group_list); if ((group_count = getgroups(nelem(group_list), group_list)) < 0)
if (group_count < 0) exim_fail("exim: getgroups() failed: %s\n", strerror(errno));
{
fprintf(stderr, "exim: getgroups() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* There is a fundamental difference in some BSD systems in the matter of /* There is a fundamental difference in some BSD systems in the matter of
groups. FreeBSD and BSDI are known to be different; NetBSD and OpenBSD are groups. FreeBSD and BSDI are known to be different; NetBSD and OpenBSD are
known not to be different. On the "different" systems there is a single group known not to be different. On the "different" systems there is a single group
list, and the first entry in it is the current group. On all other versions of list, and the first entry in it is the current group. On all other versions of
Unix there is a supplementary group list, which is in *addition* to the current Unix there is a supplementary group list, which is in *addition* to the current
group. Consequently, to get rid of all extraneous groups on a "standard" system group. Consequently, to get rid of all extraneous groups on a "standard" system
you pass over 0 groups to setgroups(), while on a "different" system you pass you pass over 0 groups to setgroups(), while on a "different" system you pass
over a single group - the current group, which is always the first group in the over a single group - the current group, which is always the first group in the
list. Calling setgroups() with zero groups on a "different" system results in list. Calling setgroups() with zero groups on a "different" system results in
an error return. The following code should cope with both types of system. an error return. The following code should cope with both types of system.
Unfortunately, recent MacOS, which should be a FreeBSD, "helpfully" succeeds
the "setgroups() with zero groups" - and changes the egid.
Thanks to that we had to stash the original_egid above, for use below
in the call to exim_setugid().
However, if this process isn't running as root, setgroups() can't be used 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 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 root here happens only in some unusual configurations. We just ignore the
error. */ error. */
if (setgroups(0, NULL) != 0) if (setgroups(0, NULL) != 0 && setgroups(1, group_list) != 0 && !unprivileged)
{ exim_fail("exim: setgroups() failed: %s\n", strerror(errno));
if (setgroups(1, group_list) != 0 && !unprivileged)
{
fprintf(stderr, "exim: setgroups() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
}
/* If the configuration file name has been altered by an argument on the /* If the configuration file name has been altered by an argument on the
command line (either a new file name or a macro definition) and the caller is command line (either a new file name or a macro definition) and the caller is
not root, or if this is a filter testing run, remove any setuid privilege the not root, or if this is a filter testing run, remove any setuid privilege the
program has and run as the underlying user. program has and run as the underlying user.
The exim user is locked out of this, which severely restricts the use of -C The exim user is locked out of this, which severely restricts the use of -C
for some purposes. for some purposes.
Otherwise, set the real ids to the effective values (should be root unless run Otherwise, set the real ids to the effective values (should be root unless run
from inetd, which it can either be root or the exim uid, if one is configured). from inetd, which it can either be root or the exim uid, if one is configured).
There is a private mechanism for bypassing some of this, in order to make it There is a private mechanism for bypassing some of this, in order to make it
possible to test lots of configurations automatically, without having either to possible to test lots of configurations automatically, without having either to
recompile each time, or to patch in an actual configuration file name and other recompile each time, or to patch in an actual configuration file name and other
values (such as the path name). If running in the test harness, pretend that values (such as the path name). If running in the test harness, pretend that
configuration file changes and macro definitions haven't happened. */ configuration file changes and macro definitions haven't happened. */
if (( /* EITHER */ if (( /* EITHER */
(!trusted_config || /* Config changed, or */ (!f.trusted_config || /* Config changed, or */
!macros_trusted(opt_D_used)) && /* impermissible macros and */ !macros_trusted(opt_D_used)) && /* impermissible macros and */
real_uid != root_uid && /* Not root, and */ real_uid != root_uid && /* Not root, and */
!running_in_test_harness /* Not fudged */ !f.running_in_test_harness /* Not fudged */
) || /* OR */ ) || /* OR */
expansion_test /* expansion testing */ expansion_test /* expansion testing */
|| /* OR */ || /* OR */
filter_test != FTEST_NONE) /* Filter testing */ filter_test != FTEST_NONE) /* Filter testing */
{ {
setgroups(group_count, group_list); setgroups(group_count, group_list);
exim_setugid(real_uid, real_gid, FALSE, exim_setugid(real_uid, real_gid, FALSE,
US"-C, -D, -be or -bf forces real uid"); US"-C, -D, -be or -bf forces real uid");
removed_privilege = TRUE; removed_privilege = TRUE;
/* In the normal case when Exim is called like this, stderr is available /* In the normal case when Exim is called like this, stderr is available
and should be used for any logging information because attempts to write and should be used for any logging information because attempts to write
to the log will usually fail. To arrange this, we unset really_exim. However, to the log will usually fail. To arrange this, we unset really_exim. However,
if no stderr is available there is no point - we might as well have a go if no stderr is available there is no point - we might as well have a go
at the log (if it fails, syslog will be written). at the log (if it fails, syslog will be written).
Note that if the invoker is Exim, the logs remain available. Messing with Note that if the invoker is Exim, the logs remain available. Messing with
this causes unlogged successful deliveries. */ this causes unlogged successful deliveries. */
if ((log_stderr != NULL) && (real_uid != exim_uid)) if (log_stderr && real_uid != exim_uid)
really_exim = FALSE; f.really_exim = FALSE;
} }
/* Privilege is to be retained for the moment. It may be dropped later, /* Privilege is to be retained for the moment. It may be dropped later,
depending on the job that this Exim process has been asked to do. For now, set depending on the job that this Exim process has been asked to do. For now, set
the real uid to the effective so that subsequent re-execs of Exim are done by a the real uid to the effective so that subsequent re-execs of Exim are done by a
privileged user. */ privileged user. */
else exim_setugid(geteuid(), getegid(), FALSE, US"forcing real = effective"); else
exim_setugid(geteuid(), original_egid, FALSE, US"forcing real = effective");
/* If testing a filter, open the file(s) now, before wasting time doing other /* If testing a filter, open the file(s) now, before wasting time doing other
setups and reading the message. */ setups and reading the message. */
if ((filter_test & FTEST_SYSTEM) != 0) if (filter_test & FTEST_SYSTEM)
{ if ((filter_sfd = Uopen(filter_test_sfile, O_RDONLY, 0)) < 0)
filter_sfd = Uopen(filter_test_sfile, O_RDONLY, 0); exim_fail("exim: failed to open %s: %s\n", filter_test_sfile,
if (filter_sfd < 0)
{
fprintf(stderr, "exim: failed to open %s: %s\n", filter_test_sfile,
strerror(errno)); strerror(errno));
return EXIT_FAILURE;
}
}
if ((filter_test & FTEST_USER) != 0) if (filter_test & FTEST_USER)
{ if ((filter_ufd = Uopen(filter_test_ufile, O_RDONLY, 0)) < 0)
filter_ufd = Uopen(filter_test_ufile, O_RDONLY, 0); exim_fail("exim: failed to open %s: %s\n", filter_test_ufile,
if (filter_ufd < 0)
{
fprintf(stderr, "exim: failed to open %s: %s\n", filter_test_ufile,
strerror(errno)); strerror(errno));
return EXIT_FAILURE;
}
}
/* Initialise lookup_list /* Initialise lookup_list
If debugging, already called above via version reporting. If debugging, already called above via version reporting.
In either case, we initialise the list of available lookups while running In either case, we initialise the list of available lookups while running
as root. All dynamically modules are loaded from a directory which is as root. All dynamically modules are loaded from a directory which is
hard-coded into the binary and is code which, if not a module, would be hard-coded into the binary and is code which, if not a module, would be
part of Exim already. Ability to modify the content of the directory part of Exim already. Ability to modify the content of the directory
is equivalent to the ability to modify a setuid binary! is equivalent to the ability to modify a setuid binary!
This needs to happen before we read the main configuration. */ This needs to happen before we read the main configuration. */
init_lookup_list(); init_lookup_list();
#ifdef SUPPORT_I18N #ifdef SUPPORT_I18N
if (running_in_test_harness) smtputf8_advertise_hosts = NULL; if (f.running_in_test_harness) smtputf8_advertise_hosts = NULL;
#endif #endif
/* Read the main runtime configuration data; this gives up if there /* Read the main runtime configuration data; this gives up if there
is a failure. It leaves the configuration file open so that the subsequent is a failure. It leaves the configuration file open so that the subsequent
configuration data for delivery can be read if needed. configuration data for delivery can be read if needed.
NOTE: immediatly after opening the configuration file we change the working NOTE: immediately after opening the configuration file we change the working
directory to "/"! Later we change to $spool_directory. We do it there, because directory to "/"! Later we change to $spool_directory. We do it there, because
during readconf_main() some expansion takes place already. */ during readconf_main() some expansion takes place already. */
/* Store the initial cwd before we change directories. Can be NULL if the /* Store the initial cwd before we change directories. Can be NULL if the
dir has already been unlinked. */ dir has already been unlinked. */
initial_cwd = os_getcwd(NULL, 0); initial_cwd = os_getcwd(NULL, 0);
/* checking: /* checking:
-be[m] expansion test - -be[m] expansion test -
-b[fF] filter test new -b[fF] filter test new
skipping to change at line 3739 skipping to change at line 3609
/* If an action on specific messages is requested, or if a daemon or queue /* If an action on specific messages is requested, or if a daemon or queue
runner is being started, we need to know if Exim was called by an admin user. runner is being started, we need to know if Exim was called by an admin user.
This is the case if the real user is root or exim, or if the real group is This is the case if the real user is root or exim, or if the real group is
exim, or if one of the supplementary groups is exim or a group listed in exim, or if one of the supplementary groups is exim or a group listed in
admin_groups. We don't fail all message actions immediately if not admin_user, admin_groups. We don't fail all message actions immediately if not admin_user,
since some actions can be performed by non-admin users. Instead, set admin_user since some actions can be performed by non-admin users. Instead, set admin_user
for later interrogation. */ for later interrogation. */
if (real_uid == root_uid || real_uid == exim_uid || real_gid == exim_gid) if (real_uid == root_uid || real_uid == exim_uid || real_gid == exim_gid)
admin_user = TRUE; f.admin_user = TRUE;
else else
{ {
int i, j; int i, j;
for (i = 0; i < group_count && !admin_user; i++) for (i = 0; i < group_count && !f.admin_user; i++)
if (group_list[i] == exim_gid) if (group_list[i] == exim_gid)
admin_user = TRUE; f.admin_user = TRUE;
else if (admin_groups) else if (admin_groups)
for (j = 1; j <= (int)admin_groups[0] && !admin_user; j++) for (j = 1; j <= (int)admin_groups[0] && !f.admin_user; j++)
if (admin_groups[j] == group_list[i]) if (admin_groups[j] == group_list[i])
admin_user = TRUE; f.admin_user = TRUE;
} }
/* Another group of privileged users are the trusted users. These are root, /* Another group of privileged users are the trusted users. These are root,
exim, and any caller matching trusted_users or trusted_groups. Trusted callers exim, and any caller matching trusted_users or trusted_groups. Trusted callers
are permitted to specify sender_addresses with -f on the command line, and are permitted to specify sender_addresses with -f on the command line, and
other message parameters as well. */ other message parameters as well. */
if (real_uid == root_uid || real_uid == exim_uid) if (real_uid == root_uid || real_uid == exim_uid)
trusted_caller = TRUE; f.trusted_caller = TRUE;
else else
{ {
int i, j; int i, j;
if (trusted_users) if (trusted_users)
for (i = 1; i <= (int)trusted_users[0] && !trusted_caller; i++) for (i = 1; i <= (int)trusted_users[0] && !f.trusted_caller; i++)
if (trusted_users[i] == real_uid) if (trusted_users[i] == real_uid)
trusted_caller = TRUE; f.trusted_caller = TRUE;
if (trusted_groups) if (trusted_groups)
for (i = 1; i <= (int)trusted_groups[0] && !trusted_caller; i++) for (i = 1; i <= (int)trusted_groups[0] && !f.trusted_caller; i++)
if (trusted_groups[i] == real_gid) if (trusted_groups[i] == real_gid)
trusted_caller = TRUE; f.trusted_caller = TRUE;
else for (j = 0; j < group_count && !trusted_caller; j++) else for (j = 0; j < group_count && !f.trusted_caller; j++)
if (trusted_groups[i] == group_list[j]) if (trusted_groups[i] == group_list[j])
trusted_caller = TRUE; f.trusted_caller = TRUE;
} }
/* At this point, we know if the user is privileged and some command-line /* At this point, we know if the user is privileged and some command-line
options become possibly impermissible, depending upon the configuration file. */ options become possibly impermissible, depending upon the configuration file. */
if (checking && commandline_checks_require_admin && !admin_user) { if (checking && commandline_checks_require_admin && !f.admin_user)
fprintf(stderr, "exim: those command-line flags are set to require admin\n"); exim_fail("exim: those command-line flags are set to require admin\n");
exit(EXIT_FAILURE);
}
/* Handle the decoding of logging options. */ /* Handle the decoding of logging options. */
decode_bits(log_selector, log_selector_size, log_notall, decode_bits(log_selector, log_selector_size, log_notall,
log_selector_string, log_options, log_options_count, US"log", 0); log_selector_string, log_options, log_options_count, US"log", 0);
DEBUG(D_any) DEBUG(D_any)
{ {
int i; int i;
debug_printf("configuration file is %s\n", config_main_filename); debug_printf("configuration file is %s\n", config_main_filename);
debug_printf("log selectors ="); debug_printf("log selectors =");
for (i = 0; i < log_selector_size; i++) for (i = 0; i < log_selector_size; i++)
debug_printf(" %08x", log_selector[i]); debug_printf(" %08x", log_selector[i]);
debug_printf("\n"); debug_printf("\n");
} }
/* If domain literals are not allowed, check the sender address that was /* If domain literals are not allowed, check the sender address that was
supplied with -f. Ditto for a stripped trailing dot. */ supplied with -f. Ditto for a stripped trailing dot. */
if (sender_address != NULL) if (sender_address)
{ {
if (sender_address[sender_address_domain] == '[' && !allow_domain_literals) if (sender_address[sender_address_domain] == '[' && !allow_domain_literals)
{ exim_fail("exim: bad -f address \"%s\": domain literals not "
fprintf(stderr, "exim: bad -f address \"%s\": domain literals not "
"allowed\n", sender_address); "allowed\n", sender_address);
return EXIT_FAILURE;
}
if (f_end_dot && !strip_trailing_dot) if (f_end_dot && !strip_trailing_dot)
{ exim_fail("exim: bad -f address \"%s.\": domain is malformed "
fprintf(stderr, "exim: bad -f address \"%s.\": domain is malformed "
"(trailing dot not allowed)\n", sender_address); "(trailing dot not allowed)\n", sender_address);
return EXIT_FAILURE;
}
} }
/* See if an admin user overrode our logging. */ /* See if an admin user overrode our logging. */
if (cmdline_syslog_name != NULL) if (cmdline_syslog_name)
{ if (f.admin_user)
if (admin_user)
{ {
syslog_processname = cmdline_syslog_name; syslog_processname = cmdline_syslog_name;
log_file_path = string_copy(CUS"syslog"); log_file_path = string_copy(CUS"syslog");
} }
else else
{
/* not a panic, non-privileged users should not be able to spam paniclog */ /* not a panic, non-privileged users should not be able to spam paniclog */
fprintf(stderr, exim_fail(
"exim: you lack sufficient privilege to specify syslog process name\n"); "exim: you lack sufficient privilege to specify syslog process name\n");
return EXIT_FAILURE;
}
}
/* Paranoia check of maximum lengths of certain strings. There is a check /* Paranoia check of maximum lengths of certain strings. There is a check
on the length of the log file path in log.c, which will come into effect on the length of the log file path in log.c, which will come into effect
if there are any calls to write the log earlier than this. However, if we if there are any calls to write the log earlier than this. However, if we
get this far but the string is very long, it is better to stop now than to get this far but the string is very long, it is better to stop now than to
carry on and (e.g.) receive a message and then have to collapse. The call to carry on and (e.g.) receive a message and then have to collapse. The call to
log_write() from here will cause the ultimate panic collapse if the complete log_write() from here will cause the ultimate panic collapse if the complete
file name exceeds the buffer length. */ file name exceeds the buffer length. */
if (Ustrlen(log_file_path) > 200) if (Ustrlen(log_file_path) > 200)
skipping to change at line 3865 skipping to change at line 3722
"spool_directory is longer than 200 chars: aborting"); "spool_directory is longer than 200 chars: aborting");
/* Length check on the process name given to syslog for its TAG field, /* Length check on the process name given to syslog for its TAG field,
which is only permitted to be 32 characters or less. See RFC 3164. */ which is only permitted to be 32 characters or less. See RFC 3164. */
if (Ustrlen(syslog_processname) > 32) if (Ustrlen(syslog_processname) > 32)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, log_write(0, LOG_MAIN|LOG_PANIC_DIE,
"syslog_processname is longer than 32 chars: aborting"); "syslog_processname is longer than 32 chars: aborting");
if (log_oneline) if (log_oneline)
if (admin_user) if (f.admin_user)
{ {
log_write(0, LOG_MAIN, "%s", log_oneline); log_write(0, LOG_MAIN, "%s", log_oneline);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
else else
return EXIT_FAILURE; return EXIT_FAILURE;
/* In some operating systems, the environment variable TMPDIR controls where /* In some operating systems, the environment variable TMPDIR controls where
temporary files are created; Exim doesn't use these (apart from when delivering temporary files are created; Exim doesn't use these (apart from when delivering
to MBX mailboxes), but called libraries such as DBM libraries may require them. to MBX mailboxes), but called libraries such as DBM libraries may require them.
skipping to change at line 3907 skipping to change at line 3764
timestamps to be in UTC (gmtime() is used instead of localtime()). Otherwise, timestamps to be in UTC (gmtime() is used instead of localtime()). Otherwise,
we may need to get rid of a bogus timezone setting. This can arise when Exim is we may need to get rid of a bogus timezone setting. This can arise when Exim is
called by a user who has set the TZ variable. This then affects the timestamps called by a user who has set the TZ variable. This then affects the timestamps
in log files and in Received: headers, and any created Date: header lines. The in log files and in Received: headers, and any created Date: header lines. The
required timezone is settable in the configuration file, so nothing can be done required timezone is settable in the configuration file, so nothing can be done
about this earlier - but hopefully nothing will normally be logged earlier than about this earlier - but hopefully nothing will normally be logged earlier than
this. We have to make a new environment if TZ is wrong, but don't bother if this. We have to make a new environment if TZ is wrong, but don't bother if
timestamps_utc is set, because then all times are in UTC anyway. */ timestamps_utc is set, because then all times are in UTC anyway. */
if (timezone_string && strcmpic(timezone_string, US"UTC") == 0) if (timezone_string && strcmpic(timezone_string, US"UTC") == 0)
timestamps_utc = TRUE; f.timestamps_utc = TRUE;
else else
{ {
uschar *envtz = US getenv("TZ"); uschar *envtz = US getenv("TZ");
if (envtz if (envtz
? !timezone_string || Ustrcmp(timezone_string, envtz) != 0 ? !timezone_string || Ustrcmp(timezone_string, envtz) != 0
: timezone_string != NULL : timezone_string != NULL
) )
{ {
uschar **p = USS environ; uschar **p = USS environ;
uschar **new; uschar **new;
skipping to change at line 3960 skipping to change at line 3817
privilege was dropped, to stop Exim trying to write to its normal log privilege was dropped, to stop Exim trying to write to its normal log
files. Therefore, re-enable normal log processing, assuming the sysadmin files. Therefore, re-enable normal log processing, assuming the sysadmin
has set up the log directory correctly. has set up the log directory correctly.
(2) If deliver_drop_privilege is not set, the configuration won't work as (2) If deliver_drop_privilege is not set, the configuration won't work as
apparently intended, and so we log a panic message. In order to retain apparently intended, and so we log a panic message. In order to retain
root for -C or -D, the caller must either be root or be invoking a root for -C or -D, the caller must either be root or be invoking a
trusted configuration file (when deliver_drop_privilege is false). */ trusted configuration file (when deliver_drop_privilege is false). */
if ( removed_privilege if ( removed_privilege
&& (!trusted_config || opt_D_used) && (!f.trusted_config || opt_D_used)
&& real_uid == exim_uid) && real_uid == exim_uid)
if (deliver_drop_privilege) if (deliver_drop_privilege)
really_exim = TRUE; /* let logging work normally */ f.really_exim = TRUE; /* let logging work normally */
else else
log_write(0, LOG_MAIN|LOG_PANIC, log_write(0, LOG_MAIN|LOG_PANIC,
"exim user lost privilege for using %s option", "exim user lost privilege for using %s option",
trusted_config? "-D" : "-C"); f.trusted_config? "-D" : "-C");
/* Start up Perl interpreter if Perl support is configured and there is a /* Start up Perl interpreter if Perl support is configured and there is a
perl_startup option, and the configuration or the command line specifies perl_startup option, and the configuration or the command line specifies
initializing starting. Note that the global variables are actually called initializing starting. Note that the global variables are actually called
opt_perl_xxx to avoid clashing with perl's namespace (perl_*). */ opt_perl_xxx to avoid clashing with perl's namespace (perl_*). */
#ifdef EXIM_PERL #ifdef EXIM_PERL
if (perl_start_option != 0) if (perl_start_option != 0)
opt_perl_at_start = (perl_start_option > 0); opt_perl_at_start = (perl_start_option > 0);
if (opt_perl_at_start && opt_perl_startup != NULL) if (opt_perl_at_start && opt_perl_startup != NULL)
{ {
uschar *errstr; uschar *errstr;
DEBUG(D_any) debug_printf("Starting Perl interpreter\n"); DEBUG(D_any) debug_printf("Starting Perl interpreter\n");
errstr = init_perl(opt_perl_startup); if ((errstr = init_perl(opt_perl_startup)))
if (errstr != NULL) exim_fail("exim: error in perl_startup code: %s\n", errstr);
{
fprintf(stderr, "exim: error in perl_startup code: %s\n", errstr);
return EXIT_FAILURE;
}
opt_perl_started = TRUE; opt_perl_started = TRUE;
} }
#endif /* EXIM_PERL */ #endif /* EXIM_PERL */
/* Log the arguments of the call if the configuration file said so. This is /* Log the arguments of the call if the configuration file said so. This is
a debugging feature for finding out what arguments certain MUAs actually use. a debugging feature for finding out what arguments certain MUAs actually use.
Don't attempt it if logging is disabled, or if listing variables or if Don't attempt it if logging is disabled, or if listing variables or if
verifying/testing addresses or expansions. */ verifying/testing addresses or expansions. */
if (((debug_selector & D_any) != 0 || LOGGING(arguments)) if ( (debug_selector & D_any || LOGGING(arguments))
&& really_exim && !list_options && !checking) && f.really_exim && !list_options && !checking)
{ {
int i; int i;
uschar *p = big_buffer; uschar *p = big_buffer;
Ustrcpy(p, "cwd= (failed)"); Ustrcpy(p, "cwd= (failed)");
Ustrncpy(p + 4, initial_cwd, big_buffer_size-5); if (!initial_cwd)
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';
}
while (*p) p++;
(void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc); (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
while (*p) p++; while (*p) p++;
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
{ {
int len = Ustrlen(argv[i]); int len = Ustrlen(argv[i]);
const uschar *printing; const uschar *printing;
uschar *quote; uschar *quote;
if (p + len + 8 >= big_buffer + big_buffer_size) if (p + len + 8 >= big_buffer + big_buffer_size)
{ {
Ustrcpy(p, " ..."); Ustrcpy(p, " ...");
skipping to change at line 4076 skipping to change at line 3937
if (alias_arg != NULL) argv[i++] = alias_arg; if (alias_arg != NULL) argv[i++] = alias_arg;
argv[i++] = NULL; argv[i++] = NULL;
setgroups(group_count, group_list); setgroups(group_count, group_list);
exim_setugid(real_uid, real_gid, FALSE, US"running bi_command"); exim_setugid(real_uid, real_gid, FALSE, US"running bi_command");
DEBUG(D_exec) debug_printf("exec %.256s %.256s\n", argv[0], DEBUG(D_exec) debug_printf("exec %.256s %.256s\n", argv[0],
(argv[1] == NULL)? US"" : argv[1]); (argv[1] == NULL)? US"" : argv[1]);
execv(CS argv[0], (char *const *)argv); execv(CS argv[0], (char *const *)argv);
fprintf(stderr, "exim: exec failed: %s\n", strerror(errno)); exim_fail("exim: exec failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
} }
else else
{ {
DEBUG(D_any) debug_printf("-bi used but bi_command not set; exiting\n"); DEBUG(D_any) debug_printf("-bi used but bi_command not set; exiting\n");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
} }
/* We moved the admin/trusted check to be immediately after reading the /* We moved the admin/trusted check to be immediately after reading the
configuration file. We leave these prints here to ensure that syslog setup, configuration file. We leave these prints here to ensure that syslog setup,
logfile setup, and so on has already happened. */ logfile setup, and so on has already happened. */
if (trusted_caller) DEBUG(D_any) debug_printf("trusted user\n"); if (f.trusted_caller) DEBUG(D_any) debug_printf("trusted user\n");
if (admin_user) DEBUG(D_any) debug_printf("admin user\n"); if (f.admin_user) DEBUG(D_any) debug_printf("admin user\n");
/* Only an admin user may start the daemon or force a queue run in the default /* Only an admin user may start the daemon or force a queue run in the default
configuration, but the queue run restriction can be relaxed. Only an admin configuration, but the queue run restriction can be relaxed. Only an admin
user may request that a message be returned to its sender forthwith. Only an user may request that a message be returned to its sender forthwith. Only an
admin user may specify a debug level greater than D_v (because it might show admin user may specify a debug level greater than D_v (because it might show
passwords, etc. in lookup queries). Only an admin user may request a queue passwords, etc. in lookup queries). Only an admin user may request a queue
count. Only an admin user can use the test interface to scan for email count. Only an admin user can use the test interface to scan for email
(because Exim will be in the spool dir and able to look at mails). */ (because Exim will be in the spool dir and able to look at mails). */
if (!admin_user) if (!f.admin_user)
{ {
BOOL debugset = (debug_selector & ~D_v) != 0; BOOL debugset = (debug_selector & ~D_v) != 0;
if (deliver_give_up || daemon_listen || malware_test_file || if (deliver_give_up || f.daemon_listen || malware_test_file ||
(count_queue && queue_list_requires_admin) || (count_queue && queue_list_requires_admin) ||
(list_queue && queue_list_requires_admin) || (list_queue && queue_list_requires_admin) ||
(queue_interval >= 0 && prod_requires_admin) || (queue_interval >= 0 && prod_requires_admin) ||
(debugset && !running_in_test_harness)) (debugset && !f.running_in_test_harness))
{ exim_fail("exim:%s permission denied\n", debugset? " debugging" : "");
fprintf(stderr, "exim:%s permission denied\n", debugset? " debugging" : "");
exit(EXIT_FAILURE);
}
} }
/* If the real user is not root or the exim uid, the argument for passing /* If the real user is not root or the exim uid, the argument for passing
in an open TCP/IP connection for another message is not permitted, nor is in an open TCP/IP connection for another message is not permitted, nor is
running with the -N option for any delivery action, unless this call to exim is running with the -N option for any delivery action, unless this call to exim is
one that supplied an input message, or we are using a patched exim for one that supplied an input message, or we are using a patched exim for
regression testing. */ regression testing. */
if (real_uid != root_uid && real_uid != exim_uid && if (real_uid != root_uid && real_uid != exim_uid &&
(continue_hostname != NULL || (continue_hostname != NULL ||
(dont_deliver && (f.dont_deliver &&
(queue_interval >= 0 || daemon_listen || msg_action_arg > 0) (queue_interval >= 0 || f.daemon_listen || msg_action_arg > 0)
)) && !running_in_test_harness) )) && !f.running_in_test_harness)
{ exim_fail("exim: Permission denied\n");
fprintf(stderr, "exim: Permission denied\n");
return EXIT_FAILURE;
}
/* If the caller is not trusted, certain arguments are ignored when running for /* If the caller is not trusted, certain arguments are ignored when running for
real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf, -bF). real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf, -bF).
Note that authority for performing certain actions on messages is tested in the Note that authority for performing certain actions on messages is tested in the
queue_action() function. */ queue_action() function. */
if (!trusted_caller && !checking) if (!f.trusted_caller && !checking)
{ {
sender_host_name = sender_host_address = interface_address = sender_host_name = sender_host_address = interface_address =
sender_ident = received_protocol = NULL; sender_ident = received_protocol = NULL;
sender_host_port = interface_port = 0; sender_host_port = interface_port = 0;
sender_host_authenticated = authenticated_sender = authenticated_id = NULL; sender_host_authenticated = authenticated_sender = authenticated_id = NULL;
} }
/* If a sender host address is set, extract the optional port number off the /* If a sender host address is set, extract the optional port number off the
end of it and check its syntax. Do the same thing for the interface address. end of it and check its syntax. Do the same thing for the interface address.
Exim exits if the syntax is bad. */ Exim exits if the syntax is bad. */
skipping to change at line 4159 skipping to change at line 4013
{ {
if (sender_host_address != NULL) if (sender_host_address != NULL)
sender_host_port = check_port(sender_host_address); sender_host_port = check_port(sender_host_address);
if (interface_address != NULL) if (interface_address != NULL)
interface_port = check_port(interface_address); interface_port = check_port(interface_address);
} }
/* If the caller is trusted, then they can use -G to suppress_local_fixups. */ /* If the caller is trusted, then they can use -G to suppress_local_fixups. */
if (flag_G) if (flag_G)
{ {
if (trusted_caller) if (f.trusted_caller)
{ {
suppress_local_fixups = suppress_local_fixups_default = TRUE; f.suppress_local_fixups = f.suppress_local_fixups_default = TRUE;
DEBUG(D_acl) debug_printf("suppress_local_fixups forced on by -G\n"); DEBUG(D_acl) debug_printf("suppress_local_fixups forced on by -G\n");
} }
else else
{ exim_fail("exim: permission denied (-G requires a trusted user)\n");
fprintf(stderr, "exim: permission denied (-G requires a trusted user)\n");
return EXIT_FAILURE;
}
} }
/* If an SMTP message is being received check to see if the standard input is a /* If an SMTP message is being received check to see if the standard input is a
TCP/IP socket. If it is, we assume that Exim was called from inetd if the TCP/IP socket. If it is, we assume that Exim was called from inetd if the
caller is root or the Exim user, or if the port is a privileged one. Otherwise, caller is root or the Exim user, or if the port is a privileged one. Otherwise,
barf. */ barf. */
if (smtp_input) if (smtp_input)
{ {
union sockaddr_46 inetd_sock; union sockaddr_46 inetd_sock;
skipping to change at line 4196 skipping to change at line 4047
size = sizeof(interface_sock); size = sizeof(interface_sock);
if (getsockname(0, (struct sockaddr *)(&interface_sock), &size) == 0) if (getsockname(0, (struct sockaddr *)(&interface_sock), &size) == 0)
interface_address = host_ntoa(-1, &interface_sock, NULL, interface_address = host_ntoa(-1, &interface_sock, NULL,
&interface_port); &interface_port);
if (host_is_tls_on_connect_port(interface_port)) tls_in.on_connect = TRUE; if (host_is_tls_on_connect_port(interface_port)) tls_in.on_connect = TRUE;
if (real_uid == root_uid || real_uid == exim_uid || interface_port < 1024) if (real_uid == root_uid || real_uid == exim_uid || interface_port < 1024)
{ {
is_inetd = TRUE; f.is_inetd = TRUE;
sender_host_address = host_ntoa(-1, (struct sockaddr *)(&inetd_sock), sender_host_address = host_ntoa(-1, (struct sockaddr *)(&inetd_sock),
NULL, &sender_host_port); NULL, &sender_host_port);
if (mua_wrapper) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Input from " if (mua_wrapper) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Input from "
"inetd is not supported when mua_wrapper is set"); "inetd is not supported when mua_wrapper is set");
} }
else else
{ exim_fail(
fprintf(stderr,
"exim: Permission denied (unprivileged user, unprivileged port)\n"); "exim: Permission denied (unprivileged user, unprivileged port)\n");
return EXIT_FAILURE;
}
} }
} }
} }
/* If the load average is going to be needed while receiving a message, get it /* If the load average is going to be needed while receiving a message, get it
now for those OS that require the first call to os_getloadavg() to be done as now for those OS that require the first call to os_getloadavg() to be done as
root. There will be further calls later for each message received. */ root. There will be further calls later for each message received. */
#ifdef LOAD_AVG_NEEDS_ROOT #ifdef LOAD_AVG_NEEDS_ROOT
if (receiving_message && if (receiving_message &&
(queue_only_load >= 0 || (queue_only_load >= 0 ||
(is_inetd && smtp_load_reserve >= 0) (f.is_inetd && smtp_load_reserve >= 0)
)) ))
{ {
load_average = OS_GETLOADAVG(); load_average = OS_GETLOADAVG();
} }
#endif #endif
/* The queue_only configuration option can be overridden by -odx on the command /* The queue_only configuration option can be overridden by -odx on the command
line, except that if queue_only_override is false, queue_only cannot be unset line, except that if queue_only_override is false, queue_only cannot be unset
from the command line. */ from the command line. */
skipping to change at line 4251 skipping to change at line 4099
root privilege above as a result of -C, -D, -be, -bf or -bF, remove it now root privilege above as a result of -C, -D, -be, -bf or -bF, remove it now
except when starting the daemon or doing some kind of delivery or address except when starting the daemon or doing some kind of delivery or address
testing (-bt). These are the only cases when root need to be retained. We run testing (-bt). These are the only cases when root need to be retained. We run
as exim for -bv and -bh. However, if deliver_drop_privilege is set, root is as exim for -bv and -bh. However, if deliver_drop_privilege is set, root is
retained only for starting the daemon. We always do the initgroups() in this retained only for starting the daemon. We always do the initgroups() in this
situation (controlled by the TRUE below), in order to be as close as possible situation (controlled by the TRUE below), in order to be as close as possible
to the state Exim usually runs in. */ to the state Exim usually runs in. */
if (!unprivileged && /* originally had root AND */ if (!unprivileged && /* originally had root AND */
!removed_privilege && /* still got root AND */ !removed_privilege && /* still got root AND */
!daemon_listen && /* not starting the daemon */ !f.daemon_listen && /* not starting the daemon */
queue_interval <= 0 && /* (either kind of daemon) */ queue_interval <= 0 && /* (either kind of daemon) */
( /* AND EITHER */ ( /* AND EITHER */
deliver_drop_privilege || /* requested unprivileged */ deliver_drop_privilege || /* requested unprivileged */
( /* OR */ ( /* OR */
queue_interval < 0 && /* not running the queue */ queue_interval < 0 && /* not running the queue */
(msg_action_arg < 0 || /* and */ (msg_action_arg < 0 || /* and */
msg_action != MSG_DELIVER) && /* not delivering and */ msg_action != MSG_DELIVER) && /* not delivering and */
(!checking || !address_test_mode) /* not address checking */ (!checking || !f.address_test_mode) /* not address checking */
) ) ) ) ) )
exim_setugid(exim_uid, exim_gid, TRUE, US"privilege not needed"); exim_setugid(exim_uid, exim_gid, TRUE, US"privilege not needed");
/* When we are retaining a privileged uid, we still change to the exim gid. */ /* When we are retaining a privileged uid, we still change to the exim gid. */
else else
{ {
int rv; int rv;
rv = setgid(exim_gid); rv = setgid(exim_gid);
/* Impact of failure is that some stuff might end up with an incorrect group. /* Impact of failure is that some stuff might end up with an incorrect group.
We track this for failures from root, since any attempt to change privilege We track this for failures from root, since any attempt to change privilege
by root should succeed and failures should be examined. For non-root, by root should succeed and failures should be examined. For non-root,
there's no security risk. For me, it's { exim -bV } on a just-built binary, there's no security risk. For me, it's { exim -bV } on a just-built binary,
no need to complain then. */ no need to complain then. */
if (rv == -1) if (rv == -1)
if (!(unprivileged || removed_privilege)) if (!(unprivileged || removed_privilege))
{ exim_fail("exim: changing group failed: %s\n", strerror(errno));
fprintf(stderr,
"exim: changing group failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
else else
DEBUG(D_any) debug_printf("changing group to %ld failed: %s\n", DEBUG(D_any) debug_printf("changing group to %ld failed: %s\n",
(long int)exim_gid, strerror(errno)); (long int)exim_gid, strerror(errno));
} }
/* Handle a request to scan a file for malware */ /* Handle a request to scan a file for malware */
if (malware_test_file) if (malware_test_file)
{ {
#ifdef WITH_CONTENT_SCAN #ifdef WITH_CONTENT_SCAN
int result; int result;
skipping to change at line 4341 skipping to change at line 4185
/* Handle actions on specific messages, except for the force delivery and /* Handle actions on specific messages, except for the force delivery and
message load actions, which are done below. Some actions take a whole list of message load actions, which are done below. Some actions take a whole list of
message ids, which are known to continue up to the end of the arguments. Others message ids, which are known to continue up to the end of the arguments. Others
take a single message id and then operate on the recipients list. */ take a single message id and then operate on the recipients list. */
if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD) if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
{ {
int yield = EXIT_SUCCESS; int yield = EXIT_SUCCESS;
set_process_info("acting on specified messages"); set_process_info("acting on specified messages");
/* ACL definitions may be needed when removing a message (-Mrm) because
event_action gets expanded */
if (msg_action == MSG_REMOVE)
readconf_rest();
if (!one_msg_action) if (!one_msg_action)
{ {
for (i = msg_action_arg; i < argc; i++) for (i = msg_action_arg; i < argc; i++)
if (!queue_action(argv[i], msg_action, NULL, 0, 0)) if (!queue_action(argv[i], msg_action, NULL, 0, 0))
yield = EXIT_FAILURE; yield = EXIT_FAILURE;
} }
else if (!queue_action(argv[msg_action_arg], msg_action, argv, argc, else if (!queue_action(argv[msg_action_arg], msg_action, argv, argc,
recipients_arg)) yield = EXIT_FAILURE; recipients_arg)) yield = EXIT_FAILURE;
exit(yield); exit(yield);
} }
/* We used to set up here to skip reading the ACL section, on /* We used to set up here to skip reading the ACL section, on
(msg_action_arg > 0 || (queue_interval == 0 && !daemon_listen) (msg_action_arg > 0 || (queue_interval == 0 && !f.daemon_listen)
Now, since the intro of the ${acl } expansion, ACL definitions may be Now, since the intro of the ${acl } expansion, ACL definitions may be
needed in transports so we lost the optimisation. */ needed in transports so we lost the optimisation. */
readconf_rest(); readconf_rest();
/* The configuration data will have been read into POOL_PERM because we won't /* The configuration data will have been read into POOL_PERM because we won't
ever want to reset back past it. Change the current pool to POOL_MAIN. In fact, ever want to reset back past it. Change the current pool to POOL_MAIN. In fact,
this is just a bit of pedantic tidiness. It wouldn't really matter if the this is just a bit of pedantic tidiness. It wouldn't really matter if the
configuration were read into POOL_MAIN, because we don't do any resets till configuration were read into POOL_MAIN, because we don't do any resets till
later on. However, it seems right, and it does ensure that both pools get used. later on. However, it seems right, and it does ensure that both pools get used.
skipping to change at line 4548 skipping to change at line 4398
prodding by hand (when the option forced_delivery will be set) or when prodding by hand (when the option forced_delivery will be set) or when
re-execing to regain root privilege. Each message delivery must happen in a re-execing to regain root privilege. Each message delivery must happen in a
separate process, so we fork a process for each one, and run them sequentially separate process, so we fork a process for each one, and run them sequentially
so that debugging output doesn't get intertwined, and to avoid spawning too so that debugging output doesn't get intertwined, and to avoid spawning too
many processes if a long list is given. However, don't fork for the last one; many processes if a long list is given. However, don't fork for the last one;
this saves a process in the common case when Exim is called to deliver just one this saves a process in the common case when Exim is called to deliver just one
message. */ message. */
if (msg_action_arg > 0 && msg_action != MSG_LOAD) if (msg_action_arg > 0 && msg_action != MSG_LOAD)
{ {
if (prod_requires_admin && !admin_user) if (prod_requires_admin && !f.admin_user)
{ {
fprintf(stderr, "exim: Permission denied\n"); fprintf(stderr, "exim: Permission denied\n");
exim_exit(EXIT_FAILURE, US"main"); exim_exit(EXIT_FAILURE, US"main");
} }
set_process_info("delivering specified messages"); set_process_info("delivering specified messages");
if (deliver_give_up) forced_delivery = deliver_force_thaw = TRUE; if (deliver_give_up) forced_delivery = f.deliver_force_thaw = TRUE;
for (i = msg_action_arg; i < argc; i++) for (i = msg_action_arg; i < argc; i++)
{ {
int status; int status;
pid_t pid; pid_t pid;
if (i == argc - 1) if (i == argc - 1)
(void)deliver_message(argv[i], forced_delivery, deliver_give_up); (void)deliver_message(argv[i], forced_delivery, deliver_give_up);
else if ((pid = fork()) == 0) else if ((pid = fork()) == 0)
{ {
(void)deliver_message(argv[i], forced_delivery, deliver_give_up); (void)deliver_message(argv[i], forced_delivery, deliver_give_up);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
skipping to change at line 4580 skipping to change at line 4430
exim_exit(EXIT_FAILURE, US"main"); exim_exit(EXIT_FAILURE, US"main");
} }
else wait(&status); else wait(&status);
} }
exim_exit(EXIT_SUCCESS, US"main"); exim_exit(EXIT_SUCCESS, US"main");
} }
/* If only a single queue run is requested, without SMTP listening, we can just /* If only a single queue run is requested, without SMTP listening, we can just
turn into a queue runner, with an optional starting message id. */ turn into a queue runner, with an optional starting message id. */
if (queue_interval == 0 && !daemon_listen) if (queue_interval == 0 && !f.daemon_listen)
{ {
DEBUG(D_queue_run) debug_printf("Single queue run%s%s%s%s\n", 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"" : US" starting at ",
(start_queue_run_id == NULL)? US"" : start_queue_run_id, (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"" : US" stopping at ",
(stop_queue_run_id == NULL)? US"" : stop_queue_run_id); (stop_queue_run_id == NULL)? US"" : stop_queue_run_id);
if (*queue_name) if (*queue_name)
set_process_info("running the '%s' queue (single queue run)", queue_name); set_process_info("running the '%s' queue (single queue run)", queue_name);
else else
set_process_info("running the queue (single queue run)"); set_process_info("running the queue (single queue run)");
skipping to change at line 4616 skipping to change at line 4466
if ((pw = getpwuid(real_uid)) != NULL) if ((pw = getpwuid(real_uid)) != NULL)
{ {
originator_login = string_copy(US pw->pw_name); originator_login = string_copy(US pw->pw_name);
originator_home = string_copy(US pw->pw_dir); originator_home = string_copy(US pw->pw_dir);
/* If user name has not been set by -F, set it from the passwd entry /* If user name has not been set by -F, set it from the passwd entry
unless -f has been used to set the sender address by a trusted user. */ unless -f has been used to set the sender address by a trusted user. */
if (!originator_name) if (!originator_name)
{ {
if (!sender_address || (!trusted_caller && filter_test == FTEST_NONE)) if (!sender_address || (!f.trusted_caller && filter_test == FTEST_NONE))
{ {
uschar *name = US pw->pw_gecos; uschar *name = US pw->pw_gecos;
uschar *amp = Ustrchr(name, '&'); uschar *amp = Ustrchr(name, '&');
uschar buffer[256]; uschar buffer[256];
/* Most Unix specify that a '&' character in the gecos field is /* Most Unix specify that a '&' character in the gecos field is
replaced by a copy of the login name, and some even specify that replaced by a copy of the login name, and some even specify that
the first character should be upper cased, so that's what we do. */ the first character should be upper cased, so that's what we do. */
if (amp) if (amp)
skipping to change at line 4681 skipping to change at line 4531
} }
if (++i > finduser_retries) break; if (++i > finduser_retries) break;
sleep(1); sleep(1);
} }
/* If we cannot get a user login, log the incident and give up, unless the /* If we cannot get a user login, log the incident and give up, unless the
configuration specifies something to use. When running in the test harness, configuration specifies something to use. When running in the test harness,
any setting of unknown_login overrides the actual name. */ any setting of unknown_login overrides the actual name. */
if (originator_login == NULL || running_in_test_harness) if (originator_login == NULL || f.running_in_test_harness)
{ {
if (unknown_login != NULL) if (unknown_login != NULL)
{ {
originator_login = expand_string(unknown_login); originator_login = expand_string(unknown_login);
if (originator_name == NULL && unknown_username != NULL) if (originator_name == NULL && unknown_username != NULL)
originator_name = expand_string(unknown_username); originator_name = expand_string(unknown_username);
if (originator_name == NULL) originator_name = US""; if (originator_name == NULL) originator_name = US"";
} }
if (originator_login == NULL) if (originator_login == NULL)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed to get user name for uid %d", log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed to get user name for uid %d",
skipping to change at line 4716 skipping to change at line 4566
originator_gid = real_gid; originator_gid = real_gid;
DEBUG(D_receive) debug_printf("originator: uid=%d gid=%d login=%s name=%s\n", DEBUG(D_receive) debug_printf("originator: uid=%d gid=%d login=%s name=%s\n",
(int)originator_uid, (int)originator_gid, originator_login, originator_name); (int)originator_uid, (int)originator_gid, originator_login, originator_name);
/* Run in daemon and/or queue-running mode. The function daemon_go() never /* Run in daemon and/or queue-running mode. The function daemon_go() never
returns. We leave this till here so that the originator_ fields are available returns. We leave this till here so that the originator_ fields are available
for incoming messages via the daemon. The daemon cannot be run in mua_wrapper for incoming messages via the daemon. The daemon cannot be run in mua_wrapper
mode. */ mode. */
if (daemon_listen || inetd_wait_mode || queue_interval > 0) if (f.daemon_listen || f.inetd_wait_mode || queue_interval > 0)
{ {
if (mua_wrapper) if (mua_wrapper)
{ {
fprintf(stderr, "Daemon cannot be run when mua_wrapper is set\n"); fprintf(stderr, "Daemon cannot be run when mua_wrapper is set\n");
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Daemon cannot be run when " log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Daemon cannot be run when "
"mua_wrapper is set"); "mua_wrapper is set");
} }
daemon_go(); daemon_go();
} }
skipping to change at line 4740 skipping to change at line 4590
if (sender_ident == NULL) sender_ident = originator_login; if (sender_ident == NULL) sender_ident = originator_login;
else if (sender_ident[0] == 0) sender_ident = NULL; else if (sender_ident[0] == 0) sender_ident = NULL;
/* Handle the -brw option, which is for checking out rewriting rules. Cause log /* Handle the -brw option, which is for checking out rewriting rules. Cause log
writes (on errors) to go to stderr instead. Can't do this earlier, as want the writes (on errors) to go to stderr instead. Can't do this earlier, as want the
originator_* variables set. */ originator_* variables set. */
if (test_rewrite_arg >= 0) if (test_rewrite_arg >= 0)
{ {
really_exim = FALSE; f.really_exim = FALSE;
if (test_rewrite_arg >= argc) if (test_rewrite_arg >= argc)
{ {
printf("-brw needs an address argument\n"); printf("-brw needs an address argument\n");
exim_exit(EXIT_FAILURE, US"main"); exim_exit(EXIT_FAILURE, US"main");
} }
rewrite_test(argv[test_rewrite_arg]); rewrite_test(argv[test_rewrite_arg]);
exim_exit(EXIT_SUCCESS, US"main"); exim_exit(EXIT_SUCCESS, US"main");
} }
/* A locally-supplied message is considered to be coming from a local user /* A locally-supplied message is considered to be coming from a local user
unless a trusted caller supplies a sender address with -f, or is passing in the unless a trusted caller supplies a sender address with -f, or is passing in the
message via SMTP (inetd invocation or otherwise). */ message via SMTP (inetd invocation or otherwise). */
if ((sender_address == NULL && !smtp_input) || if ((sender_address == NULL && !smtp_input) ||
(!trusted_caller && filter_test == FTEST_NONE)) (!f.trusted_caller && filter_test == FTEST_NONE))
{ {
sender_local = TRUE; f.sender_local = TRUE;
/* A trusted caller can supply authenticated_sender and authenticated_id /* A trusted caller can supply authenticated_sender and authenticated_id
via -oMas and -oMai and if so, they will already be set. Otherwise, force via -oMas and -oMai and if so, they will already be set. Otherwise, force
defaults except when host checking. */ defaults except when host checking. */
if (authenticated_sender == NULL && !host_checking) if (authenticated_sender == NULL && !host_checking)
authenticated_sender = string_sprintf("%s@%s", originator_login, authenticated_sender = string_sprintf("%s@%s", originator_login,
qualify_domain_sender); qualify_domain_sender);
if (authenticated_id == NULL && !host_checking) if (authenticated_id == NULL && !host_checking)
authenticated_id = originator_login; authenticated_id = originator_login;
skipping to change at line 4790 skipping to change at line 4640
non-SMTP input and the trusted caller has not set a sender. If there is no non-SMTP input and the trusted caller has not set a sender. If there is no
sender, or if a sender other than <> is set, override with the originator's sender, or if a sender other than <> is set, override with the originator's
login (which will get qualified below), except when checking things. */ login (which will get qualified below), except when checking things. */
if (sender_address == NULL /* No sender_address set */ if (sender_address == NULL /* No sender_address set */
|| /* OR */ || /* OR */
(sender_address[0] != 0 && /* Non-empty sender address, AND */ (sender_address[0] != 0 && /* Non-empty sender address, AND */
!checking)) /* Not running tests, including filter tests */ !checking)) /* Not running tests, including filter tests */
{ {
sender_address = originator_login; sender_address = originator_login;
sender_address_forced = FALSE; f.sender_address_forced = FALSE;
sender_address_domain = 0; sender_address_domain = 0;
} }
} }
/* Remember whether an untrusted caller set the sender address */ /* Remember whether an untrusted caller set the sender address */
sender_set_untrusted = sender_address != originator_login && !trusted_caller; f.sender_set_untrusted = sender_address != originator_login && !f.trusted_caller ;
/* Ensure that the sender address is fully qualified unless it is the empty /* Ensure that the sender address is fully qualified unless it is the empty
address, which indicates an error message, or doesn't exist (root caller, smtp address, which indicates an error message, or doesn't exist (root caller, smtp
interface, no -f argument). */ interface, no -f argument). */
if (sender_address != NULL && sender_address[0] != 0 && if (sender_address != NULL && sender_address[0] != 0 &&
sender_address_domain == 0) sender_address_domain == 0)
sender_address = string_sprintf("%s@%s", local_part_quote(sender_address), sender_address = string_sprintf("%s@%s", local_part_quote(sender_address),
qualify_domain_sender); qualify_domain_sender);
DEBUG(D_receive) debug_printf("sender address = %s\n", sender_address); DEBUG(D_receive) debug_printf("sender address = %s\n", sender_address);
/* Handle a request to verify a list of addresses, or test them for delivery. /* Handle a request to verify a list of addresses, or test them for delivery.
This must follow the setting of the sender address, since routers can be This must follow the setting of the sender address, since routers can be
predicated upon the sender. If no arguments are given, read addresses from predicated upon the sender. If no arguments are given, read addresses from
stdin. Set debug_level to at least D_v to get full output for address testing. stdin. Set debug_level to at least D_v to get full output for address testing.
*/ */
if (verify_address_mode || address_test_mode) if (verify_address_mode || f.address_test_mode)
{ {
int exit_value = 0; int exit_value = 0;
int flags = vopt_qualify; int flags = vopt_qualify;
if (verify_address_mode) if (verify_address_mode)
{ {
if (!verify_as_sender) flags |= vopt_is_recipient; if (!verify_as_sender) flags |= vopt_is_recipient;
DEBUG(D_verify) debug_print_ids(US"Verifying:"); DEBUG(D_verify) debug_print_ids(US"Verifying:");
} }
skipping to change at line 4876 skipping to change at line 4726
from stdin if there aren't any. If -Mset was specified, load the message so from stdin if there aren't any. If -Mset was specified, load the message so
that its variables can be used, but restrict this facility to admin users. that its variables can be used, but restrict this facility to admin users.
Otherwise, if -bem was used, read a message from stdin. */ Otherwise, if -bem was used, read a message from stdin. */
if (expansion_test) if (expansion_test)
{ {
dns_init(FALSE, FALSE, FALSE); dns_init(FALSE, FALSE, FALSE);
if (msg_action_arg > 0 && msg_action == MSG_LOAD) if (msg_action_arg > 0 && msg_action == MSG_LOAD)
{ {
uschar spoolname[256]; /* Not big_buffer; used in spool_read_header() */ uschar spoolname[256]; /* Not big_buffer; used in spool_read_header() */
if (!admin_user) if (!f.admin_user)
{ exim_fail("exim: permission denied\n");
fprintf(stderr, "exim: permission denied\n");
exit(EXIT_FAILURE);
}
message_id = argv[msg_action_arg]; message_id = argv[msg_action_arg];
(void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id); (void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id);
if ((deliver_datafile = spool_open_datafile(message_id)) < 0) if ((deliver_datafile = spool_open_datafile(message_id)) < 0)
printf ("Failed to load message datafile %s\n", message_id); printf ("Failed to load message datafile %s\n", message_id);
if (spool_read_header(spoolname, TRUE, FALSE) != spool_read_OK) if (spool_read_header(spoolname, TRUE, FALSE) != spool_read_OK)
printf ("Failed to load message %s\n", message_id); printf ("Failed to load message %s\n", message_id);
} }
/* Read a test message from a file. We fudge it up to be on stdin, saving /* Read a test message from a file. We fudge it up to be on stdin, saving
stdin itself for later reading of expansion strings. */ stdin itself for later reading of expansion strings. */
else if (expansion_test_message) else if (expansion_test_message)
{ {
int save_stdin = dup(0); int save_stdin = dup(0);
int fd = Uopen(expansion_test_message, O_RDONLY, 0); int fd = Uopen(expansion_test_message, O_RDONLY, 0);
if (fd < 0) if (fd < 0)
{ exim_fail("exim: failed to open %s: %s\n", expansion_test_message,
fprintf(stderr, "exim: failed to open %s: %s\n", expansion_test_message,
strerror(errno)); strerror(errno));
return EXIT_FAILURE;
}
(void) dup2(fd, 0); (void) dup2(fd, 0);
filter_test = FTEST_USER; /* Fudge to make it look like filter test */ filter_test = FTEST_USER; /* Fudge to make it look like filter test */
message_ended = END_NOTENDED; message_ended = END_NOTENDED;
read_message_body(receive_msg(extract_recipients)); read_message_body(receive_msg(extract_recipients));
message_linecount += body_linecount; message_linecount += body_linecount;
(void)dup2(save_stdin, 0); (void)dup2(save_stdin, 0);
(void)close(save_stdin); (void)close(save_stdin);
clearerr(stdin); /* Required by Darwin */ clearerr(stdin); /* Required by Darwin */
} }
/* Only admin users may see config-file macros this way */ /* Only admin users may see config-file macros this way */
if (!admin_user) macros_user = macros = mlast = NULL; if (!f.admin_user) macros_user = macros = mlast = NULL;
/* Allow $recipients for this testing */ /* Allow $recipients for this testing */
enable_dollar_recipients = TRUE; f.enable_dollar_recipients = TRUE;
/* Expand command line items */ /* Expand command line items */
if (recipients_arg < argc) if (recipients_arg < argc)
while (recipients_arg < argc) while (recipients_arg < argc)
expansion_test_line(argv[recipients_arg++]); expansion_test_line(argv[recipients_arg++]);
/* Read stdin */ /* Read stdin */
else else
skipping to change at line 4967 skipping to change at line 4811
/* The active host name is normally the primary host name, but it can be varied /* The active host name is normally the primary host name, but it can be varied
for hosts that want to play several parts at once. We need to ensure that it is for hosts that want to play several parts at once. We need to ensure that it is
set for host checking, and for receiving messages. */ set for host checking, and for receiving messages. */
smtp_active_hostname = primary_hostname; smtp_active_hostname = primary_hostname;
if (raw_active_hostname != NULL) if (raw_active_hostname != NULL)
{ {
uschar *nah = expand_string(raw_active_hostname); uschar *nah = expand_string(raw_active_hostname);
if (nah == NULL) if (nah == NULL)
{ {
if (!expand_string_forcedfail) if (!f.expand_string_forcedfail)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand \"%s\" " log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand \"%s\" "
"(smtp_active_hostname): %s", raw_active_hostname, "(smtp_active_hostname): %s", raw_active_hostname,
expand_string_message); expand_string_message);
} }
else if (nah[0] != 0) smtp_active_hostname = nah; else if (nah[0] != 0) smtp_active_hostname = nah;
} }
/* Handle host checking: this facility mocks up an incoming SMTP call from a /* Handle host checking: this facility mocks up an incoming SMTP call from a
given IP address so that the blocking and relay configuration can be tested. given IP address so that the blocking and relay configuration can be tested.
Unless a sender_ident was set by -oMt, we discard it (the default is the Unless a sender_ident was set by -oMt, we discard it (the default is the
skipping to change at line 4990 skipping to change at line 4834
there is no TCP/IP call to find the ident for. */ there is no TCP/IP call to find the ident for. */
if (host_checking) if (host_checking)
{ {
int x[4]; int x[4];
int size; int size;
if (!sender_ident_set) if (!sender_ident_set)
{ {
sender_ident = NULL; sender_ident = NULL;
if (running_in_test_harness && sender_host_port != 0 && if (f.running_in_test_harness && sender_host_port != 0 &&
interface_address != NULL && interface_port != 0) interface_address != NULL && interface_port != 0)
verify_get_ident(1413); verify_get_ident(1413);
} }
/* In case the given address is a non-canonical IPv6 address, canonicalize /* In case the given address is a non-canonical IPv6 address, canonicalize
it. The code works for both IPv4 and IPv6, as it happens. */ it. The code works for both IPv4 and IPv6, as it happens. */
size = host_aton(sender_host_address, x); size = host_aton(sender_host_address, x);
sender_host_address = store_get(48); /* large enough for full IPv6 */ sender_host_address = store_get(48); /* large enough for full IPv6 */
(void)host_nmtoa(size, x, -1, sender_host_address, ':'); (void)host_nmtoa(size, x, -1, sender_host_address, ':');
/* Now set up for testing */ /* Now set up for testing */
host_build_sender_fullhost(); host_build_sender_fullhost();
smtp_input = TRUE; smtp_input = TRUE;
smtp_in = stdin; smtp_in = stdin;
smtp_out = stdout; smtp_out = stdout;
sender_local = FALSE; f.sender_local = FALSE;
sender_host_notsocket = TRUE; f.sender_host_notsocket = TRUE;
debug_file = stderr; debug_file = stderr;
debug_fd = fileno(debug_file); debug_fd = fileno(debug_file);
fprintf(stdout, "\n**** SMTP testing session as if from host %s\n" fprintf(stdout, "\n**** SMTP testing session as if from host %s\n"
"**** but without any ident (RFC 1413) callback.\n" "**** but without any ident (RFC 1413) callback.\n"
"**** This is not for real!\n\n", "**** This is not for real!\n\n",
sender_host_address); sender_host_address);
memset(sender_host_cache, 0, sizeof(sender_host_cache)); memset(sender_host_cache, 0, sizeof(sender_host_cache));
if (verify_check_host(&hosts_connection_nolog) == OK) 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_connection);
skipping to change at line 5059 skipping to change at line 4903
/* Arrange for message reception if recipients or SMTP were specified; /* Arrange for message reception if recipients or SMTP were specified;
otherwise complain unless a version print (-bV) happened or this is a filter otherwise complain unless a version print (-bV) happened or this is a filter
verification test or info dump. verification test or info dump.
In the former case, show the configuration file name. */ In the former case, show the configuration file name. */
if (recipients_arg >= argc && !extract_recipients && !smtp_input) if (recipients_arg >= argc && !extract_recipients && !smtp_input)
{ {
if (version_printed) if (version_printed)
{ {
if (Ustrchr(config_main_filelist, ':'))
printf("Configuration file search path is %s\n", config_main_filelist);
printf("Configuration file is %s\n", config_main_filename); printf("Configuration file is %s\n", config_main_filename);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
if (info_flag != CMDINFO_NONE) if (info_flag != CMDINFO_NONE)
{ {
show_exim_information(info_flag, info_stdout ? stdout : stderr); show_exim_information(info_flag, info_stdout ? stdout : stderr);
return info_stdout ? EXIT_SUCCESS : EXIT_FAILURE; return info_stdout ? EXIT_SUCCESS : EXIT_FAILURE;
} }
skipping to change at line 5089 skipping to change at line 4935
(2) Errors to stderr (-oep == -oeq) (2) Errors to stderr (-oep == -oeq)
(3) No parallel remote delivery (3) No parallel remote delivery
(4) Unprivileged delivery (4) Unprivileged delivery
We don't force overall queueing options because there are several of them; We don't force overall queueing options because there are several of them;
instead, queueing is avoided below when mua_wrapper is set. However, we do need instead, queueing is avoided below when mua_wrapper is set. However, we do need
to override any SMTP queueing. */ to override any SMTP queueing. */
if (mua_wrapper) if (mua_wrapper)
{ {
synchronous_delivery = TRUE; f.synchronous_delivery = TRUE;
arg_error_handling = ERRORS_STDERR; arg_error_handling = ERRORS_STDERR;
remote_max_parallel = 1; remote_max_parallel = 1;
deliver_drop_privilege = TRUE; deliver_drop_privilege = TRUE;
queue_smtp = FALSE; f.queue_smtp = FALSE;
queue_smtp_domains = NULL; queue_smtp_domains = NULL;
#ifdef SUPPORT_I18N #ifdef SUPPORT_I18N
message_utf8_downconvert = -1; /* convert-if-needed */ message_utf8_downconvert = -1; /* convert-if-needed */
#endif #endif
} }
/* Prepare to accept one or more new messages on the standard input. When a /* Prepare to accept one or more new messages on the standard input. When a
message has been read, its id is returned in message_id[]. If doing immediate message has been read, its id is returned in message_id[]. If doing immediate
delivery, we fork a delivery process for each received message, except for the delivery, we fork a delivery process for each received message, except for the
last one, where we can save a process switch. last one, where we can save a process switch.
skipping to change at line 5115 skipping to change at line 4961
It is only in non-smtp mode that error_handling is allowed to be changed from It is only in non-smtp mode that error_handling is allowed to be changed from
its default of ERRORS_SENDER by argument. (Idle thought: are any of the its default of ERRORS_SENDER by argument. (Idle thought: are any of the
sendmail error modes other than -oem ever actually used? Later: yes.) */ sendmail error modes other than -oem ever actually used? Later: yes.) */
if (!smtp_input) error_handling = arg_error_handling; if (!smtp_input) error_handling = arg_error_handling;
/* If this is an inetd call, ensure that stderr is closed to prevent panic /* If this is an inetd call, ensure that stderr is closed to prevent panic
logging being sent down the socket and make an identd call to get the logging being sent down the socket and make an identd call to get the
sender_ident. */ sender_ident. */
else if (is_inetd) else if (f.is_inetd)
{ {
(void)fclose(stderr); (void)fclose(stderr);
exim_nullstd(); /* Re-open to /dev/null */ exim_nullstd(); /* Re-open to /dev/null */
verify_get_ident(IDENT_PORT); verify_get_ident(IDENT_PORT);
host_build_sender_fullhost(); host_build_sender_fullhost();
set_process_info("handling incoming connection from %s via inetd", set_process_info("handling incoming connection from %s via inetd",
sender_fullhost); sender_fullhost);
} }
/* If the sender host address has been set, build sender_fullhost if it hasn't /* If the sender host address has been set, build sender_fullhost if it hasn't
already been done (which it will have been for inetd). This caters for the already been done (which it will have been for inetd). This caters for the
case when it is forced by -oMa. However, we must flag that it isn't a socket, case when it is forced by -oMa. However, we must flag that it isn't a socket,
so that the test for IP options is skipped for -bs input. */ so that the test for IP options is skipped for -bs input. */
if (sender_host_address && !sender_fullhost) if (sender_host_address && !sender_fullhost)
{ {
host_build_sender_fullhost(); host_build_sender_fullhost();
set_process_info("handling incoming connection from %s via -oMa", set_process_info("handling incoming connection from %s via -oMa",
sender_fullhost); sender_fullhost);
sender_host_notsocket = TRUE; f.sender_host_notsocket = TRUE;
} }
/* Otherwise, set the sender host as unknown except for inetd calls. This /* Otherwise, set the sender host as unknown except for inetd calls. This
prevents host checking in the case of -bs not from inetd and also for -bS. */ prevents host checking in the case of -bs not from inetd and also for -bS. */
else if (!is_inetd) sender_host_unknown = TRUE; else if (!f.is_inetd) f.sender_host_unknown = TRUE;
/* If stdout does not exist, then dup stdin to stdout. This can happen /* If stdout does not exist, then dup stdin to stdout. This can happen
if exim is started from inetd. In this case fd 0 will be set to the socket, if exim is started from inetd. In this case fd 0 will be set to the socket,
but fd 1 will not be set. This also happens for passed SMTP channels. */ but fd 1 will not be set. This also happens for passed SMTP channels. */
if (fstat(1, &statbuf) < 0) (void)dup2(0, 1); if (fstat(1, &statbuf) < 0) (void)dup2(0, 1);
/* Set up the incoming protocol name and the state of the program. Root is /* Set up the incoming protocol name and the state of the program. Root is
allowed to force received protocol via the -oMr option above. If we have come allowed to force received protocol via the -oMr option above. If we have come
via inetd, the process info has already been set up. We don't set via inetd, the process info has already been set up. We don't set
received_protocol here for smtp input, as it varies according to received_protocol here for smtp input, as it varies according to
batch/HELO/EHLO/AUTH/TLS. */ batch/HELO/EHLO/AUTH/TLS. */
if (smtp_input) if (smtp_input)
{ {
if (!is_inetd) set_process_info("accepting a local %sSMTP message from <%s>", if (!f.is_inetd) set_process_info("accepting a local %sSMTP message from <%s>" ,
smtp_batched_input? "batched " : "", smtp_batched_input? "batched " : "",
(sender_address!= NULL)? sender_address : originator_login); (sender_address!= NULL)? sender_address : originator_login);
} }
else else
{ {
int old_pool = store_pool; int old_pool = store_pool;
store_pool = POOL_PERM; store_pool = POOL_PERM;
if (!received_protocol) if (!received_protocol)
received_protocol = string_sprintf("local%s", called_as); received_protocol = string_sprintf("local%s", called_as);
store_pool = old_pool; store_pool = old_pool;
skipping to change at line 5184 skipping to change at line 5030
queue_check_only(); queue_check_only();
session_local_queue_only = queue_only; session_local_queue_only = queue_only;
/* For non-SMTP and for batched SMTP input, check that there is enough space on /* For non-SMTP and for batched SMTP input, check that there is enough space on
the spool if so configured. On failure, we must not attempt to send an error the spool if so configured. On failure, we must not attempt to send an error
message! (For interactive SMTP, the check happens at MAIL FROM and an SMTP message! (For interactive SMTP, the check happens at MAIL FROM and an SMTP
error code is given.) */ error code is given.) */
if ((!smtp_input || smtp_batched_input) && !receive_check_fs(0)) if ((!smtp_input || smtp_batched_input) && !receive_check_fs(0))
{ exim_fail("exim: insufficient disk space\n");
fprintf(stderr, "exim: insufficient disk space\n");
return EXIT_FAILURE;
}
/* If this is smtp input of any kind, real or batched, handle the start of the /* If this is smtp input of any kind, real or batched, handle the start of the
SMTP session. SMTP session.
NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails, NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
because a log line has already been written for all its failure exists because a log line has already been written for all its failure exists
(usually "connection refused: <reason>") and writing another one is (usually "connection refused: <reason>") and writing another one is
unnecessary clutter. */ unnecessary clutter. */
if (smtp_input) if (smtp_input)
skipping to change at line 5254 skipping to change at line 5097
it just sets SIG_IGN. To be on the safe side it also calls waitpid() at the end it just sets SIG_IGN. To be on the safe side it also calls waitpid() at the end
of the loop below. Paranoia rules. of the loop below. Paranoia rules.
February 2003: That's *still* not the end of the story. There are now versions February 2003: That's *still* not the end of the story. There are now versions
of Linux (where SIG_IGN does work) that are picky. If, having set SIG_IGN, a of Linux (where SIG_IGN does work) that are picky. If, having set SIG_IGN, a
process then calls waitpid(), a grumble is written to the system log, because process then calls waitpid(), a grumble is written to the system log, because
this is logically inconsistent. In other words, it doesn't like the paranoia. this is logically inconsistent. In other words, it doesn't like the paranoia.
As a consequence of this, the waitpid() below is now excluded if we are sure As a consequence of this, the waitpid() below is now excluded if we are sure
that SIG_IGN works. */ that SIG_IGN works. */
if (!synchronous_delivery) if (!f.synchronous_delivery)
{ {
#ifdef SA_NOCLDWAIT #ifdef SA_NOCLDWAIT
struct sigaction act; struct sigaction act;
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
sigemptyset(&(act.sa_mask)); sigemptyset(&(act.sa_mask));
act.sa_flags = SA_NOCLDWAIT; act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, NULL); sigaction(SIGCHLD, &act, NULL);
#else #else
signal(SIGCHLD, SIG_IGN); signal(SIGCHLD, SIG_IGN);
#endif #endif
skipping to change at line 5309 skipping to change at line 5152
} }
/* For batched SMTP, we have to run the acl_not_smtp_start ACL, since it /* For batched SMTP, we have to run the acl_not_smtp_start ACL, since it
isn't really SMTP, so no other ACL will run until the acl_not_smtp one at isn't really SMTP, so no other ACL will run until the acl_not_smtp one at
the very end. The result of the ACL is ignored (as for other non-SMTP the very end. The result of the ACL is ignored (as for other non-SMTP
messages). It is run for its potential side effects. */ messages). It is run for its potential side effects. */
if (smtp_batched_input && acl_not_smtp_start != NULL) if (smtp_batched_input && acl_not_smtp_start != NULL)
{ {
uschar *user_msg, *log_msg; uschar *user_msg, *log_msg;
enable_dollar_recipients = TRUE; f.enable_dollar_recipients = TRUE;
(void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start,
&user_msg, &log_msg); &user_msg, &log_msg);
enable_dollar_recipients = FALSE; f.enable_dollar_recipients = FALSE;
} }
/* Now get the data for the message */ /* Now get the data for the message */
more = receive_msg(extract_recipients); more = receive_msg(extract_recipients);
if (message_id[0] == 0) if (message_id[0] == 0)
{ {
cancel_cutthrough_connection(TRUE, US"receive dropped"); cancel_cutthrough_connection(TRUE, US"receive dropped");
if (more) goto moreloop; if (more) goto moreloop;
smtp_log_no_mail(); /* Log no mail if configured */ smtp_log_no_mail(); /* Log no mail if configured */
skipping to change at line 5349 skipping to change at line 5192
else else
{ {
int i; int i;
int rcount = 0; int rcount = 0;
int count = argc - recipients_arg; int count = argc - recipients_arg;
uschar **list = argv + recipients_arg; uschar **list = argv + recipients_arg;
/* These options cannot be changed dynamically for non-SMTP messages */ /* These options cannot be changed dynamically for non-SMTP messages */
active_local_sender_retain = local_sender_retain; f.active_local_sender_retain = local_sender_retain;
active_local_from_check = local_from_check; f.active_local_from_check = local_from_check;
/* Save before any rewriting */ /* Save before any rewriting */
raw_sender = string_copy(sender_address); raw_sender = string_copy(sender_address);
/* Loop for each argument */ /* Loop for each argument */
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
int start, end, domain; int start, end, domain;
skipping to change at line 5403 skipping to change at line 5246
recipient = recipient =
parse_extract_address(s, &errmess, &start, &end, &domain, FALSE); parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
#ifdef SUPPORT_I18N #ifdef SUPPORT_I18N
if (string_is_utf8(recipient)) if (string_is_utf8(recipient))
message_smtputf8 = TRUE; message_smtputf8 = TRUE;
else else
allow_utf8_domains = b; allow_utf8_domains = b;
} }
#endif #endif
if (domain == 0 && !allow_unqualified_recipient) if (domain == 0 && !f.allow_unqualified_recipient)
{ {
recipient = NULL; recipient = NULL;
errmess = US"unqualified recipient address not allowed"; errmess = US"unqualified recipient address not allowed";
} }
if (recipient == NULL) if (recipient == NULL)
{ {
if (error_handling == ERRORS_STDERR) if (error_handling == ERRORS_STDERR)
{ {
fprintf(stderr, "exim: bad recipient address \"%s\": %s\n", fprintf(stderr, "exim: bad recipient address \"%s\": %s\n",
skipping to change at line 5457 skipping to change at line 5300
} }
} }
/* Run the acl_not_smtp_start ACL if required. The result of the ACL is /* Run the acl_not_smtp_start ACL if required. The result of the ACL is
ignored; rejecting here would just add complication, and it can just as ignored; rejecting here would just add complication, and it can just as
well be done later. Allow $recipients to be visible in the ACL. */ well be done later. Allow $recipients to be visible in the ACL. */
if (acl_not_smtp_start) if (acl_not_smtp_start)
{ {
uschar *user_msg, *log_msg; uschar *user_msg, *log_msg;
enable_dollar_recipients = TRUE; f.enable_dollar_recipients = TRUE;
(void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start,
&user_msg, &log_msg); &user_msg, &log_msg);
enable_dollar_recipients = FALSE; f.enable_dollar_recipients = FALSE;
} }
/* Pause for a while waiting for input. If none received in that time, /* Pause for a while waiting for input. If none received in that time,
close the logfile, if we had one open; then if we wait for a long-running close the logfile, if we had one open; then if we wait for a long-running
datasource (months, in one use-case) log rotation will not leave us holding datasource (months, in one use-case) log rotation will not leave us holding
the file copy. */ the file copy. */
if (!receive_timeout) if (!receive_timeout)
{ {
struct timeval t = { .tv_sec = 30*60, .tv_usec = 0 }; /* 30 minutes */ struct timeval t = { .tv_sec = 30*60, .tv_usec = 0 }; /* 30 minutes */
skipping to change at line 5592 skipping to change at line 5435
{ {
queue_only_reason = 3; queue_only_reason = 3;
if (queue_only_load_latch) session_local_queue_only = TRUE; if (queue_only_load_latch) session_local_queue_only = TRUE;
} }
} }
/* If running as an MUA wrapper, all queueing options and freezing options /* If running as an MUA wrapper, all queueing options and freezing options
are ignored. */ are ignored. */
if (mua_wrapper) if (mua_wrapper)
local_queue_only = queue_only_policy = deliver_freeze = FALSE; local_queue_only = f.queue_only_policy = f.deliver_freeze = FALSE;
/* Log the queueing here, when it will get a message id attached, but /* Log the queueing here, when it will get a message id attached, but
not if queue_only is set (case 0). Case 1 doesn't happen here (too many not if queue_only is set (case 0). Case 1 doesn't happen here (too many
connections). */ connections). */
if (local_queue_only) if (local_queue_only)
{ {
cancel_cutthrough_connection(TRUE, US"no delivery; queueing"); cancel_cutthrough_connection(TRUE, US"no delivery; queueing");
switch(queue_only_reason) switch(queue_only_reason)
{ {
skipping to change at line 5617 skipping to change at line 5460
break; break;
case 3: case 3:
log_write(L_delay_delivery, log_write(L_delay_delivery,
LOG_MAIN, "no immediate delivery: load average %.2f", LOG_MAIN, "no immediate delivery: load average %.2f",
(double)load_average/1000.0); (double)load_average/1000.0);
break; break;
} }
} }
else if (queue_only_policy || deliver_freeze) else if (f.queue_only_policy || f.deliver_freeze)
cancel_cutthrough_connection(TRUE, US"no delivery; queueing"); cancel_cutthrough_connection(TRUE, US"no delivery; queueing");
/* Else do the delivery unless the ACL or local_scan() called for queue only /* Else do the delivery unless the ACL or local_scan() called for queue only
or froze the message. Always deliver in a separate process. A fork failure is or froze the message. Always deliver in a separate process. A fork failure is
not a disaster, as the delivery will eventually happen on a subsequent queue not a disaster, as the delivery will eventually happen on a subsequent queue
run. The search cache must be tidied before the fork, as the parent will run. The search cache must be tidied before the fork, as the parent will
do it before exiting. The child will trigger a lookup failure and do it before exiting. The child will trigger a lookup failure and
thereby defer the delivery if it tries to use (for example) a cached ldap thereby defer the delivery if it tries to use (for example) a cached ldap
connection that the parent has called unbind on. */ connection that the parent has called unbind on. */
skipping to change at line 5669 skipping to change at line 5512
log_write(0, LOG_MAIN|LOG_PANIC, "failed to fork automatic delivery " log_write(0, LOG_MAIN|LOG_PANIC, "failed to fork automatic delivery "
"process: %s", strerror(errno)); "process: %s", strerror(errno));
} }
else else
{ {
release_cutthrough_connection(US"msg passed for delivery"); release_cutthrough_connection(US"msg passed for delivery");
/* In the parent, wait if synchronous delivery is required. This will /* In the parent, wait if synchronous delivery is required. This will
always be the case in MUA wrapper mode. */ always be the case in MUA wrapper mode. */
if (synchronous_delivery) if (f.synchronous_delivery)
{ {
int status; int status;
while (wait(&status) != pid); while (wait(&status) != pid);
if ((status & 0x00ff) != 0) if ((status & 0x00ff) != 0)
log_write(0, LOG_MAIN|LOG_PANIC, log_write(0, LOG_MAIN|LOG_PANIC,
"process %d crashed with signal %d while delivering %s", "process %d crashed with signal %d while delivering %s",
(int)pid, status & 0x00ff, message_id); (int)pid, status & 0x00ff, message_id);
if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE, US"mai n"); if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE, US"mai n");
} }
} }
 End of changes. 307 change blocks. 
556 lines changed or deleted 396 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)