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

Commit 85a07e23 authored by Atneya Nair's avatar Atneya Nair
Browse files

Add ValidatedAttributionSourceState

Add type which encapsulates a validated attribution source, either via
binder context, or if it is passed from a trusted source.

This type will be used by the controller for permission validation.

Test: atest audiopermissioncontroller_test --host
Fixes: 259493676
Bug: 338089555
Flag: EXEMPT safe, adding utilities
Change-Id: I80af36b4b766b7c876f3d7cdb50257dc4d1c7dcd
parent 9f91a5ee
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -8,6 +8,7 @@ cc_library {


    srcs: [
    srcs: [
        "NativePermissionController.cpp",
        "NativePermissionController.cpp",
        "ValidatedAttributionSourceState.cpp",
    ],
    ],
    export_include_dirs: [
    export_include_dirs: [
        "include",
        "include",
@@ -22,6 +23,7 @@ cc_library {
    ],
    ],
    static_libs: [
    static_libs: [
        "audio-permission-aidl-cpp",
        "audio-permission-aidl-cpp",
        "framework-permission-aidl-cpp",
    ],
    ],
    shared_libs: [
    shared_libs: [
        "libbase",
        "libbase",
@@ -94,6 +96,7 @@ cc_test {
    ],
    ],
    srcs: [
    srcs: [
        "tests/NativePermissionControllerTest.cpp",
        "tests/NativePermissionControllerTest.cpp",
        "tests/ValidatedAttributionSourceStateTest.cpp",
    ],
    ],
    test_options: {
    test_options: {
        unit_test: true,
        unit_test: true,
+53 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#include <media/ValidatedAttributionSourceState.h>

#include <binder/IPCThreadState.h>
#include <error/expected_utils.h>
#include <utils/Log.h>

namespace com::android::media::permission {

using ::android::base::unexpected;

Result<ValidatedAttributionSourceState> ValidatedAttributionSourceState::createFromBinderContext(
        AttributionSourceState attr, const IPermissionProvider& provider) {
    attr.pid = ::android::IPCThreadState::self()->getCallingPid();
    attr.uid = ::android::IPCThreadState::self()->getCallingUid();
    return createFromTrustedUidNoPackage(std::move(attr), provider);
}

Result<ValidatedAttributionSourceState>
ValidatedAttributionSourceState::createFromTrustedUidNoPackage(
        AttributionSourceState attr, const IPermissionProvider& provider) {
    if (attr.packageName.has_value() && attr.packageName->size() != 0) {
        if (VALUE_OR_RETURN(provider.validateUidPackagePair(attr.uid, attr.packageName.value()))) {
            return ValidatedAttributionSourceState{std::move(attr)};
        } else {
            return unexpected{::android::PERMISSION_DENIED};
        }
    } else {
        // For APIs which don't appropriately pass attribution sources or packages, we need
        // to populate the package name with our best guess.
        const auto packageNames = VALUE_OR_RETURN(provider.getPackagesForUid(attr.uid));
        LOG_ALWAYS_FATAL_IF(packageNames.empty());
        attr.packageName = std::move(packageNames[0]);
        return ValidatedAttributionSourceState{std::move(attr)};
    }
}

}  // namespace com::android::media::permission
+71 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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/content/AttributionSourceState.h>
#include <error/Result.h>

#include "IPermissionProvider.h"

namespace com::android::media::permission {

using ::android::content::AttributionSourceState;
using ::android::error::Result;

class ValidatedAttributionSourceState {
  public:
    /**
     * Validates an attribution source from within the context of a binder transaction.
     * Overwrites the uid/pid and validates the packageName
     */
    static Result<ValidatedAttributionSourceState> createFromBinderContext(
            AttributionSourceState attr, const IPermissionProvider& provider);

    /**
     * Creates a ValidatedAttributionSourceState in cases where the source is passed from a
     * trusted entity which already performed validation.
     */
    static ValidatedAttributionSourceState createFromTrustedSource(AttributionSourceState attr) {
        return ValidatedAttributionSourceState(attr);
    }

    /**
     * Create a ValidatedAttribubtionSourceState in cases where the uid/pid is trusted, but the
     * packages have not been validated. Proper use of the previous two methods should avoid the
     * necessity of this, but it is useful for migration purposes as well as testing this class.
     */
    static Result<ValidatedAttributionSourceState> createFromTrustedUidNoPackage(
            AttributionSourceState attr, const IPermissionProvider& provider);

    operator AttributionSourceState() const { return state_; }

    operator const AttributionSourceState&() const { return state_; }

    AttributionSourceState unwrapInto() && { return std::move(state_); }

    bool operator==(const ValidatedAttributionSourceState& other) const {
        return operator==(other.state_);
    }

    bool operator==(const AttributionSourceState& other) const { return state_ == other; }

  private:
    ValidatedAttributionSourceState(AttributionSourceState attr) : state_(attr) {}

    AttributionSourceState state_;
};
}  // namespace com::android::media::permission
+125 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

#include <media/ValidatedAttributionSourceState.h>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <android-base/expected.h>
#include <media/IPermissionProvider.h>

using ::android::base::unexpected;
using ::android::binder::Status;
using ::android::content::AttributionSourceState;
using ::android::error::Result;
using ::com::android::media::permission::IPermissionProvider;
using ::com::android::media::permission::ValidatedAttributionSourceState;
using ::testing::Return;

class MockPermissionProvider : public IPermissionProvider {
  public:
    MOCK_METHOD(Result<std::vector<std::string>>, getPackagesForUid, (uid_t uid),
                (override, const));
    MOCK_METHOD(Result<bool>, validateUidPackagePair, (uid_t uid, const std::string&),
                (override, const));
};

class ValidatedAttributionSourceStateTest : public ::testing::Test {
  protected:
    MockPermissionProvider mMockProvider;
    const uid_t mUid = 10001;
    const std::vector<std::string> mPackageList{"com.package1", "com.package2"};
};

#define UNWRAP_EQ(expr, desired_expr)                         \
    do {                                                      \
        auto tmp_ = (expr);                                   \
        EXPECT_TRUE(tmp_.has_value());                        \
        if (tmp_.has_value()) EXPECT_EQ(*tmp_, desired_expr); \
    } while (0)

TEST_F(ValidatedAttributionSourceStateTest, providedPackageValid) {
    const std::string package = "com.package1";
    EXPECT_CALL(mMockProvider, validateUidPackagePair(mUid, package)).WillOnce(Return(true));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = package;
    UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
              attr);
}

TEST_F(ValidatedAttributionSourceStateTest, providedPackageInvalid) {
    const std::string package = "com.package.spoof";
    EXPECT_CALL(mMockProvider, validateUidPackagePair(mUid, package)).WillOnce(Return(false));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = package;
    const auto res =
            ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
    ASSERT_FALSE(res.has_value());
    EXPECT_EQ(res.error(), ::android::PERMISSION_DENIED);
}

TEST_F(ValidatedAttributionSourceStateTest, packageLookup_whenMissingPackage) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid)).WillOnce(Return(mPackageList));
    AttributionSourceState attr;
    attr.uid = mUid;
    AttributionSourceState expectedAttr;
    expectedAttr.uid = mUid;
    expectedAttr.packageName = "com.package1";
    UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
              expectedAttr);
}

