in watchman/main.cpp [580:676]
static void verify_dir_ownership(const std::string& state_dir) {
#ifndef _WIN32
// verify ownership
struct stat st;
int dir_fd;
int ret = 0;
uid_t euid = geteuid();
// TODO: also allow a gid to be specified here
const char* sock_group_name = cfg_get_string("sock_group", nullptr);
// S_ISGID is set so that files inside this directory inherit the group
// name
mode_t dir_perms =
cfg_get_perms(
"sock_access", false /* write bits */, true /* execute bits */) |
S_ISGID;
auto dirp =
openDir(state_dir.c_str(), false /* don't need strict symlink rules */);
dir_fd = dirp->getFd();
if (dir_fd == -1) {
log(ERR, "dirfd(", state_dir, "): ", folly::errnoStr(errno), "\n");
goto bail;
}
if (fstat(dir_fd, &st) != 0) {
log(ERR, "fstat(", state_dir, "): ", folly::errnoStr(errno), "\n");
ret = 1;
goto bail;
}
if (euid != st.st_uid) {
log(ERR,
"the owner of ",
state_dir,
" is uid ",
st.st_uid,
" and doesn't match your euid ",
euid,
"\n");
ret = 1;
goto bail;
}
if (st.st_mode & 0022) {
log(ERR,
"the permissions on ",
state_dir,
" allow others to write to it. "
"Verify that you own the contents and then fix its "
"permissions by running `chmod 0700 '",
state_dir,
"'`\n");
ret = 1;
goto bail;
}
if (sock_group_name) {
const struct group* sock_group = w_get_group(sock_group_name);
if (!sock_group) {
ret = 1;
goto bail;
}
if (fchown(dir_fd, -1, sock_group->gr_gid) == -1) {
log(ERR,
"setting up group '",
sock_group_name,
"' failed: ",
folly::errnoStr(errno),
"\n");
ret = 1;
goto bail;
}
}
// Depending on group and world accessibility, change permissions on the
// directory. We can't leave the directory open and set permissions on the
// socket because not all POSIX systems respect permissions on UNIX domain
// sockets, but all POSIX systems respect permissions on the containing
// directory.
logf(DBG, "Setting permissions on state dir to {:o}\n", dir_perms);
if (fchmod(dir_fd, dir_perms) == -1) {
logf(
ERR,
"fchmod({}, {:o}): {}\n",
state_dir,
dir_perms,
folly::errnoStr(errno));
ret = 1;
goto bail;
}
bail:
if (ret) {
exit(ret);
}
#endif
}