summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/util/Preconditions.java26
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java61
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java55
3 files changed, 135 insertions, 7 deletions
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 330c15cc4614..c82a6564c1d7 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -187,6 +187,32 @@ public class Preconditions {
}
/**
+ * Ensures the truth of an expression involving whether the calling identity is authorized to
+ * call the calling method.
+ *
+ * @param expression a boolean expression
+ * @throws SecurityException if {@code expression} is false
+ */
+ public static void checkCallAuthorization(final boolean expression) {
+ if (!expression) {
+ throw new SecurityException("Calling identity is not authorized");
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving whether the calling user is authorized to
+ * call the calling method.
+ *
+ * @param expression a boolean expression
+ * @throws SecurityException if {@code expression} is false
+ */
+ public static void checkCallingUser(final boolean expression) {
+ if (!expression) {
+ throw new SecurityException("Calling user is not authorized");
+ }
+ }
+
+ /**
* Check the requested flags, throwing if any requested flags are outside
* the allowed set.
*
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
new file mode 100644
index 000000000000..5193fa85d238
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+/**
+ * Caller identity containing the caller's UID, package name and component name.
+ * All parameters are verified on object creation unless the component name is null and the
+ * caller is a delegate.
+ */
+class CallerIdentity {
+
+ private final int mUid;
+ @Nullable
+ private final String mPackageName;
+ @Nullable
+ private final ComponentName mComponentName;
+
+ CallerIdentity(int uid, @Nullable String packageName, @Nullable ComponentName componentName) {
+ mUid = uid;
+ mPackageName = packageName;
+ mComponentName = componentName;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public int getUserId() {
+ return UserHandle.getUserId(mUid);
+ }
+
+ public UserHandle getUserHandle() {
+ return UserHandle.getUserHandleForUid(mUid);
+ }
+
+ @Nullable public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Nullable public ComponentName getComponentName() {
+ return mComponentName;
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6ca4f4..c82bc3a425c8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2702,6 +2702,39 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
/**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity.
+ */
+ private CallerIdentity getCallerIdentity(String callerPackage) {
+ final int callerUid = mInjector.binderGetCallingUid();
+
+ if (!isCallingFromPackage(callerPackage, callerUid)) {
+ throw new SecurityException(
+ String.format("Caller with uid %d is not %s", callerUid, callerPackage));
+ }
+
+ return new CallerIdentity(callerUid, callerPackage, null);
+ }
+
+ /**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity.
+ */
+ private CallerIdentity getCallerIdentity(@NonNull ComponentName componentName) {
+ final int callerUid = mInjector.binderGetCallingUid();
+ final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid));
+ ActiveAdmin admin = policy.mAdminMap.get(componentName);
+
+ if (admin == null) {
+ throw new SecurityException(String.format("No active admin for %s", componentName));
+ }
+ if (admin.getUid() != callerUid) {
+ throw new SecurityException(
+ String.format("Admin %s is not owned by uid %d", componentName, callerUid));
+ }
+
+ return new CallerIdentity(callerUid, componentName.getPackageName(), componentName);
+ }
+
+ /**
* Checks if the device is in COMP mode, and if so migrates it to managed profile on a
* corporate owned device.
*/
@@ -8728,6 +8761,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private boolean isDeviceOwner(CallerIdentity identity) {
+ synchronized (getLockObject()) {
+ return mOwners.hasDeviceOwner()
+ && mOwners.getDeviceOwnerUserId() == identity.getUserId()
+ && mOwners.getDeviceOwnerComponent().equals(identity.getComponentName());
+ }
+ }
+
private boolean isDeviceOwnerPackage(String packageName, int userId) {
synchronized (getLockObject()) {
return mOwners.hasDeviceOwner()
@@ -11984,20 +12025,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
- enforceDeviceOwner(Objects.requireNonNull(who));
-
- UserHandle user = mInjector.binderGetCallingUserHandle();
+ CallerIdentity identity = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(isDeviceOwner(identity));
mInjector.binderWithCleanCallingIdentity(() -> {
boolean wasLocationEnabled = mInjector.getLocationManager().isLocationEnabledForUser(
- user);
- mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, user);
+ identity.getUserHandle());
+ mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled,
+ identity.getUserHandle());
// make a best effort to only show the notification if the admin is actually enabling
// location. this is subject to race conditions with settings changes, but those are
// unlikely to realistically interfere
- if (locationEnabled && (wasLocationEnabled != locationEnabled)) {
- showLocationSettingsEnabledNotification(user);
+ if (locationEnabled && !wasLocationEnabled) {
+ showLocationSettingsEnabledNotification(identity.getUserHandle());
}
});