summaryrefslogtreecommitdiffstats
path: root/debian/patches/84_24-CVE-2020-28008-Assorted-attacks-in-Exim-s-spool-dire.patch
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:47:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:47:27 +0000
commitd5eb37dd4a5a433c40c3c1e7ead424add62663f8 (patch)
tree6a18289cb463d11227d1fa4c990548e50a09d917 /debian/patches/84_24-CVE-2020-28008-Assorted-attacks-in-Exim-s-spool-dire.patch
parentAdding upstream version 4.92. (diff)
downloadexim4-d5eb37dd4a5a433c40c3c1e7ead424add62663f8.tar.xz
exim4-d5eb37dd4a5a433c40c3c1e7ead424add62663f8.zip
Adding debian version 4.92-8+deb10u6.debian/4.92-8+deb10u6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'debian/patches/84_24-CVE-2020-28008-Assorted-attacks-in-Exim-s-spool-dire.patch')
-rw-r--r--debian/patches/84_24-CVE-2020-28008-Assorted-attacks-in-Exim-s-spool-dire.patch205
1 files changed, 205 insertions, 0 deletions
diff --git a/debian/patches/84_24-CVE-2020-28008-Assorted-attacks-in-Exim-s-spool-dire.patch b/debian/patches/84_24-CVE-2020-28008-Assorted-attacks-in-Exim-s-spool-dire.patch
new file mode 100644
index 0000000..2bda99c
--- /dev/null
+++ b/debian/patches/84_24-CVE-2020-28008-Assorted-attacks-in-Exim-s-spool-dire.patch
@@ -0,0 +1,205 @@
+From 5fec3406547fd1e46838a76f000102beb6bfe468 Mon Sep 17 00:00:00 2001
+From: "Heiko Schlittermann (HS12-RIPE)" <hs@schlittermann.de>
+Date: Sun, 14 Mar 2021 12:16:57 +0100
+Subject: [PATCH 24/29] CVE-2020-28008: Assorted attacks in Exim's spool
+ directory
+
+We patch dbfn_open() by introducing two functions priv_drop_temp() and
+priv_restore() (inspired by OpenSSH's functions temporarily_use_uid()
+and restore_uid()), which temporarily drop and restore root privileges
+thanks to seteuid(). This goes against Exim's developers' wishes ("Exim
+(the project) doesn't trust seteuid to work reliably") but, to the best
+of our knowledge, seteuid() works everywhere and is the only way to
+securely fix dbfn_open().
+
+(cherry picked from commit 18da59151dbafa89be61c63580bdb295db36e374)
+(cherry picked from commit b05dc3573f4cd476482374b0ac0393153d344338)
+---
+ doc/ChangeLog | 6 +++
+ src/dbfn.c | 110 +++++++++++++++++++++++++-----------------
+ test/stderr/0275 | 2 +-
+ test/stderr/0278 | 2 +-
+ test/stderr/0386 | 2 +-
+ test/stderr/0388 | 2 +-
+ test/stderr/0402 | 2 +-
+ test/stderr/0403 | 2 +-
+ test/stderr/0404 | 2 +-
+ test/stderr/0408 | 2 +-
+ test/stderr/0487 | 2 +-
+ 11 files changed, 80 insertions(+), 54 deletions(-)
+
+diff --git a/doc/ChangeLog b/doc/ChangeLog
+index 5741fb212..b32347c5b 100644
+--- a/doc/ChangeLog
++++ b/doc/ChangeLog
+@@ -14,6 +14,12 @@ JH/42 Bug 2545: Fix CHUNKING for all RCPT commands rejected. Previously we
+
+ Exim version 4.92.2
+ -------------------
++QS/02 PID file creation/deletion: only possible if uid=0 or uid is the Exim
++ runtime user.
++
++QS/01 Creation of (database) files in $spool_dir: only uid=0 or the euid of
++ the Exim runtime user are allowed to create files.
++
+
+ HS/01 Handle trailing backslash gracefully. (CVE-2019-15846)
+
+diff --git a/src/dbfn.c b/src/dbfn.c
+index 336cfe73e..902756508 100644
+--- a/src/dbfn.c
++++ b/src/dbfn.c
+@@ -59,6 +59,66 @@ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg);
+
+
+
++static enum {
++ PRIV_DROPPING, PRIV_DROPPED,
++ PRIV_RESTORING, PRIV_RESTORED
++} priv_state = PRIV_RESTORED;
++
++static uid_t priv_euid;
++static gid_t priv_egid;
++static gid_t priv_groups[EXIM_GROUPLIST_SIZE + 1];
++static int priv_ngroups;
++
++/* Inspired by OpenSSH's temporarily_use_uid(). Thanks! */
++
++static void
++priv_drop_temp(const uid_t temp_uid, const gid_t temp_gid)
++{
++if (priv_state != PRIV_RESTORED) _exit(EXIT_FAILURE);
++priv_state = PRIV_DROPPING;
++
++priv_euid = geteuid();
++if (priv_euid == root_uid)
++ {
++ priv_egid = getegid();
++ priv_ngroups = getgroups(nelem(priv_groups), priv_groups);
++ if (priv_ngroups < 0) _exit(EXIT_FAILURE);
++
++ if (priv_ngroups > 0 && setgroups(1, &temp_gid) != 0) _exit(EXIT_FAILURE);
++ if (setegid(temp_gid) != 0) _exit(EXIT_FAILURE);
++ if (seteuid(temp_uid) != 0) _exit(EXIT_FAILURE);
++
++ if (geteuid() != temp_uid) _exit(EXIT_FAILURE);
++ if (getegid() != temp_gid) _exit(EXIT_FAILURE);
++ }
++
++priv_state = PRIV_DROPPED;
++}
++
++/* Inspired by OpenSSH's restore_uid(). Thanks! */
++
++static void
++priv_restore(void)
++{
++if (priv_state != PRIV_DROPPED) _exit(EXIT_FAILURE);
++priv_state = PRIV_RESTORING;
++
++if (priv_euid == root_uid)
++ {
++ if (seteuid(priv_euid) != 0) _exit(EXIT_FAILURE);
++ if (setegid(priv_egid) != 0) _exit(EXIT_FAILURE);
++ if (priv_ngroups > 0 && setgroups(priv_ngroups, priv_groups) != 0) _exit(EXIT_FAILURE);
++
++ if (geteuid() != priv_euid) _exit(EXIT_FAILURE);
++ if (getegid() != priv_egid) _exit(EXIT_FAILURE);
++ }
++
++priv_state = PRIV_RESTORED;
++}
++
++
++
++
+ /*************************************************
+ * Open and lock a database file *
+ *************************************************/
+@@ -89,7 +149,6 @@ dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof)
+ {
+ int rc, save_errno;
+ BOOL read_only = flags == O_RDONLY;
+-BOOL created = FALSE;
+ flock_t lock_data;
+ uschar dirname[256], filename[256];
+
+@@ -111,12 +170,13 @@ exists, there is no error. */
+ snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
+ snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
+
++priv_drop_temp(exim_uid, exim_gid);
+ if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
+ {
+- created = TRUE;
+ (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, TRUE);
+ dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
+ }
++priv_restore();
+
+ if (dbblock->lockfd < 0)
+ {
+@@ -165,57 +225,17 @@ it easy to pin this down, there are now debug statements on either side of the
+ open call. */
+
+ snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
+-EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
+
++priv_drop_temp(exim_uid, exim_gid);
++EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
+ if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
+ {
+ DEBUG(D_hints_lookup)
+ debug_printf_indent("%s appears not to exist: trying to create\n", filename);
+- created = TRUE;
+ EXIM_DBOPEN(filename, dirname, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr));
+ }
+-
+ save_errno = errno;
+-
+-/* If we are running as root and this is the first access to the database, its
+-files will be owned by root. We want them to be owned by exim. We detect this
+-situation by noting above when we had to create the lock file or the database
+-itself. Because the different dbm libraries use different extensions for their
+-files, I don't know of any easier way of arranging this than scanning the
+-directory for files with the appropriate base name. At least this deals with
+-the lock file at the same time. Also, the directory will typically have only
+-half a dozen files, so the scan will be quick.
+-
+-This code is placed here, before the test for successful opening, because there
+-was a case when a file was created, but the DBM library still returned NULL
+-because of some problem. It also sorts out the lock file if that was created
+-but creation of the database file failed. */
+-
+-if (created && geteuid() == root_uid)
+- {
+- DIR *dd;
+- struct dirent *ent;
+- uschar *lastname = Ustrrchr(filename, '/') + 1;
+- int namelen = Ustrlen(name);
+-
+- *lastname = 0;
+- dd = opendir(CS filename);
+-
+- while ((ent = readdir(dd)))
+- if (Ustrncmp(ent->d_name, name, namelen) == 0)
+- {
+- struct stat statbuf;
+- Ustrcpy(lastname, ent->d_name);
+- if (Ustat(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid)
+- {
+- DEBUG(D_hints_lookup) debug_printf_indent("ensuring %s is owned by exim\n", filename);
+- if (Uchown(filename, exim_uid, exim_gid))
+- DEBUG(D_hints_lookup) debug_printf_indent("failed setting %s to owned by exim\n", filename);
+- }
+- }
+-
+- closedir(dd);
+- }
++priv_restore();
+
+ /* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
+ log the event - also for debugging - but debug only if the file just doesn't
+--
+2.30.2
+