Skip to content

Commit

Permalink
setuid-wrapper activation: Approximate atomicity
Browse files Browse the repository at this point in the history
This makes the replacement of the old wapper dir with the new one
atomic if the kernel and FS support RENAME_EXCHANGE, and falls back to
at least ensuring the old wrapper dir remains on the FS if interrupted
during the (now smaller) inconsistent window.

Fixes NixOS#18124
  • Loading branch information
shlevy committed Aug 31, 2016
1 parent 6b20d5b commit 14e5cde
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 7 deletions.
43 changes: 43 additions & 0 deletions nixos/modules/security/setuid-swap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* Try to atomically swap two files with renameat2/RENAME_EXCHANGE,
* falling back to swapping through a third temporary path.
*
* We use this for atomically (or almost atomically) updating the
* setuid-wrapper dir, rather than using the always-atomic symlink
* updating pattern, to avoid any potential issues with setuid
* binaries and symlinks.
*/

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/syscall.h>
#include <unistd.h>

static void move(const char * old, const char * new) {
if (rename(old, new) == -1) {
fprintf(stderr, "moving %s to %s: %s\n", old, new, strerror(errno));
exit(1);
}
}

int main(int argc, char ** argv) {
if (argc != 4) {
fprintf(stderr, "USAGE: %s OLD NEW TMP\n", argv[0]);
return 1;
}

if (syscall(SYS_renameat2, AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_EXCHANGE) == -1) {
fprintf(stderr, "directly swapping %s and %s failed: %s\nfalling back to swapping through %s\n",
argv[1], argv[2], strerror(errno), argv[3]);

move(argv[1], argv[3]);
move(argv[2], argv[1]);
move(argv[3], argv[2]);
}

return 0;
}
22 changes: 15 additions & 7 deletions nixos/modules/security/setuid-wrappers.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ let
'';
};

setuid-swap = pkgs.runCommand "setuid-swap" {} ''
mkdir -p $out/bin
gcc -Wall -O2 ${./setuid-swap.c} -o $out/bin/setuid-swap
fixupPhase
'';

in

{
Expand Down Expand Up @@ -102,11 +108,11 @@ in
source=/nix/var/nix/profiles/default/bin/${program}
fi
cp ${setuidWrapper}/bin/setuid-wrapper ${wrapperDir}/${program}
echo -n "$source" > ${wrapperDir}/${program}.real
chmod 0000 ${wrapperDir}/${program} # to prevent races
chown ${owner}.${group} ${wrapperDir}/${program}
chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" ${wrapperDir}/${program}
cp ${setuidWrapper}/bin/setuid-wrapper ${wrapperDir}-new/${program}
echo -n "$source" > ${wrapperDir}-new/${program}.real
chmod 0000 ${wrapperDir}-new/${program} # to prevent races
chown ${owner}.${group} ${wrapperDir}-new/${program}
chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" ${wrapperDir}-new/${program}
'';

in stringAfter [ "users" ]
Expand All @@ -115,9 +121,11 @@ in
# programs to be wrapped.
SETUID_PATH=${config.system.path}/bin:${config.system.path}/sbin
rm -f ${wrapperDir}/* # */
mkdir -p ${wrapperDir}-new
${concatMapStrings makeSetuidWrapper setuidPrograms}
${setuid-swap}/bin/setuid-swap ${wrapperDir}-new ${wrapperDir} ${wrapperDir}-tmp
rm -fR ${wrapperDir}-new
'';

};
Expand Down

0 comments on commit 14e5cde

Please sign in to comment.