mirror of
https://github.com/LnL7/nix-darwin.git
synced 2025-03-31 04:04:45 +00:00
security.wrappers: copy source programs instead of wrapping in binaries
Copying the wrapper code from NixOS and hoping that I managed to catch all the relevant dyld environment variables was not particularly confidence-inducing, so this commit removes the wrappers entirely and simply copies the source programs before modifying permissions. This means that the given source programs must safely handle being setuid/setgid binaries and being located in `/run/wrappers/bin`. There's now only `default.nix` in `modules/security/wrappers`, so I could have removed the directory, but I have left it for now in anticipation of other files potentially ending up there.
This commit is contained in:
parent
4fc8bc106a
commit
5776f9bf3d
3 changed files with 2 additions and 170 deletions
|
@ -52,10 +52,6 @@ let
|
|||
(opts: mkWrapper opts)
|
||||
(builtins.attrValues cfg.wrappers);
|
||||
|
||||
securityWrapper = sourceProg : pkgs.pkgsStatic.callPackage ./wrapper.nix {
|
||||
inherit sourceProg;
|
||||
};
|
||||
|
||||
mkWrapper =
|
||||
{ program
|
||||
, source
|
||||
|
@ -71,10 +67,10 @@ let
|
|||
codesigned = if codesign
|
||||
then ''
|
||||
# codesign ${source} to "$wrapperDir/${program}" INSTEAD OF the next line
|
||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
||||
cp ${source} "$wrapperDir/${program}"
|
||||
''
|
||||
else ''
|
||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
||||
cp ${source} "$wrapperDir/${program}"
|
||||
'';
|
||||
in
|
||||
''
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <sys/errno.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))
|
||||
|
||||
extern char **environ;
|
||||
|
||||
// Wrapper debug variable name
|
||||
static char *wrapper_debug = "WRAPPER_DEBUG";
|
||||
|
||||
static noreturn void assert_failure(const char *assertion) {
|
||||
fprintf(stderr, "Assertion `%s` in NixOS's wrapper.c failed.\n", assertion);
|
||||
fflush(stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
// The following comment and list of env vars are taken from the NixOS wrapper.
|
||||
// Most of them likely have no effect on Darwin, but I have chosen to continue
|
||||
// unsetting them just in case.
|
||||
|
||||
// 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 GLIBC_UNSECURE_ENVVARS \
|
||||
"GCONV_PATH\0" \
|
||||
"GETCONF_DIR\0" \
|
||||
"GLIBC_TUNABLES\0" \
|
||||
"HOSTALIASES\0" \
|
||||
"LD_AUDIT\0" \
|
||||
"LD_BIND_NOT\0" \
|
||||
"LD_BIND_NOW\0" \
|
||||
"LD_DEBUG\0" \
|
||||
"LD_DEBUG_OUTPUT\0" \
|
||||
"LD_DYNAMIC_WEAK\0" \
|
||||
"LD_HWCAP_MASK\0" \
|
||||
"LD_LIBRARY_PATH\0" \
|
||||
"LD_ORIGIN_PATH\0" \
|
||||
"LD_PRELOAD\0" \
|
||||
"LD_PROFILE\0" \
|
||||
"LD_SHOW_AUXV\0" \
|
||||
"LD_VERBOSE\0" \
|
||||
"LD_WARN\0" \
|
||||
"LOCALDOMAIN\0" \
|
||||
"LOCPATH\0" \
|
||||
"MALLOC_ARENA_MAX\0" \
|
||||
"MALLOC_ARENA_TEST\0" \
|
||||
"MALLOC_CHECK\0" \
|
||||
"MALLOC_MMAP_MAX_\0" \
|
||||
"MALLOC_MMAP_THRESHOLD_\0" \
|
||||
"MALLOC_PERTURB_\0" \
|
||||
"MALLOC_TOP_PAD_\0" \
|
||||
"MALLOC_TRACE\0" \
|
||||
"MALLOC_TRIM_THRESHOLD_\0" \
|
||||
"NIS_PATH\0" \
|
||||
"NLSPATH\0" \
|
||||
"RESOLV_HOST_CONF\0" \
|
||||
"RES_OPTIONS\0" \
|
||||
"TMPDIR\0" \
|
||||
"TZDIR\0"
|
||||
|
||||
// These are the variables that dyld refers to to load libraries.
|
||||
// Taken from `man dyld` on macOS 14.6.1.
|
||||
// macOS appears to ignore these variables for setuid binaries,
|
||||
// but again we err on the side of caution.
|
||||
#define DYLD_UNSECURE_ENVVARS \
|
||||
"DYLD_FRAMEWORK_PATH\0" \
|
||||
"DYLD_FALLBACK_FRAMEWORK_PATH\0" \
|
||||
"DYLD_VERSIONED_FRAMEWORK_PATH\0" \
|
||||
"DYLD_LIBRARY_PATH\0" \
|
||||
"DYLD_FALLBACK_LIBRARY_PATH\0" \
|
||||
"DYLD_VERSIONED_LIBRARY_PATH\0" \
|
||||
"DYLD_IMAGE_SUFFIX\0" \
|
||||
"DYLD_INSERT_LIBRARIES\0" \
|
||||
"DYLD_PRINT_TO_FILE\0" \
|
||||
"DYLD_PRINT_LIBRARIES\0" \
|
||||
"DYLD_PRINT_LOADERS\0" \
|
||||
"DYLD_PRINT_SEARCHING\0" \
|
||||
"DYLD_PRINT_APIS\0" \
|
||||
"DYLD_PRINT_BINDINGS\0" \
|
||||
"DYLD_PRINT_INITIALIZERS\0" \
|
||||
"DYLD_PRINT_SEGMENTS\0" \
|
||||
"DYLD_PRINT_ENV\0" \
|
||||
"DYLD_SHARED_REGION\0" \
|
||||
"DYLD_SHARED_CACHE_DIR\0" \
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
ASSERT(argc >= 1);
|
||||
|
||||
// argv[0] goes into a lot of places, to a far greater degree than other elements
|
||||
// of argv. glibc has had buffer overflows relating to argv[0], eg CVE-2023-6246.
|
||||
// Since we expect the wrappers to be invoked from either $PATH or /run/wrappers/bin,
|
||||
// there should be no reason to pass any particularly large values here, so we can
|
||||
// be strict for strictness' sake.
|
||||
ASSERT(strlen(argv[0]) < 512);
|
||||
|
||||
int debug = getenv(wrapper_debug) != NULL;
|
||||
|
||||
// Drop insecure environment variables explicitly
|
||||
//
|
||||
// dyld does this automatically in SUID binaries (for everything in DYLD_ENVVARS),
|
||||
// but we'd like to cover this:
|
||||
//
|
||||
// a) for variables that dyld does not unset (such as TMPDIR)
|
||||
// b) in binaries with entitlements, but don't run with an altered effective
|
||||
// UID/GID, and thus don't have env vars stripped automatically
|
||||
//
|
||||
// We're using musl, which doesn't drop environment variables in secure mode,
|
||||
// and we'd also like dyld-specific variables to be covered.
|
||||
//
|
||||
// If we don't explicitly unset them, it's quite easy to just set DYLD_INSERT_LIBRARIES,
|
||||
// have it passed through to the wrapped program, and gain privileges.
|
||||
for (char *unsec = GLIBC_UNSECURE_ENVVARS DYLD_UNSECURE_ENVVARS; *unsec; unsec = strchr(unsec, 0) + 1) {
|
||||
if (debug) {
|
||||
fprintf(stderr, "unsetting %s\n", unsec);
|
||||
}
|
||||
unsetenv(unsec);
|
||||
}
|
||||
|
||||
execve(SOURCE_PROG, argv, environ);
|
||||
|
||||
fprintf(stderr, "%s: cannot run `%s': %s\n",
|
||||
argv[0], SOURCE_PROG, strerror(errno));
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{ stdenv, sourceProg, debug ? false }:
|
||||
# For testing:
|
||||
# $ nix-build -E 'with import <nixpkgs> {}; pkgs.callPackage ./wrapper.nix { sourceProg = "${pkgs.hello}/bin/hello"; debug = true; }'
|
||||
stdenv.mkDerivation {
|
||||
name = "security-wrapper-${baseNameOf sourceProg}";
|
||||
dontUnpack = true;
|
||||
CFLAGS = [
|
||||
''-DSOURCE_PROG="${sourceProg}"''
|
||||
] ++ (if debug then [
|
||||
"-Werror" "-Og" "-g"
|
||||
] else [
|
||||
"-Wall" "-O2"
|
||||
]);
|
||||
dontStrip = debug;
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
$CC $CFLAGS ${./wrapper.c} -o $out/bin/security-wrapper
|
||||
'';
|
||||
}
|
Loading…
Add table
Reference in a new issue