#ifdef HAVE_LIBCAP
#include <sys/capability.h>
+#include <sys/prctl.h>
#endif
#include "capabilities.hh"
#include "misc.hh"
-void dropCapabilities(std::set<std::string> capabilitiesToKeep)
+bool dropCapabilities(std::set<std::string> capabilitiesToKeep)
{
#ifdef HAVE_LIBCAP
cap_t caps = cap_get_proc();
if (cap_set_proc(caps) != 0) {
cap_free(caps);
+ if (errno == EPERM) {
+ return false;
+ }
throw std::runtime_error("Unable to drop capabilities: " + stringerror());
}
cap_free(caps);
+ return true;
}
+#endif /* HAVE_LIBCAP */
+ return false;
+}
+
+bool dropCapabilitiesAfterSwitchingIDs()
+{
+#ifdef HAVE_LIBCAP
+#ifdef PR_SET_KEEPCAPS
+ if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0) {
+ return true;
+ }
+#endif /* PR_SET_KEEPCAPS */
+ return false;
+#else
+ return false;
+#endif /* HAVE_LIBCAP */
+}
+
+bool keepCapabilitiesAfterSwitchingIDs()
+{
+#ifdef HAVE_LIBCAP
+#ifdef PR_SET_KEEPCAPS
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0) {
+ return true;
+ }
+#endif /* PR_SET_KEEPCAPS */
+ return false;
+#else
+ return false;
#endif /* HAVE_LIBCAP */
}
#include <set>
-void dropCapabilities(std::set<std::string> capabilitiesToKeep = {});
+/* return true on success, false if support is not available or we don't
+ have enough capabilities to drop our capabilities (I know),
+ and throw on more unexpected errors.
+*/
+bool dropCapabilities(std::set<std::string> capabilitiesToKeep = {});
+/* drop capabilities on setuid()/setgid() */
+bool dropCapabilitiesAfterSwitchingIDs();
+/* retain capabilities on setuid()/setgid() */
+bool keepCapabilitiesAfterSwitchingIDs();
uid_t newgid=getegid();
gid_t newuid=geteuid();
- if(!g_cmdLine.gid.empty())
+ if (!g_cmdLine.gid.empty()) {
newgid = strToGID(g_cmdLine.gid.c_str());
+ }
- if(!g_cmdLine.uid.empty())
+ if (!g_cmdLine.uid.empty()) {
newuid = strToUID(g_cmdLine.uid.c_str());
+ }
+
+ bool retainedCapabilities = true;
+ if (!g_capabilitiesToRetain.empty() &&
+ (getegid() != newgid || geteuid() != newuid)) {
+ retainedCapabilities = keepCapabilitiesAfterSwitchingIDs();
+ }
if (getegid() != newgid) {
if (running_in_service_mgr()) {
dropUserPrivs(newuid);
}
+ if (retainedCapabilities) {
+ dropCapabilitiesAfterSwitchingIDs();
+ }
+
try {
/* we might still have capabilities remaining,
for example if we have been started as root