diff options
5 files changed, 353 insertions, 1 deletions
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp index a5712b319f..1ae0a001a3 100644 --- a/libs/permission/Android.bp +++ b/libs/permission/Android.bp @@ -7,17 +7,42 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aidl_interface { + name: "framework-permission-aidl", + unstable: true, + local_include_dir: "aidl", + backend: { + ndk: { + enabled: false + } + }, + srcs: [ + "aidl/android/content/AttributionSourceState.aidl", + "aidl/android/permission/IPermissionChecker.aidl", + ], +} + cc_library_shared { name: "libpermission", + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], srcs: [ "AppOpsManager.cpp", "IAppOpsCallback.cpp", "IAppOpsService.cpp", + "android/permission/PermissionChecker.cpp", ], export_include_dirs: ["include"], shared_libs: [ + "libutils", "libbinder", + "libcutils", "liblog", - "libutils", + ], + static_libs: [ + "framework-permission-aidl-cpp", ], } diff --git a/libs/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl new file mode 100644 index 0000000000..b6e54bf153 --- /dev/null +++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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. + */ + +package android.content; + +/** + * Payload for the {@link AttributionSource} class needed to interoperate + * with different languages. + * + * {@hide} + */ +parcelable AttributionSourceState { + /** The UID that is accessing the permission protected data. */ + int uid; + /** The package that is accessing the permission protected data. */ + @nullable @utf8InCpp String packageName; + /** The attribution tag of the app accessing the permission protected data. */ + @nullable @utf8InCpp String attributionTag; + /** Unique token for that source. */ + @nullable IBinder token; + /** Permissions that should be considered revoked regardless if granted. */ + @nullable @utf8InCpp String[] renouncedPermissions; + /** The next app to receive the permission protected data. */ + // TODO: We use an array as a workaround - the C++ backend doesn't + // support referring to the parcelable as it expects ctor/dtor + @nullable AttributionSourceState[] next; +} diff --git a/libs/permission/aidl/android/permission/IPermissionChecker.aidl b/libs/permission/aidl/android/permission/IPermissionChecker.aidl new file mode 100644 index 0000000000..1f0e32d248 --- /dev/null +++ b/libs/permission/aidl/android/permission/IPermissionChecker.aidl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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. + */ + +package android.permission; + +import android.content.AttributionSourceState; + +/** + * Interface to communicate directly with the permission checker service. + */ +interface IPermissionChecker { + const int PERMISSION_GRANTED = 0; + const int PERMISSION_SOFT_DENIED = 1; + const int PERMISSION_HARD_DENIED = 2; + + int checkPermission(String permission, in AttributionSourceState attributionSource, + @nullable String message, boolean forDataDelivery, boolean startDataDelivery, + boolean fromDatasource); + + void finishDataDelivery(String op, in AttributionSourceState attributionSource); + + int checkOp(int op, in AttributionSourceState attributionSource, + String message, boolean forDataDelivery, boolean startDataDelivery); +} diff --git a/libs/permission/android/permission/PermissionChecker.cpp b/libs/permission/android/permission/PermissionChecker.cpp new file mode 100644 index 0000000000..a8083ee410 --- /dev/null +++ b/libs/permission/android/permission/PermissionChecker.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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 <mutex> +#include <include/android/permission/PermissionChecker.h> +#include <binder/Binder.h> +#include <binder/IServiceManager.h> + +#include <utils/SystemClock.h> + +#include <sys/types.h> +#include <private/android_filesystem_config.h> + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "PermissionChecker" + +namespace android { + +using android::content::AttributionSourceState; + +PermissionChecker::PermissionChecker() +{ +} + +sp<IPermissionChecker> PermissionChecker::getService() +{ + static String16 permission_checker("permission_checker"); + + std::lock_guard<Mutex> scoped_lock(mLock); + int64_t startTime = 0; + sp<IPermissionChecker> service = mService; + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp<IBinder> binder = defaultServiceManager()->checkService(permission_checker); + if (binder == nullptr) { + // Wait for the permission checker service to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + ALOGW("Waiting for permission checker service"); + } else if ((uptimeMillis() - startTime) > 10000) { + ALOGE("Waiting too long for permission checker service, giving up"); + service = nullptr; + break; + } + sleep(1); + } else { + mService = interface_cast<IPermissionChecker>(binder); + } + } + return mService; +} + +PermissionChecker::PermissionResult + PermissionChecker::checkPermissionForDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message) +{ + return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message, + /*forDataDelivery*/ true, /*startDataDelivery*/ false,/*fromDatasource*/ true)); +} + +PermissionChecker::PermissionResult + PermissionChecker::checkPermissionForStartDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message) +{ + return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message, + /*forDataDelivery*/ true, /*startDataDelivery*/ true, /*fromDatasource*/ true)); +} + +void PermissionChecker::finishDataDelivery(const String16& op, + AttributionSourceState& attributionSource) +{ + sp<IPermissionChecker> service = getService(); + if (service != nullptr) { + binder::Status status = service->finishDataDelivery(op, attributionSource); + if (!status.isOk()) { + ALOGE("finishDataDelivery failed: %s", status.exceptionMessage().c_str()); + } + } +} + +int32_t PermissionChecker::checkPermission(const String16& permission, + AttributionSourceState& attributionSource, const String16& message, + bool forDataDelivery, bool startDataDelivery, bool fromDatasource) +{ + sp<IPermissionChecker> service = getService(); + if (service != nullptr) { + int32_t result; + binder::Status status = service->checkPermission(permission, attributionSource, message, + forDataDelivery, startDataDelivery, fromDatasource, &result); + if (status.isOk()) { + return result; + } + ALOGE("checkPermission failed: %s", status.exceptionMessage().c_str()); + } + return PERMISSION_DENIED; +} + +} // namespace android diff --git a/libs/permission/include/android/permission/PermissionChecker.h b/libs/permission/include/android/permission/PermissionChecker.h new file mode 100644 index 0000000000..20ab51fc8a --- /dev/null +++ b/libs/permission/include/android/permission/PermissionChecker.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 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 <android/permission/IPermissionChecker.h> + +#include <utils/threads.h> + +#include <optional> + +#ifdef __ANDROID_VNDK__ +#error "This header is not visible to vendors" +#endif + +// --------------------------------------------------------------------------- +namespace android { + +using android::content::AttributionSourceState; +using android::permission::IPermissionChecker; + +class PermissionChecker +{ +public: + + enum PermissionResult { + + /** + * The permission is granted. + */ + PERMISSION_GRANTED = IPermissionChecker::PERMISSION_GRANTED, + + /** + * The permission is denied. Applicable only to runtime and app op permissions. + * + * Returned when: + * - the runtime permission is granted, but the corresponding app op is denied + * for runtime permissions. + * - the app ops is ignored for app op permissions. + * + */ + PERMISSION_SOFT_DENIED = IPermissionChecker::PERMISSION_SOFT_DENIED, + + /** + * The permission is denied. + * + * Returned when: + * - the permission is denied for non app op permissions. + * - the app op is denied or app op is AppOpsManager#MODE_DEFAULT and permission is denied. + */ + PERMISSION_HARD_DENIED = IPermissionChecker::PERMISSION_HARD_DENIED + }; + + PermissionChecker(); + + /** + * Checks whether a given data access chain described by the given attribution source + * has a given permission and whether the app op that corresponds to this permission + * is allowed. Call this method if you are the datasource which would not blame you for + * access to the data since you are the data. Note that the attribution source chain + * + * NOTE: The attribution source should be for yourself with its next attribution + * source being the app that would receive the data from you. + * + * NOTE: Use this method only for permission checks at the point where you will deliver + * the permission protected data to clients. + * + * @param permission The permission to check. + * @param attributionSource The attribution chain to check. + * @param message A message describing the reason the permission was checked. + * @return The permission check result which is either PERMISSION_GRANTED, + * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED. + */ + PermissionChecker::PermissionResult checkPermissionForDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message); + + /** + * Checks whether a given data access chain described by the given attribution source + * has a given permission and whether the app op that corresponds to this permission + * is allowed. The app ops are also marked as started. This is useful for long running + * permissions like camera and microphone. + * + * NOTE: The attribution source should be for yourself with its next attribution + * source being the app that would receive the data from you. + * + * NOTE: Use this method only for permission checks at the point where you will deliver + * the permission protected data to clients. + * + * @param permission The permission to check. + * @param attributionSource The attribution chain to check. + * @param message A message describing the reason the permission was checked. + * @return The permission check result which is either PERMISSION_GRANTED, + * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED. + */ + PermissionResult checkPermissionForStartDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message); + + /** + * Finishes an ongoing op for data access chain described by the given + * attribution source. + * + * @param op The op to finish. + * @param attributionSource The attribution chain for which to finish data delivery. + */ + void finishDataDelivery(const String16& op, AttributionSourceState& attributionSource); + +private: + Mutex mLock; + sp<IPermissionChecker> mService; + sp<IPermissionChecker> getService(); + + int32_t checkPermission(const String16& permission, AttributionSourceState& attributionSource, + const String16& message, bool forDataDelivery, bool startDataDelivery, + bool fromDatasource); +}; + + +} // namespace android + +// --------------------------------------------------------------------------- |