diff --git a/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.cpp b/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.cpp index 8734b1be5db..1170b1bef46 100644 --- a/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.cpp +++ b/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.cpp @@ -15,20 +15,25 @@ */ #include "cuttlefish/host/commands/cvdalloc/privilege.h" +#include #include #include +#include +#include #include #if defined(__linux__) #include #include #include +#include #include #include -#include #include #endif +#include #include +#include #include "absl/log/log.h" @@ -95,10 +100,10 @@ Result ValidateCvdallocBinary(std::string_view path) { #if defined(__linux__) (void)st; /* Try and determine if the cvdalloc binary has any capabilities. */ - struct vfs_cap_data cap; + struct vfs_cap_data cap = {}; ssize_t s = getxattr(path.data(), XATTR_NAME_CAPS, &cap, sizeof(cap)); CF_EXPECTF( - s != 1 && (cap.data[0].permitted & (1 << CAP_NET_ADMIN)) != 0, + s != -1 && (cap.data[0].permitted & (1 << CAP_NET_ADMIN)) != 0, "cvdalloc binary does not have permissions to allocate resources.\n" "As root, please\n\n setcap cap_net_admin,cap_net_bind_service," "cap_net_raw=+ep `realpath {}`", @@ -157,4 +162,43 @@ int DropPrivileges(uid_t orig) { return setuid(orig); } +namespace { +constexpr char kTrustedPath[] = "/usr/sbin:/usr/bin:/sbin:/bin"; +} // namespace + +Result ScopedPrivileges::Elevate() { + uid_t orig = getuid(); + // The child processes we exec run with elevated privilege (CAP_NET_ADMIN via + // ambient caps) but with AT_SECURE=0, so the dynamic linker won't scrub their + // environment for us. Sanitize with an allowlist. +#if defined(__linux__) + // On Linux, only sanitize when this exec actually gained privilege (e.g. via + // file caps), as signalled by AT_SECURE. + const bool should_sanitize = getauxval(AT_SECURE) != 0; +#else + // Elsewhere we can't rely on AT_SECURE, so sanitize unconditionally. + const bool should_sanitize = true; +#endif + if (should_sanitize) { + CF_EXPECTF(clearenv() == 0, "Couldn't clear environment: {}", + StrError(errno)); + CF_EXPECTF(setenv("PATH", kTrustedPath, /*overwrite=*/1) == 0, + "Couldn't set PATH: {}", StrError(errno)); + } + CF_EXPECTF(BeginElevatedPrivileges() != -1, + "Couldn't elevate permissions: {}", StrError(errno)); + return ScopedPrivileges(orig); +} + +ScopedPrivileges::ScopedPrivileges(uid_t orig) : orig_(orig) {} + +ScopedPrivileges::ScopedPrivileges(ScopedPrivileges&& other) noexcept + : orig_(std::exchange(other.orig_, std::nullopt)) {} + +ScopedPrivileges::~ScopedPrivileges() { + if (orig_.has_value() && DropPrivileges(*orig_) == -1) { + LOG(ERROR) << "cvdalloc: couldn't drop privileges: " << StrError(errno); + } +} + } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.h b/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.h index c78a266a900..0140d08b2ca 100644 --- a/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.h +++ b/base/cvd/cuttlefish/host/commands/cvdalloc/privilege.h @@ -13,8 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include +#ifndef CUTTLEFISH_HOST_COMMANDS_CVDALLOC_PRIVILEGE_H_ +#define CUTTLEFISH_HOST_COMMANDS_CVDALLOC_PRIVILEGE_H_ +#include + +#include #include #include "cuttlefish/result/result_type.h" @@ -25,4 +29,22 @@ int BeginElevatedPrivileges(); int DropPrivileges(uid_t orig); Result ValidateCvdallocBinary(std::string_view path); +class ScopedPrivileges { + public: + static Result Elevate(); + + ScopedPrivileges(ScopedPrivileges&& other) noexcept; + ScopedPrivileges& operator=(ScopedPrivileges&& other) = delete; + ScopedPrivileges(const ScopedPrivileges&) = delete; + ScopedPrivileges& operator=(const ScopedPrivileges&) = delete; + ~ScopedPrivileges(); + + private: + explicit ScopedPrivileges(uid_t orig); + + std::optional orig_; +}; + } // namespace cuttlefish + +#endif // CUTTLEFISH_HOST_COMMANDS_CVDALLOC_PRIVILEGE_H_