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

Commit de6f16ff authored by Jan Sebechlebsky's avatar Jan Sebechlebsky
Browse files

Enforce CREATE_VIRTUAL_DEVICE permission for virtual camera service

Bug: 301023410
Test: atest
Change-Id: Ia3850ef0ace096dcda6c1d2fe8d3c5fd39e0a271
parent f8505e07
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ cc_library_static {
        "util/EglProgram.cc",
        "util/EglSurfaceTexture.cc",
        "util/EglUtil.cc",
        "util/Permissions.cc"
    ],
    defaults: [
        "libvirtualcamera_defaults",
+26 −2
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include "android/binder_auto_utils.h"
#include "android/binder_libbinder.h"
#include "binder/Status.h"
#include "util/Permissions.h"
#include "util/Util.h"

using ::android::binder::Status;
@@ -54,6 +55,8 @@ Available commands:
 * enable_test_camera
 * disable_test_camera
)";
constexpr char kCreateVirtualDevicePermission[] =
    "android.permission.CREATE_VIRTUAL_DEVICE";

ndk::ScopedAStatus validateConfiguration(
    const VirtualCameraConfiguration& configuration) {
@@ -79,17 +82,26 @@ ndk::ScopedAStatus validateConfiguration(
}  // namespace

VirtualCameraService::VirtualCameraService(
    std::shared_ptr<VirtualCameraProvider> virtualCameraProvider)
    : mVirtualCameraProvider(virtualCameraProvider) {
    std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
    const PermissionsProxy& permissionProxy)
    : mVirtualCameraProvider(virtualCameraProvider),
      mPermissionProxy(permissionProxy) {
}

ndk::ScopedAStatus VirtualCameraService::registerCamera(
    const ::ndk::SpAIBinder& token,
    const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
  if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
    ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
          getpid(), getuid(), kCreateVirtualDevicePermission);
    return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
  }

  if (_aidl_return == nullptr) {
    return ndk::ScopedAStatus::fromServiceSpecificError(
        Status::EX_ILLEGAL_ARGUMENT);
  }

  *_aidl_return = true;

  auto status = validateConfiguration(configuration);
@@ -127,6 +139,12 @@ ndk::ScopedAStatus VirtualCameraService::registerCamera(

ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
    const ::ndk::SpAIBinder& token) {
  if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
    ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
          getpid(), getuid(), kCreateVirtualDevicePermission);
    return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
  }

  std::lock_guard lock(mLock);

  auto it = mTokenToCameraName.find(token);
