]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Keep retained capabilities even when switching user/group 11761/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 5 Jul 2022 15:48:14 +0000 (17:48 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 5 Jul 2022 15:48:14 +0000 (17:48 +0200)
On Linux, we support retaining some capabilities if we are running
as root (eeew) or as an unprivileged user with ambiant capabilities,
but we did not yet support keeping these if we were started as root
but then switched to a different user ID and/or group ID.
This commit uses `PR_SET_KEEPCAPS`, when available, to do just that,
to be able to retain the capabilities we need without running as a
fully privileged users even when we cannot easily use ambiant
capabilities.

pdns/capabilities.cc
pdns/capabilities.hh
pdns/dnsdist.cc

index 534d66554229bc51f94764b8c81d070e319ce677..e073d2c77bd671d7a004740292c2c0b9efacac1a 100644 (file)
 
 #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();
@@ -66,10 +67,43 @@ void dropCapabilities(std::set<std::string> capabilitiesToKeep)
 
      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 */
 }
index 4d0b0ded3c2619afd338ed1d329c0e339ef44d9d..d12c6cb4d1d0d590ef3ef7773044e4c00df99e8f 100644 (file)
 
 #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();
index f86586a6a02682f50e6232d8eba4cedecd532d61..55943875d9d4feaaae83bcdedd651931c3915bff 100644 (file)
@@ -2617,11 +2617,19 @@ int main(int argc, char** argv)
     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()) {
@@ -2639,6 +2647,10 @@ int main(int argc, char** argv)
       dropUserPrivs(newuid);
     }
 
+    if (retainedCapabilities) {
+      dropCapabilitiesAfterSwitchingIDs();
+    }
+
     try {
       /* we might still have capabilities remaining,
          for example if we have been started as root