diff --git a/nix-capabilities.cc b/nix-capabilities.cc new file mode 100644 index 000000000000..4e51edc9d752 --- /dev/null +++ b/nix-capabilities.cc @@ -0,0 +1,101 @@ +/* +- 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 + +void usage() { + std::cerr << "USAGE:" + << " " + << " " + << " " + << " [gid]" << std::endl; +} + +uint32_t getPerm(const std::string &perm) { + // 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) { + char* 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; + } + + std::string 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) { + std::cerr << "Path '" << argv[2] << "' should point to /nix/*"; + return -1; + } + + std::string uidOrPerm{argv[3]}; + if (mode == "chown") { + if (argc != 5) { + usage(); + return -1; + } + std::string gid{argv[4]}; + 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(); + 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 {