diff options
author | Alyssa Ross <hi@alyssa.is> | 2023-11-21 16:12:21 +0100 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2023-11-21 16:12:48 +0100 |
commit | 048a4cd441a59cbf89defb18bb45c9f0b4429b35 (patch) | |
tree | f8f5850ff05521ab82d65745894714a8796cbfb6 /nixos/modules/security/wrappers/wrapper.c | |
parent | 030c5028b07afcedce7c5956015c629486cc79d9 (diff) | |
parent | 4c2d05dd6435d449a3651a6dd314d9411b5f8146 (diff) | |
download | nixpkgs-rootfs.tar nixpkgs-rootfs.tar.gz nixpkgs-rootfs.tar.bz2 nixpkgs-rootfs.tar.lz nixpkgs-rootfs.tar.xz nixpkgs-rootfs.tar.zst nixpkgs-rootfs.zip |
Signed-off-by: Alyssa Ross <hi@alyssa.is>
Diffstat (limited to 'nixos/modules/security/wrappers/wrapper.c')
-rw-r--r-- | nixos/modules/security/wrappers/wrapper.c | 57 |
1 files changed, 49 insertions, 8 deletions
diff --git a/nixos/modules/security/wrappers/wrapper.c b/nixos/modules/security/wrappers/wrapper.c index 2cf1727a31c..3277e7ef6f7 100644 --- a/nixos/modules/security/wrappers/wrapper.c +++ b/nixos/modules/security/wrappers/wrapper.c @@ -17,14 +17,15 @@ #include <syscall.h> #include <byteswap.h> +// imported from glibc +#include "unsecvars.h" + #ifndef SOURCE_PROG #error SOURCE_PROG should be defined via preprocessor commandline #endif // 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; @@ -45,12 +46,6 @@ 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) { @@ -151,9 +146,55 @@ static int make_caps_ambient(const char *self_path) { return 0; } +// These are environment variable aliases for glibc tunables. +// This list shouldn't grow further, since this is a legacy mechanism. +// Any future tunables are expected to only be accessible through GLIBC_TUNABLES. +// +// They are not included in the glibc-provided UNSECURE_ENVVARS list, +// since any SUID executable ignores them. This wrapper also serves +// executables that are merely granted ambient capabilities, rather than +// being SUID, and hence don't run in secure mode. We'd like them to +// defend those in depth as well, so we clear these explicitly. +// +// Except for MALLOC_CHECK_ (which is marked SXID_ERASE), these are all +// marked SXID_IGNORE (ignored in secure mode), so even the glibc version +// of this wrapper would leave them intact. +#define UNSECURE_ENVVARS_TUNABLES \ + "MALLOC_CHECK_\0" \ + "MALLOC_TOP_PAD_\0" \ + "MALLOC_PERTURB_\0" \ + "MALLOC_MMAP_THRESHOLD_\0" \ + "MALLOC_TRIM_THRESHOLD_\0" \ + "MALLOC_MMAP_MAX_\0" \ + "MALLOC_ARENA_MAX\0" \ + "MALLOC_ARENA_TEST\0" + int main(int argc, char **argv) { ASSERT(argc >= 1); + int debug = getenv(wrapper_debug) != NULL; + + // Drop insecure environment variables explicitly + // + // glibc does this automatically in SUID binaries, but we'd like to cover this: + // + // a) before it gets to glibc + // b) in binaries that are only granted ambient capabilities by the wrapper, + // but don't run with an altered effective UID/GID, nor directly gain + // capabilities themselves, and thus don't run in secure mode. + // + // We're using musl, which doesn't drop environment variables in secure mode, + // and we'd also like glibc-specific variables to be covered. + // + // If we don't explicitly unset them, it's quite easy to just set LD_PRELOAD, + // have it passed through to the wrapped program, and gain privileges. + for (char *unsec = UNSECURE_ENVVARS_TUNABLES UNSECURE_ENVVARS; *unsec; unsec = strchr(unsec, 0) + 1) { + if (debug) { + fprintf(stderr, "unsetting %s\n", unsec); + } + unsetenv(unsec); + } + // Read the capabilities set on the wrapper and raise them in to // the ambient set so the program we're wrapping receives the // capabilities too! |