Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 491e4da3 authored by David Anderson's avatar David Anderson
Browse files

init: Add an selinux transition for snapuserd.

With compressed VAB updates, it is not possible to mount /system without
first running snapuserd, which is the userspace component to the dm-user
kernel module. This poses a problem because as soon as selinux
enforcement is enabled, snapuserd (running in a kernel context) does not
have access to read and decompress the underlying system partition.

To account for this, we split SelinuxInitialize into multiple steps:

First, sepolicy is read into an in-memory string.

Second, the device-mapper tables for all snapshots are rebuilt. This
flushes any pending reads and creates new dm-user devices. The original
kernel-privileged snapuserd is then killed.

Third, sepolicy is loaded from the in-memory string.

Fourth, we re-launch snapuserd and connect it to the newly created
dm-user devices. As part of this step we restorecon device-mapper
devices and /dev/block/by-name/super, since the new snapuserd is in a
limited context.

Finally, we set enforcing mode.

This sequence ensures that snapuserd has appropriate privileges with a
minimal number of permissive audits.

Bug: 173476209
Test: full OTA with VABC applies and boots
Change-Id: Ie4e0f5166b01c31a6f337afc26fc58b96217604e
parent 5266e041
Loading
Loading
Loading
Loading
+24 −7
Original line number Diff line number Diff line
@@ -306,13 +306,17 @@ class SnapshotManager final : public ISnapshotManager {
    // Helper function for second stage init to restorecon on the rollback indicator.
    static std::string GetGlobalRollbackIndicatorPath();

    // Initiate the transition from first-stage to second-stage snapuserd. This
    // process involves re-creating the dm-user table entries for each device,
    // so that they connect to the new daemon. Once all new tables have been
    // activated, we ask the first-stage daemon to cleanly exit.
    //
    // The caller must pass a function which starts snapuserd.
    bool PerformSecondStageTransition();
    // Detach dm-user devices from the current snapuserd, and populate
    // |snapuserd_argv| with the necessary arguments to restart snapuserd
    // and reattach them.
    bool DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv);

    // Perform the transition from the selinux stage of snapuserd into the
    // second-stage of snapuserd. This process involves re-creating the dm-user
    // table entries for each device, so that they connect to the new daemon.
    // Once all new tables have been activated, we ask the first-stage daemon
    // to cleanly exit.
    bool PerformSecondStageInitTransition();

    // ISnapshotManager overrides.
    bool BeginUpdate() override;
@@ -693,6 +697,19 @@ class SnapshotManager final : public ISnapshotManager {
    // returns true.
    bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);

    enum class InitTransition { SELINUX_DETACH, SECOND_STAGE };

    // Initiate the transition from first-stage to second-stage snapuserd. This
    // process involves re-creating the dm-user table entries for each device,
    // so that they connect to the new daemon. Once all new tables have been
    // activated, we ask the first-stage daemon to cleanly exit.
    //
    // If the mode is SELINUX_DETACH, snapuserd_argv must be non-null and will
    // be populated with a list of snapuserd arguments to pass to execve(). It
    // is otherwise ignored.
    bool PerformInitTransition(InitTransition transition,
                               std::vector<std::string>* snapuserd_argv = nullptr);

    std::string gsid_dir_;
    std::string metadata_dir_;
    std::unique_ptr<IDeviceInfo> device_;
+0 −2
Original line number Diff line number Diff line
@@ -32,8 +32,6 @@ static constexpr uint32_t PACKET_SIZE = 512;

static constexpr char kSnapuserdSocket[] = "snapuserd";

static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";

// Ensure that the second-stage daemon for snapuserd is running.
bool EnsureSnapuserdStarted();

+26 −3
Original line number Diff line number Diff line
@@ -1291,12 +1291,13 @@ bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,
    return RemoveAllUpdateState(lock, before_cancel);
}

bool SnapshotManager::PerformSecondStageTransition() {
    LOG(INFO) << "Performing second-stage transition for snapuserd.";
bool SnapshotManager::PerformInitTransition(InitTransition transition,
                                            std::vector<std::string>* snapuserd_argv) {
    LOG(INFO) << "Performing transition for snapuserd.";

    // Don't use EnsuerSnapuserdConnected() because this is called from init,
    // and attempting to do so will deadlock.
    if (!snapuserd_client_) {
    if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
        snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
        if (!snapuserd_client_) {
            LOG(ERROR) << "Unable to connect to snapuserd";
@@ -1343,6 +1344,9 @@ bool SnapshotManager::PerformSecondStageTransition() {
        }

        auto misc_name = user_cow_name;
        if (transition == InitTransition::SELINUX_DETACH) {
            misc_name += "-selinux";
        }

        DmTable table;
        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
@@ -1378,6 +1382,17 @@ bool SnapshotManager::PerformSecondStageTransition() {
            continue;
        }

        if (transition == InitTransition::SELINUX_DETACH) {
            auto message = misc_name + "," + cow_image_device + "," + backing_device;
            snapuserd_argv->emplace_back(std::move(message));

            // Do not attempt to connect to the new snapuserd yet, it hasn't
            // been started. We do however want to wait for the misc device
            // to have been created.
            ok_cows++;
            continue;
        }

        uint64_t base_sectors =
                snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
        if (base_sectors == 0) {
@@ -3311,5 +3326,13 @@ bool SnapshotManager::IsSnapuserdRequired() {
    return status.state() != UpdateState::None && status.compression_enabled();
}

bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
    return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv);
}

bool SnapshotManager::PerformSecondStageInitTransition() {
    return PerformInitTransition(InitTransition::SECOND_STAGE);
}

}  // namespace snapshot
}  // namespace android
+4 −1
Original line number Diff line number Diff line
@@ -1778,6 +1778,9 @@ TEST_F(SnapshotUpdateTest, DaemonTransition) {

    ASSERT_TRUE(init->EnsureSnapuserdConnected());
    init->set_use_first_stage_snapuserd(true);
    init->SetUeventRegenCallback([](const std::string& device) -> bool {
        return android::fs_mgr::WaitForFile(device, snapshot_timeout_);
    });

    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1785,7 +1788,7 @@ TEST_F(SnapshotUpdateTest, DaemonTransition) {
    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);

    ASSERT_TRUE(init->PerformSecondStageTransition());
    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));

    // The control device should have been renamed.
    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
+1 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ init_device_sources = [
    "selabel.cpp",
    "selinux.cpp",
    "sigchld_handler.cpp",
    "snapuserd_transition.cpp",
    "switch_root.cpp",
    "uevent_listener.cpp",
    "ueventd.cpp",
Loading