Merge pull request #259039 from edef1c/cve-2023-4911-mitigation
nixos/security/wrappers: use musl rather than glibc and explicitly unset insecure env vars
This commit is contained in:
commit
e462c9172c
|
@ -5,8 +5,29 @@ let
|
||||||
|
|
||||||
parentWrapperDir = dirOf wrapperDir;
|
parentWrapperDir = dirOf wrapperDir;
|
||||||
|
|
||||||
securityWrapper = sourceProg : pkgs.callPackage ./wrapper.nix {
|
# This is security-sensitive code, and glibc vulns happen from time to time.
|
||||||
|
# musl is security-focused and generally more minimal, so it's a better choice here.
|
||||||
|
# The dynamic linker is still a fairly complex piece of code, and the wrappers are
|
||||||
|
# quite small, so linking it statically is more appropriate.
|
||||||
|
securityWrapper = sourceProg : pkgs.pkgsStatic.callPackage ./wrapper.nix {
|
||||||
inherit sourceProg;
|
inherit sourceProg;
|
||||||
|
|
||||||
|
# glibc definitions of insecure environment variables
|
||||||
|
#
|
||||||
|
# We extract the single header file we need into its own derivation,
|
||||||
|
# so that we don't have to pull full glibc sources to build wrappers.
|
||||||
|
#
|
||||||
|
# They're taken from pkgs.glibc so that we don't have to keep as close
|
||||||
|
# an eye on glibc changes. Not every relevant variable is in this header,
|
||||||
|
# so we maintain a slightly stricter list in wrapper.c itself as well.
|
||||||
|
unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc)
|
||||||
|
({ name, ... }: {
|
||||||
|
name = "${name}-unsecvars";
|
||||||
|
installPhase = ''
|
||||||
|
mkdir $out
|
||||||
|
cp sysdeps/generic/unsecvars.h $out
|
||||||
|
'';
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
fileModeType =
|
fileModeType =
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include <syscall.h>
|
#include <syscall.h>
|
||||||
#include <byteswap.h>
|
#include <byteswap.h>
|
||||||
|
|
||||||
|
// imported from glibc
|
||||||
|
#include "unsecvars.h"
|
||||||
|
|
||||||
#ifndef SOURCE_PROG
|
#ifndef SOURCE_PROG
|
||||||
#error SOURCE_PROG should be defined via preprocessor commandline
|
#error SOURCE_PROG should be defined via preprocessor commandline
|
||||||
#endif
|
#endif
|
||||||
|
@ -151,9 +154,55 @@ static int make_caps_ambient(const char *self_path) {
|
||||||
return 0;
|
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) {
|
int main(int argc, char **argv) {
|
||||||
ASSERT(argc >= 1);
|
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
|
// Read the capabilities set on the wrapper and raise them in to
|
||||||
// the ambient set so the program we're wrapping receives the
|
// the ambient set so the program we're wrapping receives the
|
||||||
// capabilities too!
|
// capabilities too!
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{ stdenv, linuxHeaders, sourceProg, debug ? false }:
|
{ stdenv, unsecvars, linuxHeaders, sourceProg, debug ? false }:
|
||||||
# For testing:
|
# For testing:
|
||||||
# $ nix-build -E 'with import <nixpkgs> {}; pkgs.callPackage ./wrapper.nix { parentWrapperDir = "/run/wrappers"; debug = true; }'
|
# $ nix-build -E 'with import <nixpkgs> {}; pkgs.callPackage ./wrapper.nix { parentWrapperDir = "/run/wrappers"; debug = true; }'
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
|
@ -16,6 +16,6 @@ stdenv.mkDerivation {
|
||||||
dontStrip = debug;
|
dontStrip = debug;
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p $out/bin
|
mkdir -p $out/bin
|
||||||
$CC $CFLAGS ${./wrapper.c} -o $out/bin/security-wrapper
|
$CC $CFLAGS ${./wrapper.c} -I${unsecvars} -o $out/bin/security-wrapper
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue