summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Leme <felipeal@google.com>2019-03-26 14:02:25 -0700
committerFelipe Leme <felipeal@google.com>2019-03-28 19:31:52 -0700
commitafbba9fb3681515288608402390904f1cb96546b (patch)
treeb732fb3d1ebbfdbc00bbbd56e6c0530d662edef2
parenta8d33c24f8531b65020436c15d868d72677a5b97 (diff)
Checks package name belongs to called UID on some content capture methods.
Also refactored how the SecurityException is thrown back to the caller app. Bug: 122959591 Test: manual verification Test: atest CtsContentCaptureServiceTestCases # sanity check (minus usual flakiness) Change-Id: I4d2a68e61dc1c801d80734a30f4bbe6fdae8555d
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java52
-rw-r--r--core/java/com/android/internal/util/SyncResultReceiver.java3
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java66
-rw-r--r--services/core/java/com/android/server/infra/AbstractMasterSystemService.java17
4 files changed, 89 insertions, 49 deletions
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 35f802303fa1..253935680cb8 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -525,15 +525,12 @@ public final class ContentCaptureManager {
// the service to fine tune how long-lived apps (like browsers) are whitelisted.
if (!isContentCaptureEnabled() && !mOptions.lite) return null;
- final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
- try {
- mService.getContentCaptureConditions(mContext.getPackageName(), resultReceiver);
- final ArrayList<ContentCaptureCondition> result = resultReceiver
- .getParcelableListResult();
- return toSet(result);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ final SyncResultReceiver resultReceiver = syncRun(
+ (r) -> mService.getContentCaptureConditions(mContext.getPackageName(), r));
+
+ final ArrayList<ContentCaptureCondition> result = resultReceiver
+ .getParcelableListResult();
+ return toSet(result);
}
/**
@@ -566,21 +563,14 @@ public final class ContentCaptureManager {
@SystemApi
@TestApi
public boolean isContentCaptureFeatureEnabled() {
- final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
- final int resultCode;
- try {
- mService.isContentCaptureFeatureEnabled(resultReceiver);
- resultCode = resultReceiver.getIntResult();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ final SyncResultReceiver resultReceiver = syncRun(
+ (r) -> mService.isContentCaptureFeatureEnabled(r));
+ final int resultCode = resultReceiver.getIntResult();
switch (resultCode) {
case RESULT_CODE_TRUE:
return true;
case RESULT_CODE_FALSE:
return false;
- case RESULT_CODE_SECURITY_EXCEPTION:
- throw new SecurityException("caller is not user's ContentCapture service");
default:
Log.wtf(TAG, "received invalid result: " + resultCode);
return false;
@@ -603,6 +593,26 @@ public final class ContentCaptureManager {
}
}
+ /**
+ * Runs a sync method in the service, properly handling exceptions.
+ *
+ * @throws SecurityException if caller is not allowed to execute the method.
+ */
+ @NonNull
+ private SyncResultReceiver syncRun(@NonNull MyRunnable r) {
+ final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
+ try {
+ r.run(resultReceiver);
+ final int resultCode = resultReceiver.getIntResult();
+ if (resultCode == RESULT_CODE_SECURITY_EXCEPTION) {
+ throw new SecurityException(resultReceiver.getStringResult());
+ }
+ return resultReceiver;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.println("ContentCaptureManager");
@@ -626,4 +636,8 @@ public final class ContentCaptureManager {
}
}
}
+
+ private interface MyRunnable {
+ void run(@NonNull SyncResultReceiver receiver) throws RemoteException;
+ }
}
diff --git a/core/java/com/android/internal/util/SyncResultReceiver.java b/core/java/com/android/internal/util/SyncResultReceiver.java
index 6fad84bb6c43..00e91017a9eb 100644
--- a/core/java/com/android/internal/util/SyncResultReceiver.java
+++ b/core/java/com/android/internal/util/SyncResultReceiver.java
@@ -19,7 +19,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcelable;
-import android.os.RemoteException;
import com.android.internal.os.IResultReceiver;
@@ -183,7 +182,7 @@ public final class SyncResultReceiver extends IResultReceiver.Stub {
}
/** @hide */
- public static final class TimeoutException extends RemoteException {
+ public static final class TimeoutException extends RuntimeException {
private TimeoutException(String msg) {
super(msg);
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 2894662becac..9b02c4e72cfc 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -450,23 +450,16 @@ public final class ContentCaptureManagerService extends
}
@GuardedBy("mLock")
- private boolean assertCalledByServiceLocked(@NonNull String methodName, @UserIdInt int userId,
- int callingUid, @NonNull IResultReceiver result) {
- final boolean isService = isCalledByServiceLocked(methodName, userId, callingUid);
- if (isService) return true;
-
- try {
- result.send(RESULT_CODE_SECURITY_EXCEPTION, /* resultData= */ null);
- } catch (RemoteException e) {
- Slog.w(mTag, "Unable to send isContentCaptureFeatureEnabled(): " + e);
+ private void assertCalledByServiceLocked(@NonNull String methodName) {
+ if (!isCalledByServiceLocked(methodName)) {
+ throw new SecurityException("caller is not user's ContentCapture service");
}
- return false;
}
@GuardedBy("mLock")
- private boolean isCalledByServiceLocked(@NonNull String methodName, @UserIdInt int userId,
- int callingUid) {
-
+ private boolean isCalledByServiceLocked(@NonNull String methodName) {
+ final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
final String serviceName = mServiceNameResolver.getServiceName(userId);
if (serviceName == null) {
Slog.e(mTag, methodName + ": called by UID " + callingUid
@@ -499,6 +492,27 @@ public final class ContentCaptureManagerService extends
return true;
}
+ /**
+ * Executes the given {@code runnable} and if it throws a {@link SecurityException},
+ * send it back to the receiver.
+ *
+ * @return whether the exception was thrown or not.
+ */
+ private boolean throwsSecurityException(@NonNull IResultReceiver result,
+ @NonNull Runnable runable) {
+ try {
+ runable.run();
+ return false;
+ } catch (SecurityException e) {
+ try {
+ result.send(RESULT_CODE_SECURITY_EXCEPTION, bundleFor(e.getMessage()));
+ } catch (RemoteException e2) {
+ Slog.w(mTag, "Unable to send security exception (" + e + "): ", e2);
+ }
+ }
+ return true;
+ }
+
@Override // from AbstractMasterSystemService
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
@@ -570,7 +584,7 @@ public final class ContentCaptureManagerService extends
@Override
public void removeUserData(@NonNull UserDataRemovalRequest request) {
Preconditions.checkNotNull(request);
- // TODO(b/122959591): check caller uid owns the package name
+ assertCalledByPackageOwner(request.getPackageName());
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
@@ -581,13 +595,14 @@ public final class ContentCaptureManagerService extends
@Override
public void isContentCaptureFeatureEnabled(@NonNull IResultReceiver result) {
- final int userId = UserHandle.getCallingUserId();
boolean enabled;
synchronized (mLock) {
- final boolean isService = assertCalledByServiceLocked(
- "isContentCaptureFeatureEnabled()", userId, Binder.getCallingUid(), result);
- if (!isService) return;
+ if (throwsSecurityException(result,
+ () -> assertCalledByServiceLocked("isContentCaptureFeatureEnabled()"))) {
+ return;
+ }
+ final int userId = UserHandle.getCallingUserId();
enabled = !mDisabledByDeviceConfig && !isDisabledBySettingsLocked(userId);
}
try {
@@ -599,15 +614,8 @@ public final class ContentCaptureManagerService extends
@Override
public void getServiceSettingsActivity(@NonNull IResultReceiver result) {
- try {
- enforceCallingPermissionForManagement();
- } catch (SecurityException e) {
- try {
- result.send(RESULT_CODE_SECURITY_EXCEPTION, bundleFor(e.getMessage()));
- } catch (RemoteException e2) {
- Slog.w(mTag, "Unable to send getServiceSettingsIntent() exception: " + e2);
- return;
- }
+ if (throwsSecurityException(result, () -> enforceCallingPermissionForManagement())) {
+ return;
}
final int userId = UserHandle.getCallingUserId();
@@ -627,7 +635,9 @@ public final class ContentCaptureManagerService extends
@Override
public void getContentCaptureConditions(@NonNull String packageName,
@NonNull IResultReceiver result) {
- // TODO(b/122959591): check caller uid owns the package name
+ if (throwsSecurityException(result, () -> assertCalledByPackageOwner(packageName))) {
+ return;
+ }
final int userId = UserHandle.getCallingUserId();
final ArrayList<ContentCaptureCondition> conditions;
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 39e93f5e44fe..098b0e9d4d39 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -616,6 +616,23 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
mServicesCache.clear();
}
+ /**
+ * Asserts that the given package name is owned by the UID making this call.
+ *
+ * @throws SecurityException when it's not...
+ */
+ protected final void assertCalledByPackageOwner(@NonNull String packageName) {
+ Preconditions.checkNotNull(packageName);
+ final int uid = Binder.getCallingUid();
+ final String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
+ if (packages != null) {
+ for (String candidate : packages) {
+ if (packageName.equals(candidate)) return; // Found it
+ }
+ }
+ throw new SecurityException("UID " + uid + " does not own " + packageName);
+ }
+
// TODO(b/117779333): support proto
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;