setcap-wrapper: Addressing more PR feedback, unifying drvs, and cleaning up a bit

This commit is contained in:
Parnell Springmeyer 2017-01-29 01:07:12 -06:00
parent e92b8402b0
commit 3fe7b1a4c9
No known key found for this signature in database
GPG Key ID: DCCF89258EAD874A
4 changed files with 46 additions and 109 deletions

View File

@ -3,17 +3,27 @@ let
inherit (config.security) wrapperDir; inherit (config.security) wrapperDir;
isNotNull = v: if v != null || v != "" then true else false; wrappers = config.security.wrappers;
mkWrapper = { program, source ? null, ...}: ''
if ! source=${if source != null then source else "$(readlink -f $(PATH=$WRAPPER_PATH type -tP ${program}))"}; then
# If we can't find the program, fall back to the
# system profile.
source=/nix/var/nix/profiles/default/bin/${program}
fi
cfg = config.security.wrappers; gcc -Wall -O2 -DSOURCE_PROG=\"$source\" -DWRAPPER_DIR=\"${config.security.wrapperDir}\" \
-lcap-ng -lcap ${./permissions-wrapper.c} -o $out/bin/${program}.wrapper -L ${pkgs.libcap.lib}/lib -L ${pkgs.libcap_ng}/lib \
-I ${pkgs.libcap.dev}/include -I ${pkgs.libcap_ng}/include -I ${pkgs.linuxHeaders}/include
'';
setcapWrappers = import ./setcap-wrapper-drv.nix { wrappedPrograms = pkgs.stdenv.mkDerivation {
inherit config lib pkgs; name = "permissions-wrapper";
}; unpackPhase = "true";
installPhase = ''
setuidWrappers = import ./setuid-wrapper-drv.nix { mkdir -p $out/bin
inherit config lib pkgs; ${lib.concatMapStrings mkWrapper wrappers}
}; '';
}
###### Activation script for the setcap wrappers ###### Activation script for the setcap wrappers
mkSetcapProgram = mkSetcapProgram =
@ -23,8 +33,10 @@ let
, owner ? "nobody" , owner ? "nobody"
, group ? "nogroup" , group ? "nogroup"
... ...
}: '' }:
cp ${setcapWrappers}/bin/${program}.wrapper $wrapperDir/${program} assert (lib.versionAtLeast (lib.getVersion config.boot.kernelPackages.kernel) "4.3");
''
cp ${wrappedPrograms}/bin/${program}.wrapper $wrapperDir/${program}
# Prevent races # Prevent races
chmod 0000 $wrapperDir/${program} chmod 0000 $wrapperDir/${program}
@ -33,9 +45,6 @@ let
# Set desired capabilities on the file plus cap_setpcap so # Set desired capabilities on the file plus cap_setpcap so
# the wrapper program can elevate the capabilities set on # the wrapper program can elevate the capabilities set on
# its file into the Ambient set. # its file into the Ambient set.
#
# Only set the capabilities though if we're being told to
# do so.
${pkgs.libcap.out}/bin/setcap "cap_setpcap,${capabilities}" $wrapperDir/${program} ${pkgs.libcap.out}/bin/setcap "cap_setpcap,${capabilities}" $wrapperDir/${program}
# Set the executable bit # Set the executable bit
@ -53,7 +62,7 @@ let
, permissions ? "u+rx,g+x,o+x" , permissions ? "u+rx,g+x,o+x"
... ...
}: '' }: ''
cp ${setuidWrappers}/bin/${program}.wrapper $wrapperDir/${program} cp ${wrappedPrograms}/bin/${program}.wrapper $wrapperDir/${program}
# Prevent races # Prevent races
chmod 0000 $wrapperDir/${program} chmod 0000 $wrapperDir/${program}
@ -147,10 +156,10 @@ in
###### implementation ###### implementation
config = { config = {
# Make sure our setcap-wrapper dir exports to the PATH env # Make sure our wrapperDir exports to the PATH env variable when
# variable when initializing the shell # initializing the shell
environment.extraInit = '' environment.extraInit = ''
# The permissions wrappers override other bin directories. # Wrappers override other bin directories.
export PATH="${wrapperDir}:$PATH" export PATH="${wrapperDir}:$PATH"
''; '';
@ -162,16 +171,17 @@ in
config.security.setuidPrograms) config.security.setuidPrograms)
++ lib.mapAttrsToList ++ lib.mapAttrsToList
(n: v: (if v ? "program" then v else v // {program=n;})) (n: v: (if v ? "program" then v else v // {program=n;}))
cfg.wrappers; wrappers;
wrapperPrograms = mkWrappedPrograms =
builtins.map builtins.map
(s: if (s ? "setuid" && s.setuid == true) || (s: if (s ? "capabilities")
then mkSetcapProgram s
else if
(s ? "setuid" && s.setuid == true) ||
(s ? "setguid" && s.setguid == true) || (s ? "setguid" && s.setguid == true) ||
(s ? "permissions") (s ? "permissions")
then mkSetuidProgram s then mkSetuidProgram s
else if (s ? "capabilities")
then mkSetcapProgram s
else "" else ""
) programs; ) programs;
@ -185,7 +195,7 @@ in
wrapperDir=$(mktemp --directory --tmpdir=${wrapperDir} wrappers.XXXXXXXXXX) wrapperDir=$(mktemp --directory --tmpdir=${wrapperDir} wrappers.XXXXXXXXXX)
chmod a+rx $wrapperDir chmod a+rx $wrapperDir
${lib.concatStringsSep "\n" (builtins.filter isNotNull cfg.wrappers)} ${lib.concatStringsSep "\n" mkWrappedPrograms}
''; '';
}; };
} }

