diff options
2 files changed, 93 insertions, 22 deletions
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 36efb39909a6..080de73ff933 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.ChangeId; 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 com.android.server.pm.parsing.pkg.AndroidPackage; 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 @@ public class DomainVerificationCollector { 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 @@ public class DomainVerificationCollector { 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 9389e63404f4..a80406548719 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.DomainVerificationManager; 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 com.android.server.pm.verify.domain.DomainVerificationMessageCodes; 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 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { 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 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { // 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 { |