From ab083b9b29e3a1e1a09e97917e99ccca9d53c1d1 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 5 Jul 2022 17:48:14 +0200 Subject: [PATCH] dnsdist: Keep retained capabilities even when switching user/group 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 | 36 +++++++++++++++++++++++++++++++++++- pdns/capabilities.hh | 10 +++++++++- pdns/dnsdist.cc | 16 ++++++++++++++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/pdns/capabilities.cc b/pdns/capabilities.cc index 534d66554229..e073d2c77bd6 100644 --- a/pdns/capabilities.cc +++ b/pdns/capabilities.cc @@ -27,12 +27,13 @@ #ifdef HAVE_LIBCAP #include +#include #endif #include "capabilities.hh" #include "misc.hh" -void dropCapabilities(std::set capabilitiesToKeep) +bool dropCapabilities(std::set capabilitiesToKeep) { #ifdef HAVE_LIBCAP cap_t caps = cap_get_proc(); @@ -66,10 +67,43 @@ void dropCapabilities(std::set 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 */ } diff --git a/pdns/capabilities.hh b/pdns/capabilities.hh index 4d0b0ded3c26..d12c6cb4d1d0 100644 --- a/pdns/capabilities.hh +++ b/pdns/capabilities.hh @@ -23,4 +23,12 @@ #include -void dropCapabilities(std::set 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 capabilitiesToKeep = {}); +/* drop capabilities on setuid()/setgid() */ +bool dropCapabilitiesAfterSwitchingIDs(); +/* retain capabilities on setuid()/setgid() */ +bool keepCapabilitiesAfterSwitchingIDs(); diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index f86586a6a026..55943875d9d4 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -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