From ac5711f131a462ee74961ce2396b7a82eccf851d Mon Sep 17 00:00:00 2001 From: Dmitry Prishep Date: Wed, 11 Aug 2021 14:06:58 +0300 Subject: [PATCH 1/4] Move to capabilities model rather than sudo --- nix-capabilities.cc | 96 +++++++++++++++++++++++++++++++++++++ src/libstore/local-store.cc | 44 +++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 nix-capabilities.cc diff --git a/nix-capabilities.cc b/nix-capabilities.cc new file mode 100644 index 000000000000..e23110ce5f94 --- /dev/null +++ b/nix-capabilities.cc @@ -0,0 +1,96 @@ +/* +- Complile with +``` +> g++ ./nix-capabilities.cc -o nix-capabilites +``` +- Set CAP_CHOWN and CAP_FOWNER capabilites for `nix-capabilities` +``` +> sudo setcap 'cap_chown,cap_fowner+eip' nix-capabilites +``` +- Test with /nix folder +``` +> ls -la /nix/ +drwxr-xr-x 4 dimap dimap 4096 Jun 30 22:40 var +> ./nix-capabilities chown /nix/var 0 0 +> ls -la /nix +... +drwxr-xr-x 4 root root 4096 Jun 30 22:40 var +``` +> ./nix-capabilities chmod /nix/var 1222 +> ls -la /nix/ +... +d-w--w--wT 4 root root 4096 Jun 30 22:40 var +*/ + +#include +#include +#include +#include +#include +#include + +void usage() { + std::cerr << "USAGE:" + << " " + << " " + << " " + << " [gid]" << std::endl; +} + +uint32_t getPerm(const std::string &perm) { + return std::stoul(perm, (std::size_t *)0, 8) & 0xfff; +} + +std::string getCanonicalizedPath(const char *path) { + auto res = realpath(path, NULL); + if (res == NULL) { + return ""; + } + std::string pathToFile{res}; + free(res); + return pathToFile; +} + +int main(int argc, char *argv[]) { + if (argc < 4) { + usage(); + return -1; + } + + std::string mode{argv[1]}; + if (mode != "chmod" && mode != "chown") { + usage(); + return -1; + } + + auto pathToFile = getCanonicalizedPath(argv[2]); + if (pathToFile.length() == 0) { + std::cerr << "Path " << argv[2] << " doesn't exist" << std::endl; + return -1; + }; + if (pathToFile.rfind("/nix/", 0) != 0) { + usage(); + return -1; + } + + std::string uidOrPerm{argv[3]}; + if (mode == "chown") { + if (argc != 5) { + usage(); + return -1; + } + std::string gid{argv[4]}; + return chown(pathToFile.c_str(), std::stoul(uidOrPerm), + std::stoul(gid)); + } else if (mode == "chmod") { + if (argc != 4) { + usage(); + return -1; + } + return chmod(pathToFile.c_str(), getPerm(uidOrPerm)); + } else { + usage(); + return -1; + } + return 0; +} diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 84ddd964b469..0a779129e9e4 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -36,6 +36,50 @@ #include +#include + +pid_t exec(const char *__file, char *const __argv[]) { + pid_t pid = fork(); + if (pid == 0) { + exit(execvp(__file, __argv)); + } else if (pid < 0) { + std::cerr << "Cannot spawn child process" << std::endl; + return -1; + } + pid_t status; + wait(&status); + return status; +} + +int chownx(const char *__file, __uid_t __owner, __gid_t __group) { + char *envVar = std::getenv("NIX_CAPABILITIES_BIN"); + std::string capabilitesBinary = + envVar == nullptr ? "nix-capabilities" : std::string{envVar}; + std::vector argv{capabilitesBinary.c_str(), + "chown", + __file, + std::to_string(__owner).c_str(), + std::to_string(__group).c_str(), + nullptr}; + return exec(argv[0], const_cast(argv.data())); +} + +int chmodx(const char *__file, __mode_t __mode) { + if (chmod(__file, __mode) == 0) { + return 0; + } + std::ostringstream mode; + mode << std::oct << (__mode & 0xfff); + char *envVar = std::getenv("NIX_CAPABILITIES_BIN"); + std::string capabilitesBinary = + envVar == nullptr ? "nix-capabilities" : std::string{envVar}; + std::vector argv{capabilitesBinary.c_str(), "chmod", __file, + mode.str().c_str(), nullptr}; + return exec(argv[0], const_cast(argv.data())); +} + +#define chmod(file, mode) chmodx(file, mode) +#define chown(file, user, group) chownx(file, user, group) namespace nix { From db31e8edf291f0fa0a5acc2661a3fb3b0efb1060 Mon Sep 17 00:00:00 2001 From: Dmitry Prishep Date: Wed, 11 Aug 2021 14:44:27 +0300 Subject: [PATCH 2/4] Downgrade to c++11 --- nix-capabilities.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nix-capabilities.cc b/nix-capabilities.cc index e23110ce5f94..bfb33dfb4563 100644 --- a/nix-capabilities.cc +++ b/nix-capabilities.cc @@ -22,9 +22,7 @@ drwxr-xr-x 4 root root 4096 Jun 30 22:40 var d-w--w--wT 4 root root 4096 Jun 30 22:40 var */ -#include #include -#include #include #include #include @@ -42,7 +40,7 @@ uint32_t getPerm(const std::string &perm) { } std::string getCanonicalizedPath(const char *path) { - auto res = realpath(path, NULL); + char* res = realpath(path, NULL); if (res == NULL) { return ""; } @@ -63,7 +61,7 @@ int main(int argc, char *argv[]) { return -1; } - auto pathToFile = getCanonicalizedPath(argv[2]); + std::string pathToFile = getCanonicalizedPath(argv[2]); if (pathToFile.length() == 0) { std::cerr << "Path " << argv[2] << " doesn't exist" << std::endl; return -1; From 57a58890a7349a42c7f074320fbfbc8f7fcf4759 Mon Sep 17 00:00:00 2001 From: Dmitry Prishep Date: Wed, 25 Aug 2021 15:53:32 +0300 Subject: [PATCH 3/4] Add check for root and remove setuid/setgid/... powers --- nix-capabilities.cc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/nix-capabilities.cc b/nix-capabilities.cc index bfb33dfb4563..f1f675f13de3 100644 --- a/nix-capabilities.cc +++ b/nix-capabilities.cc @@ -36,7 +36,9 @@ void usage() { } uint32_t getPerm(const std::string &perm) { - return std::stoul(perm, (std::size_t *)0, 8) & 0xfff; + // 0x1ff will drop suid/sgid/sticky-bit/... stuff remaining + // only plain-old `user-group-others` permissions + return std::stoul(perm, (std::size_t *)0, 8) & 0x1ff; } std::string getCanonicalizedPath(const char *path) { @@ -63,11 +65,11 @@ int main(int argc, char *argv[]) { std::string pathToFile = getCanonicalizedPath(argv[2]); if (pathToFile.length() == 0) { - std::cerr << "Path " << argv[2] << " doesn't exist" << std::endl; + std::cerr << "Path '" << argv[2] << "' doesn't exist" << std::endl; return -1; }; if (pathToFile.rfind("/nix/", 0) != 0) { - usage(); + std::cerr << "Path '" << argv[2] << "' should point to /nix/*"; return -1; } @@ -78,8 +80,13 @@ int main(int argc, char *argv[]) { return -1; } std::string gid{argv[4]}; - return chown(pathToFile.c_str(), std::stoul(uidOrPerm), - std::stoul(gid)); + uint32_t u_uid = std::stoul(uidOrPerm); + uint32_t u_gid = std::stoul(gid); + if (u_uid == 0 || u_gid == 0) { + std::cerr << "You cannot change owner or group to root user" << std::endl; + return -1; + } + return chown(pathToFile.c_str(), u_uid, u_gid); } else if (mode == "chmod") { if (argc != 4) { usage(); From 70524cf9a95d62cec8a84a861f4c006aaeb07dc5 Mon Sep 17 00:00:00 2001 From: Dmitry Prishep Date: Wed, 25 Aug 2021 16:29:41 +0300 Subject: [PATCH 4/4] Return sticky-bit --- nix-capabilities.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix-capabilities.cc b/nix-capabilities.cc index f1f675f13de3..4e51edc9d752 100644 --- a/nix-capabilities.cc +++ b/nix-capabilities.cc @@ -36,9 +36,9 @@ void usage() { } uint32_t getPerm(const std::string &perm) { - // 0x1ff will drop suid/sgid/sticky-bit/... stuff remaining - // only plain-old `user-group-others` permissions - return std::stoul(perm, (std::size_t *)0, 8) & 0x1ff; + // 0x3ff will drop suid/sgid stuff remaining + // only `user-group-others` + sticky-bit permissions + return std::stoul(perm, (std::size_t *)0, 8) & 0x3ff; } std::string getCanonicalizedPath(const char *path) {