Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions base/cvd/cuttlefish/host/commands/cvdalloc/privilege.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,25 @@
*/
#include "cuttlefish/host/commands/cvdalloc/privilege.h"

#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#if defined(__linux__)
#include <linux/capability.h>
#include <linux/prctl.h>
#include <linux/xattr.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/xattr.h>
#endif

#include <optional>
#include <string_view>
#include <utility>

#include "absl/log/log.h"

Expand Down Expand Up @@ -95,10 +100,10 @@ Result<void> 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 {}`",
Expand Down Expand Up @@ -157,4 +162,44 @@ int DropPrivileges(uid_t orig) {
return setuid(orig);
}

namespace {
constexpr char kTrustedPath[] = "/usr/sbin:/usr/bin:/sbin:/bin";
Comment thread
dxapd marked this conversation as resolved.
} // namespace

// Activate this instance and gain privileges.
// WARNING: We treat elevating privileges as a one-way
// action. Activating an instance of ScopedPrivileges
// will scrub its process' environment.
Result<ScopedPrivileges> ScopedPrivileges::Elevate() {
uid_t orig = getuid();
bool should_sanitize_env = true;
#if defined(__linux__)
// The child processes we exec run with elevated privilege (CAP_NET_ADMIN via
Comment thread
dxapd marked this conversation as resolved.
// ambient caps) but with AT_SECURE=0, so the dynamic linker won't scrub their
Comment thread
dxapd marked this conversation as resolved.
// environment for us. Only sanitize the environment when this exec actually
// gained privilege (e.g. via file caps), as signalled by AT_SECURE.
should_sanitize_env = getauxval(AT_SECURE) != 0;
#endif
if (should_sanitize_env) {
CF_EXPECTF(clearenv() == 0, "Couldn't clear environment: {}",
Comment thread
dxapd marked this conversation as resolved.
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
24 changes: 23 additions & 1 deletion base/cvd/cuttlefish/host/commands/cvdalloc/privilege.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/types.h>
#ifndef CUTTLEFISH_HOST_COMMANDS_CVDALLOC_PRIVILEGE_H_
#define CUTTLEFISH_HOST_COMMANDS_CVDALLOC_PRIVILEGE_H_

#include <unistd.h>

#include <optional>
#include <string_view>

#include "cuttlefish/result/result_type.h"
Expand All @@ -25,4 +29,22 @@ int BeginElevatedPrivileges();
int DropPrivileges(uid_t orig);
Result<void> ValidateCvdallocBinary(std::string_view path);

class ScopedPrivileges {
public:
static Result<ScopedPrivileges> 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<uid_t> orig_;
};

} // namespace cuttlefish

#endif // CUTTLEFISH_HOST_COMMANDS_CVDALLOC_PRIVILEGE_H_
Loading