diff options
Diffstat (limited to 'nixpkgs/nixos/modules/security/wrappers/wrapper.c')
-rw-r--r-- | nixpkgs/nixos/modules/security/wrappers/wrapper.c | 40 |
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))); |