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