Made it so that we can inject more tha one TLS protocol version
diff --git a/checkstyle.xml b/checkstyle.xml
index 864649b..d695476 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -25,6 +25,10 @@
<property name="localeLanguage" value="en"/>
<module name="JavadocPackage"/>
+ <module name="ArrayTypeStyle">
+ <property name="javaStyle" value="true"/>
+ </module>
+
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
diff --git a/core/src/main/java/org/apache/ftpserver/command/impl/AUTH.java b/core/src/main/java/org/apache/ftpserver/command/impl/AUTH.java
index b5956cd..3e7fa1a 100644
--- a/core/src/main/java/org/apache/ftpserver/command/impl/AUTH.java
+++ b/core/src/main/java/org/apache/ftpserver/command/impl/AUTH.java
@@ -139,8 +139,8 @@
sslFilter.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
}
- if (ssl.getEnabledProtocol() != null) {
- sslFilter.setEnabledProtocols(new String[] { ssl.getEnabledProtocol() });
+ if (ssl.getEnabledProtocols() != null) {
+ sslFilter.setEnabledProtocols(ssl.getEnabledProtocols());
}
session.getFilterChain().addFirst(SSL_SESSION_FILTER_NAME, sslFilter);
diff --git a/core/src/main/java/org/apache/ftpserver/config/spring/ListenerBeanDefinitionParser.java b/core/src/main/java/org/apache/ftpserver/config/spring/ListenerBeanDefinitionParser.java
index 9c668b3..428ddc2 100644
--- a/core/src/main/java/org/apache/ftpserver/config/spring/ListenerBeanDefinitionParser.java
+++ b/core/src/main/java/org/apache/ftpserver/config/spring/ListenerBeanDefinitionParser.java
@@ -215,6 +215,7 @@
}
String protocol = SpringUtil.parseString(sslElm, "protocol");
+
if (protocol != null) {
ssl.setSslProtocol(protocol);
}
diff --git a/core/src/main/java/org/apache/ftpserver/impl/IODataConnectionFactory.java b/core/src/main/java/org/apache/ftpserver/impl/IODataConnectionFactory.java
index bf1b792..180800f 100644
--- a/core/src/main/java/org/apache/ftpserver/impl/IODataConnectionFactory.java
+++ b/core/src/main/java/org/apache/ftpserver/impl/IODataConnectionFactory.java
@@ -72,11 +72,11 @@
FtpIoSession session;
public IODataConnectionFactory(final FtpServerContext serverContext, final FtpIoSession session) {
- this.session = session;
- this.serverContext = serverContext;
- if ((session != null) && (session.getListener() != null) && session.getListener().getDataConnectionConfiguration().isImplicitSsl()) {
- secure = true;
- }
+ this.session = session;
+ this.serverContext = serverContext;
+ if ((session != null) && (session.getListener() != null) && session.getListener().getDataConnectionConfiguration().isImplicitSsl()) {
+ secure = true;
+ }
}
/**
@@ -120,87 +120,88 @@
* Port command.
*/
public synchronized void initActiveDataConnection(final InetSocketAddress address) {
-
- // close old sockets if any
- closeDataConnection();
-
- // set variables
- passive = false;
- this.address = address.getAddress();
- port = address.getPort();
- requestTime = System.currentTimeMillis();
- }
-
- private SslConfiguration getSslConfiguration() {
- DataConnectionConfiguration dataCfg = session.getListener().getDataConnectionConfiguration();
-
- SslConfiguration configuration = dataCfg.getSslConfiguration();
-
- // fall back if no configuration has been provided on the data connection config
- if (configuration == null) {
- configuration = session.getListener().getSslConfiguration();
- }
-
- return configuration;
+ // close old sockets if any
+ closeDataConnection();
+
+ // set variables
+ passive = false;
+ this.address = address.getAddress();
+ port = address.getPort();
+ requestTime = System.currentTimeMillis();
+ }
+
+ private SslConfiguration getSslConfiguration() {
+ DataConnectionConfiguration dataCfg = session.getListener().getDataConnectionConfiguration();
+
+ SslConfiguration configuration = dataCfg.getSslConfiguration();
+
+ // fall back if no configuration has been provided on the data connection config
+ if (configuration == null) {
+ configuration = session.getListener().getSslConfiguration();
+ }
+
+ return configuration;
}
/**
* Initiate a data connection in passive mode (server listening).
*/
public synchronized InetSocketAddress initPassiveDataConnection() throws DataConnectionException {
- LOG.debug("Initiating passive data connection");
- // close old sockets if any
- closeDataConnection();
-
- // get the passive port
- int passivePort = session.getListener().getDataConnectionConfiguration().requestPassivePort();
- if (passivePort == -1) {
- servSoc = null;
- throw new DataConnectionException("Cannot find an available passive port.");
- }
-
- // open passive server socket and get parameters
- try {
- DataConnectionConfiguration dataCfg = session.getListener().getDataConnectionConfiguration();
-
- String passiveAddress = dataCfg.getPassiveAddress();
-
- if (passiveAddress == null) {
- address = serverControlAddress;
- } else {
- address = resolveAddress(dataCfg.getPassiveAddress());
- }
-
- if (secure) {
- LOG.debug("Opening SSL passive data connection on address \"{}\" and port {}", address, passivePort);
- SslConfiguration ssl = getSslConfiguration();
- if (ssl == null) {
- throw new DataConnectionException("Data connection SSL required but not configured.");
- }
-
- // this method does not actually create the SSL socket, due to a JVM bug
- // (https://issues.apache.org/jira/browse/FTPSERVER-241).
- // Instead, it creates a regular
- // ServerSocket that will be wrapped as a SSL socket in createDataSocket()
- servSoc = new ServerSocket(passivePort, 0, address);
- LOG.debug("SSL Passive data connection created on address \"{}\" and port {}", address, passivePort);
- } else {
- LOG.debug("Opening passive data connection on address \"{}\" and port {}", address, passivePort);
- servSoc = new ServerSocket(passivePort, 0, address);
- LOG.debug("Passive data connection created on address \"{}\" and port {}", address, passivePort);
- }
- port = servSoc.getLocalPort();
- servSoc.setSoTimeout(dataCfg.getIdleTime() * 1000);
-
- // set different state variables
- passive = true;
- requestTime = System.currentTimeMillis();
-
- return new InetSocketAddress(address, port);
- } catch (Exception ex) {
+ LOG.debug("Initiating passive data connection");
+ // close old sockets if any
closeDataConnection();
- throw new DataConnectionException("Failed to initate passive data connection: " + ex.getMessage(), ex);
- }
+
+ // get the passive port
+ int passivePort = session.getListener().getDataConnectionConfiguration().requestPassivePort();
+ if (passivePort == -1) {
+ servSoc = null;
+ throw new DataConnectionException("Cannot find an available passive port.");
+ }
+
+ // open passive server socket and get parameters
+ try {
+ DataConnectionConfiguration dataCfg = session.getListener().getDataConnectionConfiguration();
+
+ String passiveAddress = dataCfg.getPassiveAddress();
+
+ if (passiveAddress == null) {
+ address = serverControlAddress;
+ } else {
+ address = resolveAddress(dataCfg.getPassiveAddress());
+ }
+
+ if (secure) {
+ LOG.debug("Opening SSL passive data connection on address \"{}\" and port {}", address, passivePort);
+ SslConfiguration ssl = getSslConfiguration();
+
+ if (ssl == null) {
+ throw new DataConnectionException("Data connection SSL required but not configured.");
+ }
+
+ // this method does not actually create the SSL socket, due to a JVM bug
+ // (https://issues.apache.org/jira/browse/FTPSERVER-241).
+ // Instead, it creates a regular
+ // ServerSocket that will be wrapped as a SSL socket in createDataSocket()
+ servSoc = new ServerSocket(passivePort, 0, address);
+ LOG.debug("SSL Passive data connection created on address \"{}\" and port {}", address, passivePort);
+ } else {
+ LOG.debug("Opening passive data connection on address \"{}\" and port {}", address, passivePort);
+ servSoc = new ServerSocket(passivePort, 0, address);
+ LOG.debug("Passive data connection created on address \"{}\" and port {}", address, passivePort);
+ }
+
+ port = servSoc.getLocalPort();
+ servSoc.setSoTimeout(dataCfg.getIdleTime() * 1000);
+
+ // set different state variables
+ passive = true;
+ requestTime = System.currentTimeMillis();
+
+ return new InetSocketAddress(address, port);
+ } catch (Exception ex) {
+ closeDataConnection();
+ throw new DataConnectionException("Failed to initate passive data connection: " + ex.getMessage(), ex);
+ }
}
/*
@@ -209,7 +210,7 @@
* @see org.apache.ftpserver.FtpDataConnectionFactory2#getInetAddress()
*/
public InetAddress getInetAddress() {
- return address;
+ return address;
}
/*
@@ -218,7 +219,7 @@
* @see org.apache.ftpserver.FtpDataConnectionFactory2#getPort()
*/
public int getPort() {
- return port;
+ return port;
}
/*
@@ -227,154 +228,155 @@
* @see org.apache.ftpserver.FtpDataConnectionFactory2#openConnection()
*/
public DataConnection openConnection() throws Exception {
- return new IODataConnection(createDataSocket(), session, this);
+ return new IODataConnection(createDataSocket(), session, this);
}
/**
* Get the data socket. In case of error returns null.
*/
private synchronized Socket createDataSocket() throws Exception {
-
- // get socket depending on the selection
- dataSoc = null;
- DataConnectionConfiguration dataConfig = session.getListener().getDataConnectionConfiguration();
- try {
- if (!passive) {
- if (secure) {
- LOG.debug("Opening secure active data connection");
- SslConfiguration ssl = getSslConfiguration();
- if (ssl == null) {
- throw new FtpException("Data connection SSL not configured");
+ // get socket depending on the selection
+ dataSoc = null;
+ DataConnectionConfiguration dataConfig = session.getListener().getDataConnectionConfiguration();
+ try {
+ if (!passive) {
+ if (secure) {
+ LOG.debug("Opening secure active data connection");
+ SslConfiguration ssl = getSslConfiguration();
+
+ if (ssl == null) {
+ throw new FtpException("Data connection SSL not configured");
+ }
+
+ // get socket factory
+ SSLSocketFactory socFactory = ssl.getSocketFactory();
+
+ // create socket
+ SSLSocket ssoc = (SSLSocket) socFactory.createSocket();
+ ssoc.setUseClientMode(false);
+
+ // initialize socket
+ if (ssl.getEnabledCipherSuites() != null) {
+ ssoc.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
+ }
+
+ if (ssl.getEnabledProtocols() != null) {
+ ssoc.setEnabledProtocols(ssl.getEnabledProtocols());
+ }
+
+ dataSoc = ssoc;
+ } else {
+ LOG.debug("Opening active data connection");
+ dataSoc = new Socket();
+ }
+
+ dataSoc.setReuseAddress(true);
+
+ InetAddress localAddr = resolveAddress(dataConfig.getActiveLocalAddress());
+
+ // if no local address has been configured, make sure we use the same as the client connects from
+ if (localAddr == null) {
+ localAddr = ((InetSocketAddress) session.getLocalAddress()).getAddress();
+ }
+
+ SocketAddress localSocketAddress = new InetSocketAddress(localAddr, dataConfig.getActiveLocalPort());
+
+ LOG.debug("Binding active data connection to {}", localSocketAddress);
+ dataSoc.bind(localSocketAddress);
+
+ dataSoc.connect(new InetSocketAddress(address, port));
+ } else {
+
+ if (secure) {
+ LOG.debug("Opening secure passive data connection");
+ // this is where we wrap the unsecured socket as a SSLSocket. This is
+ // due to the JVM bug described in FTPSERVER-241.
+
+ // get server socket factory
+ SslConfiguration ssl = getSslConfiguration();
+
+ // we've already checked this, but let's do it again
+ if (ssl == null) {
+ throw new FtpException("Data connection SSL not configured");
+ }
+
+ SSLSocketFactory ssocketFactory = ssl.getSocketFactory();
+
+ Socket serverSocket = servSoc.accept();
+
+ SSLSocket sslSocket = (SSLSocket) ssocketFactory.createSocket(serverSocket, serverSocket.getInetAddress().getHostAddress(), serverSocket.getPort(), true);
+ sslSocket.setUseClientMode(false);
+
+ // initialize server socket
+ if (ssl.getClientAuth() == ClientAuth.NEED) {
+ sslSocket.setNeedClientAuth(true);
+ } else if (ssl.getClientAuth() == ClientAuth.WANT) {
+ sslSocket.setWantClientAuth(true);
+ }
+
+ if (ssl.getEnabledCipherSuites() != null) {
+ sslSocket.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
+ }
+
+ if (ssl.getEnabledProtocols() != null) {
+ sslSocket.setEnabledProtocols(ssl.getEnabledProtocols());
+ }
+
+ dataSoc = sslSocket;
+ } else {
+ LOG.debug("Opening passive data connection");
+
+ dataSoc = servSoc.accept();
}
-
- // get socket factory
- SSLSocketFactory socFactory = ssl.getSocketFactory();
-
- // create socket
- SSLSocket ssoc = (SSLSocket) socFactory.createSocket();
- ssoc.setUseClientMode(false);
-
- // initialize socket
- if (ssl.getEnabledCipherSuites() != null) {
- ssoc.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
+
+ if (dataConfig.isPassiveIpCheck()) {
+ // Let's make sure we got the connection from the same
+ // client that we are expecting
+ InetAddress remoteAddress = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
+ InetAddress dataSocketAddress = dataSoc.getInetAddress();
+ if (!dataSocketAddress.equals(remoteAddress)) {
+ LOG.warn("Passive IP Check failed. Closing data connection from " + dataSocketAddress + " as it does not match the expected address " + remoteAddress);
+ closeDataConnection();
+ return null;
+ }
}
-
- if (ssl.getEnabledProtocol() != null) {
- ssoc.setEnabledProtocols(new String[] { ssl.getEnabledProtocol() });
+
+ DataConnectionConfiguration dataCfg = session.getListener().getDataConnectionConfiguration();
+
+ dataSoc.setSoTimeout(dataCfg.getIdleTime() * 1000);
+ LOG.debug("Passive data connection opened");
}
- dataSoc = ssoc;
- } else {
- LOG.debug("Opening active data connection");
- dataSoc = new Socket();
- }
-
- dataSoc.setReuseAddress(true);
-
- InetAddress localAddr = resolveAddress(dataConfig.getActiveLocalAddress());
-
- // if no local address has been configured, make sure we use the same as the client connects from
- if (localAddr == null) {
- localAddr = ((InetSocketAddress) session.getLocalAddress()).getAddress();
- }
-
- SocketAddress localSocketAddress = new InetSocketAddress(localAddr, dataConfig.getActiveLocalPort());
-
- LOG.debug("Binding active data connection to {}", localSocketAddress);
- dataSoc.bind(localSocketAddress);
-
- dataSoc.connect(new InetSocketAddress(address, port));
- } else {
-
- if (secure) {
- LOG.debug("Opening secure passive data connection");
- // this is where we wrap the unsecured socket as a SSLSocket. This is
- // due to the JVM bug described in FTPSERVER-241.
-
- // get server socket factory
- SslConfiguration ssl = getSslConfiguration();
-
- // we've already checked this, but let's do it again
- if (ssl == null) {
- throw new FtpException("Data connection SSL not configured");
- }
-
- SSLSocketFactory ssocketFactory = ssl.getSocketFactory();
-
- Socket serverSocket = servSoc.accept();
-
- SSLSocket sslSocket = (SSLSocket) ssocketFactory.createSocket(serverSocket, serverSocket.getInetAddress().getHostAddress(), serverSocket.getPort(), true);
- sslSocket.setUseClientMode(false);
-
- // initialize server socket
- if (ssl.getClientAuth() == ClientAuth.NEED) {
- sslSocket.setNeedClientAuth(true);
- } else if (ssl.getClientAuth() == ClientAuth.WANT) {
- sslSocket.setWantClientAuth(true);
- }
-
- if (ssl.getEnabledCipherSuites() != null) {
- sslSocket.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
- }
-
- if (ssl.getEnabledProtocol() != null) {
- sslSocket.setEnabledProtocols(new String[] { ssl.getEnabledProtocol() });
- }
-
- dataSoc = sslSocket;
- } else {
- LOG.debug("Opening passive data connection");
-
- dataSoc = servSoc.accept();
- }
-
- if (dataConfig.isPassiveIpCheck()) {
- // Let's make sure we got the connection from the same
- // client that we are expecting
- InetAddress remoteAddress = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
- InetAddress dataSocketAddress = dataSoc.getInetAddress();
- if (!dataSocketAddress.equals(remoteAddress)) {
- LOG.warn("Passive IP Check failed. Closing data connection from " + dataSocketAddress + " as it does not match the expected address " + remoteAddress);
+ } catch (Exception ex) {
closeDataConnection();
- return null;
- }
+ LOG.warn("FtpDataConnection.getDataSocket()", ex);
+ throw ex;
}
-
- DataConnectionConfiguration dataCfg = session.getListener().getDataConnectionConfiguration();
-
- dataSoc.setSoTimeout(dataCfg.getIdleTime() * 1000);
- LOG.debug("Passive data connection opened");
+ dataSoc.setSoTimeout(dataConfig.getIdleTime() * 1000);
+
+ // Make sure we initiate the SSL handshake, or we'll
+ // get an error if we turn out not to send any data
+ // e.g. during the listing of an empty directory
+ if (dataSoc instanceof SSLSocket) {
+ ((SSLSocket) dataSoc).startHandshake();
}
- } catch (Exception ex) {
- closeDataConnection();
- LOG.warn("FtpDataConnection.getDataSocket()", ex);
- throw ex;
- }
- dataSoc.setSoTimeout(dataConfig.getIdleTime() * 1000);
-
- // Make sure we initiate the SSL handshake, or we'll
- // get an error if we turn out not to send any data
- // e.g. during the listing of an empty directory
- if (dataSoc instanceof SSLSocket) {
- ((SSLSocket) dataSoc).startHandshake();
- }
-
- return dataSoc;
+
+ return dataSoc;
}
/*
* (non-Javadoc) Returns an InetAddress object from a hostname or IP address.
*/
private InetAddress resolveAddress(String host) throws DataConnectionException {
- if (host == null) {
- return null;
- } else {
- try {
- return InetAddress.getByName(host);
- } catch (UnknownHostException ex) {
- throw new DataConnectionException("Failed to resolve address", ex);
+ if (host == null) {
+ return null;
+ } else {
+ try {
+ return InetAddress.getByName(host);
+ } catch (UnknownHostException ex) {
+ throw new DataConnectionException("Failed to resolve address", ex);
+ }
}
}
- }
/*
* (non-Javadoc)
diff --git a/core/src/main/java/org/apache/ftpserver/listener/nio/NioListener.java b/core/src/main/java/org/apache/ftpserver/listener/nio/NioListener.java
index 0b1e5a7..cfa5d45 100644
--- a/core/src/main/java/org/apache/ftpserver/listener/nio/NioListener.java
+++ b/core/src/main/java/org/apache/ftpserver/listener/nio/NioListener.java
@@ -146,8 +146,8 @@
ssl_filter.setWantClientAuth(true);
}
- if (ssl_conf.getEnabledProtocol() != null) {
- ssl_filter.setEnabledProtocols(new String[] { ssl_conf.getEnabledProtocol() });
+ if (ssl_conf.getEnabledProtocols() != null) {
+ ssl_filter.setEnabledProtocols(ssl_conf.getEnabledProtocols());
}
if (ssl_conf.getEnabledCipherSuites() != null) {
diff --git a/core/src/main/java/org/apache/ftpserver/ssl/SslConfiguration.java b/core/src/main/java/org/apache/ftpserver/ssl/SslConfiguration.java
index c4da591..1a338ea 100644
--- a/core/src/main/java/org/apache/ftpserver/ssl/SslConfiguration.java
+++ b/core/src/main/java/org/apache/ftpserver/ssl/SslConfiguration.java
@@ -72,7 +72,7 @@
*
* @return The name of the protocol as a String
*/
- String getEnabledProtocol();
+ String[] getEnabledProtocols();
/**
* Return the required client authentication setting
diff --git a/core/src/main/java/org/apache/ftpserver/ssl/SslConfigurationFactory.java b/core/src/main/java/org/apache/ftpserver/ssl/SslConfigurationFactory.java
index b6778d7..6ee613a 100644
--- a/core/src/main/java/org/apache/ftpserver/ssl/SslConfigurationFactory.java
+++ b/core/src/main/java/org/apache/ftpserver/ssl/SslConfigurationFactory.java
@@ -60,7 +60,7 @@
private String trustStoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
- private String sslProtocol = "TLS";
+ private String[] sslProtocols = new String[] {"TLSv1.2"};
private ClientAuth clientAuth = ClientAuth.NONE;
@@ -158,20 +158,22 @@
*
* @return The SSL protocol
*/
- public String getSslProtocol() {
- return sslProtocol;
+ public String[] getSslProtocols() {
+ return sslProtocols;
}
/**
- * Set the SSL protocol used for this channel. Supported values are "SSL" and "TLS". Defaults to "TLS".
+ * Set the SSL protocols used for this channel. Defaults to "TLSv1.2".
*
- * @param sslProtocol
- * The SSL protocol
+ * @param sslProtocols
+ * The SSL protocols
*/
- public void setSslProtocol(String sslProtocol) {
- if (sslProtocol == null || sslProtocol.length() == 0)
- throw new FtpServerConfigurationException("SslProcotol must not be null or zero length");
- this.sslProtocol = sslProtocol;
+ public void setSslProtocol(String... sslProtocols) {
+ if (sslProtocols == null || sslProtocols.length == 0) {
+ throw new FtpServerConfigurationException("SslProcotol must not be null or zero length");
+ }
+
+ this.sslProtocols = sslProtocols;
}
/**
@@ -182,13 +184,13 @@
* The desired authentication level
*/
public void setClientAuthentication(String clientAuthReqd) {
- if ("true".equalsIgnoreCase(clientAuthReqd) || "yes".equalsIgnoreCase(clientAuthReqd) || "need".equalsIgnoreCase(clientAuthReqd)) {
- this.clientAuth = ClientAuth.NEED;
- } else if ("want".equalsIgnoreCase(clientAuthReqd)) {
- this.clientAuth = ClientAuth.WANT;
- } else {
- this.clientAuth = ClientAuth.NONE;
- }
+ if ("true".equalsIgnoreCase(clientAuthReqd) || "yes".equalsIgnoreCase(clientAuthReqd) || "need".equalsIgnoreCase(clientAuthReqd)) {
+ this.clientAuth = ClientAuth.NEED;
+ } else if ("want".equalsIgnoreCase(clientAuthReqd)) {
+ this.clientAuth = ClientAuth.WANT;
+ } else {
+ this.clientAuth = ClientAuth.NONE;
+ }
}
/**
@@ -324,19 +326,22 @@
KeyStore keyStore = loadStore(keystoreFile, keystoreType, keystorePass);
KeyStore trustStore;
+
if (trustStoreFile != null) {
- LOG.debug("Loading trust store from \"{}\", using the key store type \"{}\"", trustStoreFile.getAbsolutePath(), trustStoreType);
- trustStore = loadStore(trustStoreFile, trustStoreType, trustStorePass);
+ LOG.debug("Loading trust store from \"{}\", using the key store type \"{}\"", trustStoreFile.getAbsolutePath(), trustStoreType);
+ trustStore = loadStore(trustStoreFile, trustStoreType, trustStorePass);
} else {
- trustStore = keyStore;
+ trustStore = keyStore;
}
String keyPassToUse;
+
if (keyPass == null) {
- keyPassToUse = keystorePass;
+ keyPassToUse = keystorePass;
} else {
- keyPassToUse = keyPass;
+ keyPassToUse = keyPass;
}
+
// initialize key manager factory
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keystoreAlgorithm);
keyManagerFactory.init(keyStore, keyPassToUse.toCharArray());
@@ -345,7 +350,8 @@
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(trustStoreAlgorithm);
trustManagerFactory.init(trustStore);
- return new DefaultSslConfiguration(keyManagerFactory, trustManagerFactory, clientAuth, sslProtocol, enabledCipherSuites, keyAlias);
+ return new DefaultSslConfiguration(keyManagerFactory, trustManagerFactory, clientAuth, sslProtocols,
+ enabledCipherSuites, keyAlias);
} catch (Exception ex) {
LOG.error("DefaultSsl.configure()", ex);
throw new FtpServerConfigurationException("DefaultSsl.configure()", ex);
diff --git a/core/src/main/java/org/apache/ftpserver/ssl/impl/DefaultSslConfiguration.java b/core/src/main/java/org/apache/ftpserver/ssl/impl/DefaultSslConfiguration.java
index df960ef..9f17e9d 100644
--- a/core/src/main/java/org/apache/ftpserver/ssl/impl/DefaultSslConfiguration.java
+++ b/core/src/main/java/org/apache/ftpserver/ssl/impl/DefaultSslConfiguration.java
@@ -48,7 +48,7 @@
private final TrustManagerFactory trustManagerFactory;
- private String enabledProtocol = "TLS";
+ private String[] enabledProtocols = new String[] {"TLSv1.2"};
private final ClientAuth clientAuth;// = ClientAuth.NONE;
@@ -65,78 +65,80 @@
*
* @throws GeneralSecurityException
*/
- public DefaultSslConfiguration(KeyManagerFactory keyManagerFactory, TrustManagerFactory trustManagerFactory, ClientAuth clientAuthReqd, String sslProtocol, String[] enabledCipherSuites, String keyAlias) throws GeneralSecurityException {
- super();
- this.clientAuth = clientAuthReqd;
- this.enabledCipherSuites = enabledCipherSuites;
- this.keyAlias = keyAlias;
- this.keyManagerFactory = keyManagerFactory;
- this.enabledProtocol = sslProtocol;
- this.trustManagerFactory = trustManagerFactory;
- this.sslContext = initContext();
- this.socketFactory = sslContext.getSocketFactory();
+ public DefaultSslConfiguration(KeyManagerFactory keyManagerFactory, TrustManagerFactory trustManagerFactory,
+ ClientAuth clientAuthReqd, String[] sslProtocols, String[] enabledCipherSuites, String keyAlias) throws GeneralSecurityException {
+ super();
+ this.clientAuth = clientAuthReqd;
+ this.enabledCipherSuites = enabledCipherSuites;
+ this.keyAlias = keyAlias;
+ this.keyManagerFactory = keyManagerFactory;
+ this.enabledProtocols = sslProtocols;
+ this.trustManagerFactory = trustManagerFactory;
+ this.sslContext = initContext();
+ this.socketFactory = sslContext.getSocketFactory();
}
public SSLSocketFactory getSocketFactory() throws GeneralSecurityException {
- return socketFactory;
+ return socketFactory;
}
/**
* @see SslConfiguration#getSSLContext(String)
*/
- public SSLContext getSSLContext(String protocol) throws GeneralSecurityException {
- return sslContext;
+ public SSLContext getSSLContext(String enabledProtocol) throws GeneralSecurityException {
+ return sslContext;
}
/**
* @see SslConfiguration#getEnabledProtocol()
*/
- public String getEnabledProtocol() {
- return enabledProtocol;
+ public String[] getEnabledProtocols() {
+ return enabledProtocols;
}
/**
* @see SslConfiguration#getClientAuth()
*/
public ClientAuth getClientAuth() {
- return clientAuth;
+ return clientAuth;
}
/**
* @see SslConfiguration#getSSLContext()
*/
public SSLContext getSSLContext() throws GeneralSecurityException {
- return getSSLContext(enabledProtocol);
+ return getSSLContext(enabledProtocols[0]);
}
/**
* @see SslConfiguration#getEnabledCipherSuites()
*/
public String[] getEnabledCipherSuites() {
- if (enabledCipherSuites != null) {
- return enabledCipherSuites.clone();
- } else {
- return null;
- }
- }
-
- private SSLContext initContext() throws GeneralSecurityException {
- KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
-
- // wrap key managers to allow us to control their behavior
- // (FTPSERVER-93)
- for (int i = 0; i < keyManagers.length; i++) {
- if (ClassUtils.extendsClass(keyManagers[i].getClass(), "javax.net.ssl.X509ExtendedKeyManager")) {
- keyManagers[i] = new ExtendedAliasKeyManager(keyManagers[i], keyAlias);
- } else if (keyManagers[i] instanceof X509KeyManager) {
- keyManagers[i] = new AliasKeyManager(keyManagers[i], keyAlias);
+ if (enabledCipherSuites != null) {
+ return enabledCipherSuites.clone();
+ } else {
+ return null;
}
}
- // create and initialize the SSLContext
- SSLContext ctx = SSLContext.getInstance(enabledProtocol);
- ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null);
- // Create the socket factory
- return ctx;
+ private SSLContext initContext() throws GeneralSecurityException {
+ KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
+
+ // wrap key managers to allow us to control their behavior
+ // (FTPSERVER-93)
+ for (int i = 0; i < keyManagers.length; i++) {
+ if (ClassUtils.extendsClass(keyManagers[i].getClass(), "javax.net.ssl.X509ExtendedKeyManager")) {
+ keyManagers[i] = new ExtendedAliasKeyManager(keyManagers[i], keyAlias);
+ } else if (keyManagers[i] instanceof X509KeyManager) {
+ keyManagers[i] = new AliasKeyManager(keyManagers[i], keyAlias);
+ }
+ }
+
+ // create and initialize the SSLContext
+ SSLContext ctx = SSLContext.getInstance(enabledProtocols[0]);
+ ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null);
+
+ // Create the socket factory
+ return ctx;
}
}
diff --git a/core/src/main/java/org/apache/ftpserver/util/StringUtils.java b/core/src/main/java/org/apache/ftpserver/util/StringUtils.java
index 8642079..7bba65d 100644
--- a/core/src/main/java/org/apache/ftpserver/util/StringUtils.java
+++ b/core/src/main/java/org/apache/ftpserver/util/StringUtils.java
@@ -28,12 +28,20 @@
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
-public class StringUtils {
+public final class StringUtils {
+ private StringUtils() {
+ // Nothing to do
+ }
/**
* This is a string replacement method.
+ *
+ * @param source The original string
+ * @param oldStr The substring to replace
+ * @param newStr The replacement string
+ * @return The modified string
*/
- public static final String replaceString(String source, String oldStr,
+ public static String replaceString(String source, String oldStr,
String newStr) {
StringBuilder sb = new StringBuilder(source.length());
int sind = 0;
@@ -48,9 +56,19 @@
}
/**
- * Replace string.
+ * Replace strings using placeholders. Each occurence of {} in the original
+ * string will be replaced with the object at the same index. For instance:
+ * <br>
+ * <code>replaceString( "This {} a {}", ["is', "string"])</code><br>
+ * will produce:<br>
+ * <code>"This is a string"</code><br>
+ *
+ * @param source The original string
+ * @param args The array of object to replace the placeholders in
+ * the original string
+ * @return The modified string
*/
- public static final String replaceString(String source, Object[] args) {
+ public static String replaceString(String source, Object[] args) {
int startIndex = 0;
int openIndex = source.indexOf('{', startIndex);
if (openIndex == -1) {
@@ -87,9 +105,19 @@
}
/**
- * Replace string.
+ * Replace strings using placeholders. Each occurence of {} in the original
+ * string will be replaced with the object at the same index. For instance:
+ * <br>
+ * <code>replaceString( "This {} a {}", Map("is', "string"))</code><br>
+ * will produce:<br>
+ * <code>"This is a string"</code><br>
+ *
+ * @param source The original string
+ * @param args The map of object to replace the placeholders in
+ * the original string
+ * @return The modified string
*/
- public static final String replaceString(String source,
+ public static String replaceString(String source,
Map<String, Object> args) {
int startIndex = 0;
int openIndex = source.indexOf('{', startIndex);
@@ -129,15 +157,15 @@
}
/**
- * This method is used to insert HTML block dynamically.
- *
- * @param source the HTML code to be processes
- * @param bReplaceNl if true '\n' will be replaced by <br>
- * @param bReplaceTag if true '<' will be replaced by < and
- * '>' will be replaced by >
- * @param bReplaceQuote if true '\"' will be replaced by "
- */
- public static final String formatHtml(String source, boolean bReplaceNl,
+ * This method is used to insert HTML block dynamically.
+ *
+ * @param source the HTML code to be processes
+ * @param bReplaceNl if true '\n' will be replaced by <br>
+ * @param bReplaceTag if true '<' will be replaced by < and
+ * '>' will be replaced by >
+ * @param bReplaceQuote if true '\"' will be replaced by "
+ */
+ public static String formatHtml(String source, boolean bReplaceNl,
boolean bReplaceTag, boolean bReplaceQuote) {
StringBuilder sb = new StringBuilder();
@@ -169,7 +197,7 @@
} else {
sb.append(c);
}
-
+
break;
case '\n':
@@ -203,7 +231,7 @@
/**
* Pad string object.
*/
- public static final String pad(String src, char padChar, boolean rightPad,
+ public static String pad(String src, char padChar, boolean rightPad,
int totalLength) {
int srcLength = src.length();
@@ -227,7 +255,7 @@
/**
* Get hex string from byte array.
*/
- public static final String toHexString(byte[] res) {
+ public static String toHexString(byte[] res) {
StringBuilder sb = new StringBuilder(res.length << 1);
for (int i = 0; i < res.length; i++) {
String digit = Integer.toHexString(0xFF & res[i]);
@@ -242,7 +270,7 @@
/**
* Get byte array from hex string.
*/
- public static final byte[] toByteArray(String hexString) {
+ public static byte[] toByteArray(String hexString) {
int arrLength = hexString.length() >> 1;
byte buff[] = new byte[arrLength];
for (int i = 0; i < arrLength; i++) {
diff --git a/core/src/test/resources/spring-config/config-property-placeholder.xml b/core/src/test/resources/spring-config/config-property-placeholder.xml
index 49ee22d..e32682d 100644
--- a/core/src/test/resources/spring-config/config-property-placeholder.xml
+++ b/core/src/test/resources/spring-config/config-property-placeholder.xml
@@ -24,7 +24,7 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
- http://mina.apache.org/ftpserver/spring/v1 http://mina.apache.org/ftpserver/ftpserver-1.0.xsd
+ http://mina.apache.org/ftpserver/spring/v1 https://mina.apache.org/ftpserver-project/ftpserver-1.0.xsd
">
<context:property-placeholder location="src/test/resources/spring-config/placeholder.properties"/>
diff --git a/core/src/test/resources/spring-config/config-spring-1.xml b/core/src/test/resources/spring-config/config-spring-1.xml
index b836cf6..d5bf7f9 100644
--- a/core/src/test/resources/spring-config/config-spring-1.xml
+++ b/core/src/test/resources/spring-config/config-spring-1.xml
@@ -21,9 +21,10 @@
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://mina.apache.org/ftpserver/spring/v1 http://mina.apache.org/ftpserver/ftpserver-1.0.xsd
- "
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://mina.apache.org/ftpserver/spring/v1
+ https://mina.apache.org/ftpserver-project/ftpserver-1.0.xsd"
max-logins="500"
anon-enabled="false"
max-anon-logins="123"
@@ -31,12 +32,11 @@
login-failure-delay="125"
>
-
<listeners>
<nio-listener name="listener0" port="2222" local-address="1.2.3.4">
- <ssl>
- <keystore file="src/test/resources/ftpserver.jks" password="password"/>
- </ssl>
+ <ssl>
+ <keystore file="src/test/resources/ftpserver.jks" password="password"/>
+ </ssl>
<data-connection idle-timeout="100" implicit-ssl="true">
<active enabled="true" local-address="1.2.3.4"/>
@@ -44,6 +44,7 @@
</data-connection>
<blacklist>1.2.3.0/16, 1.2.4.0/16, 1.2.3.4</blacklist>
</nio-listener>
+
<listener name="listener1">
<beans:bean id="listener1" class="org.apache.ftpserver.config.spring.MyCustomListener">
<beans:property name="port" value="2223"/>
@@ -75,7 +76,9 @@
<user-manager>
<beans:bean class="org.apache.ftpserver.config.spring.MockUserManager"/>
</user-manager>
+
<native-filesystem case-insensitive="true" create-home="true" />
+
<commands use-default="false">
<command name="FOO">
<beans:bean class="org.apache.ftpserver.command.impl.HELP" />
diff --git a/distribution/res/conf/ftpd-full.xml b/distribution/res/conf/ftpd-full.xml
index 747ee14..8f690c7 100644
--- a/distribution/res/conf/ftpd-full.xml
+++ b/distribution/res/conf/ftpd-full.xml
@@ -17,7 +17,7 @@
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://mina.apache.org/ftpserver/spring/v1 http://mina.apache.org/ftpserver/ftpserver-1.0.xsd
+ http://mina.apache.org/ftpserver/spring/v1 https://mina.apache.org/ftpserver-project/ftpserver-1.0.xsd
"
id="myServer">
<!--
@@ -33,8 +33,7 @@
<truststore file="mytruststore.jks" password="secret" />
</ssl>
<data-connection idle-timeout="60">
- <active enabled="true" local-address="1.2.3.4" local-port="2323"
- ip-check="true" />
+ <active enabled="true" local-address="1.2.3.4" local-port="2323" ip-check="true" />
<passive ports="123-125" address="1.2.3.4" external-address="1.2.3.4" />
</data-connection>
<blacklist>1.2.3.0/16, 1.2.4.0/16, 1.2.3.4</blacklist>
diff --git a/distribution/res/conf/ftpd-typical.xml b/distribution/res/conf/ftpd-typical.xml
index 68f3569..d78db46 100644
--- a/distribution/res/conf/ftpd-typical.xml
+++ b/distribution/res/conf/ftpd-typical.xml
@@ -16,7 +16,7 @@
<server xmlns="http://mina.apache.org/ftpserver/spring/v1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
- http://mina.apache.org/ftpserver/spring/v1 http://mina.apache.org/ftpserver-project/ftpserver-1.0.xsd
+ http://mina.apache.org/ftpserver/spring/v1 https://mina.apache.org/ftpserver-project/ftpserver-1.0.xsd
"
id="myServer">
<listeners>