1 /* Copyright (c) 2001-2016, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 package org.hsqldb.server;
33 
34 import java.net.InetAddress;
35 import java.net.ServerSocket;
36 import java.net.Socket;
37 import java.net.UnknownHostException;
38 import java.security.Principal;
39 import java.security.PublicKey;
40 
41 import javax.net.ssl.HandshakeCompletedEvent;
42 import javax.net.ssl.HandshakeCompletedListener;
43 import javax.net.ssl.SSLServerSocket;
44 import javax.net.ssl.SSLServerSocketFactory;
45 import javax.net.ssl.SSLSession;
46 import javax.net.ssl.SSLSocket;
47 import javax.net.ssl.SSLSocketFactory;
48 import javax.security.cert.X509Certificate;
49 
50 import org.hsqldb.error.Error;
51 import org.hsqldb.error.ErrorCode;
52 import org.hsqldb.lib.StringConverter;
53 
54 /**
55  * The default secure socket factory implementation.
56  *
57  * @author Campbell Burnet (boucherb@users dot sourceforge.net)
58  * @author Blaine Simpson (blaine dot simpson at admc dot com)
59  *
60  * @version 2.3.1
61  * @since 1.7.2
62  */
63 public final class HsqlSocketFactorySecure extends HsqlSocketFactory
64 implements HandshakeCompletedListener {
65 
66 // --------------------------------- members -----------------------------------
67 
68     /** The underlying socket factory implementation. */
69     protected Object socketFactory;
70 
71     /** The underlying server socket factory implementation. */
72     protected Object serverSocketFactory;
73 
74     /**
75      * Monitor object to guard against concurrent modification
76      * of the underlying socket factory implementation member.
77      */
78     protected final Object socket_factory_mutex = new Object();
79 
80     /**
81      * Monitor object to guard against concurrent modification of
82      * the underlying server socket factory implementation member.
83      */
84     protected final Object server_socket_factory_mutex = new Object();
85 
86 // ------------------------------ constructors ---------------------------------
87 
88     /**
89      * External construction disabled.  New factory instances are retrieved
90      * through the newHsqlSocketFactory method instead.
91      */
HsqlSocketFactorySecure()92     protected HsqlSocketFactorySecure() throws Exception {
93         super();
94     }
95 
96 // ----------------------------- subclass overrides ----------------------------
configureSocket(Socket socket)97     public void configureSocket(Socket socket) {
98 
99         SSLSocket s;
100 
101         super.configureSocket(socket);
102 
103         s = (SSLSocket) socket;
104 
105         s.addHandshakeCompletedListener(this);
106     }
107 
108     /**
109      * Creates a secure server socket bound to the specified port.
110      * The socket is configured with the socket options
111      * given to this factory.
112      *
113      * @return the secure ServerSocket
114      * @param port the port to which to bind the secure ServerSocket
115      * @throws Exception if a network or security provider error occurs
116      */
createServerSocket(int port)117     public ServerSocket createServerSocket(int port) throws Exception {
118 
119         SSLServerSocket ss;
120 
121         ss = (SSLServerSocket) getServerSocketFactoryImpl().createServerSocket(
122             port);
123 
124         if (Error.TRACESYSTEMOUT) {
125             Error.printSystemOut("[" + this + "]: createServerSocket()");
126             Error.printSystemOut("capabilities for " + ss + ":");
127             Error.printSystemOut("----------------------------");
128             dump("supported cipher suites", ss.getSupportedCipherSuites());
129             dump("enabled cipher suites", ss.getEnabledCipherSuites());
130         }
131 
132         return ss;
133     }
134 
135     /**
136      * Creates a secure server socket bound to the specified port.
137      * The socket is configured with the socket options
138      * given to this factory.
139      *
140      * @return the secure ServerSocket
141      * @param port the port to which to bind the secure ServerSocket
142      * @throws Exception if a network or security provider error occurs
143      */
createServerSocket(int port, String address)144     public ServerSocket createServerSocket(int port,
145                                            String address) throws Exception {
146 
147         SSLServerSocket ss;
148         InetAddress     addr;
149 
150         addr = InetAddress.getByName(address);
151         ss = (SSLServerSocket) getServerSocketFactoryImpl().createServerSocket(
152             port, 128, addr);
153 
154         if (Error.TRACESYSTEMOUT) {
155             Error.printSystemOut("[" + this + "]: createServerSocket()");
156             Error.printSystemOut("capabilities for " + ss + ":");
157             Error.printSystemOut("----------------------------");
158             dump("supported cipher suites", ss.getSupportedCipherSuites());
159             dump("enabled cipher suites", ss.getEnabledCipherSuites());
160         }
161 
162         return ss;
163     }
164 
dump(String title, String[] as)165     private static void dump(String title, String[] as) {
166 
167         Error.printSystemOut(title);
168         Error.printSystemOut("----------------------------");
169 
170         for (int i = 0; i < as.length; i++) {
171             Error.printSystemOut(String.valueOf(as[i]));
172         }
173 
174         Error.printSystemOut("----------------------------");
175     }
176 
177     /**
178      * if socket argument is not null, creates a secure Socket as a wrapper for
179      * the normal, non-SSL socket. If the socket is null, create a new secure
180      * socket. The secure socket is configured using the
181      * socket options established for this factory.
182      *
183      * @return the socket
184      * @param socket the existing socket
185      * @param host the server host
186      * @param port the server port
187      * @throws Exception if a network or security provider error occurs
188      */
createSocket(Socket socket, String host, int port)189     public Socket createSocket(Socket socket, String host,
190                                int port) throws Exception {
191 
192         SSLSocket sslSocket;
193 
194         if (socket == null) {
195             return createSocket(host, port);
196         }
197 
198         sslSocket = (SSLSocket) getSocketFactoryImpl().createSocket(socket,
199                 host, port, true);
200 
201         sslSocket.addHandshakeCompletedListener(this);
202         sslSocket.startHandshake();
203         verify(host, sslSocket.getSession());
204 
205         return sslSocket;
206     }
207 
208     /**
209      * Creates a secure Socket and connects it to the specified remote host
210      * at the specified remote port. This socket is configured using the
211      * socket options established for this factory.
212      *
213      * @return the socket
214      * @param host the server host
215      * @param port the server port
216      * @throws Exception if a network or security provider error occurs
217      */
createSocket(String host, int port)218     public Socket createSocket(String host, int port) throws Exception {
219 
220         SSLSocket socket;
221 
222         socket = (SSLSocket) getSocketFactoryImpl().createSocket(host, port);
223 
224         socket.addHandshakeCompletedListener(this);
225         socket.startHandshake();
226 
227 // unsaved@users
228 // For https protocol, the protocol handler should do this verification
229 // (Sun's implementation does), but if we do not use the Protocol
230 // handler (which is only available in Java >= 1.4), then we need to do
231 // the verification: hostname == cert CN
232 //
233 // boucherb@users 20030503:
234 // CHEKME/TODO:
235 //
236 // Stricter verify?  Either require SunJSSE (assume its trust manager properly
237 // verifies whole chain), or implement our own TrustManager layer?
238 //
239 // What about v1/v3 and signing checks (re: man-in-the-middle attack),
240 // CRL check, basic constraints? notBefore? notAfter?
241 //
242 // Reference:  http://www.securitytracker.com/alerts/2002/Aug/1005030.html
243 //
244 // That is, we can't guarantee that installed/preferred provider trust manager
245 // implementations verify the whole chain properly and there are still
246 // v1 certs out there (i.e. have no basic constraints, etc.), meaning that
247 // we should check for and reject any intermediate certs that are not v3+
248 // (cannot be checked for basic constraints).  Only root and intermediate
249 // certs found in the trust store should be allowed to be v1 (since we must
250 // be trusting them for them to be there).  All other intermediate signers,
251 // however, should be required to be v3+, otherwise anybody with any kind
252 // of cert issued somehow via a trust chain from the root can pose as an
253 // intermediate signing CA and hence leave things open to man-in-the-middle
254 // style attack.  Also, we should really check CRLs, just in case
255 // it turns out that trust chain has been breached and thus issuer has revoked
256 // on some cert(s).  Of course, this really begs the question, as it is not
257 // guaranteed that all CAs in trust store have valid, working CRL URL
258 //
259 // So what to do?
260 //
261 // Maybe best to leave this all up to DBA?
262         verify(host, socket.getSession());
263 
264         return socket;
265     }
266 
267     /**
268      * Retrieves whether this factory produces secure sockets.
269      *
270      * @return true iff this factory creates secure sockets
271      */
isSecure()272     public boolean isSecure() {
273         return true;
274     }
275 
276 // ----------------------- internal implementation -----------------------------
277 
278     /**
279      * Retrieves the underlying javax.net.ssl.SSLServerSocketFactory.
280      *
281      * @throws Exception if there is a problem retrieving the
282      *      underlying factory
283      * @return the underlying javax.net.ssl.SSLServerSocketFactory
284      */
getServerSocketFactoryImpl()285     protected SSLServerSocketFactory getServerSocketFactoryImpl()
286     throws Exception {
287 
288         Object factory;
289 
290         synchronized (server_socket_factory_mutex) {
291             factory = serverSocketFactory;
292 
293             if (factory == null) {
294                 factory             = SSLServerSocketFactory.getDefault();
295                 serverSocketFactory = factory;
296             }
297         }
298 
299         return (SSLServerSocketFactory) factory;
300     }
301 
302     /**
303      * Retrieves the underlying javax.net.ssl.SSLSocketFactory.
304      *
305      * @throws Exception if there is a problem retrieving the
306      *      underlying factory
307      * @return the underlying javax.net.ssl.SSLSocketFactory
308      */
getSocketFactoryImpl()309     protected SSLSocketFactory getSocketFactoryImpl() throws Exception {
310 
311         Object factory;
312 
313         synchronized (socket_factory_mutex) {
314             factory = socketFactory;
315 
316             if (factory == null) {
317                 factory       = SSLSocketFactory.getDefault();
318                 socketFactory = factory;
319             }
320         }
321 
322         return (SSLSocketFactory) factory;
323     }
324 
325     /**
326      * Verifies the certificate chain presented by the server to which
327      * a secure Socket has just connected.  Specifically, the provided host
328      * name is checked against the Common Name of the server certificate;
329      * additional checks may or may not be performed.
330      *
331      * @param host the requested host name
332      * @param session SSLSession used on the connection to host
333      * @throws Exception if the certificate chain cannot be verified
334      */
verify(String host, SSLSession session)335     protected void verify(String host, SSLSession session) throws Exception {
336 
337         X509Certificate[] chain;
338         X509Certificate   certificate;
339         Principal         principal;
340         PublicKey         publicKey;
341         String            DN;
342         String            CN;
343         int               start;
344         int               end;
345         String            emsg;
346 
347         chain       = session.getPeerCertificateChain();
348         certificate = chain[0];
349         principal   = certificate.getSubjectDN();
350         DN          = String.valueOf(principal);
351         start       = DN.indexOf("CN=");
352 
353         if (start < 0) {
354             throw new UnknownHostException(
355                 Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_1));
356         }
357 
358         start += 3;
359         end   = DN.indexOf(',', start);
360         CN    = DN.substring(start, (end > -1) ? end
361                                                : DN.length());
362 
363         if (CN.length() < 1) {
364             throw new UnknownHostException(
365                 Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_2));
366         }
367 
368         if (!CN.equalsIgnoreCase(host)) {
369 
370             // TLS_HOSTNAME_MISMATCH
371             throw new UnknownHostException(
372                 Error.getMessage(
373                     ErrorCode.M_SERVER_SECURE_VERIFY_3, 0, new Object[] {
374                 CN, host
375             }));
376         }
377     }
378 
handshakeCompleted(HandshakeCompletedEvent evt)379     public void handshakeCompleted(HandshakeCompletedEvent evt) {
380 
381         SSLSession session;
382         String     sessionId;
383         SSLSocket  socket;
384 
385         if (Error.TRACESYSTEMOUT) {
386             socket  = evt.getSocket();
387             session = evt.getSession();
388 
389             Error.printSystemOut("SSL handshake completed:");
390             Error.printSystemOut(
391                 "------------------------------------------------");
392             Error.printSystemOut("socket:      : " + socket);
393             Error.printSystemOut("cipher suite : " + session.getCipherSuite());
394 
395             sessionId = StringConverter.byteArrayToHexString(session.getId());
396 
397             Error.printSystemOut("session id   : " + sessionId);
398             Error.printSystemOut(
399                 "------------------------------------------------");
400         }
401     }
402 }
403