@@ -145,6 +163,12 @@ ndk::ScopedAStatus VirtualCameraService::unregisterCamera(

ndk::ScopedAStatus VirtualCameraService::getCameraId(
        const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
  if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
    ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
          getpid(), getuid(), kCreateVirtualDevicePermission);
    return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
  }

  if (_aidl_return == nullptr) {
    return ndk::ScopedAStatus::fromServiceSpecificError(
            Status::EX_ILLEGAL_ARGUMENT);
+5 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "VirtualCameraDevice.h"
#include "VirtualCameraProvider.h"
#include "aidl/android/companion/virtualcamera/BnVirtualCameraService.h"
#include "util/Permissions.h"

namespace android {
namespace companion {
@@ -34,7 +35,8 @@ class VirtualCameraService
    : public aidl::android::companion::virtualcamera::BnVirtualCameraService {
 public:
  VirtualCameraService(
      std::shared_ptr<VirtualCameraProvider> virtualCameraProvider);
      std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
      const PermissionsProxy& permissionProxy = PermissionsProxy::get());

  // Register camera corresponding to the binder token.
  ndk::ScopedAStatus registerCamera(
@@ -68,6 +70,8 @@ class VirtualCameraService

  std::shared_ptr<VirtualCameraProvider> mVirtualCameraProvider;

  const PermissionsProxy& mPermissionProxy;

  std::mutex mLock;
  struct BinderTokenHash {
    std::size_t operator()(const ::ndk::SpAIBinder& key) const {
+50 −2
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "binder/Binder.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "util/Permissions.h"
#include "utils/Errors.h"

namespace android {
@@ -50,10 +51,13 @@ using ::testing::Ge;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Not;
using ::testing::Return;
using ::testing::SizeIs;

constexpr int kVgaWidth = 640;
constexpr int kVgaHeight = 480;
constexpr char kCreateVirtualDevicePermissions[] =
    "android.permission.CREATE_VIRTUAL_DEVICE";

const VirtualCameraConfiguration kEmptyVirtualCameraConfiguration;

@@ -76,6 +80,12 @@ class MockCameraProviderCallback : public BnCameraProviderCallback {
              (override));
};

class MockPermissionsProxy : public PermissionsProxy {
 public:
  MOCK_METHOD(bool, checkCallingPermission, (const std::string&),
              (const override));
};

class VirtualCameraServiceTest : public ::testing::Test {
 public:
  void SetUp() override {
@@ -87,8 +97,11 @@ class VirtualCameraServiceTest : public ::testing::Test {
          return ndk::ScopedAStatus::ok();
        });
    mCameraProvider->setCallback(mMockCameraProviderCallback);
    mCameraService =
        ndk::SharedRefBase::make<VirtualCameraService>(mCameraProvider);
    mCameraService = ndk::SharedRefBase::make<VirtualCameraService>(
        mCameraProvider, mMockPermissionsProxy);

    ON_CALL(mMockPermissionsProxy, checkCallingPermission)
        .WillByDefault(Return(true));

    mDevNullFd = open("/dev/null", O_RDWR);
    ASSERT_THAT(mDevNullFd, Ge(0));
@@ -129,6 +142,7 @@ class VirtualCameraServiceTest : public ::testing::Test {
  std::shared_ptr<VirtualCameraProvider> mCameraProvider;
  std::shared_ptr<MockCameraProviderCallback> mMockCameraProviderCallback =
      ndk::SharedRefBase::make<MockCameraProviderCallback>();
  MockPermissionsProxy mMockPermissionsProxy;

  sp<BBinder> mOwnerToken;
  ndk::SpAIBinder mNdkOwnerToken;
@@ -242,6 +256,40 @@ TEST_F(VirtualCameraServiceTest, UnregisterCamera) {
  EXPECT_THAT(mCameraService->getCamera(mNdkOwnerToken), IsNull());
}

TEST_F(VirtualCameraServiceTest, RegisterCameraWithoutPermissionFails) {
  bool aidlRet;
  EXPECT_CALL(mMockPermissionsProxy,
              checkCallingPermission(kCreateVirtualDevicePermissions))
      .WillOnce(Return(false));

  EXPECT_THAT(mCameraService
                  ->registerCamera(mNdkOwnerToken, mVgaYUV420OnlyConfiguration,
                                   &aidlRet)
                  .getExceptionCode(),
              Eq(EX_SECURITY));
}

TEST_F(VirtualCameraServiceTest, UnregisterCameraWithoutPermissionFails) {
  EXPECT_CALL(mMockPermissionsProxy,
              checkCallingPermission(kCreateVirtualDevicePermissions))
      .WillOnce(Return(false));

  EXPECT_THAT(
      mCameraService->unregisterCamera(mNdkOwnerToken).getExceptionCode(),
      Eq(EX_SECURITY));
}

TEST_F(VirtualCameraServiceTest, GetIdWithoutPermissionFails) {
  int32_t aidlRet;
  EXPECT_CALL(mMockPermissionsProxy,
              checkCallingPermission(kCreateVirtualDevicePermissions))
      .WillOnce(Return(false));

  EXPECT_THAT(
      mCameraService->getCameraId(mNdkOwnerToken, &aidlRet).getExceptionCode(),
      Eq(EX_SECURITY));
}

TEST_F(VirtualCameraServiceTest, UnregisterCameraWithUnknownToken) {
  createCamera();

+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

// #define LOG_NDEBUG 0
#define LOG_TAG "VirtualCameraPermissions"

#include "Permissions.h"

#include "binder/PermissionCache.h"
#include "log/log.h"

namespace android {
namespace companion {
namespace virtualcamera {
namespace {

class PermissionsProxyImpl : public PermissionsProxy {
 public:
  bool checkCallingPermission(const std::string& permission) const override;
};

bool PermissionsProxyImpl::checkCallingPermission(
    const std::string& permission) const {
  int32_t uid;
  int32_t pid;
  const bool hasPermission = PermissionCache::checkCallingPermission(
      String16(permission.c_str()), &pid, &uid);

  ALOGV("%s: Checking %s permission for pid %d uid %d: %s", __func__,
        permission.c_str(), pid, uid, hasPermission ? "granted" : "denied");
  return hasPermission;
}
}  // namespace

const PermissionsProxy& PermissionsProxy::get() {
  static PermissionsProxyImpl sPermissionProxyImpl;
  return sPermissionProxyImpl;
}

}  // namespace virtualcamera
}  // namespace companion
}  // namespace android
Loading