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

Commit 96e350b3 authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Disallow read logs collection if user changes their mind.

Bug: b/152633648
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest
Test: adb shell appops set 1000 GET_USAGE_STATS deny
Change-Id: I7fc8356f84fe30669483470579eedf546f81f297
parent b131e1b7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ cc_defaults {
        "libbinder",
        "libcrypto",
        "libcutils",
        "libdataloader",
        "libincfs",
        "liblog",
        "libz",
+98 −47
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#define LOG_TAG "IncrementalService"

#include "IncrementalService.h"
#include "IncrementalServiceValidation.h"

#include <android-base/file.h>
#include <android-base/logging.h>
@@ -50,6 +51,9 @@ using namespace std::literals;
using namespace android::content::pm;
namespace fs = std::filesystem;

constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS";
constexpr const char* kOpUsage = "android:get_usage_stats";

namespace android::incremental {

namespace {
@@ -232,6 +236,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
      : mVold(sm.getVoldService()),
        mDataLoaderManager(sm.getDataLoaderManager()),
        mIncFs(sm.getIncFs()),
        mAppOpsManager(sm.getAppOpsManager()),
        mIncrementalDir(rootDir) {
    if (!mVold) {
        LOG(FATAL) << "Vold service is unavailable";
@@ -239,6 +244,9 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
    if (!mDataLoaderManager) {
        LOG(FATAL) << "DataLoaderManagerService is unavailable";
    }
    if (!mAppOpsManager) {
        LOG(FATAL) << "AppOpsManager is unavailable";
    }
    mountExistingImages();
}

@@ -279,12 +287,9 @@ void IncrementalService::onDump(int fd) {
        dprintf(fd, "\t\troot: %s\n", mnt.root.c_str());
        dprintf(fd, "\t\tnextStorageDirNo: %d\n", mnt.nextStorageDirNo.load());
        dprintf(fd, "\t\tdataLoaderStatus: %d\n", mnt.dataLoaderStatus.load());
        dprintf(fd, "\t\tconnectionLostTime: %s\n", toString(mnt.connectionLostTime));
        dprintf(fd, "\t\tsavedDataLoaderParams:\n");
        if (!mnt.savedDataLoaderParams) {
            dprintf(fd, "\t\t\tnone\n");
        } else {
            const auto& params = mnt.savedDataLoaderParams.value();
        {
            const auto& params = mnt.dataLoaderParams;
            dprintf(fd, "\t\tdataLoaderParams:\n");
            dprintf(fd, "\t\t\ttype: %s\n", toString(params.type).c_str());
            dprintf(fd, "\t\t\tpackageName: %s\n", params.packageName.c_str());
            dprintf(fd, "\t\t\tclassName: %s\n", params.className.c_str());
@@ -332,6 +337,7 @@ std::optional<std::future<void>> IncrementalService::onSystemReady() {
    }

    std::thread([this, mounts = std::move(mounts)]() {
        /* TODO(b/151241369): restore data loaders on reboot.
        for (auto&& ifs : mounts) {
            if (prepareDataLoader(*ifs)) {
                LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId;
@@ -340,6 +346,7 @@ std::optional<std::future<void>> IncrementalService::onSystemReady() {
                LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId;
            }
        }
        */
        mPrepareDataLoaders.set_value_at_thread_exit();
    }).detach();
    return mPrepareDataLoaders.get_future();
@@ -459,13 +466,15 @@ StorageId IncrementalService::createStorage(
        return kInvalidStorageId;
    }

    ifs->dataLoaderParams = std::move(dataLoaderParams);

    {
        metadata::Mount m;
        m.mutable_storage()->set_id(ifs->mountId);
        m.mutable_loader()->set_type((int)dataLoaderParams.type);
        m.mutable_loader()->set_package_name(dataLoaderParams.packageName);
        m.mutable_loader()->set_class_name(dataLoaderParams.className);
        m.mutable_loader()->set_arguments(dataLoaderParams.arguments);
        m.mutable_loader()->set_type((int)ifs->dataLoaderParams.type);
        m.mutable_loader()->set_package_name(ifs->dataLoaderParams.packageName);
        m.mutable_loader()->set_class_name(ifs->dataLoaderParams.className);
        m.mutable_loader()->set_arguments(ifs->dataLoaderParams.arguments);
        const auto metadata = m.SerializeAsString();
        m.mutable_loader()->release_arguments();
        m.mutable_loader()->release_class_name();
@@ -493,7 +502,7 @@ StorageId IncrementalService::createStorage(
    // Done here as well, all data structures are in good state.
    secondCleanupOnFailure.release();

    if (!prepareDataLoader(*ifs, &dataLoaderParams, &dataLoaderStatusListener)) {
    if (!prepareDataLoader(*ifs, &dataLoaderStatusListener)) {
        LOG(ERROR) << "prepareDataLoader() failed";
        deleteStorageLocked(*ifs, std::move(l));
        return kInvalidStorageId;
@@ -573,11 +582,30 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog
        return -EINVAL;
    }

    ifs->dataLoaderFilesystemParams.readLogsEnabled = enableReadLogs;
    if (enableReadLogs) {
        // We never unregister the callbacks, but given a restricted number of data loaders and even fewer asking for read log access, should be ok.
        registerAppOpsCallback(ifs->dataLoaderParams.packageName);
    }

    return applyStorageParams(*ifs);
}

int IncrementalService::applyStorageParams(IncFsMount& ifs) {
    const bool enableReadLogs = ifs.dataLoaderFilesystemParams.readLogsEnabled;
    if (enableReadLogs) {
        if (auto status = CheckPermissionForDataDelivery(kDataUsageStats, kOpUsage);
            !status.isOk()) {
            LOG(ERROR) << "CheckPermissionForDataDelivery failed: " << status.toString8();
            return fromBinderStatus(status);
        }
    }

    using unique_fd = ::android::base::unique_fd;
    ::android::os::incremental::IncrementalFileSystemControlParcel control;
    control.cmd.reset(unique_fd(dup(ifs->control.cmd())));
    control.pendingReads.reset(unique_fd(dup(ifs->control.pendingReads())));
    auto logsFd = ifs->control.logs();
    control.cmd.reset(unique_fd(dup(ifs.control.cmd())));
    control.pendingReads.reset(unique_fd(dup(ifs.control.pendingReads())));
    auto logsFd = ifs.control.logs();
    if (logsFd >= 0) {
        control.log.reset(unique_fd(dup(logsFd)));
    }
@@ -586,12 +614,7 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog
    const auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
    if (!status.isOk()) {
        LOG(ERROR) << "Calling Vold::setIncFsMountOptions() failed: " << status.toString8();
        return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
                ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode()
                                                        : status.serviceSpecificErrorCode() == 0
                                ? -EFAULT
                                : status.serviceSpecificErrorCode()
                : -EIO;
        return fromBinderStatus(status);
    }

    return 0;
@@ -1015,16 +1038,26 @@ bool IncrementalService::mountExistingImage(std::string_view root) {

    auto ifs = std::make_shared<IncFsMount>(std::string(root), -1, std::move(control), *this);

    auto m = parseFromIncfs<metadata::Mount>(mIncFs.get(), ifs->control,
    auto mount = parseFromIncfs<metadata::Mount>(mIncFs.get(), ifs->control,
                                                 path::join(mountTarget, constants().infoMdName));
    if (!m.has_loader() || !m.has_storage()) {
    if (!mount.has_loader() || !mount.has_storage()) {
        LOG(ERROR) << "Bad mount metadata in mount at " << root;
        return false;
    }

    ifs->mountId = m.storage().id();
    ifs->mountId = mount.storage().id();
    mNextId = std::max(mNextId, ifs->mountId + 1);

    // DataLoader params
    {
        auto& dlp = ifs->dataLoaderParams;
        const auto& loader = mount.loader();
        dlp.type = (android::content::pm::DataLoaderType)loader.type();
        dlp.packageName = loader.package_name();
        dlp.className = loader.class_name();
        dlp.arguments = loader.arguments();
    }

    std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints;
    auto d = openDir(path::c_str(mountTarget));
    while (auto e = ::readdir(d.get())) {
@@ -1095,23 +1128,9 @@ bool IncrementalService::mountExistingImage(std::string_view root) {
}

bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
                                           DataLoaderParamsParcel* params,
                                           const DataLoaderStatusListener* externalListener) {
    if (!mSystemReady.load(std::memory_order_relaxed)) {
        std::unique_lock l(ifs.lock);
        if (params) {
            if (ifs.savedDataLoaderParams) {
                LOG(WARNING) << "Trying to pass second set of data loader parameters, ignored it";
            } else {
                ifs.savedDataLoaderParams = std::move(*params);
            }
        } else {
            if (!ifs.savedDataLoaderParams) {
                LOG(ERROR) << "Mount " << ifs.mountId
                           << " is broken: no data loader params (system is not ready yet)";
                return false;
            }
        }
        return true; // eventually...
    }

@@ -1121,12 +1140,6 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
        return true;
    }

    auto* dlp = params ? params
                       : ifs.savedDataLoaderParams ? &ifs.savedDataLoaderParams.value() : nullptr;
    if (!dlp) {
        LOG(ERROR) << "Mount " << ifs.mountId << " is broken: no data loader params";
        return false;
    }
    FileSystemControlParcel fsControlParcel;
    fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>();
    fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd())));
@@ -1138,13 +1151,11 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
                                              externalListener ? *externalListener
                                                               : DataLoaderStatusListener());
    bool created = false;
    auto status = mDataLoaderManager->initializeDataLoader(ifs.mountId, *dlp, fsControlParcel,
                                                           listener, &created);
    auto status = mDataLoaderManager->initializeDataLoader(ifs.mountId, ifs.dataLoaderParams, fsControlParcel, listener, &created);
    if (!status.isOk() || !created) {
        LOG(ERROR) << "Failed to create a data loader for mount " << ifs.mountId;
        return false;
    }
    ifs.savedDataLoaderParams.reset();
    return true;
}

@@ -1268,6 +1279,42 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
    return success;
}

void IncrementalService::registerAppOpsCallback(const std::string& packageName) {
    if (packageName.empty()) {
        return;
    }

    {
        std::unique_lock lock{mCallbacksLock};
        if (!mCallbackRegistered.insert(packageName).second) {
            return;
        }
    }

    /* TODO(b/152633648): restore callback after it's not crashing Binder anymore.
    sp<AppOpsListener> listener = new AppOpsListener(*this, packageName);
    mAppOpsManager->startWatchingMode(AppOpsManager::OP_GET_USAGE_STATS, String16(packageName.c_str()), listener);
    */
}

void IncrementalService::onAppOppChanged(const std::string& packageName) {
    std::vector<IfsMountPtr> affected;
    {
        std::lock_guard l(mLock);
        affected.reserve(mMounts.size());
        for (auto&& [id, ifs] : mMounts) {
            if (ifs->dataLoaderFilesystemParams.readLogsEnabled && ifs->dataLoaderParams.packageName == packageName) {
                affected.push_back(ifs);
            }
        }
    }
    /* TODO(b/152633648): restore callback after it's not crashing Kernel anymore.
    for (auto&& ifs : affected) {
        applyStorageParams(*ifs);
    }
    */
}

binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
                                                                                  int newStatus) {
    if (externalListener) {
@@ -1331,4 +1378,8 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange
    return binder::Status::ok();
}

void IncrementalService::AppOpsListener::opChanged(int32_t op, const String16&) {
    incrementalService.onAppOppChanged(packageName);
}

} // namespace android::incremental
+30 −8
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#include "ServiceWrappers.h"
#include "android/content/pm/BnDataLoaderStatusListener.h"
#include "incfs.h"
#include "dataloader_ndk.h"
#include "path.h"

using namespace android::os::incremental;
@@ -132,6 +133,7 @@ public:
    bool startLoading(StorageId storage) const;
    bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
                                 std::string_view libDirRelativePath, std::string_view abi);

    class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
    public:
        IncrementalDataLoaderListener(IncrementalService& incrementalService,
@@ -145,6 +147,16 @@ public:
        DataLoaderStatusListener externalListener;
    };

    class AppOpsListener : public android::BnAppOpsCallback {
    public:
        AppOpsListener(IncrementalService& incrementalService, std::string packageName) : incrementalService(incrementalService), packageName(std::move(packageName)) {}
        void opChanged(int32_t op, const String16& packageName) override;

    private:
        IncrementalService& incrementalService;
        const std::string packageName;
    };

private:
    struct IncFsMount {
        struct Bind {
@@ -169,11 +181,11 @@ private:
        /*const*/ MountId mountId;
        StorageMap storages;
        BindMap bindPoints;
        std::optional<DataLoaderParamsParcel> savedDataLoaderParams;
        DataLoaderParamsParcel dataLoaderParams;
        DataLoaderFilesystemParams dataLoaderFilesystemParams;
        std::atomic<int> nextStorageDirNo{0};
        std::atomic<int> dataLoaderStatus = -1;
        bool dataLoaderStartRequested = false;
        TimePoint connectionLostTime = TimePoint();
        const IncrementalService& incrementalService;

        IncFsMount(std::string root, MountId mountId, Control control,
@@ -181,7 +193,9 @@ private:
              : root(std::move(root)),
                control(std::move(control)),
                mountId(mountId),
                incrementalService(incrementalService) {}
                incrementalService(incrementalService) {
            dataLoaderFilesystemParams.readLogsEnabled = false;
        }
        IncFsMount(IncFsMount&&) = delete;
        IncFsMount& operator=(IncFsMount&&) = delete;
        ~IncFsMount();
@@ -208,8 +222,7 @@ private:
                           std::string&& source, std::string&& target, BindKind kind,
                           std::unique_lock<std::mutex>& mainLock);

    bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr,
                           const DataLoaderStatusListener* externalListener = nullptr);
    bool prepareDataLoader(IncFsMount& ifs, const DataLoaderStatusListener* externalListener = nullptr);
    bool startDataLoader(MountId mountId) const;

    BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
@@ -221,10 +234,16 @@ private:
    std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage,
                                       std::string_view path);

    int applyStorageParams(IncFsMount& ifs);

    void registerAppOpsCallback(const std::string& packageName);
    void onAppOppChanged(const std::string& packageName);

    // Member variables
    std::unique_ptr<VoldServiceWrapper> mVold;
    std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager;
    std::unique_ptr<IncFsWrapper> mIncFs;
    std::unique_ptr<VoldServiceWrapper> const mVold;
    std::unique_ptr<DataLoaderManagerWrapper> const mDataLoaderManager;
    std::unique_ptr<IncFsWrapper> const mIncFs;
    std::unique_ptr<AppOpsManagerWrapper> const mAppOpsManager;
    const std::string mIncrementalDir;

    mutable std::mutex mLock;
@@ -232,6 +251,9 @@ private:
    MountMap mMounts;
    BindPathMap mBindsByPath;

    std::mutex mCallbacksLock;
    std::set<std::string> mCallbackRegistered;

    std::atomic_bool mSystemReady = false;
    StorageId mNextId = 0;
    std::promise<void> mPrepareDataLoaders;
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <android-base/stringprintf.h>
#include <binder/IPCThreadState.h>
#include <binder/PermissionCache.h>
#include <binder/PermissionController.h>
#include <binder/Status.h>

namespace android::incremental {

inline binder::Status Ok() {
    return binder::Status::ok();
}

inline binder::Status Exception(uint32_t code, const std::string& msg) {
    return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
}

inline int fromBinderStatus(const binder::Status& status) {
    return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
            ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode()
                                                    : status.serviceSpecificErrorCode() == 0
                            ? -EFAULT
                            : status.serviceSpecificErrorCode()
            : -EIO;
}

inline binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation) {
    using android::base::StringPrintf;

    int32_t pid;
    int32_t uid;

    if (!PermissionCache::checkCallingPermission(String16(permission), &pid, &uid)) {
        return Exception(binder::Status::EX_SECURITY,
                         StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
    }

    // Caller must also have op granted.
    PermissionController pc;
    // Package is a required parameter. Need to obtain one.
    Vector<String16> packages;
    pc.getPackagesForUid(uid, packages);
    if (packages.empty()) {
        return Exception(binder::Status::EX_SECURITY,
                         StringPrintf("UID %d / PID %d has no packages", uid, pid));
    }
    switch (auto result = pc.noteOp(String16(operation), uid, packages[0]); result) {
        case PermissionController::MODE_ALLOWED:
        case PermissionController::MODE_DEFAULT:
            return binder::Status::ok();
        default:
            return Exception(binder::Status::EX_SECURITY,
                             StringPrintf("UID %d / PID %d lacks app-op %s, error %d", uid, pid,
                                          operation, result));
    }
}

} // namespace android::incremental
+4 −0
Original line number Diff line number Diff line
@@ -59,4 +59,8 @@ std::unique_ptr<IncFsWrapper> RealServiceManager::getIncFs() {
    return std::make_unique<RealIncFs>();
}

std::unique_ptr<AppOpsManagerWrapper> RealServiceManager::getAppOpsManager() {
    return std::make_unique<RealAppOpsManager>();
}

} // namespace android::os::incremental
Loading