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