View File

@ -26,16 +26,6 @@ extern char **environ;
static char * sourceProg = SOURCE_PROG; static char * sourceProg = SOURCE_PROG;
static char * wrapperDir = WRAPPER_DIR; static char * wrapperDir = WRAPPER_DIR;
// Make sure we have the WRAPPER_TYPE macro specified at compile
// time...
#ifdef WRAPPER_SETCAP
static char * wrapperType = "setcap";
#elif defined WRAPPER_SETUID
static char * wrapperType = "setuid";
#else
#error "Program must be compiled with either the WRAPPER_SETCAP or WRAPPER_SETUID macro"
#endif
// Update the capabilities of the running process to include the given // Update the capabilities of the running process to include the given
// capability in the Ambient set. // capability in the Ambient set.
static void set_ambient_cap(cap_value_t cap) static void set_ambient_cap(cap_value_t cap)
@ -66,7 +56,7 @@ static int make_caps_ambient(const char *selfPath)
if(!caps) if(!caps)
{ {
fprintf(stderr, "could not retreive the capability set for this file\n"); fprintf(stderr, "no caps set or could not retrieve the caps for this file, not doing anything...\n");
return 1; return 1;
} }
@ -171,6 +161,16 @@ int main(int argc, char * * argv)
assert(selfPathSize > 0); assert(selfPathSize > 0);
// Assert we have room for the zero byte, this ensures the path
// isn't being truncated because it's too big for the buffer.
//
// A better way to handle this might be to use something like the
// whereami library (https://github.com/gpakosz/whereami) or a
// loop that resizes the buffer and re-reads the link if the
// contents are being truncated.
assert(selfPathSize < sizeof(selfPath));
// Set the zero byte since readlink doesn't do that for us.
selfPath[selfPathSize] = '\0'; selfPath[selfPathSize] = '\0';
// Make sure that we are being executed from the right location, // Make sure that we are being executed from the right location,
@ -207,8 +207,7 @@ int main(int argc, char * * argv)
// Read the capabilities set on the file and raise them in to the // Read the capabilities set on the file and raise them in to the
// Ambient set so the program we're wrapping receives the // Ambient set so the program we're wrapping receives the
// capabilities too! // capabilities too!
if (strcmp(wrapperType, "setcap") == 0) make_caps_ambient(selfPath);
assert(!make_caps_ambient(selfPath));
execve(sourceProg, argv, environ); execve(sourceProg, argv, environ);

View File

@ -1,37 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.security.wrappers;
# Produce a shell-code splice intended to be stitched into one of
# the build or install phases within the derivation.
mkSetcapWrapper = { program, source ? null, ...}: ''
if ! source=${if source != null then source else "$(readlink -f $(PATH=$PERMISSIONS_WRAPPER_PATH type -tP ${program}))"}; then
# If we can't find the program, fall back to the
# system profile.
source=/nix/var/nix/profiles/default/bin/${program}
fi
gcc -Wall -O2 -DWRAPPER_SETCAP=1 -DSOURCE_PROG=\"$source\" -DWRAPPER_DIR=\"${config.security.run-wrapperDir}\" \
-lcap-ng -lcap ${./permissions-wrapper.c} -o $out/bin/${program}.wrapper -L ${pkgs.libcap.lib}/lib -L ${pkgs.libcap_ng}/lib \
-I ${pkgs.libcap.dev}/include -I ${pkgs.libcap_ng}/include -I ${pkgs.linuxHeaders}/include
'';
in
# This is only useful for Linux platforms and a kernel version of
# 4.3 or greater
assert pkgs.stdenv.isLinux;
assert lib.versionAtLeast (lib.getVersion config.boot.kernelPackages.kernel) "4.3";
pkgs.stdenv.mkDerivation {
name = "setcap-wrapper";
unpackPhase = "true";
buildInputs = [ pkgs.linuxHeaders ];
installPhase = ''
mkdir -p $out/bin
# Concat together all of our shell splices to compile
# binary wrapper programs for all configured setcap programs.
${lib.concatMapStrings mkSetcapWrapper cfg.setcap}
'';
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.security.wrappers;
# Produce a shell-code splice intended to be stitched into one of
# the build or install phases within the derivation.
mkSetuidWrapper = { program, source ? null, ...}: ''
if ! source=${if source != null then source else "$(readlink -f $(PATH=$WRAPPER_PATH type -tP ${program}))"}; then
# If we can't find the program, fall back to the
# system profile.
source=/nix/var/nix/profiles/default/bin/${program}
fi
gcc -Wall -O2 -DWRAPPER_SETUID=1 -DSOURCE_PROG=\"$source\" -DWRAPPER_DIR=\"${config.security.run-wrapperDir}\" \
-lcap-ng -lcap ${./permissions-wrapper.c} -o $out/bin/${program}.wrapper -L ${pkgs.libcap.lib}/lib -L ${pkgs.libcap_ng}/lib \
-I ${pkgs.libcap.dev}/include -I ${pkgs.libcap_ng}/include -I ${pkgs.linuxHeaders}/include
'';
in
# This is only useful for Linux platforms and a kernel version of
# 4.3 or greater
assert pkgs.stdenv.isLinux;
pkgs.stdenv.mkDerivation {
name = "setuid-wrapper";
unpackPhase = "true";
installPhase = ''
mkdir -p $out/bin
# Concat together all of our shell splices to compile
# binary wrapper programs for all configured setcap programs.
${lib.concatMapStrings mkSetuidWrapper cfg.setuid}
'';
}