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

Commit 32d2a102 authored by Chad Brubaker's avatar Chad Brubaker
Browse files

Dont trust the user added CA store by default for apps targeting N

Android's security model is such that the applications data is secure by
default unless the application specifically grants access to it.
Application data in transit should have similar security properties.

Bug: 27301579
Change-Id: I72f106aefecccd6edfcc1d3ae10131ad2f69a559
parent d78bf970
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) {
                    }
                }
            }
        }
    }
}