Home | History | Annotate | Download | only in config
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.security.net.config;
     18 
     19 import android.os.Build;
     20 import android.util.ArrayMap;
     21 import android.util.ArraySet;
     22 import java.security.cert.X509Certificate;
     23 import java.util.ArrayList;
     24 import java.util.Collection;
     25 import java.util.Collections;
     26 import java.util.Comparator;
     27 import java.util.List;
     28 import java.util.Map;
     29 import java.util.Set;
     30 
     31 import javax.net.ssl.X509TrustManager;
     32 
     33 /**
     34  * @hide
     35  */
     36 public final class NetworkSecurityConfig {
     37     /** @hide */
     38     public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
     39     /** @hide */
     40     public static final boolean DEFAULT_HSTS_ENFORCED = false;
     41 
     42     private final boolean mCleartextTrafficPermitted;
     43     private final boolean mHstsEnforced;
     44     private final PinSet mPins;
     45     private final List<CertificatesEntryRef> mCertificatesEntryRefs;
     46     private Set<TrustAnchor> mAnchors;
     47     private final Object mAnchorsLock = new Object();
     48     private NetworkSecurityTrustManager mTrustManager;
     49     private final Object mTrustManagerLock = new Object();
     50 
     51     private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
     52             PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) {
     53         mCleartextTrafficPermitted = cleartextTrafficPermitted;
     54         mHstsEnforced = hstsEnforced;
     55         mPins = pins;
     56         mCertificatesEntryRefs = certificatesEntryRefs;
     57         // Sort the certificates entry refs so that all entries that override pins come before
     58         // non-override pin entries. This allows us to handle the case where a certificate is in
     59         // multiple entry refs by returning the certificate from the first entry ref.
     60         Collections.sort(mCertificatesEntryRefs, new Comparator<CertificatesEntryRef>() {
     61             @Override
     62             public int compare(CertificatesEntryRef lhs, CertificatesEntryRef rhs) {
     63                 if (lhs.overridesPins()) {
     64                     return rhs.overridesPins() ? 0 : -1;
     65                 } else {
     66                     return rhs.overridesPins() ? 1 : 0;
     67                 }
     68             }
     69         });
     70     }
     71 
     72     public Set<TrustAnchor> getTrustAnchors() {
     73         synchronized (mAnchorsLock) {
     74             if (mAnchors != null) {
     75                 return mAnchors;
     76             }
     77             // Merge trust anchors based on the X509Certificate.
     78             // If we see the same certificate in two TrustAnchors, one with overridesPins and one
     79             // without, the one with overridesPins wins.
     80             // Because mCertificatesEntryRefs is sorted with all overridesPins anchors coming first
     81             // this can be simplified to just using the first occurrence of a certificate.
     82             Map<X509Certificate, TrustAnchor> anchorMap = new ArrayMap<>();
     83             for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
     84                 Set<TrustAnchor> anchors = ref.getTrustAnchors();
     85                 for (TrustAnchor anchor : anchors) {
     86                     X509Certificate cert = anchor.certificate;
     87                     if (!anchorMap.containsKey(cert)) {
     88                         anchorMap.put(cert, anchor);
     89                     }
     90                 }
     91             }
     92             ArraySet<TrustAnchor> anchors = new ArraySet<TrustAnchor>(anchorMap.size());
     93             anchors.addAll(anchorMap.values());
     94             mAnchors = anchors;
     95             return mAnchors;
     96         }
     97     }
     98 
     99     public boolean isCleartextTrafficPermitted() {
    100         return mCleartextTrafficPermitted;
    101     }
    102 
    103     public boolean isHstsEnforced() {
    104         return mHstsEnforced;
    105     }
    106 
    107     public PinSet getPins() {
    108         return mPins;
    109     }
    110 
    111     public NetworkSecurityTrustManager getTrustManager() {
    112         synchronized(mTrustManagerLock) {
    113             if (mTrustManager == null) {
    114                 mTrustManager = new NetworkSecurityTrustManager(this);
    115             }
    116             return mTrustManager;
    117         }
    118     }
    119 
    120     /** @hide */
    121     public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
    122         for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
    123             TrustAnchor anchor = ref.findBySubjectAndPublicKey(cert);
    124             if (anchor != null) {
    125                 return anchor;
    126             }
    127         }
    128         return null;
    129     }
    130 
    131     /** @hide */
    132     public TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate cert) {
    133         for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
    134             TrustAnchor anchor = ref.findByIssuerAndSignature(cert);
    135             if (anchor != null) {
    136                 return anchor;
    137             }
    138         }
    139         return null;
    140     }
    141 
    142     /** @hide */
    143     public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
    144         Set<X509Certificate> certs = new ArraySet<X509Certificate>();
    145         for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
    146             certs.addAll(ref.findAllCertificatesByIssuerAndSignature(cert));
    147         }
    148         return certs;
    149     }
    150 
    151     public void handleTrustStorageUpdate() {
    152         synchronized (mAnchorsLock) {
    153             mAnchors = null;
    154             for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
    155                 ref.handleTrustStorageUpdate();
    156             }
    157         }
    158         getTrustManager().handleTrustStorageUpdate();
    159     }
    160 
    161     /**
    162      * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
    163      *
    164      * <p>
    165      * The default configuration has the following properties:
    166      * <ol>
    167      * <li>Cleartext traffic is permitted.</li>
    168      * <li>HSTS is not enforced.</li>
    169      * <li>No certificate pinning is used.</li>
    170      * <li>The system certificate store is trusted for connections.</li>
    171      * <li>If the application targets API level 23 (Android M) or lower then the user certificate
    172      * store is trusted by default as well.</li>
    173      * </ol>
    174      *
    175      * @hide
    176      */
    177     public static final Builder getDefaultBuilder(int targetSdkVersion) {
    178         Builder builder = new Builder()
    179                 .setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)
    180                 .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
    181                 // System certificate store, does not bypass static pins.
    182                 .addCertificatesEntryRef(
    183                         new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
    184         // Applications targeting N and above must opt in into trusting the user added certificate
    185         // store.
    186         if (targetSdkVersion <= Build.VERSION_CODES.M) {
    187             // User certificate store, does not bypass static pins.
    188             builder.addCertificatesEntryRef(
    189                     new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
    190         }
    191         return builder;
    192     }
    193 
    194     /**
    195      * Builder for creating {@code NetworkSecurityConfig} objects.
    196      * @hide
    197      */
    198     public static final class Builder {
    199         private List<CertificatesEntryRef> mCertificatesEntryRefs;
    200         private PinSet mPinSet;
    201         private boolean mCleartextTrafficPermitted = DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
    202         private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED;
    203         private boolean mCleartextTrafficPermittedSet = false;
    204         private boolean mHstsEnforcedSet = false;
    205         private Builder mParentBuilder;
    206 
    207         /**
    208          * Sets the parent {@code Builder} for this {@code Builder}.
    209          * The parent will be used to determine values not configured in this {@code Builder}
    210          * in {@link Builder#build()}, recursively if needed.
    211          */
    212         public Builder setParent(Builder parent) {
    213             // Sanity check to avoid adding loops.
    214             Builder current = parent;
    215             while (current != null) {
    216                 if (current == this) {
    217                     throw new IllegalArgumentException("Loops are not allowed in Builder parents");
    218                 }
    219                 current = current.getParent();
    220             }
    221             mParentBuilder = parent;
    222             return this;
    223         }
    224 
    225         public Builder getParent() {
    226             return mParentBuilder;
    227         }
    228 
    229         public Builder setPinSet(PinSet pinSet) {
    230             mPinSet = pinSet;
    231             return this;
    232         }
    233 
    234         private PinSet getEffectivePinSet() {
    235             if (mPinSet != null) {
    236                 return mPinSet;
    237             }
    238             if (mParentBuilder != null) {
    239                 return mParentBuilder.getEffectivePinSet();
    240             }
    241             return PinSet.EMPTY_PINSET;
    242         }
    243 
    244         public Builder setCleartextTrafficPermitted(boolean cleartextTrafficPermitted) {
    245             mCleartextTrafficPermitted = cleartextTrafficPermitted;
    246             mCleartextTrafficPermittedSet = true;
    247             return this;
    248         }
    249 
    250         private boolean getEffectiveCleartextTrafficPermitted() {
    251             if (mCleartextTrafficPermittedSet) {
    252                 return mCleartextTrafficPermitted;
    253             }
    254             if (mParentBuilder != null) {
    255                 return mParentBuilder.getEffectiveCleartextTrafficPermitted();
    256             }
    257             return DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
    258         }
    259 
    260         public Builder setHstsEnforced(boolean hstsEnforced) {
    261             mHstsEnforced = hstsEnforced;
    262             mHstsEnforcedSet = true;
    263             return this;
    264         }
    265 
    266         private boolean getEffectiveHstsEnforced() {
    267             if (mHstsEnforcedSet) {
    268                 return mHstsEnforced;
    269             }
    270             if (mParentBuilder != null) {
    271                 return mParentBuilder.getEffectiveHstsEnforced();
    272             }
    273             return DEFAULT_HSTS_ENFORCED;
    274         }
    275 
    276         public Builder addCertificatesEntryRef(CertificatesEntryRef ref) {
    277             if (mCertificatesEntryRefs == null) {
    278                 mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
    279             }
    280             mCertificatesEntryRefs.add(ref);
    281             return this;
    282         }
    283 
    284         public Builder addCertificatesEntryRefs(Collection<? extends CertificatesEntryRef> refs) {
    285             if (mCertificatesEntryRefs == null) {
    286                 mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
    287             }
    288             mCertificatesEntryRefs.addAll(refs);
    289             return this;
    290         }
    291 
    292         private List<CertificatesEntryRef> getEffectiveCertificatesEntryRefs() {
    293             if (mCertificatesEntryRefs != null) {
    294                 return mCertificatesEntryRefs;
    295             }
    296             if (mParentBuilder != null) {
    297                 return mParentBuilder.getEffectiveCertificatesEntryRefs();
    298             }
    299             return Collections.<CertificatesEntryRef>emptyList();
    300         }
    301 
    302         public boolean hasCertificatesEntryRefs() {
    303             return mCertificatesEntryRefs != null;
    304         }
    305 
    306         List<CertificatesEntryRef> getCertificatesEntryRefs() {
    307             return mCertificatesEntryRefs;
    308         }
    309 
    310         public NetworkSecurityConfig build() {
    311             boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
    312             boolean hstsEnforced = getEffectiveHstsEnforced();
    313             PinSet pinSet = getEffectivePinSet();
    314             List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();
    315             return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);
    316         }
    317     }
    318 }
    319