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
17package android.security.net.config;
18
19import java.net.Socket;
20import java.security.cert.CertificateException;
21import java.security.cert.X509Certificate;
22import java.util.List;
23
24import javax.net.ssl.SSLSocket;
25import javax.net.ssl.SSLEngine;
26import javax.net.ssl.SSLSession;
27import 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 */
40public class RootTrustManager extends X509ExtendedTrustManager {
41    private final ApplicationConfig mConfig;
42
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
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
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
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
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
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
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     */
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
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     */
149    public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
150        return mConfig.getConfigForHostname(hostname1)
151                .equals(mConfig.getConfigForHostname(hostname2));
152    }
153}
154