summaryrefslogtreecommitdiffstats
path: root/src/priv.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 09:44:07 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 09:44:07 +0000
commit39ce00b8d520cbecbd6af87257e8fb11df0ec273 (patch)
tree4c21a2674c19e5c44be3b3550b476b9e63d8ae3d /src/priv.c
parentInitial commit. (diff)
downloadexim4-upstream/4.94.2.tar.xz
exim4-upstream/4.94.2.zip
Adding upstream version 4.94.2.upstream/4.94.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/priv.c')
-rw-r--r--src/priv.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/src/priv.c b/src/priv.c
new file mode 100644
index 0000000..94d4254
--- /dev/null
+++ b/src/priv.c
@@ -0,0 +1,76 @@
+#include "exim.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+
+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! */
+
+void
+priv_drop_temp(const uid_t temp_uid, const gid_t temp_gid)
+{
+if (priv_state != PRIV_RESTORED)
+ log_write(0, LOG_PANIC_DIE, "priv_drop_temp: unexpected priv_state %d != %d", priv_state, PRIV_RESTORED);
+
+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)
+ log_write(0, LOG_PANIC_DIE, "getgroups: %s", strerror(errno));
+
+ if (priv_ngroups > 0 && setgroups(1, &temp_gid) != 0)
+ log_write(0, LOG_PANIC_DIE, "setgroups: %s", strerror(errno));
+ if (setegid(temp_gid) != 0)
+ log_write(0, LOG_PANIC_DIE, "setegid(%d): %s", temp_gid, strerror(errno));
+ if (seteuid(temp_uid) != 0)
+ log_write(0, LOG_PANIC_DIE, "seteuid(%d): %s", temp_uid, strerror(errno));
+
+ if (geteuid() != temp_uid)
+ log_write(0, LOG_PANIC_DIE, "getdeuid() != %d", temp_uid);
+ if (getegid() != temp_gid)
+ log_write(0, LOG_PANIC_DIE, "getegid() != %d", temp_gid);
+ }
+
+priv_state = PRIV_DROPPED;
+}
+
+/* Inspired by OpenSSH's restore_uid(). Thanks! */
+
+void
+priv_restore(void)
+{
+if (priv_state != PRIV_DROPPED)
+ log_write(0, LOG_PANIC_DIE, "priv_restore: unexpected priv_state %d != %d", priv_state, PRIV_DROPPED);
+priv_state = PRIV_RESTORING;
+
+if (priv_euid == root_uid)
+ {
+ if (seteuid(priv_euid) != 0)
+ log_write(0, LOG_PANIC_DIE, "seteuid(%d): %s", priv_euid, strerror(errno));
+ if (setegid(priv_egid) != 0)
+ log_write(0, LOG_PANIC_DIE, "setegid(%d): %s", priv_egid, strerror(errno));
+ if (priv_ngroups > 0 && setgroups(priv_ngroups, priv_groups) != 0)
+ log_write(0, LOG_PANIC_DIE, "setgroups: %s", strerror(errno));
+
+ if (geteuid() != priv_euid)
+ log_write(0, LOG_PANIC_DIE, "getdeuid() != %d", priv_euid);
+ if (getegid() != priv_egid)
+ log_write(0, LOG_PANIC_DIE, "getdegid() != %d", priv_egid);
+ }
+
+priv_state = PRIV_RESTORED;
+}