Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab328e51 authored by Thiébaud Weksteen's avatar Thiébaud Weksteen
Browse files

Disable CT verification for inline certificate and user store

When an app uses its own certificate (or the user store), it is likely
that the certificate is not public and therefore not verifiable via
certificate transparency. By default, disable CT verification for these
cases.

It is still possible to force the verification using
<certificateTransparency enabled="true" /> in the domain configuration.

For each <domain-config>, the evaluation follows this order:
1. If <certificateTransparency> is set, use it.
2. If any <trust-anchors> is "user" or inline (i.e., "@raw/cert.pem"),
   disable the verification.
3. Otherwise, rely on the inherited configuration (either a parent
   configuration or the default configuration).

Bug: 377281304
Flag: AndroidSecurityCertificateTransparencyConfigurationLaunch
Test: atest NetworkSecurityConfigTests:android.security.net.config.XmlConfigTests#testCertificateTransparencyDomainConfig
Test: atest CtsNetSecConfigCertificateTransparencyTestCases
Change-Id: Id13555cc973ac4bb526c7aa194fcfcf76a4483a4
parent 0222489d
Loading
Loading
Loading
Loading
+9 −1
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package android.security.net.config;
package android.security.net.config;


import android.util.ArraySet;
import android.util.ArraySet;

import java.security.cert.X509Certificate;
import java.security.cert.X509Certificate;
import java.util.Set;
import java.util.Set;


@@ -24,16 +25,23 @@ import java.util.Set;
public final class CertificatesEntryRef {
public final class CertificatesEntryRef {
    private final CertificateSource mSource;
    private final CertificateSource mSource;
    private final boolean mOverridesPins;
    private final boolean mOverridesPins;
    private final boolean mDisableCT;


    public CertificatesEntryRef(CertificateSource source, boolean overridesPins) {
    public CertificatesEntryRef(CertificateSource source, boolean overridesPins,
            boolean disableCT) {
        mSource = source;
        mSource = source;
        mOverridesPins = overridesPins;
        mOverridesPins = overridesPins;
        mDisableCT = disableCT;
    }
    }


    boolean overridesPins() {
    boolean overridesPins() {
        return mOverridesPins;
        return mOverridesPins;
    }
    }


    boolean disableCT() {
        return mDisableCT;
    }

