about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/security/wrappers/wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/security/wrappers/wrapper.c')
-rw-r--r--nixpkgs/nixos/modules/security/wrappers/wrapper.c40
1 files changed, 34 insertions, 6 deletions
diff --git a/nixpkgs/nixos/modules/security/wrappers/wrapper.c b/nixpkgs/nixos/modules/security/wrappers/wrapper.c
index a21ec500208d..17776a97af81 100644
--- a/nixpkgs/nixos/modules/security/wrappers/wrapper.c
+++ b/nixpkgs/nixos/modules/security/wrappers/wrapper.c
@@ -1,3 +1,4 @@
+#define _GNU_SOURCE
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -16,7 +17,10 @@
 #include <syscall.h>
 #include <byteswap.h>
 
+// aborts when false, printing the failed expression
 #define ASSERT(expr) ((expr) ? (void) 0 : assert_failure(#expr))
+// aborts when returns non-zero, printing the failed expression and errno
+#define MUSTSUCCEED(expr) ((expr) ? print_errno_and_die(#expr) : (void) 0)
 
 extern char **environ;
 
@@ -41,6 +45,12 @@ static noreturn void assert_failure(const char *assertion) {
     abort();
 }
 
+static noreturn void print_errno_and_die(const char *assertion) {
+    fprintf(stderr, "Call `%s` in NixOS's wrapper.c failed: %s\n", assertion, strerror(errno));
+    fflush(stderr);
+    abort();
+}
+
 int get_last_cap(unsigned *last_cap) {
     FILE* file = fopen("/proc/sys/kernel/cap_last_cap", "r");
     if (file == NULL) {
@@ -177,6 +187,17 @@ int main(int argc, char **argv) {
         fprintf(stderr, "cannot readlink /proc/self/exe: %s", strerror(-self_path_size));
     }
 
+    unsigned int ruid, euid, suid, rgid, egid, sgid;
+    MUSTSUCCEED(getresuid(&ruid, &euid, &suid));
+    MUSTSUCCEED(getresgid(&rgid, &egid, &sgid));
+
+    // If true, then we did not benefit from setuid privilege escalation,
+    // where the original uid is still in ruid and different from euid == suid.
+    int didnt_suid = (ruid == euid) && (euid == suid);
+    // If true, then we did not benefit from setgid privilege escalation
+    int didnt_sgid = (rgid == egid) && (egid == sgid);
+
+
     // Make sure that we are being executed from the right location,
     // i.e., `safe_wrapper_dir'.  This is to prevent someone from creating
     // hard link `X' from some other location, along with a false
@@ -189,15 +210,22 @@ int main(int argc, char **argv) {
     ASSERT('/' == wrapper_dir[0]);
     ASSERT('/' == self_path[len]);
 
-    // Make *really* *really* sure that we were executed as
-    // `self_path', and not, say, as some other setuid program. That
-    // is, our effective uid/gid should match the uid/gid of
-    // `self_path'.
+    // If we got privileges with the fs set[ug]id bit, check that the privilege we
+    // got matches the one one we expected, ie that our effective uid/gid
+    // matches the uid/gid of `self_path`. This ensures that we were executed as
+    // `self_path', and not, say, as some other setuid program.
+    // We don't check that if we did not benefit from the set[ug]id bit, as
+    // can be the case in nosuid mounts or user namespaces.
     struct stat st;
     ASSERT(lstat(self_path, &st) != -1);
 
-    ASSERT(!(st.st_mode & S_ISUID) || (st.st_uid == geteuid()));
-    ASSERT(!(st.st_mode & S_ISGID) || (st.st_gid == getegid()));
+    // if the wrapper gained privilege with suid, check that we got the uid of the file owner
+    ASSERT(!((st.st_mode & S_ISUID) && !didnt_suid) || (st.st_uid == euid));
+    // if the wrapper gained privilege with sgid, check that we got the gid of the file group
+    ASSERT(!((st.st_mode & S_ISGID) && !didnt_sgid) || (st.st_gid == egid));
+    // same, but with suid instead of euid
+    ASSERT(!((st.st_mode & S_ISUID) && !didnt_suid) || (st.st_uid == suid));
+    ASSERT(!((st.st_mode & S_ISGID) && !didnt_sgid) || (st.st_gid == sgid));
 
     // And, of course, we shouldn't be writable.
     ASSERT(!(st.st_mode & (S_IWGRP | S_IWOTH)));