From f3f824ee42892fb69cb0d9b0557cd9c5aed357d2 Mon Sep 17 00:00:00 2001 From: Jorge Lucangeli Obes Date: Thu, 15 Dec 2016 12:13:38 -0500 Subject: [PATCH] capabilities: Check ambient caps, last valid runtime cap. Partners have expressed interest in using the 'capabilities' keyword in init, so make the code more resilient: -Check that ambient capabilities are supported by the kernel. -Check that the last valid cap at runtime is not higher than what's in kernel headers. -Check that the user is not requesting a capability present in kernel headers but not supported by the kernel at runtime. -Don't attempt to drop bounding set capabilities not supported at runtime. This CL also fixes a small bug where < should have been used instead of <=, and uses 'static' instead of anonymous namespaces. Bug: 32438163 Test: Use a test service that uses capabilities. Test: Apply in internal tree and test with angler and rild. Change-Id: Ia271cc7eb389d1d526d61f897261e4bac4d19e5d --- init/capabilities.cpp | 48 ++++++++++++++++++++++++++++++++++--------- init/capabilities.h | 7 +++++++ init/service.cpp | 19 +++++++++++++++-- 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/init/capabilities.cpp b/init/capabilities.cpp index 4592adcc0..b8a9ec06f 100644 --- a/init/capabilities.cpp +++ b/init/capabilities.cpp @@ -25,8 +25,7 @@ #define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap } -namespace { -const std::map cap_map = { +static const std::map cap_map = { CAP_MAP_ENTRY(CHOWN), CAP_MAP_ENTRY(DAC_OVERRIDE), CAP_MAP_ENTRY(DAC_READ_SEARCH), @@ -69,9 +68,30 @@ const std::map cap_map = { static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ"); -bool DropBoundingSet(const CapSet& to_keep) { - for (size_t cap = 0; cap < to_keep.size(); ++cap) { - if (to_keep.test(cap)) { +static bool ComputeCapAmbientSupported() { + return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0; +} + +static unsigned int ComputeLastValidCap() { + // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see + // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360. + unsigned int last_valid_cap = CAP_WAKE_ALARM; + for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; ++last_valid_cap); + + // |last_valid_cap| will be the first failing value. + return last_valid_cap - 1; +} + +static bool DropBoundingSet(const CapSet& to_keep) { + unsigned int last_valid_cap = GetLastValidCap(); + // When dropping the bounding set, attempt to drop capabilities reported at + // run-time, not at compile-time. + // If the run-time kernel is older than the compile-time headers, this + // avoids dropping an invalid capability. If the run-time kernel is newer + // than the headers, this guarantees all capabilities (even those unknown at + // compile time) will be dropped. + for (size_t cap = 0; cap <= last_valid_cap; ++cap) { + if (cap < to_keep.size() && to_keep.test(cap)) { // No need to drop this capability. continue; } @@ -83,14 +103,14 @@ bool DropBoundingSet(const CapSet& to_keep) { return true; } -bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) { +static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) { cap_t caps = cap_init(); auto deleter = [](cap_t* p) { cap_free(*p); }; std::unique_ptr ptr_caps(&caps, deleter); cap_clear(caps); cap_value_t value[1]; - for (size_t cap = 0; cap <= to_keep.size(); ++cap) { + for (size_t cap = 0; cap < to_keep.size(); ++cap) { if (to_keep.test(cap)) { value[0] = cap; if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 || @@ -117,7 +137,7 @@ bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) { return true; } -bool SetAmbientCaps(const CapSet& to_raise) { +static bool SetAmbientCaps(const CapSet& to_raise) { for (size_t cap = 0; cap < to_raise.size(); ++cap) { if (to_raise.test(cap)) { if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) { @@ -129,8 +149,6 @@ bool SetAmbientCaps(const CapSet& to_raise) { return true; } -} // namespace anonymous - int LookupCap(const std::string& cap_name) { auto e = cap_map.find(cap_name); if (e != cap_map.end()) { @@ -140,6 +158,16 @@ int LookupCap(const std::string& cap_name) { } } +bool CapAmbientSupported() { + static bool cap_ambient_supported = ComputeCapAmbientSupported(); + return cap_ambient_supported; +} + +unsigned int GetLastValidCap() { + static unsigned int last_valid_cap = ComputeLastValidCap(); + return last_valid_cap; +} + bool SetCapsForExec(const CapSet& to_keep) { // Need to keep SETPCAP to drop bounding set below. bool add_setpcap = true; diff --git a/init/capabilities.h b/init/capabilities.h index 368178d2b..abd7fb222 100644 --- a/init/capabilities.h +++ b/init/capabilities.h @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef _INIT_CAPABILITIES_H +#define _INIT_CAPABILITIES_H + #include #include @@ -20,4 +23,8 @@ using CapSet = std::bitset; int LookupCap(const std::string& cap_name); +bool CapAmbientSupported(); +unsigned int GetLastValidCap(); bool SetCapsForExec(const CapSet& to_keep); + +#endif // _INIT_CAPABILITIES_H diff --git a/init/service.cpp b/init/service.cpp index e967a7c31..7cff348d6 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -312,13 +312,28 @@ void Service::DumpState() const { bool Service::ParseCapabilities(const std::vector& args, std::string* err) { capabilities_ = 0; + if (!CapAmbientSupported()) { + *err = "capabilities requested but the kernel does not support ambient capabilities"; + return false; + } + + unsigned int last_valid_cap = GetLastValidCap(); + if (last_valid_cap >= capabilities_.size()) { + LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP"; + } + for (size_t i = 1; i < args.size(); i++) { const std::string& arg = args[i]; - int cap = LookupCap(arg); - if (cap == -1) { + int res = LookupCap(arg); + if (res < 0) { *err = StringPrintf("invalid capability '%s'", arg.c_str()); return false; } + unsigned int cap = static_cast(res); // |res| is >= 0. + if (cap > last_valid_cap) { + *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str()); + return false; + } capabilities_[cap] = true; } return true;