    public Set<TrustAnchor> getTrustAnchors() {
    public Set<TrustAnchor> getTrustAnchors() {
        // TODO: cache this [but handle mutable sources]
        // TODO: cache this [but handle mutable sources]
        Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
        Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
+2 −2
Original line number Original line Diff line number Diff line
@@ -17,8 +17,8 @@
package android.security.net.config;
package android.security.net.config;


import android.util.Pair;
import android.util.Pair;

import java.security.KeyStore;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.util.Set;
import java.util.Set;


/**
/**
@@ -32,7 +32,7 @@ class KeyStoreConfigSource implements ConfigSource {
        mConfig = new NetworkSecurityConfig.Builder()
        mConfig = new NetworkSecurityConfig.Builder()
                .addCertificatesEntryRef(
                .addCertificatesEntryRef(
                        // Use the KeyStore and do not override pins (of which there are none).
                        // Use the KeyStore and do not override pins (of which there are none).
                        new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false))
                        new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false, false))
                .build();
                .build();
    }
    }


+16 −6
Original line number Original line Diff line number Diff line
@@ -112,7 +112,6 @@ public final class NetworkSecurityConfig {
        return mHstsEnforced;
        return mHstsEnforced;
    }
    }


    // TODO(b/28746284): add exceptions for user-added certificates and enterprise overrides.
    public boolean isCertificateTransparencyVerificationRequired() {
    public boolean isCertificateTransparencyVerificationRequired() {
        return mCertificateTransparencyVerificationRequired;
        return mCertificateTransparencyVerificationRequired;
    }
    }
@@ -192,20 +191,21 @@ public final class NetworkSecurityConfig {
     * @hide
     * @hide
     */
     */
    public static Builder getDefaultBuilder(ApplicationInfo info) {
    public static Builder getDefaultBuilder(ApplicationInfo info) {
        // System certificate store, does not bypass static pins, does not disable CT.
        CertificatesEntryRef systemRef = new CertificatesEntryRef(
                SystemCertificateSource.getInstance(), false, false);
        Builder builder = new Builder()
        Builder builder = new Builder()
                .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
                .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
                // System certificate store, does not bypass static pins.
                .addCertificatesEntryRef(systemRef);
                .addCertificatesEntryRef(
                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
        final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
        final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
                && !info.isInstantApp();
                && !info.isInstantApp();
        builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
        builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
        // Applications targeting N and above must opt in into trusting the user added certificate
        // Applications targeting N and above must opt in into trusting the user added certificate
        // store.
        // store.
        if (info.targetSdkVersion <= Build.VERSION_CODES.M && !info.isPrivilegedApp()) {
        if (info.targetSdkVersion <= Build.VERSION_CODES.M && !info.isPrivilegedApp()) {
            // User certificate store, does not bypass static pins.
            // User certificate store, does not bypass static pins. CT is disabled.
            builder.addCertificatesEntryRef(
            builder.addCertificatesEntryRef(
                    new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
                    new CertificatesEntryRef(UserCertificateSource.getInstance(), false, true));
        }
        }
        return builder;
        return builder;
    }
    }
@@ -339,6 +339,16 @@ public final class NetworkSecurityConfig {
            if (mCertificateTransparencyVerificationRequiredSet) {
            if (mCertificateTransparencyVerificationRequiredSet) {
                return mCertificateTransparencyVerificationRequired;
                return mCertificateTransparencyVerificationRequired;
            }
            }
            // CT verification has not been set explicitly. Before deferring to
            // the parent, check if any of the CertificatesEntryRef requires it
            // to be disabled (i.e., user store or inline certificate).
            if (hasCertificatesEntryRefs()) {
                for (CertificatesEntryRef ref : getCertificatesEntryRefs()) {
                    if (ref.disableCT()) {
                        return false;
                    }
                }
            }
            if (mParentBuilder != null) {
            if (mParentBuilder != null) {
                return mParentBuilder.getCertificateTransparencyVerificationRequired();
                return mParentBuilder.getCertificateTransparencyVerificationRequired();
            }
            }
+4 −1
Original line number Original line Diff line number Diff line
@@ -182,6 +182,7 @@ public class XmlConfigSource implements ConfigSource {
        boolean overridePins =
        boolean overridePins =
                parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins);
                parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins);
        int sourceId = parser.getAttributeResourceValue(null, "src", -1);
        int sourceId = parser.getAttributeResourceValue(null, "src", -1);
        boolean disableCT = false;
        String sourceString = parser.getAttributeValue(null, "src");
        String sourceString = parser.getAttributeValue(null, "src");
        CertificateSource source = null;
        CertificateSource source = null;
        if (sourceString == null) {
        if (sourceString == null) {
@@ -190,10 +191,12 @@ public class XmlConfigSource implements ConfigSource {
        if (sourceId != -1) {
        if (sourceId != -1) {
            // TODO: Cache ResourceCertificateSources by sourceId
            // TODO: Cache ResourceCertificateSources by sourceId
            source = new ResourceCertificateSource(sourceId, mContext);
            source = new ResourceCertificateSource(sourceId, mContext);
            disableCT = true;
        } else if ("system".equals(sourceString)) {
        } else if ("system".equals(sourceString)) {
            source = SystemCertificateSource.getInstance();
            source = SystemCertificateSource.getInstance();
        } else if ("user".equals(sourceString)) {
        } else if ("user".equals(sourceString)) {
            source = UserCertificateSource.getInstance();
            source = UserCertificateSource.getInstance();
            disableCT = true;
        } else if ("wfa".equals(sourceString)) {
        } else if ("wfa".equals(sourceString)) {
            source = WfaCertificateSource.getInstance();
            source = WfaCertificateSource.getInstance();
        } else {
        } else {
@@ -201,7 +204,7 @@ public class XmlConfigSource implements ConfigSource {
                    + "Should be one of system|user|@resourceVal");
                    + "Should be one of system|user|@resourceVal");
        }
        }
        XmlUtils.skipCurrentTag(parser);
        XmlUtils.skipCurrentTag(parser);
        return new CertificatesEntryRef(source, overridePins);
        return new CertificatesEntryRef(source, overridePins, disableCT);
    }
    }


    private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser,
    private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser,
+38 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <base-config>
      <certificateTransparency enabled="true" />
  </base-config>
  <domain-config>
    <domain>android.com</domain>
    <trust-anchors>
      <certificates src="system" />
    </trust-anchors>
  </domain-config>
  <domain-config>
    <domain>subdomain_user.android.com</domain>
    <trust-anchors>
      <certificates src="user" />
    </trust-anchors>
  </domain-config>
  <domain-config>
    <certificateTransparency enabled="true" />
    <domain>subdomain_user_ct.android.com</domain>
    <trust-anchors>
      <certificates src="user" />
    </trust-anchors>
  </domain-config>
  <domain-config>
    <domain>subdomain_inline.android.com</domain>
    <trust-anchors>
      <certificates src="@raw/ca_certs_pem" />
    </trust-anchors>
  </domain-config>
  <domain-config>
    <certificateTransparency enabled="true" />
    <domain>subdomain_inline_ct.android.com</domain>
    <trust-anchors>
      <certificates src="@raw/ca_certs_pem" />
    </trust-anchors>
  </domain-config>
</network-security-config>
Loading