Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions nix-capabilities.cc
Original file line number Diff line number Diff line change
@@ -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 <iostream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>

void usage() {
std::cerr << "USAGE:"
<< " <chown|chmod>"
<< " <path to file/directory under /nix to change ownership>"
<< " <uid|perm>"
<< " [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;
}
44 changes: 44 additions & 0 deletions src/libstore/local-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,50 @@

#include <sqlite3.h>

#include <sys/wait.h>

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<const char *> 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<char *const *>(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<const char *> argv{capabilitesBinary.c_str(), "chmod", __file,
mode.str().c_str(), nullptr};
return exec(argv[0], const_cast<char *const *>(argv.data()));
}

#define chmod(file, mode) chmodx(file, mode)
#define chown(file, user, group) chownx(file, user, group)

namespace nix {

Expand Down