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 java.net.Socket;
20 import java.security.cert.CertificateException;
21 import java.security.cert.X509Certificate;
22 import java.util.List;
23 
24 import javax.net.ssl.SSLSocket;
25 import javax.net.ssl.SSLEngine;
26 import javax.net.ssl.SSLSession;
27 import javax.net.ssl.X509ExtendedTrustManager;
28 
29 /**
30  * {@link X509ExtendedTrustManager} based on an {@link ApplicationConfig}.
31  *
32  * <p>This trust manager delegates to the specific trust manager for the hostname being used for
33  * the connection (See {@link ApplicationConfig#getConfigForHostname(String)} and
34  * {@link NetworkSecurityTrustManager}).</p>
35  *
36  * Note that if the {@code ApplicationConfig} has per-domain configurations the hostname aware
37  * {@link #checkServerTrusted(X509Certificate[], String String)} must be used instead of the normal
38  * non-aware call.
39  * @hide */
40 public class RootTrustManager extends X509ExtendedTrustManager {
41     private final ApplicationConfig mConfig;
42 
RootTrustManager(ApplicationConfig config)43     public RootTrustManager(ApplicationConfig config) {
44         if (config == null) {
45             throw new NullPointerException("config must not be null");
46         }
47         mConfig = config;
48     }
49 
50     @Override
checkClientTrusted(X509Certificate[] chain, String authType)51     public void checkClientTrusted(X509Certificate[] chain, String authType)
52             throws CertificateException {
53         // Use the default configuration for all client authentication. Domain specific configs are
54         // only for use in checking server trust not client trust.
55         NetworkSecurityConfig config = mConfig.getConfigForHostname("");
56         config.getTrustManager().checkClientTrusted(chain, authType);
57     }
58 
59     @Override
checkClientTrusted(X509Certificate[] certs, String authType, Socket socket)60     public void checkClientTrusted(X509Certificate[] certs, String authType, Socket socket)
61             throws CertificateException {
62         // Use the default configuration for all client authentication. Domain specific configs are
63         // only for use in checking server trust not client trust.
64         NetworkSecurityConfig config = mConfig.getConfigForHostname("");
65         config.getTrustManager().checkClientTrusted(certs, authType, socket);
66     }
67 
68     @Override
checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine)69     public void checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
70             throws CertificateException {
71         // Use the default configuration for all client authentication. Domain specific configs are
72         // only for use in checking server trust not client trust.
73         NetworkSecurityConfig config = mConfig.getConfigForHostname("");
74         config.getTrustManager().checkClientTrusted(certs, authType, engine);
75     }
76 
77     @Override
checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)78     public void checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)
79             throws CertificateException {
80         if (socket instanceof SSLSocket) {
81             SSLSocket sslSocket = (SSLSocket) socket;
82             SSLSession session = sslSocket.getHandshakeSession();
83             if (session == null) {
84                 throw new CertificateException("Not in handshake; no session available");
85             }
86             String host = session.getPeerHost();
87             NetworkSecurityConfig config = mConfig.getConfigForHostname(host);
88             config.getTrustManager().checkServerTrusted(certs, authType, socket);
89         } else {
90             // Not an SSLSocket, use the hostname unaware checkServerTrusted.
91             checkServerTrusted(certs, authType);
92         }
93     }
94 
95     @Override
checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)96     public void checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
97             throws CertificateException {
98         SSLSession session = engine.getHandshakeSession();
99         if (session == null) {
100             throw new CertificateException("Not in handshake; no session available");
101         }
102         String host = session.getPeerHost();
103         NetworkSecurityConfig config = mConfig.getConfigForHostname(host);
104         config.getTrustManager().checkServerTrusted(certs, authType, engine);
105     }
106 
107     @Override
checkServerTrusted(X509Certificate[] certs, String authType)108     public void checkServerTrusted(X509Certificate[] certs, String authType)
109             throws CertificateException {
110         if (mConfig.hasPerDomainConfigs()) {
111             throw new CertificateException(
112                     "Domain specific configurations require that hostname aware"
113                     + " checkServerTrusted(X509Certificate[], String, String) is used");
114         }
115         NetworkSecurityConfig config = mConfig.getConfigForHostname("");
116         config.getTrustManager().checkServerTrusted(certs, authType);
117     }
118 
119     /**
120      * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
121      * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
122      * modify without modifying those callers.
123      */
checkServerTrusted(X509Certificate[] certs, String authType, String hostname)124     public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
125             String hostname) throws CertificateException {
126         if (hostname == null && mConfig.hasPerDomainConfigs()) {
127             throw new CertificateException(
128                     "Domain specific configurations require that the hostname be provided");
129         }
130         NetworkSecurityConfig config = mConfig.getConfigForHostname(hostname);
131         return config.getTrustManager().checkServerTrusted(certs, authType, hostname);
132     }
133 
134     @Override
getAcceptedIssuers()135     public X509Certificate[] getAcceptedIssuers() {
136         // getAcceptedIssuers is meant to be used to determine which trust anchors the server will
137         // accept when verifying clients. Domain specific configs are only for use in checking
138         // server trust not client trust so use the default config.
139         NetworkSecurityConfig config = mConfig.getConfigForHostname("");
140         return config.getTrustManager().getAcceptedIssuers();
141     }
142 
143     /**
144      * Returns {@code true} if this trust manager uses the same trust configuration for the provided
145      * hostnames.
146      *
147      * <p>This is required by android.net.http.X509TrustManagerExtensions.
148      */
isSameTrustConfiguration(String hostname1, String hostname2)149     public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
150         return mConfig.getConfigForHostname(hostname1)
151                 .equals(mConfig.getConfigForHostname(hostname2));
152     }
153 }
154