/* Copyright (c) 2001-2017, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.server;
import java.net.
InetAddress;
import java.net.
ServerSocket;
import java.net.
Socket;
import java.net.
UnknownHostException;
import java.security.
Principal;
import java.security.
PublicKey;
import javax.net.ssl.
HandshakeCompletedEvent;
import javax.net.ssl.
HandshakeCompletedListener;
import javax.net.ssl.
SSLServerSocket;
import javax.net.ssl.
SSLServerSocketFactory;
import javax.net.ssl.
SSLSession;
import javax.net.ssl.
SSLSocket;
import javax.net.ssl.
SSLSocketFactory;
import javax.security.cert.
X509Certificate;
import org.hsqldb.error.
Error;
import org.hsqldb.error.
ErrorCode;
import org.hsqldb.lib.
StringConverter;
/**
* The default secure socket factory implementation.
*
* @author Campbell Burnet (campbell-burnet@users dot sourceforge.net)
* @author Blaine Simpson (blaine dot simpson at admc dot com)
*
* @version 2.3.1
* @since 1.7.2
*/
public final class
HsqlSocketFactorySecure extends
HsqlSocketFactory
implements
HandshakeCompletedListener {
// --------------------------------- members -----------------------------------
/** The underlying socket factory implementation. */
protected
Object socketFactory;
/** The underlying server socket factory implementation. */
protected
Object serverSocketFactory;
/**
* Monitor object to guard against concurrent modification
* of the underlying socket factory implementation member.
*/
protected final
Object socket_factory_mutex = new
Object();
/**
* Monitor object to guard against concurrent modification of
* the underlying server socket factory implementation member.
*/
protected final
Object server_socket_factory_mutex = new
Object();
// ------------------------------ constructors ---------------------------------
/**
* External construction disabled. New factory instances are retrieved
* through the newHsqlSocketFactory method instead.
*/
protected
HsqlSocketFactorySecure() throws
Exception {
super();
}
// ----------------------------- subclass overrides ----------------------------
public void
configureSocket(
Socket socket) {
SSLSocket s;
super.configureSocket(
socket);
s = (
SSLSocket)
socket;
s.
addHandshakeCompletedListener(this);
}
/**
* Creates a secure server socket bound to the specified port.
* The socket is configured with the socket options
* given to this factory.
*
* @return the secure ServerSocket
* @param port the port to which to bind the secure ServerSocket
* @throws Exception if a network or security provider error occurs
*/
public
ServerSocket createServerSocket(int
port) throws
Exception {
SSLServerSocket ss;
ss = (
SSLServerSocket)
getServerSocketFactoryImpl().
createServerSocket(
port);
if (
Error.
TRACESYSTEMOUT) {
Error.
printSystemOut("[" + this + "]: createServerSocket()");
Error.
printSystemOut("capabilities for " +
ss + ":");
Error.
printSystemOut("----------------------------");
dump("supported cipher suites",
ss.
getSupportedCipherSuites());
dump("enabled cipher suites",
ss.
getEnabledCipherSuites());
}
return
ss;
}
/**
* Creates a secure server socket bound to the specified port.
* The socket is configured with the socket options
* given to this factory.
*
* @return the secure ServerSocket
* @param port the port to which to bind the secure ServerSocket
* @throws Exception if a network or security provider error occurs
*/
public
ServerSocket createServerSocket(int
port,
String address) throws
Exception {
SSLServerSocket ss;
InetAddress addr;
addr =
InetAddress.
getByName(
address);
ss = (
SSLServerSocket)
getServerSocketFactoryImpl().
createServerSocket(
port, 128,
addr);
if (
Error.
TRACESYSTEMOUT) {
Error.
printSystemOut("[" + this + "]: createServerSocket()");
Error.
printSystemOut("capabilities for " +
ss + ":");
Error.
printSystemOut("----------------------------");
dump("supported cipher suites",
ss.
getSupportedCipherSuites());
dump("enabled cipher suites",
ss.
getEnabledCipherSuites());
}
return
ss;
}
private static void
dump(
String title,
String[]
as) {
Error.
printSystemOut(
title);
Error.
printSystemOut("----------------------------");
for (int
i = 0;
i <
as.length;
i++) {
Error.
printSystemOut(
String.
valueOf(
as[
i]));
}
Error.
printSystemOut("----------------------------");
}
/**
* if socket argument is not null, creates a secure Socket as a wrapper for
* the normal, non-SSL socket. If the socket is null, create a new secure
* socket. The secure socket is configured using the
* socket options established for this factory.
*
* @return the socket
* @param socket the existing socket
* @param host the server host
* @param port the server port
* @throws Exception if a network or security provider error occurs
*/
public
Socket createSocket(
Socket socket,
String host,
int
port) throws
Exception {
SSLSocket sslSocket;
if (
socket == null) {
return
createSocket(
host,
port);
}
sslSocket = (
SSLSocket)
getSocketFactoryImpl().
createSocket(
socket,
host,
port, true);
sslSocket.
addHandshakeCompletedListener(this);
sslSocket.
startHandshake();
verify(
host,
sslSocket.
getSession());
return
sslSocket;
}
/**
* Creates a secure Socket and connects it to the specified remote host
* at the specified remote port. This socket is configured using the
* socket options established for this factory.
*
* @return the socket
* @param host the server host
* @param port the server port
* @throws Exception if a network or security provider error occurs
*/
public
Socket createSocket(
String host, int
port) throws
Exception {
SSLSocket socket;
socket = (
SSLSocket)
getSocketFactoryImpl().
createSocket(
host,
port);
socket.
addHandshakeCompletedListener(this);
socket.
startHandshake();
// unsaved@users
// For https protocol, the protocol handler should do this verification
// (Sun's implementation does), but if we do not use the Protocol
// handler (which is only available in Java >= 1.4), then we need to do
// the verification: hostname == cert CN
//
// campbell-burnet@users 20030503:
// CHEKME/TODO:
//
// Stricter verify? Either require SunJSSE (assume its trust manager properly
// verifies whole chain), or implement our own TrustManager layer?
//
// What about v1/v3 and signing checks (re: man-in-the-middle attack),
// CRL check, basic constraints? notBefore? notAfter?
//
// Reference: http://www.securitytracker.com/alerts/2002/Aug/1005030.html
//
// That is, we can't guarantee that installed/preferred provider trust manager
// implementations verify the whole chain properly and there are still
// v1 certs out there (i.e. have no basic constraints, etc.), meaning that
// we should check for and reject any intermediate certs that are not v3+
// (cannot be checked for basic constraints). Only root and intermediate
// certs found in the trust store should be allowed to be v1 (since we must
// be trusting them for them to be there). All other intermediate signers,
// however, should be required to be v3+, otherwise anybody with any kind
// of cert issued somehow via a trust chain from the root can pose as an
// intermediate signing CA and hence leave things open to man-in-the-middle
// style attack. Also, we should really check CRLs, just in case
// it turns out that trust chain has been breached and thus issuer has revoked
// on some cert(s). Of course, this really begs the question, as it is not
// guaranteed that all CAs in trust store have valid, working CRL URL
//
// So what to do?
//
// Maybe best to leave this all up to DBA?
verify(
host,
socket.
getSession());
return
socket;
}
/**
* Retrieves whether this factory produces secure sockets.
*
* @return true iff this factory creates secure sockets
*/
public boolean
isSecure() {
return true;
}
// ----------------------- internal implementation -----------------------------
/**
* Retrieves the underlying javax.net.ssl.SSLServerSocketFactory.
*
* @throws Exception if there is a problem retrieving the
* underlying factory
* @return the underlying javax.net.ssl.SSLServerSocketFactory
*/
protected
SSLServerSocketFactory getServerSocketFactoryImpl()
throws
Exception {
Object factory;
synchronized (
server_socket_factory_mutex) {
factory =
serverSocketFactory;
if (
factory == null) {
factory =
SSLServerSocketFactory.
getDefault();
serverSocketFactory =
factory;
}
}
return (
SSLServerSocketFactory)
factory;
}
/**
* Retrieves the underlying javax.net.ssl.SSLSocketFactory.
*
* @throws Exception if there is a problem retrieving the
* underlying factory
* @return the underlying javax.net.ssl.SSLSocketFactory
*/
protected
SSLSocketFactory getSocketFactoryImpl() throws
Exception {
Object factory;
synchronized (
socket_factory_mutex) {
factory =
socketFactory;
if (
factory == null) {
factory =
SSLSocketFactory.
getDefault();
socketFactory =
factory;
}
}
return (
SSLSocketFactory)
factory;
}
/**
* Verifies the certificate chain presented by the server to which
* a secure Socket has just connected. Specifically, the provided host
* name is checked against the Common Name of the server certificate;
* additional checks may or may not be performed.
*
* @param host the requested host name
* @param session SSLSession used on the connection to host
* @throws Exception if the certificate chain cannot be verified
*/
protected void
verify(
String host,
SSLSession session) throws
Exception {
X509Certificate[]
chain;
X509Certificate certificate;
Principal principal;
PublicKey publicKey;
String DN;
String CN;
int
start;
int
end;
String emsg;
chain =
session.
getPeerCertificateChain();
certificate =
chain[0];
principal =
certificate.
getSubjectDN();
DN =
String.
valueOf(
principal);
start =
DN.
indexOf("CN=");
if (
start < 0) {
throw new
UnknownHostException(
Error.
getMessage(
ErrorCode.
M_SERVER_SECURE_VERIFY_1));
}
start += 3;
end =
DN.
indexOf(',',
start);
CN =
DN.
substring(
start, (
end > -1) ?
end
:
DN.
length());
if (
CN.
length() < 1) {
throw new
UnknownHostException(
Error.
getMessage(
ErrorCode.
M_SERVER_SECURE_VERIFY_2));
}
if (!
CN.
equalsIgnoreCase(
host)) {
// TLS_HOSTNAME_MISMATCH
throw new
UnknownHostException(
Error.
getMessage(
ErrorCode.
M_SERVER_SECURE_VERIFY_3, 0, new
Object[] {
CN,
host
}));
}
}
public void
handshakeCompleted(
HandshakeCompletedEvent evt) {
SSLSession session;
String sessionId;
SSLSocket socket;
if (
Error.
TRACESYSTEMOUT) {
socket =
evt.
getSocket();
session =
evt.
getSession();
Error.
printSystemOut("SSL handshake completed:");
Error.
printSystemOut(
"------------------------------------------------");
Error.
printSystemOut("socket: : " +
socket);
Error.
printSystemOut("cipher suite : " +
session.
getCipherSuite());
sessionId =
StringConverter.
byteArrayToHexString(
session.
getId());
Error.
printSystemOut("session id : " +
sessionId);
Error.
printSystemOut(
"------------------------------------------------");
}
}
}