blob: 9eab80ca077133b86f1deb73ff14f1609d516066 [file] [log] [blame]
Chad Brubaker6bc1e392015-10-23 15:33:56 -07001/*
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
17package android.security.net.config;
18
Chad Brubaker2bd2eb32015-11-10 10:52:27 -080019import android.util.ArrayMap;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070020import android.util.ArraySet;
Chad Brubaker2bd2eb32015-11-10 10:52:27 -080021import java.security.cert.X509Certificate;
Chad Brubaker80a73f52015-11-04 23:36:07 -080022import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070025import java.util.List;
Chad Brubaker2bd2eb32015-11-10 10:52:27 -080026import java.util.Map;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070027import java.util.Set;
28
29import javax.net.ssl.X509TrustManager;
30
31/**
32 * @hide
33 */
34public final class NetworkSecurityConfig {
Chad Brubaker80a73f52015-11-04 23:36:07 -080035 /** @hide */
36 public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
37 /** @hide */
38 public static final boolean DEFAULT_HSTS_ENFORCED = false;
39 public static final NetworkSecurityConfig DEFAULT = getDefaultBuilder().build();
40
Chad Brubaker6bc1e392015-10-23 15:33:56 -070041 private final boolean mCleartextTrafficPermitted;
42 private final boolean mHstsEnforced;
43 private final PinSet mPins;
44 private final List<CertificatesEntryRef> mCertificatesEntryRefs;
45 private Set<TrustAnchor> mAnchors;
46 private final Object mAnchorsLock = new Object();
Chad Brubaker93962c22015-11-10 15:50:08 -080047 private NetworkSecurityTrustManager mTrustManager;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070048 private final Object mTrustManagerLock = new Object();
49
Chad Brubaker80a73f52015-11-04 23:36:07 -080050 private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
Chad Brubaker6bc1e392015-10-23 15:33:56 -070051 PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) {
52 mCleartextTrafficPermitted = cleartextTrafficPermitted;
53 mHstsEnforced = hstsEnforced;
54 mPins = pins;
55 mCertificatesEntryRefs = certificatesEntryRefs;
56 }
57
58 public Set<TrustAnchor> getTrustAnchors() {
59 synchronized (mAnchorsLock) {
60 if (mAnchors != null) {
61 return mAnchors;
62 }
Chad Brubaker2bd2eb32015-11-10 10:52:27 -080063 // Merge trust anchors based on the X509Certificate.
64 // If we see the same certificate in two TrustAnchors, one with overridesPins and one
65 // without, the one with overridesPins wins.
66 Map<X509Certificate, TrustAnchor> anchorMap = new ArrayMap<>();
Chad Brubaker6bc1e392015-10-23 15:33:56 -070067 for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
Chad Brubaker2bd2eb32015-11-10 10:52:27 -080068 Set<TrustAnchor> anchors = ref.getTrustAnchors();
69 for (TrustAnchor anchor : anchors) {
70 if (anchor.overridesPins) {
71 anchorMap.put(anchor.certificate, anchor);
72 } else if (!anchorMap.containsKey(anchor.certificate)) {
73 anchorMap.put(anchor.certificate, anchor);
74 }
75 }
Chad Brubaker6bc1e392015-10-23 15:33:56 -070076 }
Chad Brubaker2bd2eb32015-11-10 10:52:27 -080077 ArraySet<TrustAnchor> anchors = new ArraySet<TrustAnchor>(anchorMap.size());
78 anchors.addAll(anchorMap.values());
Chad Brubaker6bc1e392015-10-23 15:33:56 -070079 mAnchors = anchors;
Chad Brubaker2bd2eb32015-11-10 10:52:27 -080080 return mAnchors;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070081 }
82 }
83
84 public boolean isCleartextTrafficPermitted() {
85 return mCleartextTrafficPermitted;
86 }
87
88 public boolean isHstsEnforced() {
89 return mHstsEnforced;
90 }
91
92 public PinSet getPins() {
93 return mPins;
94 }
95
Chad Brubaker93962c22015-11-10 15:50:08 -080096 public NetworkSecurityTrustManager getTrustManager() {
Chad Brubaker6bc1e392015-10-23 15:33:56 -070097 synchronized(mTrustManagerLock) {
98 if (mTrustManager == null) {
99 mTrustManager = new NetworkSecurityTrustManager(this);
100 }
101 return mTrustManager;
102 }
103 }
104
105 void onTrustStoreChange() {
106 synchronized (mAnchorsLock) {
107 mAnchors = null;
108 }
109 }
Chad Brubaker80a73f52015-11-04 23:36:07 -0800110
111 /**
112 * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
113 *
114 * <p>
115 * The default configuration has the following properties:
116 * <ol>
117 * <li>Cleartext traffic is permitted.</li>
118 * <li>HSTS is not enforced.</li>
119 * <li>No certificate pinning is used.</li>
120 * <li>The system and user added trusted certificate stores are trusted for connections.</li>
121 * </ol>
122 *
123 * @hide
124 */
125 public static final Builder getDefaultBuilder() {
126 return new Builder()
127 .setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)
128 .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
129 // System certificate store, does not bypass static pins.
130 .addCertificatesEntryRef(
131 new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
132 // User certificate store, does not bypass static pins.
133 .addCertificatesEntryRef(
134 new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
135 }
136
137 /**
138 * Builder for creating {@code NetworkSecurityConfig} objects.
139 * @hide
140 */
141 public static final class Builder {
142 private List<CertificatesEntryRef> mCertificatesEntryRefs;
143 private PinSet mPinSet;
144 private boolean mCleartextTrafficPermitted = DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
145 private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED;
146 private boolean mCleartextTrafficPermittedSet = false;
147 private boolean mHstsEnforcedSet = false;
148 private Builder mParentBuilder;
149
150 /**
151 * Sets the parent {@code Builder} for this {@code Builder}.
152 * The parent will be used to determine values not configured in this {@code Builder}
153 * in {@link Builder#build()}, recursively if needed.
154 */
155 public Builder setParent(Builder parent) {
156 // Sanity check to avoid adding loops.
157 Builder current = parent;
158 while (current != null) {
159 if (current == this) {
160 throw new IllegalArgumentException("Loops are not allowed in Builder parents");
161 }
162 current = current.getParent();
163 }
164 mParentBuilder = parent;
165 return this;
166 }
167
168 public Builder getParent() {
169 return mParentBuilder;
170 }
171
172 public Builder setPinSet(PinSet pinSet) {
173 mPinSet = pinSet;
174 return this;
175 }
176
177 private PinSet getEffectivePinSet() {
178 if (mPinSet != null) {
179 return mPinSet;
180 }
181 if (mParentBuilder != null) {
182 return mParentBuilder.getEffectivePinSet();
183 }
184 return PinSet.EMPTY_PINSET;
185 }
186
187 public Builder setCleartextTrafficPermitted(boolean cleartextTrafficPermitted) {
188 mCleartextTrafficPermitted = cleartextTrafficPermitted;
189 mCleartextTrafficPermittedSet = true;
190 return this;
191 }
192
193 private boolean getEffectiveCleartextTrafficPermitted() {
194 if (mCleartextTrafficPermittedSet) {
195 return mCleartextTrafficPermitted;
196 }
197 if (mParentBuilder != null) {
198 return mParentBuilder.getEffectiveCleartextTrafficPermitted();
199 }
200 return DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
201 }
202
203 public Builder setHstsEnforced(boolean hstsEnforced) {
204 mHstsEnforced = hstsEnforced;
205 mHstsEnforcedSet = true;
206 return this;
207 }
208
209 private boolean getEffectiveHstsEnforced() {
210 if (mHstsEnforcedSet) {
211 return mHstsEnforced;
212 }
213 if (mParentBuilder != null) {
214 return mParentBuilder.getEffectiveHstsEnforced();
215 }
216 return DEFAULT_HSTS_ENFORCED;
217 }
218
219 public Builder addCertificatesEntryRef(CertificatesEntryRef ref) {
220 if (mCertificatesEntryRefs == null) {
221 mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
222 }
223 mCertificatesEntryRefs.add(ref);
224 return this;
225 }
226
227 public Builder addCertificatesEntryRefs(Collection<? extends CertificatesEntryRef> refs) {
228 if (mCertificatesEntryRefs == null) {
229 mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
230 }
231 mCertificatesEntryRefs.addAll(refs);
232 return this;
233 }
234
235 private List<CertificatesEntryRef> getEffectiveCertificatesEntryRefs() {
236 if (mCertificatesEntryRefs != null) {
237 return mCertificatesEntryRefs;
238 }
239 if (mParentBuilder != null) {
240 return mParentBuilder.getEffectiveCertificatesEntryRefs();
241 }
242 return Collections.<CertificatesEntryRef>emptyList();
243 }
244
Chad Brubaker08d36202015-11-09 13:38:51 -0800245 public boolean hasCertificatesEntryRefs() {
Chad Brubaker80a73f52015-11-04 23:36:07 -0800246 return mCertificatesEntryRefs != null;
247 }
248
Chad Brubaker08d36202015-11-09 13:38:51 -0800249 List<CertificatesEntryRef> getCertificatesEntryRefs() {
250 return mCertificatesEntryRefs;
251 }
252
Chad Brubaker80a73f52015-11-04 23:36:07 -0800253 public NetworkSecurityConfig build() {
254 boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
Chad Brubaker825c3f92015-11-06 18:08:12 -0800255 boolean hstsEnforced = getEffectiveHstsEnforced();
Chad Brubaker80a73f52015-11-04 23:36:07 -0800256 PinSet pinSet = getEffectivePinSet();
257 List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();
258 return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);
259 }
260 }
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700261}