TEST_F(ValidatedAttributionSourceStateTest, packageLookup_whenEmptyPackage) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid)).WillOnce(Return(mPackageList));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = std::string{};
    AttributionSourceState expectedAttr;
    expectedAttr.uid = mUid;
    expectedAttr.packageName = "com.package1";
    UNWRAP_EQ(ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider),
              expectedAttr);
}

TEST_F(ValidatedAttributionSourceStateTest, controllerNotInitialized) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid))
            .WillOnce(Return(unexpected{::android::NO_INIT}));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = std::string{};
    AttributionSourceState expectedAttr;
    expectedAttr.uid = mUid;
    expectedAttr.packageName = "com.package1";
    const auto res =
            ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
    ASSERT_FALSE(res.has_value());
    EXPECT_EQ(res.error(), ::android::NO_INIT);
}

TEST_F(ValidatedAttributionSourceStateTest, uidNotFound) {
    EXPECT_CALL(mMockProvider, getPackagesForUid(mUid))
            .WillOnce(Return(unexpected{::android::BAD_VALUE}));
    AttributionSourceState attr;
    attr.uid = mUid;
    attr.packageName = std::string{};
    const auto res =
            ValidatedAttributionSourceState::createFromTrustedUidNoPackage(attr, mMockProvider);
    ASSERT_FALSE(res.has_value());
    EXPECT_EQ(res.error(), ::android::BAD_VALUE);
}