1/*
2 * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.net.ssl.internal.www.protocol.https;
27
28import java.net.URL;
29import java.net.Proxy;
30import java.io.IOException;
31import java.util.Collection;
32import java.util.List;
33import java.util.Iterator;
34
35import java.security.Principal;
36import java.security.cert.*;
37
38import javax.security.auth.x500.X500Principal;
39
40import sun.security.util.HostnameChecker;
41import sun.security.util.DerValue;
42import sun.security.x509.X500Name;
43
44import sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection;
45
46/**
47 * This class was introduced to provide an additional level of
48 * abstraction between javax.net.ssl.HttpURLConnection and
49 * com.sun.net.ssl.HttpURLConnection objects. <p>
50 *
51 * javax.net.ssl.HttpURLConnection is used in the new sun.net version
52 * of protocol implementation (this one)
53 * com.sun.net.ssl.HttpURLConnection is used in the com.sun version.
54 *
55 */
56@Deprecated(since="9")
57@SuppressWarnings("deprecation") // HttpsURLConnection is deprecated
58public class DelegateHttpsURLConnection extends AbstractDelegateHttpsURLConnection {
59
60    // we need a reference to the HttpsURLConnection to get
61    // the properties set there
62    // we also need it to be public so that it can be referenced
63    // from sun.net.www.protocol.http.HttpURLConnection
64    // this is for ResponseCache.put(URI, URLConnection)
65    // second parameter needs to be cast to javax.net.ssl.HttpsURLConnection
66    // instead of AbstractDelegateHttpsURLConnection
67
68    public com.sun.net.ssl.HttpsURLConnection httpsURLConnection;
69
70    DelegateHttpsURLConnection(URL url,
71            sun.net.www.protocol.http.Handler handler,
72            com.sun.net.ssl.HttpsURLConnection httpsURLConnection)
73            throws IOException {
74        this(url, null, handler, httpsURLConnection);
75    }
76
77    DelegateHttpsURLConnection(URL url, Proxy p,
78            sun.net.www.protocol.http.Handler handler,
79            com.sun.net.ssl.HttpsURLConnection httpsURLConnection)
80            throws IOException {
81        super(url, p, handler);
82        this.httpsURLConnection = httpsURLConnection;
83    }
84
85    protected javax.net.ssl.SSLSocketFactory getSSLSocketFactory() {
86        return httpsURLConnection.getSSLSocketFactory();
87    }
88
89    protected javax.net.ssl.HostnameVerifier getHostnameVerifier() {
90        // note: getHostnameVerifier() never returns null
91        return new VerifierWrapper(httpsURLConnection.getHostnameVerifier());
92    }
93
94    /*
95     * Called by layered delegator's finalize() method to handle closing
96     * the underlying object.
97     */
98    protected void dispose() throws Throwable {
99        super.finalize();
100    }
101}
102
103class VerifierWrapper implements javax.net.ssl.HostnameVerifier {
104    @SuppressWarnings("deprecation")
105    private com.sun.net.ssl.HostnameVerifier verifier;
106
107    @SuppressWarnings("deprecation")
108    VerifierWrapper(com.sun.net.ssl.HostnameVerifier verifier) {
109        this.verifier = verifier;
110    }
111
112    /*
113     * In com.sun.net.ssl.HostnameVerifier the method is defined
114     * as verify(String urlHostname, String certHostname).
115     * This means we need to extract the hostname from the X.509 certificate
116     * or from the Kerberos principal name, in this wrapper.
117     */
118    public boolean verify(String hostname, javax.net.ssl.SSLSession session) {
119        try {
120            String serverName;
121            // Use ciphersuite to determine whether Kerberos is active.
122            if (session.getCipherSuite().startsWith("TLS_KRB5")) {
123                serverName =
124                    HostnameChecker.getServerName(getPeerPrincipal(session));
125
126            } else { // X.509
127                Certificate[] serverChain = session.getPeerCertificates();
128                if ((serverChain == null) || (serverChain.length == 0)) {
129                    return false;
130                }
131                if (serverChain[0] instanceof X509Certificate == false) {
132                    return false;
133                }
134                X509Certificate serverCert = (X509Certificate)serverChain[0];
135                serverName = getServername(serverCert);
136            }
137            if (serverName == null) {
138                return false;
139            }
140            return verifier.verify(hostname, serverName);
141        } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
142            return false;
143        }
144    }
145
146    /*
147     * Get the peer principal from the session
148     */
149    private Principal getPeerPrincipal(javax.net.ssl.SSLSession session)
150        throws javax.net.ssl.SSLPeerUnverifiedException
151    {
152        Principal principal;
153        try {
154            principal = session.getPeerPrincipal();
155        } catch (AbstractMethodError e) {
156            // if the provider does not support it, return null, since
157            // we need it only for Kerberos.
158            principal = null;
159        }
160        return principal;
161    }
162
163    /*
164     * Extract the name of the SSL server from the certificate.
165     *
166     * Note this code is essentially a subset of the hostname extraction
167     * code in HostnameChecker.
168     */
169    private static String getServername(X509Certificate peerCert) {
170        try {
171            // compare to subjectAltNames if dnsName is present
172            Collection<List<?>> subjAltNames = peerCert.getSubjectAlternativeNames();
173            if (subjAltNames != null) {
174                for (Iterator<List<?>> itr = subjAltNames.iterator(); itr.hasNext(); ) {
175                    List<?> next = itr.next();
176                    if (((Integer)next.get(0)).intValue() == 2) {
177                        // compare dNSName with host in url
178                        String dnsName = ((String)next.get(1));
179                        return dnsName;
180                    }
181                }
182            }
183
184            // else check against common name in the subject field
185            X500Name subject = HostnameChecker.getSubjectX500Name(peerCert);
186
187            DerValue derValue = subject.findMostSpecificAttribute
188                                                (X500Name.commonName_oid);
189            if (derValue != null) {
190                try {
191                    String name = derValue.getAsString();
192                    return name;
193                } catch (IOException e) {
194                    // ignore
195                }
196            }
197        } catch (java.security.cert.CertificateException e) {
198            // ignore
199        }
200        return null;
201    }
202
203}
204