···56 parentWrapperDir = dirOf wrapperDir;
78+ # This is security-sensitive code, and glibc vulns happen from time to time.
9+ # musl is security-focused and generally more minimal, so it's a better choice here.
10+ # The dynamic linker is still a fairly complex piece of code, and the wrappers are
11+ # quite small, so linking it statically is more appropriate.
12+ securityWrapper = sourceProg : pkgs.pkgsStatic.callPackage ./wrapper.nix {
13 inherit sourceProg;
14+15+ # glibc definitions of insecure environment variables
16+ #
17+ # We extract the single header file we need into its own derivation,
18+ # so that we don't have to pull full glibc sources to build wrappers.
19+ #
20+ # They're taken from pkgs.glibc so that we don't have to keep as close
21+ # an eye on glibc changes. Not every relevant variable is in this header,
22+ # so we maintain a slightly stricter list in wrapper.c itself as well.
23+ unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc)
24+ ({ name, ... }: {
25+ name = "${name}-unsecvars";
26+ installPhase = ''
27+ mkdir $out
28+ cp sysdeps/generic/unsecvars.h $out
29+ '';
30+ });
31 };
3233 fileModeType =
+49
nixos/modules/security/wrappers/wrapper.c
···17#include <syscall.h>
18#include <byteswap.h>
1900020#ifndef SOURCE_PROG
21#error SOURCE_PROG should be defined via preprocessor commandline
22#endif
···151 return 0;
152}
15300000000000000000000000154int main(int argc, char **argv) {
155 ASSERT(argc >= 1);
00000000000000000000000156157 // Read the capabilities set on the wrapper and raise them in to
158 // the ambient set so the program we're wrapping receives the
···17#include <syscall.h>
18#include <byteswap.h>
1920+// imported from glibc
21+#include "unsecvars.h"
22+23#ifndef SOURCE_PROG
24#error SOURCE_PROG should be defined via preprocessor commandline
25#endif
···154 return 0;
155}
156157+// These are environment variable aliases for glibc tunables.
158+// This list shouldn't grow further, since this is a legacy mechanism.
159+// Any future tunables are expected to only be accessible through GLIBC_TUNABLES.
160+//
161+// They are not included in the glibc-provided UNSECURE_ENVVARS list,
162+// since any SUID executable ignores them. This wrapper also serves
163+// executables that are merely granted ambient capabilities, rather than
164+// being SUID, and hence don't run in secure mode. We'd like them to
165+// defend those in depth as well, so we clear these explicitly.
166+//
167+// Except for MALLOC_CHECK_ (which is marked SXID_ERASE), these are all
168+// marked SXID_IGNORE (ignored in secure mode), so even the glibc version
169+// of this wrapper would leave them intact.
170+#define UNSECURE_ENVVARS_TUNABLES \
171+ "MALLOC_CHECK_\0" \
172+ "MALLOC_TOP_PAD_\0" \
173+ "MALLOC_PERTURB_\0" \
174+ "MALLOC_MMAP_THRESHOLD_\0" \
175+ "MALLOC_TRIM_THRESHOLD_\0" \
176+ "MALLOC_MMAP_MAX_\0" \
177+ "MALLOC_ARENA_MAX\0" \
178+ "MALLOC_ARENA_TEST\0"
179+180int main(int argc, char **argv) {
181 ASSERT(argc >= 1);
182+183+ int debug = getenv(wrapper_debug) != NULL;
184+185+ // Drop insecure environment variables explicitly
186+ //
187+ // glibc does this automatically in SUID binaries, but we'd like to cover this:
188+ //
189+ // a) before it gets to glibc
190+ // b) in binaries that are only granted ambient capabilities by the wrapper,
191+ // but don't run with an altered effective UID/GID, nor directly gain
192+ // capabilities themselves, and thus don't run in secure mode.
193+ //
194+ // We're using musl, which doesn't drop environment variables in secure mode,
195+ // and we'd also like glibc-specific variables to be covered.
196+ //
197+ // If we don't explicitly unset them, it's quite easy to just set LD_PRELOAD,
198+ // have it passed through to the wrapped program, and gain privileges.
199+ for (char *unsec = UNSECURE_ENVVARS_TUNABLES UNSECURE_ENVVARS; *unsec; unsec = strchr(unsec, 0) + 1) {
200+ if (debug) {
201+ fprintf(stderr, "unsetting %s\n", unsec);
202+ }
203+ unsetenv(unsec);
204+ }
205206 // Read the capabilities set on the wrapper and raise them in to
207 // the ambient set so the program we're wrapping receives the