From 261dc43e32f6039781ca92535e56f5caaa68b809 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 12 Dec 2010 02:41:37 +0000 Subject: [PATCH 1/1] Add TRUSTED_CONFIG_PREFIX_FILE option (Bug 1044, CVE-2010-4345) --- doc/doc-docbook/spec.xfpt | 51 ++++++++++--------- doc/doc-src/FAQ.src | 34 ++++++------- doc/doc-txt/ChangeLog | 3 ++ src/src/EDITME | 13 +++++ src/src/config.h.defaults | 1 + src/src/exim.c | 101 ++++++++++++++++++++++++++++++++++++-- 6 files changed, 157 insertions(+), 46 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 578485ddd..bbc3949c6 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -3334,14 +3334,17 @@ proceeding any further along the list, and an error is generated. When this option is used by a caller other than root, and the list is different from the compiled-in list, Exim gives up its root privilege immediately, and runs with the real and effective uid and gid set to those of the caller. - -This behaviour precludes the possibility of testing a configuration using -&%-C%& right through message reception and delivery, even if the caller is -root. The reception works, but by that time, Exim is running as the Exim user, -so when it re-executes to regain privilege for the delivery, the use of &%-C%& -causes privilege to be lost. However, root can test reception and delivery -using two separate commands (one to put a message on the queue, using &%-odq%&, -and another to do the delivery, using &%-M%&). +However, if a TRUSTED_CONFIG_PREFIX_LIST file is defined in &_Local/Makefile_&, +root privilege is retained for any configuration file which matches a prefix +listed in that file. + +Leaving TRUSTED_CONFIG_PREFIX_LIST unset precludes the possibility of testing +a configuration using &%-C%& right through message reception and delivery, +even if the caller is root. The reception works, but by that time, Exim is +running as the Exim user, so when it re-executes to regain privilege for the +delivery, the use of &%-C%& causes privilege to be lost. However, root can +test reception and delivery using two separate commands (one to put a message +on the queue, using &%-odq%&, and another to do the delivery, using &%-M%&). If ALT_CONFIG_PREFIX is defined &_in Local/Makefile_&, it specifies a prefix string with which any file named in a &%-C%& command line option @@ -4525,19 +4528,21 @@ A one-off alternate configuration can be specified by the &%-C%& command line option, which may specify a single file or a list of files. However, when &%-C%& is used, Exim gives up its root privilege, unless called by root (or unless the argument for &%-C%& is identical to the built-in value from -CONFIGURE_FILE). &%-C%& is useful mainly for checking the syntax of -configuration files before installing them. No owner or group checks are done -on a configuration file specified by &%-C%&. - -The Exim user is not trusted to specify an arbitrary configuration file with -the &%-C%& option to be executed with root privileges. This locks out the -possibility of testing a configuration using &%-C%& right through message -reception and delivery, even if the caller is root. The reception works, but -by that time, Exim is running as the Exim user, so when it re-execs to regain -privilege for the delivery, the use of &%-C%& causes privilege to be lost. -However, root can test reception and delivery using two separate commands -(one to put a message on the queue, using &%-odq%&, and another to do the -delivery, using &%-M%&). +CONFIGURE_FILE) or matches a prefix listed in the TRUSTED_CONFIG_PREFIX_LIST +file. &%-C%& is useful mainly for checking the syntax of configuration files +before installing them. No owner or group checks are done on a configuration +file specified by &%-C%&, if root privilege has been dropped. + +Even the Exim user is not trusted to specify an arbitrary configuration file +with the &%-C%& option to be used with root privileges, unless that file is +listed in the TRUSTED_CONFIG_PREFIX_LIST file. This locks out the possibility +of testing a configuration using &%-C%& right through message reception and +delivery, even if the caller is root. The reception works, but by that time, +Exim is running as the Exim user, so when it re-execs to regain privilege for +the delivery, the use of &%-C%& causes privilege to be lost. However, root +can test reception and delivery using two separate commands (one to put a +message on the queue, using &%-odq%&, and another to do the delivery, using +&%-M%&). If ALT_CONFIG_PREFIX is defined &_in Local/Makefile_&, it specifies a prefix string with which any file named in a &%-C%& command line option must @@ -33797,7 +33802,9 @@ which only root has access, this guards against someone who has broken into the Exim account from running a privileged Exim with an arbitrary configuration file, and using it to break into other accounts. .next -If a non-default configuration file is specified with &%-C%&, or macros are +If a non-trusted configuration file (i.e. the default configuration file or +one which is trusted by virtue of matching a prefix listed in the +TRUSTED_CONFIG_PREFIX_LIST file) is specified with &%-C%&, or if macros are given with &%-D%&, then root privilege is retained only if the caller of Exim is root. This locks out the possibility of testing a configuration using &%-C%& right through message reception and delivery, even if the caller is root. The diff --git a/doc/doc-src/FAQ.src b/doc/doc-src/FAQ.src index b53070e07..461b1a608 100644 --- a/doc/doc-src/FAQ.src +++ b/doc/doc-src/FAQ.src @@ -851,7 +851,9 @@ A0044: Exim has been unable to create a file in its spool area in which to If you are running Exim with an alternate configuration file using a command such as \"exim -C altconfig..."\, remember that the use of -C - takes away Exim's root privilege. + takes away Exim's root privilege, unless \\TRUSTED_CONFIG_PREFIX_FILE\\ + is set in \(Local/Makefile)\ and the corresponding file contains a + prefix which matches the alternative configuration file being used. Check that you have defined the spool directory correctly by running @@ -1147,25 +1149,17 @@ Q0065: When (as \/root/\) I use -C to run Exim with an alternate configuration trying to run an \%autoreply%\ transport. Why is this? A0065: When Exim is called with -C, it passes on -C to any instances of itself - that it calls (so that the whole sequence uses the same config file). If - it's running as \/exim/\ when it does this, all is well. However, if it - happens as a consequence of a non-privileged user running \%autoreply%\, - the called Exim gives up its root privilege. Then it can't write to the - spool. - - This means that you can't use -C (even as \/root/\) to run an instance of - Exim that is going to try to run \%autoreply%\ from a process that is - neither \/root/\ nor \/exim/\. Because of the architecture of Exim (using - re-execs to regain privilege), there isn't any way round this - restriction. Therefore, the only way you can make this scenario work is - to run the \%autoreply%\ transport as \/exim/\ (that is, the user that - owns the Exim spool files). This may be satisfactory for autoreplies - that are essentially system-generated, but of course is no good for - autoreplies from unprivileged users, where you want the \%autoreply%\ - transport to be run as the user. To get that to work with an alternate - configuration, you'll have to use two Exim binaries, with different - configuration file names in each. See S001 for a script that patches - the configuration name in an Exim binary. + that it calls (so that the whole sequence uses the same config file). + However, Exim gives up its root privilege if any user except \/root\/ + passes a -C option to use a non-default configuration file, and that + includes the case where Exim re-execs itself to regain root privilege. + Thus it can't write to the spool. + + The fix for this is to use the \\TRUSTED_CONFIG_PREFIX_LIST\\ build-time + option. This defines a file containing a list of 'trusted' prefixes for + configuration files. Any configuration file specified with -C, if it + matches a prefix listed in that file, will be used without dropping root + privileges (as long as it is not writeable by a non-root user). Q0066: What does the message \*unable to set gid=xxx or uid=xxx*\ mean? diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index afc854e44..cf307014b 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -86,6 +86,9 @@ DW/23 Bugzilla 1044: CVE-2010-4345 - part two: extend checks for writeability DW/24 Bugzilla 1044: CVE-2010-4345 - part three: remove ALT_CONFIG_ROOT_ONLY option (effectively making it always true). +DW/25 Add TRUSTED_CONFIG_PREFIX_FILE option to allow alternative configuration + files to be used while preserving root privileges. + Exim version 4.72 ----------------- diff --git a/src/src/EDITME b/src/src/EDITME index f8ba7cfe3..1ae139944 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -472,6 +472,19 @@ FIXED_NEVER_USERS=root # ALT_CONFIG_PREFIX=/some/directory/exim.conf- +#------------------------------------------------------------------------------ +# When a user other than root uses the -C option to override the configuration +# file (including the Exim user when re-executing Exim to regain root +# privileges for local message delivery), this will normally cause Exim to +# drop root privileges. The TRUSTED_CONFIG_PREFIX_LIST option, specifies +# a file which contains a list of trusted configuration prefixes (like the +# ALT_CONFIG_PREFIX above), one per line. If the -C option is used to specify +# a configuration file which matches a trusted prefix, root privileges are not +# dropped by Exim. + +# TRUSTED_CONFIG_PREFIX_LIST=/usr/exim/trusted_configs + + #------------------------------------------------------------------------------ # Uncommenting this option disables the use of the -D command line option, # which changes the values of macros in the runtime configuration file. diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 9e4556e8c..d3d7d65bb 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -13,6 +13,7 @@ in config.h unless some value is defined in Local/Makefile. If there is data, it's a default value. */ #define ALT_CONFIG_PREFIX +#define TRUSTED_CONFIG_PREFIX_LIST #define APPENDFILE_MODE 0600 #define APPENDFILE_DIRECTORY_MODE 0700 diff --git a/src/src/exim.c b/src/src/exim.c index 0d9378d46..9db61e2a9 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1845,10 +1845,103 @@ for (i = 1; i < argc; i++) } } #endif + if (real_uid != root_uid) + { + #ifdef TRUSTED_CONFIG_PREFIX_LIST + + if (Ustrstr(argrest, "/../")) + trusted_config = FALSE; + else + { + FILE *trust_list = Ufopen(TRUSTED_CONFIG_PREFIX_LIST, "rb"); + if (trust_list) + { + struct stat statbuf; + + if (fstat(fileno(trust_list), &statbuf) != 0 || + (statbuf.st_uid != root_uid /* owner not root */ + #ifdef CONFIGURE_OWNER + && statbuf.st_uid != config_uid /* owner not the special one */ + #endif + ) || /* or */ + (statbuf.st_gid != root_gid /* group not root */ + #ifdef CONFIGURE_GROUP + && statbuf.st_gid != config_gid /* group not the special one */ + #endif + && (statbuf.st_mode & 020) != 0 /* group writeable */ + ) || /* or */ + (statbuf.st_mode & 2) != 0) /* world writeable */ + { + trusted_config = FALSE; + fclose(trust_list); + } + else + { + /* Well, the trust list at least is up to scratch... */ + void *reset_point = store_get(0); + uschar *trusted_prefixes[32]; + int nr_prefixes = 0; + int i = 0; + + while (Ufgets(big_buffer, big_buffer_size, trust_list)) + { + uschar *start = big_buffer, *nl; + while (*start && isspace(*start)) + start++; + if (*start == '#') + continue; + nl = Ustrchr(start, '\n'); + if (nl) + *nl = 0; + trusted_prefixes[nr_prefixes++] = string_copy(start); + if (nr_prefixes == 32) + break; + } + fclose(trust_list); + + if (nr_prefixes) + { + int sep = 0; + uschar *list = argrest; + uschar *filename; + while (trusted_config && (filename = string_nextinlist(&list, + &sep, big_buffer, big_buffer_size)) != NULL) + { + for (i=0; i < nr_prefixes; i++) + { + int len = Ustrlen(trusted_prefixes[i]); + if (Ustrlen(filename) >= len && + Ustrncmp(filename, trusted_prefixes[i], len) == 0) + break; + } + if (i == nr_prefixes) + { + trusted_config = FALSE; + break; + } + } + } + else + { + /* No valid prefixes found in trust_list file. */ + trusted_config = FALSE; + } + } + } + else + { + /* Could not open trust_list file. */ + trusted_config = FALSE; + } + } + #else + /* Not root; don't trust config */ + trusted_config = FALSE; + #endif + } config_main_filelist = argrest; config_changed = TRUE; - trusted_config = FALSE; } break; @@ -3241,7 +3334,7 @@ else There is a problem if we were running as the Exim user. The sysadmin may expect this case to retain privilege because "the binary was called by the Exim user", but it hasn't, because either the -D option set macros, or the --C option set a non-default configuration file. There are two possibilities: +-C option set a non-trusted configuration file. There are two possibilities: (1) If deliver_drop_privilege is set, Exim is not going to re-exec in order to do message deliveries. Thus, the fact that it is running as a @@ -3253,8 +3346,8 @@ Exim user", but it hasn't, because either the -D option set macros, or the (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 - root for -C or -D, the caller must be root (when deliver_drop_privilege - is false). */ + root for -C or -D, the caller must either be root or be invoking a + trusted configuration file (when deliver_drop_privilege is false). */ if (removed_privilege && (!trusted_config || macros != NULL) && real_uid == exim_uid) -- 2.30.2