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

Commit c1b0236d authored by Chad Brubaker's avatar Chad Brubaker Committed by Android (Google) Code Review
Browse files

Merge "Dont trust the user added CA store by default for apps targeting N" into nyc-dev

parents 8dcdaaf1 32d2a102
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ public class ManifestConfigSource implements ConfigSource {
            } catch (PackageManager.NameNotFoundException e) {
                throw new RuntimeException("Failed to look up ApplicationInfo", e);
            }
            int targetSdkVersion = info.targetSdkVersion;
            int configResourceId = 0;
            if (info != null && info.metaData != null) {
                configResourceId = info.metaData.getInt(META_DATA_NETWORK_SECURITY_CONFIG);
@@ -74,14 +75,15 @@ public class ManifestConfigSource implements ConfigSource {
                            + mContext.getResources().getResourceEntryName(configResourceId)
                            + " debugBuild: " + debugBuild);
                }
                source = new XmlConfigSource(mContext, configResourceId, debugBuild);
                source = new XmlConfigSource(mContext, configResourceId, debugBuild,
                        targetSdkVersion);
            } else {
                if (DBG) {
                    Log.d(LOG_TAG, "No Network Security Config specified, using platform default");
                }
                boolean usesCleartextTraffic =
                        (info.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0;
                source = new DefaultConfigSource(usesCleartextTraffic);
                source = new DefaultConfigSource(usesCleartextTraffic, targetSdkVersion);
            }
            mConfigSource = source;
            return mConfigSource;
@@ -92,8 +94,8 @@ public class ManifestConfigSource implements ConfigSource {

        private final NetworkSecurityConfig mDefaultConfig;

        public DefaultConfigSource(boolean usesCleartextTraffic) {
            mDefaultConfig = NetworkSecurityConfig.getDefaultBuilder()
        public DefaultConfigSource(boolean usesCleartextTraffic, int targetSdkVersion) {
            mDefaultConfig = NetworkSecurityConfig.getDefaultBuilder(targetSdkVersion)
                    .setCleartextTrafficPermitted(usesCleartextTraffic)
                    .build();
        }
+15 −8
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.security.net.config;

import android.os.Build;
import android.util.ArrayMap;
import android.util.ArraySet;
import java.security.cert.X509Certificate;
@@ -37,7 +38,6 @@ public final class NetworkSecurityConfig {
    public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
    /** @hide */
    public static final boolean DEFAULT_HSTS_ENFORCED = false;
    public static final NetworkSecurityConfig DEFAULT = getDefaultBuilder().build();

    private final boolean mCleartextTrafficPermitted;
    private final boolean mHstsEnforced;
@@ -163,22 +163,29 @@ public final class NetworkSecurityConfig {
     * <li>Cleartext traffic is permitted.</li>
     * <li>HSTS is not enforced.</li>
     * <li>No certificate pinning is used.</li>
     * <li>The system and user added trusted certificate stores are trusted for connections.</li>
     * <li>The system certificate store is trusted for connections.</li>
     * <li>If the application targets API level 23 (Android M) or lower then the user certificate
     * store is trusted by default as well.</li>
     * </ol>
     *
     * @hide
     */
    public static final Builder getDefaultBuilder() {
        return new Builder()
    public static final Builder getDefaultBuilder(int targetSdkVersion) {
        Builder builder = new Builder()
                .setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)
                .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
                // System certificate store, does not bypass static pins.
                .addCertificatesEntryRef(
                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
        // Applications targeting N and above must opt in into trusting the user added certificate
        // store.
        if (targetSdkVersion <= Build.VERSION_CODES.M) {
            // User certificate store, does not bypass static pins.
                .addCertificatesEntryRef(
            builder.addCertificatesEntryRef(
                    new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
        }
        return builder;
    }

    /**
     * Builder for creating {@code NetworkSecurityConfig} objects.
+12 −1
Original line number Diff line number Diff line
@@ -3,9 +3,11 @@ package android.security.net.config;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.util.ArraySet;
import android.util.Base64;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
@@ -34,20 +36,29 @@ public class XmlConfigSource implements ConfigSource {
    private final Object mLock = new Object();
    private final int mResourceId;
    private final boolean mDebugBuild;
    private final int mTargetSdkVersion;

    private boolean mInitialized;
    private NetworkSecurityConfig mDefaultConfig;
    private Set<Pair<Domain, NetworkSecurityConfig>> mDomainMap;
    private Context mContext;

    @VisibleForTesting
    public XmlConfigSource(Context context, int resourceId) {
        this(context, resourceId, false);
    }

    @VisibleForTesting
    public XmlConfigSource(Context context, int resourceId, boolean debugBuild) {
        this(context, resourceId, debugBuild, Build.VERSION_CODES.CUR_DEVELOPMENT);
    }

    public XmlConfigSource(Context context, int resourceId, boolean debugBuild,
            int targetSdkVersion) {
        mResourceId = resourceId;
        mContext = context;
        mDebugBuild = debugBuild;
        mTargetSdkVersion = targetSdkVersion;
    }

    public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
@@ -341,7 +352,7 @@ public class XmlConfigSource implements ConfigSource {
        // Use the platform default as the parent of the base config for any values not provided
        // there. If there is no base config use the platform default.
        NetworkSecurityConfig.Builder platformDefaultBuilder =
                NetworkSecurityConfig.getDefaultBuilder();
                NetworkSecurityConfig.getDefaultBuilder(mTargetSdkVersion);
        addDebugAnchorsIfNeeded(debugConfigBuilder, platformDefaultBuilder);
        if (baseConfigBuilder != null) {
            baseConfigBuilder.setParent(platformDefaultBuilder);
+89 −2
Original line number Diff line number Diff line
@@ -17,19 +17,28 @@
package android.security.net.config;

import android.app.Activity;
import android.os.Build;
import android.test.ActivityUnitTestCase;
import android.util.ArraySet;
import android.util.Pair;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;

import com.android.org.conscrypt.TrustedCertificateStore;

public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {

    public NetworkSecurityConfigTests() {
@@ -40,6 +49,51 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
    private static final byte[] G2_SPKI_SHA256
            = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776");

    private static final byte[] TEST_CA_BYTES
            = hexToBytes(
                    "3082036130820249a003020102020900bd54597d6750ea62300d06092a86"
                    + "4886f70d01010b05003047310b3009060355040613025553310b30090603"
                    + "5504080c0243413110300e060355040a0c07416e64726f69643119301706"
                    + "035504030c104e53436f6e6669672054657374204341301e170d31363032"
                    + "32343030313130325a170d3136303332353030313130325a3047310b3009"
                    + "060355040613025553310b300906035504080c0243413110300e06035504"
                    + "0a0c07416e64726f69643119301706035504030c104e53436f6e66696720"
                    + "5465737420434130820122300d06092a864886f70d01010105000382010f"
                    + "003082010a0282010100e15ce8fd5794029841e760d68d6e0159c9c67630"
                    + "089775bc728d83dae7e29e23fe5f6e113b789f4c5b22f052300ec6d5faa5"
                    + "724432e7bac96682792ef6e9617c939c4329dce8788cbdf3a11b621fac9e"
                    + "2edbec2d7e5e07296bbb544b89263137a6a31573a2362e05ca8ff9c886bf"
                    + "52df4ff93c45475145a40a83f2670e23669220a5a4bf2c6860edb78d3022"
                    + "192fb5dc5e8c118f70870f89da292dfe522751462f020ed556653c8b07f8"
                    + "89712a6e8196c457a637439e3073d7d917ab55aa51a146826367f7b5922a"
                    + "64fb2f95099de21eb98341fa76faa79ffbda123fe5b8adc614b16174e8b0"
                    + "dfdac2bbc4d526d2487ad2b009d53996ec23ffbd732112efa66b02030100"
                    + "01a350304e301d0603551d0e04160414f66e1a95486c879edd60a5756bc2"
                    + "f1f4677e128e301f0603551d23041830168014f66e1a95486c879edd60a5"
                    + "756bc2f1f4677e128e300c0603551d13040530030101ff300d06092a8648"
                    + "86f70d01010b05000382010100d2856130dccae24e5f8901900d94bc642f"
                    + "85466ab7cfa1066399077a168cd4b56603a9e2af9d2e58aec13101e338a4"
                    + "8e95e9c7a84d7991f0d381d4965eaada1b80fbbd8277445f449babe64f53"
                    + "ba625387460b592a1a97b14b8251115e6610350021a6e716ae22b905f8d4"
                    + "eae24e668e71b12ab51fd2f2bb600e074487dec720c3db14dbca504844b6"
                    + "933bb0248283ea95464747689c37d706d4839c7d0e9bd86abf98ddce5d36"
                    + "8b38bfe5062353e28d5be378827fade1caa6bba3df9cd9ebf83d839eae52"
                    + "780181f31973f15f982686ba6d899f7b644fd1f26c8ebb99f4c986faaf4c"
                    + "1b9e3d9d391943ce3fb9fa2e631bd66b8ef3d47fd85acf09ea3a30f15f");

    private static final X509Certificate TEST_CA_CERT;

    static {
        try {
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            Certificate cert = factory.generateCertificate(new ByteArrayInputStream(TEST_CA_BYTES));
            TEST_CA_CERT = (X509Certificate) cert;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    private static byte[] hexToBytes(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
@@ -51,7 +105,6 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
    }



    /**
     * Return a NetworkSecurityConfig that has an empty TrustAnchor set. This should always cause a
     * SSLHandshakeException when used for a connection.
@@ -174,7 +227,7 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
    public void testConfigBuilderUsesParents() throws Exception {
        // Check that a builder with a parent uses the parent's values when non is set.
        NetworkSecurityConfig config = new NetworkSecurityConfig.Builder()
                .setParent(NetworkSecurityConfig.getDefaultBuilder())
                .setParent(NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.N))
                .build();
        assert(!config.getTrustAnchors().isEmpty());
    }
@@ -208,4 +261,38 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
        TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
        TestUtils.assertUrlConnectionFails(context, "google.com", 443);
    }

    public void testUserAddedCaOptIn() throws Exception {
        TrustedCertificateStore store = new TrustedCertificateStore();
        try {
            // Install the test CA.
            store.installCertificate(TEST_CA_CERT);
            NetworkSecurityConfig preNConfig =
                    NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.M).build();
            NetworkSecurityConfig nConfig =
                    NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.N).build();
            Set<TrustAnchor> preNAnchors = preNConfig.getTrustAnchors();
            Set<TrustAnchor> nAnchors = nConfig.getTrustAnchors();
            Set<X509Certificate> preNCerts = new HashSet<X509Certificate>();
            for (TrustAnchor anchor : preNAnchors) {
                preNCerts.add(anchor.certificate);
            }
            Set<X509Certificate> nCerts = new HashSet<X509Certificate>();
            for (TrustAnchor anchor : nAnchors) {
                nCerts.add(anchor.certificate);
            }
            assertTrue(preNCerts.contains(TEST_CA_CERT));
            assertFalse(nCerts.contains(TEST_CA_CERT));
        } finally {
            // Delete the user added CA. We don't know the alias so just delete them all.
            for (String alias : store.aliases()) {
                if (store.isUser(alias)) {
                    try {
                        store.deleteCertificateEntry(alias);
                    } catch (Exception ignored) {
                    }
                }
            }
        }
    }
}