Merge "Fix domain collection for wildcards" into sc-dev
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index 36efb39..080de73 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -21,11 +21,8 @@
import android.compat.annotation.EnabledSince;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.os.Binder;
import android.os.Build;
import android.util.ArraySet;
import android.util.Patterns;
@@ -36,19 +33,31 @@
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class DomainVerificationCollector {
+ // The default domain name matcher doesn't account for wildcards, so prefix with *.
+ private static final Pattern DOMAIN_NAME_WITH_WILDCARD =
+ Pattern.compile("(\\*\\.)?" + Patterns.DOMAIN_NAME.pattern());
+
@NonNull
private final PlatformCompat mPlatformCompat;
@NonNull
private final SystemConfig mSystemConfig;
+ @NonNull
+ private final Matcher mDomainMatcher;
+
public DomainVerificationCollector(@NonNull PlatformCompat platformCompat,
@NonNull SystemConfig systemConfig) {
mPlatformCompat = platformCompat;
mSystemConfig = systemConfig;
+
+ // Cache the matcher to avoid calling into native on each check
+ mDomainMatcher = DOMAIN_NAME_WITH_WILDCARD.matcher("");
}
/**
@@ -144,7 +153,10 @@
if (intent.handlesWebUris(false)) {
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount; index++) {
- domains.add(intent.getDataAuthority(index).getHost());
+ String host = intent.getDataAuthority(index).getHost();
+ if (isValidHost(host)) {
+ domains.add(host);
+ }
}
}
}
@@ -188,13 +200,22 @@
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount; index++) {
String host = intent.getDataAuthority(index).getHost();
- // It's easy to misconfigure autoVerify intent filters, so to avoid
- // adding unintended hosts, check if the host is an HTTP domain.
- if (Patterns.DOMAIN_NAME.matcher(host).matches()) {
+ if (isValidHost(host)) {
domains.add(host);
}
}
}
}
}
+
+ /**
+ * It's easy to mis-configure autoVerify intent filters, so to avoid adding unintended hosts,
+ * check if the host is an HTTP domain. This applies for both legacy and modern versions of
+ * the API, which will strip invalid hosts from the legacy parsing result. This is done to
+ * improve the reliability of any legacy verifiers.
+ */
+ private boolean isValidHost(String host) {
+ mDomainMatcher.reset(host);
+ return mDomainMatcher.matches();
+ }
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index 9389e63..a804065 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -30,7 +30,6 @@
import android.content.pm.verify.domain.DomainVerificationState;
import android.os.Process;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
@@ -45,6 +44,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -168,22 +168,58 @@
return true;
}
- Set<String> successfulDomains = new ArraySet<>(info.getHostToStateMap().keySet());
- successfulDomains.removeAll(response.failedDomains);
+ AndroidPackage pkg = mConnection.getPackage(packageName);
+ if (pkg == null) {
+ return true;
+ }
+
+ ArraySet<String> failedDomains = new ArraySet<>(response.failedDomains);
+ Map<String, Integer> hostToStateMap = info.getHostToStateMap();
+ Set<String> hostKeySet = hostToStateMap.keySet();
+ ArraySet<String> successfulDomains = new ArraySet<>(hostKeySet);
+ successfulDomains.removeAll(failedDomains);
+
+ // v1 doesn't handle wildcard domains, so check them here for the verifier
+ int size = successfulDomains.size();
+ for (int index = size - 1; index >= 0; index--) {
+ String domain = successfulDomains.valueAt(index);
+ if (domain.startsWith("*.")) {
+ String nonWildcardDomain = domain.substring(2);
+ if (failedDomains.contains(nonWildcardDomain)) {
+ failedDomains.add(domain);
+ successfulDomains.removeAt(index);
+
+ // It's possible to declare a wildcard without declaring its
+ // non-wildcard equivalent, so if it wasn't originally declared,
+ // remove the transformed domain from the failed set. Otherwise the
+ // manager will not accept the failed set as it contains an undeclared
+ // domain.
+ if (!hostKeySet.contains(nonWildcardDomain)) {
+ failedDomains.remove(nonWildcardDomain);
+ }
+ }
+ }
+ }
int callingUid = response.callingUid;
- try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- successfulDomains, DomainVerificationState.STATE_SUCCESS);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException ignored) {
+ if (!successfulDomains.isEmpty()) {
+ try {
+ mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ successfulDomains, DomainVerificationState.STATE_SUCCESS);
+ } catch (DomainVerificationManager.InvalidDomainSetException
+ | PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Failure reporting successful domains for " + packageName, e);
+ }
}
- try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- new ArraySet<>(response.failedDomains),
- DomainVerificationState.STATE_LEGACY_FAILURE);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException ignored) {
+
+ if (!failedDomains.isEmpty()) {
+ try {
+ mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE);
+ } catch (DomainVerificationManager.InvalidDomainSetException
+ | PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Failure reporting failed domains for " + packageName, e);
+ }
}
return true;
@@ -235,7 +271,21 @@
// The collector itself handles the v1 vs v2 behavior, which is based on targetSdkVersion,
// not the version of the verification agent on device.
ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
- return TextUtils.join(" ", domains);
+
+ // v1 doesn't handle wildcard domains, so transform them here to the root
+ StringBuilder builder = new StringBuilder();
+ int size = domains.size();
+ for (int index = 0; index < size; index++) {
+ if (index > 0) {
+ builder.append(" ");
+ }
+ String domain = domains.valueAt(index);
+ if (domain.startsWith("*.")) {
+ domain = domain.substring(2);
+ }
+ builder.append(domain);
+ }
+ return builder.toString();
}
private static class Response {