From a453d0d278ee916bf68c98093fcfd9fa924ca454 Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Thu, 11 Jan 2018 15:37:58 -0800 Subject: [PATCH] No camera for idle uids - av framework If a UID is idle (being in the background for more than cartain amount of time) it should not be able to use the camera. If the UID becomes idle we generate an eror and close the cameras for this UID. If an app in an idle UID tries to use the camera we immediately generate an error. Since apps already should handle these errors it is safe to apply this policy to all apps to protect user privacy. Test: Pass - cts-tradefed run cts -m CtsCameraTestCases Added - CameraTest#testCameraAccessForIdleUid Bug: 63938985 Change-Id: Ic0111d7c651b3d84c644b9f3d30e24133544f4fa --- .../camera2/ICameraDeviceCallbacks.aidl | 1 + .../camera/libcameraservice/CameraService.cpp | 241 +++++++++++++++++- .../camera/libcameraservice/CameraService.h | 54 ++++ 3 files changed, 290 insertions(+), 6 deletions(-) diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl index 28252c0d3e..4db7f8573a 100644 --- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl +++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl @@ -30,6 +30,7 @@ interface ICameraDeviceCallbacks const int ERROR_CAMERA_REQUEST = 3; const int ERROR_CAMERA_RESULT = 4; const int ERROR_CAMERA_BUFFER = 5; + const int ERROR_CAMERA_DISABLED = 6; oneway void onDeviceError(int errorCode, in CaptureResultExtras resultExtras); oneway void onDeviceIdle(); diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 6abfa81e73..5fc1fa331b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -33,14 +33,18 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include #include +#include #include #include #include @@ -165,6 +169,8 @@ static void torch_mode_status_change( // ---------------------------------------------------------------------------- +static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA"); + CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH), mNumberOfCameras(0), mNumberOfNormalCameras(0), @@ -196,6 +202,9 @@ void CameraService::onFirstRef() } CameraService::pingCameraServiceProxy(); + + mUidPolicy = new UidPolicy(this); + mUidPolicy->registerSelf(); } status_t CameraService::enumerateProviders() { @@ -275,6 +284,7 @@ void CameraService::pingCameraServiceProxy() { CameraService::~CameraService() { VendorTagDescriptor::clearGlobalVendorTagDescriptor(); + mUidPolicy->unregisterSelf(); } void CameraService::onNewProviderRegistered() { @@ -933,6 +943,15 @@ Status CameraService::validateClientPermissionsLocked(const String8& cameraId, clientName8.string(), clientUid, clientPid, cameraId.string()); } + // Make sure the UID is in an active state to use the camera + if (!mUidPolicy->isUidActive(callingUid)) { + ALOGE("Access Denial: can't use the camera from an idle UID pid=%d, uid=%d", + clientPid, clientUid); + return STATUS_ERROR_FMT(ERROR_DISABLED, + "Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" from background", + clientName8.string(), clientUid, clientPid, cameraId.string()); + } + // Only use passed in clientPid to check permission. Use calling PID as the client PID that's // connected to camera service directly. originalClientPid = clientPid; @@ -1969,6 +1988,30 @@ status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* re // Permission checks switch (code) { + case SHELL_COMMAND_TRANSACTION: { + int in = data.readFileDescriptor(); + int out = data.readFileDescriptor(); + int err = data.readFileDescriptor(); + int argc = data.readInt32(); + Vector args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(data.readString16()); + } + sp unusedCallback; + sp resultReceiver; + status_t status; + if ((status = data.readNullableStrongBinder(&unusedCallback)) != NO_ERROR) { + return status; + } + if ((status = data.readNullableStrongBinder(&resultReceiver)) != NO_ERROR) { + return status; + } + status = shellCommand(in, out, err, args); + if (resultReceiver != nullptr) { + resultReceiver->send(status); + } + return NO_ERROR; + } case BnCameraService::NOTIFYSYSTEMEVENT: { if (pid != selfPid) { // Ensure we're being called by system_server, or similar process with @@ -2286,15 +2329,21 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa if (res != AppOpsManager::MODE_ALLOWED) { ALOGI("Camera %s: Access for \"%s\" revoked", mCameraIdStr.string(), myName.string()); - // Reset the client PID to allow server-initiated disconnect, - // and to prevent further calls by client. - mClientPid = getCallingPid(); - CaptureResultExtras resultExtras; // a dummy result (invalid) - notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE, resultExtras); - disconnect(); + block(); } } +void CameraService::BasicClient::block() { + ATRACE_CALL(); + + // Reset the client PID to allow server-initiated disconnect, + // and to prevent further calls by client. + mClientPid = getCallingPid(); + CaptureResultExtras resultExtras; // a dummy result (invalid) + notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISABLED, resultExtras); + disconnect(); +} + // ---------------------------------------------------------------------------- void CameraService::Client::notifyError(int32_t errorCode, @@ -2330,6 +2379,98 @@ void CameraService::Client::OpsCallback::opChanged(int32_t op, } } +// ---------------------------------------------------------------------------- +// UidPolicy +// ---------------------------------------------------------------------------- + +void CameraService::UidPolicy::registerSelf() { + ActivityManager am; + am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE + | ActivityManager::UID_OBSERVER_IDLE + | ActivityManager::UID_OBSERVER_ACTIVE, + ActivityManager::PROCESS_STATE_UNKNOWN, + String16("cameraserver")); +} + +void CameraService::UidPolicy::unregisterSelf() { + ActivityManager am; + am.unregisterUidObserver(this); +} + +void CameraService::UidPolicy::onUidGone(uid_t uid, bool disabled) { + onUidIdle(uid, disabled); +} + +void CameraService::UidPolicy::onUidActive(uid_t uid) { + Mutex::Autolock _l(mUidLock); + mActiveUids.insert(uid); +} + +void CameraService::UidPolicy::onUidIdle(uid_t uid, bool /* disabled */) { + bool deleted = false; + { + Mutex::Autolock _l(mUidLock); + if (mActiveUids.erase(uid) > 0) { + deleted = true; + } + } + if (deleted) { + sp service = mService.promote(); + if (service != nullptr) { + service->blockClientsForUid(uid); + } + } +} + +bool CameraService::UidPolicy::isUidActive(uid_t uid) { + // Non-app UIDs are considered always active + if (uid < FIRST_APPLICATION_UID) { + return true; + } + Mutex::Autolock _l(mUidLock); + return isUidActiveLocked(uid); +} + +bool CameraService::UidPolicy::isUidActiveLocked(uid_t uid) { + // Non-app UIDs are considered always active + if (uid < FIRST_APPLICATION_UID) { + return true; + } + auto it = mOverrideUids.find(uid); + if (it != mOverrideUids.end()) { + return it->second; + } + return mActiveUids.find(uid) != mActiveUids.end(); +} + +void CameraService::UidPolicy::UidPolicy::addOverrideUid(uid_t uid, bool active) { + updateOverrideUid(uid, active, true); +} + +void CameraService::UidPolicy::removeOverrideUid(uid_t uid) { + updateOverrideUid(uid, false, false); +} + +void CameraService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) { + bool wasActive = false; + bool isActive = false; + { + Mutex::Autolock _l(mUidLock); + wasActive = isUidActiveLocked(uid); + mOverrideUids.erase(uid); + if (insert) { + mOverrideUids.insert(std::pair(uid, active)); + } + isActive = isUidActiveLocked(uid); + } + if (wasActive != isActive && !isActive) { + sp service = mService.promote(); + if (service != nullptr) { + service->blockClientsForUid(uid); + } + } +} + // ---------------------------------------------------------------------------- // CameraState // ---------------------------------------------------------------------------- @@ -2791,4 +2932,92 @@ status_t CameraService::setTorchStatusLocked(const String8& cameraId, return OK; } +void CameraService::blockClientsForUid(uid_t uid) { + const auto clients = mActiveClientManager.getAll(); + for (auto& current : clients) { + if (current != nullptr) { + const auto basicClient = current->getValue(); + if (basicClient.get() != nullptr && basicClient->getClientUid() == uid) { + basicClient->block(); + } + } + } +} + +// NOTE: This is a remote API - make sure all args are validated +status_t CameraService::shellCommand(int in, int out, int err, const Vector& args) { + if (!checkCallingPermission(sManageCameraPermission, nullptr, nullptr)) { + return PERMISSION_DENIED; + } + if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) { + return BAD_VALUE; + } + if (args.size() == 3 && args[0] == String16("set-uid-state")) { + return handleSetUidState(args, err); + } else if (args.size() == 2 && args[0] == String16("reset-uid-state")) { + return handleResetUidState(args, err); + } else if (args.size() == 2 && args[0] == String16("get-uid-state")) { + return handleGetUidState(args, out, err); + } else if (args.size() == 1 && args[0] == String16("help")) { + printHelp(out); + return NO_ERROR; + } + printHelp(err); + return BAD_VALUE; +} + +status_t CameraService::handleSetUidState(const Vector& args, int err) { + PermissionController pc; + int uid = pc.getPackageUid(args[1], 0); + if (uid <= 0) { + ALOGE("Unknown package: '%s'", String8(args[1]).string()); + dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string()); + return BAD_VALUE; + } + bool active = false; + if (args[2] == String16("active")) { + active = true; + } else if ((args[2] != String16("idle"))) { + ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string()); + return BAD_VALUE; + } + mUidPolicy->addOverrideUid(uid, active); + return NO_ERROR; +} + +status_t CameraService::handleResetUidState(const Vector& args, int err) { + PermissionController pc; + int uid = pc.getPackageUid(args[1], 0); + if (uid < 0) { + ALOGE("Unknown package: '%s'", String8(args[1]).string()); + dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string()); + return BAD_VALUE; + } + mUidPolicy->removeOverrideUid(uid); + return NO_ERROR; +} + +status_t CameraService::handleGetUidState(const Vector& args, int out, int err) { + PermissionController pc; + int uid = pc.getPackageUid(args[1], 0); + if (uid <= 0) { + ALOGE("Unknown package: '%s'", String8(args[1]).string()); + dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string()); + return BAD_VALUE; + } + if (mUidPolicy->isUidActive(uid)) { + return dprintf(out, "active\n"); + } else { + return dprintf(out, "idle\n"); + } +} + +status_t CameraService::printHelp(int out) { + return dprintf(out, "Camera service commands:\n" + " get-uid-state gets the uid state\n" + " set-uid-state overrides the uid state\n" + " reset-uid-state clears the uid state override\n" + " help print this message\n"); +} + }; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index e9373a6489..575cebf8ef 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,8 @@ #include #include #include +#include +#include namespace android { @@ -163,6 +166,8 @@ public: virtual status_t dump(int fd, const Vector& args); + virtual status_t shellCommand(int in, int out, int err, const Vector& args); + ///////////////////////////////////////////////////////////////////// // Client functionality @@ -233,6 +238,9 @@ public: // Check what API level is used for this client. This is used to determine which // superclass this can be cast to. virtual bool canCastToApiClient(apiLevel level) const; + + // Block the client form using the camera + virtual void block(); protected: BasicClient(const sp& cameraService, const sp& remoteCallback, @@ -506,6 +514,37 @@ private: CameraParameters mShimParams; }; // class CameraState + // Observer for UID lifecycle enforcing that UIDs in idle + // state cannot use the camera to protect user privacy. + class UidPolicy : public BnUidObserver { + public: + explicit UidPolicy(sp service) + : mService(service) {} + + void registerSelf(); + void unregisterSelf(); + + bool isUidActive(uid_t uid); + + void onUidGone(uid_t uid, bool disabled); + void onUidActive(uid_t uid); + void onUidIdle(uid_t uid, bool disabled); + + void addOverrideUid(uid_t uid, bool active); + void removeOverrideUid(uid_t uid); + + private: + bool isUidActiveLocked(uid_t uid); + void updateOverrideUid(uid_t uid, bool active, bool insert); + + Mutex mUidLock; + wp mService; + std::unordered_set mActiveUids; + std::unordered_map mOverrideUids; + }; // class UidPolicy + + sp mUidPolicy; + // Delay-load the Camera HAL module virtual void onFirstRef(); @@ -755,6 +794,21 @@ private: */ binder::Status getLegacyParametersLazy(int cameraId, /*out*/CameraParameters* parameters); + // Blocks all clients from the UID + void blockClientsForUid(uid_t uid); + + // Overrides the UID state as if it is idle + status_t handleSetUidState(const Vector& args, int err); + + // Clears the override for the UID state + status_t handleResetUidState(const Vector& args, int err); + + // Gets the UID state + status_t handleGetUidState(const Vector& args, int out, int err); + + // Prints the shell command help + status_t printHelp(int out); + static int getCallingPid(); static int getCallingUid();