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)
|
(opts: mkWrapper opts)
|
||||||
(builtins.attrValues cfg.wrappers);
|
(builtins.attrValues cfg.wrappers);
|
||||||
|
|
||||||
securityWrapper = sourceProg : pkgs.pkgsStatic.callPackage ./wrapper.nix {
|
|
||||||
inherit sourceProg;
|
|
||||||
};
|
|
||||||
|
|
||||||
mkWrapper =
|
mkWrapper =
|
||||||
{ program
|
{ program
|
||||||
, source
|
, source
|
||||||
|
@ -71,10 +67,10 @@ let
|
||||||
codesigned = if codesign
|
codesigned = if codesign
|
||||||
then ''
|
then ''
|
||||||
# codesign ${source} to "$wrapperDir/${program}" INSTEAD OF the next line
|
# codesign ${source} to "$wrapperDir/${program}" INSTEAD OF the next line
|
||||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
cp ${source} "$wrapperDir/${program}"
|
||||||
''
|
''
|
||||||
else ''
|
else ''
|
||||||
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
|
cp ${source} "$wrapperDir/${program}"
|
||||||
'';
|
'';
|
||||||
in
|
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