sslbd/chroot.cpp
#include "chroot.hpp"
#include <grp.h>
#include <pwd.h>
#include <sys/prctl.h>
#if (HAVE_SYS_CAPABILITY_H)
#include <sys/capability.h>
#else
MESSAGE(capabilities disabled - root might be needed at runtime)
#endif
static bool goto_jail(void*&, char* value) {
char* p = strchr(value, ':'); // so its "[user:]path"
if (p) {
*p = '\0';
++p;
return jail(p, value);
} else {
return jail(value, NULL);
}
}
CONF_DEF(config) {
ConfigKey jail;
};
CONF_INIT(config) {
CONF_KEY_INIT(jail, false, false, &goto_jail);
}
bool jail(const char* path, const char* user) {
if ((user || path) && (geteuid() != 0)) {
log(error, "!r00t");
return false;
}
uid_t uid = 0;
gid_t gid = 0;
if (user) {
errno = 0;
struct passwd* pass = getpwnam(user);
if (!pass) {
log_errno(error, "getpwnam(%s)", user);
return false;
}
uid = pass->pw_uid;
gid = pass->pw_gid;
}
#if (HAVE_SYS_CAPABILITY_H)
cap_t curr_caps;
cap_value_t wanted_caps[] = { CAP_NET_BIND_SERVICE, CAP_NET_ADMIN };
if (user) {
curr_caps = cap_get_proc();
if (cap_set_flag(curr_caps, CAP_PERMITTED, 1, wanted_caps, CAP_SET) != 0) {
log_errno(error, "cap_set_flag(CAP_PERMITTED)");
return false; // TODO: need cleanup in case of errors?
} else if (cap_set_proc(curr_caps) != 0) {
log_errno(error, "cap_set_proc(CAP_PERMITTED)");
return false;
} else if (prctl(PR_SET_KEEPCAPS, 1) == -1) {
log_errno(error, "prctl(PR_SET_KEEPCAPS)");
return false;
} else if (cap_free(curr_caps) != 0) {
log_errno(error, "cap_free");
return false;
}
}
#endif
if (path) {
if (chdir(path) != 0) {
log_errno(error, "chdir(%s)", path);
return false;
} else if (chroot(path) != 0) {
log_errno(error, "chroot(%s)", path);
return false;
} else if (chdir("/") != 0) {
log_errno(error, "chdir(%s,/)", path);
return false;
}
}
if (user) {
if (setgid(gid) != 0) {
log_errno(error, "setgid(%s,%d)", user, (int)gid);
return false;
} else if (setuid(uid) != 0) {
log_errno(error, "setuid(%s,%d)", user, (int)uid);
return false;
}
}
#if (HAVE_SYS_CAPABILITY_H)
if (user) {
curr_caps = cap_get_proc();
if (cap_set_flag(curr_caps, CAP_EFFECTIVE, 1, wanted_caps, CAP_SET) != 0) {
log_errno(error, "cap_set_flag(CAP_EFFECTIVE)");
return false;
} else if (cap_set_proc(curr_caps) != 0) {
log_errno(error, "cap_set_proc(CAP_EFFECTIVE)");
return false;
} else if (cap_free(curr_caps) != 0) {
log_errno(error, "cap_free");
return false;
}
}
#endif
if (user) {
if (prctl(PR_SET_DUMPABLE, 1) != 0) {
log_errno(error, "prctl(PR_SET_DUMPABLE)");
return false;
}
}
log(info, "chrooted to '%s' as user '%s'", path?:"/", user?:"-");
return true;
}