diff options
Diffstat (limited to 'osdep')
-rw-r--r-- | osdep/LinuxDropPrivileges.cpp | 164 | ||||
-rw-r--r-- | osdep/LinuxDropPrivileges.hpp | 9 |
2 files changed, 0 insertions, 173 deletions
diff --git a/osdep/LinuxDropPrivileges.cpp b/osdep/LinuxDropPrivileges.cpp deleted file mode 100644 index e2688e65..00000000 --- a/osdep/LinuxDropPrivileges.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "LinuxDropPrivileges.hpp" -#include <linux/capability.h> -#include <linux/securebits.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <pwd.h> -#include <stdlib.h> -#include <unistd.h> - -namespace ZeroTier { - -#ifndef PR_CAP_AMBIENT -// if we are on old libc, dropPrivileges is nop -void dropPrivileges(std::string homeDir) {} - -#else - -const char* TARGET_USER_NAME = "zerotier-one"; - -struct cap_header_struct { - __u32 version; - int pid; -}; - -struct cap_data_struct { - __u32 effective; - __u32 permitted; - __u32 inheritable; -}; - -// libc doesn't export capset, it is instead located in libcap -// We ignore libcap and call it manually. - -int capset(cap_header_struct* hdrp, cap_data_struct* datap) { - return syscall(SYS_capset, hdrp, datap); -} - -void notDropping(std::string homeDir) { - struct stat buf; - if (lstat(homeDir.c_str(), &buf) < 0) { - if (buf.st_uid != 0 || buf.st_gid != 0) { - fprintf(stderr, "ERROR: failed to drop privileges. Refusing to run as root, because %s was already used in nonprivileged mode.\n", homeDir.c_str()); - exit(1); - } - } - fprintf(stderr, "WARNING: failed to drop privileges, running as root\n"); -} - -int setCapabilities(int flags) { - cap_header_struct capheader = {_LINUX_CAPABILITY_VERSION_1, 0}; - cap_data_struct capdata; - capdata.inheritable = capdata.permitted = capdata.effective = flags; - return capset(&capheader, &capdata); -} - -void createOwnedHomedir(std::string homeDir, struct passwd* targetUser) { - struct stat buf; - if (lstat(homeDir.c_str(), &buf) < 0) { - if (errno == ENOENT) { - mkdir(homeDir.c_str(), 0755); - } else { - perror("cannot access home directory"); - exit(1); - } - } - - if (buf.st_uid != 0 || buf.st_gid != 0) { - // should be already owned by zerotier-one - if (targetUser->pw_uid != buf.st_uid) { - fprintf(stderr, "ERROR: %s not owned by zerotier-one or root\n", homeDir.c_str()); - exit(1); - } - return; - } - - // Change homedir owner to zerotier-one user. This is safe, because this directory is writable only by root, so no one could have created malicious hardlink. - long p = (long)fork(); - int exitcode = -1; - if (p > 0) { - waitpid(p, &exitcode, 0); - } else if (p == 0) { - std::string ownerString = std::to_string(targetUser->pw_uid) + ":" + std::to_string(targetUser->pw_gid); - execlp("chown", "chown", "-R", ownerString.c_str(), "--", homeDir.c_str(), NULL); - _exit(-1); - } - - if (exitcode != 0) { - fprintf(stderr, "failed to change owner of %s to %s\n", homeDir.c_str(), targetUser->pw_name); - exit(1); - } -} - -void dropPrivileges(std::string homeDir) { - // dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN - // and CAP_NET_RAW capabilities. - struct passwd* targetUser = getpwnam(TARGET_USER_NAME); - if (targetUser == NULL) { - // zerotier-one user not configured by package - return; - } - - createOwnedHomedir(homeDir, targetUser); - - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_RAW, 0, 0) < 0) { - // Kernel has no support for ambient capabilities. - notDropping(homeDir); - return; - } - - if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_NOROOT) < 0) { - notDropping(homeDir); - return; - } - - if (setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID)) < 0) { - fprintf(stderr, "ERROR: failed to set capabilities (not running as real root?)\n"); - exit(1); - } - - int oldDumpable = prctl(PR_GET_DUMPABLE); - - if (prctl(PR_SET_DUMPABLE, 0) < 0) { - // Disable ptracing. Otherwise there is a small window when previous - // compromised ZeroTier process could ptrace us, when we still have CAP_SETUID. - // (this is mitigated anyway on most distros by ptrace_scope=1) - perror("prctl(PR_SET_DUMPABLE)"); - exit(1); - } - - if (setgid(targetUser->pw_gid) < 0) { - perror("setgid"); - exit(1); - } - if (setuid(targetUser->pw_uid) < 0) { - perror("setuid"); - exit(1); - } - - if (setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW)) < 0) { - perror("could not drop capabilities after setuid"); - exit(1); - } - - if (prctl(PR_SET_DUMPABLE, oldDumpable) < 0) { - perror("could not restore dumpable flag"); - exit(1); - } - - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_ADMIN, 0, 0) < 0) { - perror("could not raise ambient CAP_NET_ADMIN"); - exit(1); - } - - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0) < 0) { - perror("could not raise ambient CAP_NET_RAW"); - exit(1); - } -} - -#endif -} diff --git a/osdep/LinuxDropPrivileges.hpp b/osdep/LinuxDropPrivileges.hpp deleted file mode 100644 index 111f682e..00000000 --- a/osdep/LinuxDropPrivileges.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef ZT_LINUXDROPPRIVILEGES_HPP -#define ZT_LINUXDROPPRIVILEGES_HPP -#include <string> - -namespace ZeroTier { - void dropPrivileges(std::string homeDir); -} - -#endif |