Re: [exim-dev] [Exim-maintainers] Remote root vulnerability …

Top Page
Delete this message
Reply to this message
Author: David Woodhouse
Date:  
To: exim-dev
Subject: Re: [exim-dev] [Exim-maintainers] Remote root vulnerability in Exim
[Resend to exim-dev for comment, since exim-maintainers is quiet]

On Thu, 2010-12-09 at 10:18 +0000, David Woodhouse wrote:
> In the meantime, I'm looking at the second bug; the privilege
> escalation. I think the best answer to that is to kill the
> ALT_CONFIG_ROOT_ONLY option completely, and *always* allow the trusted
> user to use an alternative config but *only* if that config is listed in
> a file such as /etc/exim/allowed-configs. Does that seem sane?


Hm, we already have ALT_CONFIG_PREFIX which *almost* does this. It's
just that when defined, it refuses to run at all with a config that
doesn't match the prefix; rather than simply running without privileges.

I don't really see the point in refusing to run at all; the user can
always build their *own* Exim binary and run it without privileges.

So I've come up with the patch below. It kills the ALT_CONFIG_ROOT_ONLY
option, by making it effectively always set and never allowing even the
trusted Exim user to use arbitrary config files.

It repurposes the existing ALT_CONFIG_PREFIX, rather than adding a new
option and making things more complex. Instead of refusing to run with
config files that don't match the prefix, Exim will simply run without
root privileges. And it *will* run with root privileges when invoked
with the -C option for a file which *does* match the prefix.

Important user-visible changes would be:

- If you previously had ALT_CONFIG_ROOT_ONLY unset (the default), then
you must set ALT_CONFIG_PREFIX and use matching files if you want
to use alternative config files.

- If you previously had ALT_CONFIG_PREFIX set, then you should be aware
that matching config files will now be invoked with root privileges
regardless of the uid of the invoking user.

We could change the latter so that non-root and non-exim users invoking
config files in ALT_CONFIG_PREFIX are *never* granted root privs, but
I'm not sure we should. Comments?

diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 1ec4181..7a3bd9d 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -3331,35 +3331,27 @@ name, but it can be a colon-separated list of names. In this case, the first
file that exists is used. Failure to open an existing file stops Exim from
proceeding any further along the list, and an error is generated.

-When this option is used by a caller other than root or the Exim user, 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. However, if ALT_CONFIG_ROOT_ONLY is defined in
-&_Local/Makefile_&, root privilege is retained for &%-C%& only if the caller of
-Exim is root.
-
-That is, the Exim user is no longer privileged in this regard. This build-time
-option is not set by default in the Exim source distribution tarbundle.
-However, if you are using a &"packaged"& version of Exim (source or binary),
-the packagers might have enabled it.
-
-Setting ALT_CONFIG_ROOT_ONLY 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-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%&).
+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%&).

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 start. In addition, the file name must not contain the sequence &`/../`&.
-However, if the value of the &%-C%& option is identical to the value of
-CONFIGURE_FILE in &_Local/Makefile_&, Exim ignores &%-C%& and proceeds as
-usual. There is no default setting for ALT_CONFIG_PREFIX; when it is
-unset, any file name can be used with &%-C%&.
-
-ALT_CONFIG_PREFIX can be used to confine alternative configuration files
+prefix string which will be compared with any file named in a &%-C%& command
+line option. If the specified configuration file matches the prefix, Exim
+will retain root privilege. In addition, the file name must not contain the
+sequence &`/../`&. There is no default setting for ALT_CONFIG_PREFIX; when
+it is unset, any use of &%-C%& by a non-root user (including the Exim user)
+will lead to root privileges being dropped.
+
+If used, ALT_CONFIG_PREFIX should confine alternative configuration files
to a directory to which only root has access. This prevents someone who has
broken into the Exim account from running a privileged Exim with an arbitrary
configuration file.
diff --git a/doc/doc-src/FAQ.src b/doc/doc-src/FAQ.src
index b53070e..a9dbd80 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 the specified configuration
+       file matches the setting of \\ALT_CONFIG_PREFIX\\ that the Exim binary
+       was built with.


        Check that you have defined the spool directory correctly by running


@@ -1148,24 +1150,15 @@ Q0065: When (as \/root/\) I use -C to run Exim with an alternate configuration

 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.
+       the config file does not match the \\ALT_CONFIG_PREFIX\\ setting in your
+       \(Local/Makefile)\, the called Exim gives up its root privilege. Then it
+       can't write to the spool.
+
+       This means that you can't use -C with arbitrary config files (even as
+       \/root/\) to run an instance of Exim that is going to try to run
+       \%autoreply%\ from a process that is not \/root/\. You must ensure that
+       you use a config file matching the \\ALT_CONFIG_PREFIX\\ that your Exim
+       binary was built with.



 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 630c190..22e8f88 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -71,6 +71,9 @@ PP/20 Added a CONTRIBUTING file.  Fixed the documentation build to use http:
       XSL and documented dependency on system catalogs, with examples of how
       it normally works.


+DW/21 Remove ALT_CONFIG_ROOT_ONLY option (effectively making it always true).
+      Change ALT_CONFIG_PREFIX to allow invocation of matching config files
+      with root privileges, to offset this loss of functionality.


Exim version 4.72
-----------------
diff --git a/src/src/EDITME b/src/src/EDITME
index 050d9ad..e5feec7 100644
--- a/src/src/EDITME
+++ b/src/src/EDITME
@@ -459,35 +459,20 @@ FIXED_NEVER_USERS=root

