Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security enhancement for the JTOpen library #200

Merged
merged 4 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/com/ibm/as400/access/AS400.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.util.TimeZone;
import java.util.Vector;

import javax.net.ssl.SSLSocketFactory;

import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSManager;

Expand Down Expand Up @@ -5873,4 +5875,18 @@ public void setEnabledCipherSuites(String[] suites)
// ======== END =================
// Previous chunk of code moved from SecureAS400
// ======== END =================

/**
* Set the {@link SSLSocketFactory} that will be used when making secure connections.
* <p>
* <b>Note:</b>An exception will be thrown if the AS400 object is not an instance of SecureAS400.
*
* @param sslSocketFactory the {@link SSLSocketFactory} to use
*/
public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory)
{
ensureSecureInstance();

useSSLConnection_.sslSocketFactory_ = sslSocketFactory;
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/ibm/as400/access/AS400JDBCDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.util.logging.Logger;

import javax.net.ssl.SSLSocketFactory;

/* endif */
import java.util.Properties;
import java.util.MissingResourceException;
Expand Down Expand Up @@ -123,6 +126,8 @@ public class AS400JDBCDriver
static final String DATABASE_PRODUCT_NAME_ = "DB2 UDB for AS/400"; // @D0A
static final String DRIVER_NAME_ = "AS/400 Toolbox for Java JDBC Driver"; // @D0C @C5C @C6C
static final String DRIVER_LEVEL_ = Copyright.DRIVER_LEVEL;

public static final String PROPERTY_SSL_SOCKET_FACTORY = "property.ssl-socket-factory";

/* ifdef JDBC40 */
public static final int JDBC_MAJOR_VERSION_ = 4; // JDBC spec version: 4.0
Expand Down Expand Up @@ -1186,6 +1191,10 @@ else if (clearPassword == null)
as400 = AS400.newInstance(secure, serverName, userName);
else
as400 = AS400.newInstance(secure, serverName, userName, clearPassword, additionalAuthenticationFactor);
SSLSocketFactory sslSocketFactoryObject = jdProperties.getCustomSSLSocketFactory();
if (null != sslSocketFactoryObject) {
as400.setSSLSocketFactory(sslSocketFactoryObject);
}
}
catch (AS400SecurityException e)
{
Expand Down
139 changes: 138 additions & 1 deletion src/main/java/com/ibm/as400/access/JDProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,25 @@
package com.ibm.as400.access;

import java.io.Serializable;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.io.FileInputStream;
import java.io.IOException; // @W2a
import java.sql.DriverPropertyInfo;

import java.util.Enumeration;
import java.util.Properties;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;



/**
Expand Down Expand Up @@ -181,9 +194,12 @@ public class JDProperties implements Serializable, Cloneable //@PDC 550
static final int ADDITIONAL_AUTHENTICATION_FACTOR=101;
static final int STAY_ALIVE = 102;

static final int TLS_TRUSTSTORE_FILE = 103;
static final int TLS_TRUSTSTORE_FILE_PASS = 104;

// @W2 always add to the end of the array!

private static final int NUMBER_OF_ATTRIBUTES_ = 103;
private static final int NUMBER_OF_ATTRIBUTES_ = 105;


// Property names.
Expand Down Expand Up @@ -254,6 +270,8 @@ public class JDProperties implements Serializable, Cloneable //@PDC 550
private static final String TIME_FORMAT_ = "time format";
private static final String TIMESTAMP_FORMAT_ = "timestamp format";
private static final String TIME_SEPARATOR_ = "time separator";
private static final String TLS_TRUSTSTORE_FILE_ = "tls truststore";
private static final String TLS_TRUSTSTORE_FILE_PASS_ = "tls truststore password";
private static final String TRACE_ = "trace";
private static final String TRACE_SERVER_ = "server trace"; // @j1a
private static final String TRACE_TOOLBOX_ = "toolbox trace"; // @K1A
Expand Down Expand Up @@ -1652,8 +1670,22 @@ public class JDProperties implements Serializable, Cloneable //@PDC 550
dpi_[i].required = false;
dpi_[i].choices = new String[0];
defaults_[i] = "0";

i = TLS_TRUSTSTORE_FILE;
dpi_[i] = new DriverPropertyInfo (TLS_TRUSTSTORE_FILE_, "");
dpi_[i].description = "TLS_TRUSTSTORE_FILE";
dpi_[i].required = false;
dpi_[i].choices = new String[0];
defaults_[i] = EMPTY_;


i = TLS_TRUSTSTORE_FILE_PASS;
dpi_[i] = new DriverPropertyInfo (TLS_TRUSTSTORE_FILE_PASS_, "");
dpi_[i].description = "TLS_TRUSTSTORE_FILE_PASS";
dpi_[i].required = false;
dpi_[i].choices = new String[0];
defaults_[i] = EMPTY_;

}


Expand Down Expand Up @@ -2019,6 +2051,111 @@ String getString (int index)
return value.trim();
}

/**
* Gets a custom SSL Socket Factory, or returns <tt>null</tt> if no custom SSL Socket factory is specified
* (in which case, the system default factory will be used).
*
* The custom SSL Socket Factory is determined as follows:
* <ul>
* <li>If an {@link SSLSocketFactory} object was provided through the special property defined by
* {@link AS400JDBCDriver#PROPERTY_SSL_SOCKET_FACTORY}, all other properties are ignored and
* that object is returned.
* <li>A {@link SSLSocketFactory} will be created if both the {@value #TLS_TRUSTSTORE_FILE_} {@value #TLS_TRUSTSTORE_FILE_PASS_}
* properties were specified, indicating a JKS-format truststore file and password. Note that the special value '*ANY'
* can be used to disable all verification.
* </ul>
*/
SSLSocketFactory getCustomSSLSocketFactory() {
Properties originalProps = this.getOriginalInfo();
Object sslSocketFactoryObject = null == originalProps ? null: originalProps.get(AS400JDBCDriver.PROPERTY_SSL_SOCKET_FACTORY);
if ((sslSocketFactoryObject != null) && (sslSocketFactoryObject instanceof SSLSocketFactory)) {
return (SSLSocketFactory) sslSocketFactoryObject;
}
final String truststoreFile = getString(TLS_TRUSTSTORE_FILE);
final String truststorePass = getString(TLS_TRUSTSTORE_FILE_PASS);
if (null != truststoreFile && !truststoreFile.isEmpty()) {
return new SSLSocketFactory() {
private SSLSocketFactory sslSocketFactory_ = null;

private synchronized SSLSocketFactory getSSLSocketFactory() throws IOException {
if (null != sslSocketFactory_) {
return sslSocketFactory_;
}
if ("*ANY".equalsIgnoreCase(truststoreFile) && "*ANY".equalsIgnoreCase(truststorePass)) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
//@formatter:off
ctx.init(null, new TrustManager[] {
new X509TrustManager() {
@Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
@Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
@Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
}, null);
//@formatter:on
return sslSocketFactory_ = ctx.getSocketFactory();
} catch (Exception e) {
throw e instanceof IOException ? (IOException) e : new IOException(e);
}
}
try (FileInputStream trustFile = new FileInputStream(truststoreFile)) {
KeyStore myTrustStore = KeyStore.getInstance("JKS");
myTrustStore.load(trustFile, null == truststorePass ? null :truststorePass.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(myTrustStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustManagerFactory.getTrustManagers(), null);
return sslSocketFactory_ = ctx.getSocketFactory();
} catch (Exception e) {
throw e instanceof IOException ? (IOException) e : new IOException(e);
}
}

//@formatter:off
@Override
public String[] getDefaultCipherSuites() {
try { return getSSLSocketFactory().getDefaultCipherSuites();} catch (Exception e) { }
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
try { return getSSLSocketFactory().getSupportedCipherSuites(); } catch (Exception e) { }
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getSupportedCipherSuites();
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return getSSLSocketFactory().createSocket(s, host, port, autoClose);
}

@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return getSSLSocketFactory().createSocket(host, port);
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return getSSLSocketFactory().createSocket(host, port, localHost, localPort);
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return getSSLSocketFactory().createSocket(host, port);
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException {
return getSSLSocketFactory().createSocket(address, port, localAddress, localPort);
}
//@formatter:on

};
}
return null;
}

/**
* Get the clear password. The caller is responsible for clearing the array
* after it is done with the password
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/ibm/as400/access/PortMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ static SocketContainer getServerSocket(String systemName,
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Starting a secure socket to " + serviceName);
{ // JSSE is supported since v5r4.
sc = (SocketContainer)AS400.loadImpl("com.ibm.as400.access.SocketContainerJSSE");
sc.setProperties(socket, null, systemName, srvPort, null);
sc.setProperties(socket, null, systemName, srvPort, useSSL);
}
}
else
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/ibm/as400/access/SSLOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import java.io.Serializable;

import javax.net.ssl.SSLSocketFactory;

// Class to move SSL configuration options from proxy client to proxy server.
class SSLOptions implements Serializable
{
Expand Down Expand Up @@ -47,4 +49,5 @@ class SSLOptions implements Serializable
int proxyEncryptionMode_ = SecureAS400.CLIENT_TO_SERVER;
// Sslight removed
boolean useSslight_ = false;
SSLSocketFactory sslSocketFactory_ = null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void setProperties(Socket socket, String serviceName, String systemName, int por
{
if (Trace.isTraceOn()) Trace.log(Trace.DIAGNOSTIC, "SocketContainerJSSE: create SSLSocket");

SSLSocketFactory sslFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocketFactory sslFactory = ((options != null) && (options.sslSocketFactory_ != null)) ? options.sslSocketFactory_ : (SSLSocketFactory)SSLSocketFactory.getDefault();
sslSocket_ = (SSLSocket)sslFactory.createSocket(socket, systemName, port, true);
//@P4A START
if(SecureAS400.changeCipherSuites)
Expand Down
Loading