#------------------------------------------------------------------------------
# The -C option allows Exim to be run with an alternate runtime configuration
-# file. When this is used by root or the Exim user, root privilege is retained
-# by the binary (for any other caller, it is dropped). You can restrict the
-# location of alternate configurations by defining a prefix below. Any file
-# used with -C must then start with this prefix (except that /dev/null is also
-# permitted if the caller is root, because that is used in the install script).
+# file. When this is used by the root user, root privilege is retained by the
+# binary (for any other caller including the Exim user, it is dropped). You can
+# allow the use of alternative configurations by defining a prefix below. When
+# using any file matching this prefix, Exim will retain root privileges.
# If the prefix specifies a directory that is owned by root, a compromise of
# the Exim account does not permit arbitrary alternate configurations to be
-# used. The prefix can be more restrictive than just a directory (the second
-# example).
+# used with root privileges. The prefix can be more restrictive than just a
+# directory (the second example).

# ALT_CONFIG_PREFIX=/some/directory/
# ALT_CONFIG_PREFIX=/some/directory/exim.conf-


#------------------------------------------------------------------------------
-# If you uncomment the following line, only root may use the -C or -D options
-# without losing root privilege. The -C option specifies an alternate runtime
-# configuration file, and the -D option changes macro values in the runtime
-# configuration. Uncommenting this line restricts what can be done with these
-# options. A call to receive a message (either one-off or via a daemon) cannot
-# successfully continue to deliver it, because the re-exec of Exim to regain
-# root privilege will fail, owing to the use of -C or -D by the Exim user.
-# However, you can still use -C for testing (as root) if you do separate Exim
-# calls for receiving a message and subsequently delivering it.
-
-# ALT_CONFIG_ROOT_ONLY=yes
-
-
-#------------------------------------------------------------------------------
# Uncommenting this option disables the use of the -D command line option,
# which changes the values of macros in the runtime configuration file.
# This is another protection against somebody breaking into the Exim account.
diff --git a/src/src/exim.c b/src/src/exim.c
index 50950fa..bfd0f47 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -3029,12 +3029,9 @@ if (setgroups(0, NULL) != 0)
}

/* 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
-not root or the exim user, or if this is a filter testing run, remove any
-setuid privilege the program has, and run as the underlying user.
-
-If ALT_CONFIG_ROOT_ONLY is defined, the exim user is locked out of this, which
-severely restricts the use of -C for some purposes.
+command line (either a new file name not matching ALT_CONFIG_PREFIX or a macro
+definition) and the caller is not root, or if this is a filter testing run,
+remove any setuid privilege the program has, and run as the underlying user.

 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).
@@ -3048,9 +3045,6 @@ configuration file changes and macro definitions haven't happened. */
 if ((                                            /* EITHER */
     (config_changed || macros != NULL) &&        /* Config changed, and */
     real_uid != root_uid &&                      /* Not root, and */
-    #ifndef ALT_CONFIG_ROOT_ONLY                 /* (when not locked out) */
-    real_uid != exim_uid &&                      /* Not exim, and */
-    #endif
     !running_in_test_harness                     /* Not fudged */
     ) ||                                         /*   OR   */
     expansion_test                               /* expansion testing */
@@ -3238,47 +3232,16 @@ else
   }


 /* Handle the case when we have removed the setuid privilege because of -C or
--D. This means that the caller of Exim was not root, and, provided that
-ALT_CONFIG_ROOT_ONLY is not defined, was not the Exim user that is built into
-the binary.
-
-If ALT_CONFIG_ROOT_ONLY is not defined, there is a problem if it turns out we
-were running as the exim user defined in the configuration file (different to
-the one in the binary). The sysadmin may expect this case to retain privilege
-because "the binary was called by the Exim user", but it hasn't, because of the
-order in which it handles this stuff. 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
-      non-privileged user is plausible, and might be wanted in some special
-      configurations. However, really_exim will have been set false when
-      privilege was dropped, to stop Exim trying to write to its normal log
-      files. Therefore, re-enable normal log processing, assuming the sysadmin
-      has set up the log directory correctly.
-
-  (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 either be root or the Exim user
-      defined in the binary (when deliver_drop_ privilege is false).
-
-If ALT_CONFIG_ROOT_ONLY is defined, we don't know whether we were called by the
-built-in exim user or one defined in the configuration. In either event,
-re-enable log processing, assuming the sysadmin knows what they are doing. */
+-D. This means that the caller of Exim was not root.
+
+We don't know whether we were called by the built-in exim user or one defined
+in the configuration. In either event, re-enable log processing, assuming the
+sysadmin knows what they are doing. */


 if (removed_privilege && (config_changed || macros != NULL) &&
     real_uid == exim_uid)
   {
-  #ifdef ALT_CONFIG_ROOT_ONLY
   really_exim = TRUE;   /* let logging work normally */
-  #else
-
-  if (deliver_drop_privilege)
-    really_exim = TRUE; /* let logging work normally */
-  else
-    log_write(0, LOG_MAIN|LOG_PANIC,
-      "exim user (uid=%d) is defined only at runtime; privilege lost for %s",
-      (int)exim_uid, config_changed? "-C" : "-D");
-  #endif
   }


/* Start up Perl interpreter if Perl support is configured and there is a


-- 
David Woodhouse                            Open Source Technology Centre
David.Woodhouse@???                              Intel Corporation