Commit e58d590b authored by Guus der Kinderen's avatar Guus der Kinderen

OF-946: Centralize connection configuration.

This commit (which is compilable, but breaks some important
functionality - work in progess) aims to centralize the configuration
for connections. "Connections" are used in a broad sense here, and
includes socket-based (TLS and legacy-mode SSL) client connections,
BOSH-based client connections, component connections, connectionmanager
(multiplexer) connections, but also connections to the admin panel.
parent 123c96e5
......@@ -56,6 +56,15 @@
</filter>
</appender>
<appender name="all-out" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="${openfireHome}/logs/all.log" />
<param name="MaxFileSize" value="1024KB"/>
<param name="MaxBackupIndex" value="5"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy.MM.dd HH:mm:ss} %-5p [%t]: %c - %m%n" />
</layout>
</appender>
<!-- OF-506: Jetty INFO messages are generally not useful. Ignore them by default. -->
<logger name="org.eclipse.jetty">
<level value="warn" />
......@@ -63,6 +72,7 @@
<root>
<level value="info" />
<appender-ref ref="all-out" />
<appender-ref ref="debug-out" />
<appender-ref ref="info-out" />
<appender-ref ref="warn-out" />
......
......@@ -114,6 +114,19 @@ if [ ! -x "$JAVACMD" ] ; then
exit 1
fi
for arguments in "$@"
do
case $arguments in
-debug)
JAVACMD="$JAVACMD -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
;;
*)
# unknown option
;;
esac
done
if [ -z "$LOCALCLASSPATH" ] ; then
LOCALCLASSPATH=$OPENFIRE_LIB/startup.jar
else
......
......@@ -2424,24 +2424,24 @@ ssl.signing-request.signing-request=Signing Request
# Certificate management
certificate-management.purpose.SOCKETBASED_IDENTITYSTORE.title=Identity Store (socket)
certificate-management.purpose.SOCKETBASED_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for plain socket-based connections.
certificate-management.purpose.SOCKETBASED_S2S_TRUSTSTORE.title=Server-to-Server Trust Store (socket)
certificate-management.purpose.SOCKETBASED_S2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify other XMPP servers. These certificates are used during server-to-server federation via plain socket-based connections.
certificate-management.purpose.SOCKETBASED_C2S_TRUSTSTORE.title=Client-to-Server Trust Store (socket)
certificate-management.purpose.SOCKETBASED_C2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify XMPP clients. These certificates are used during mutual authentication via plain socket-based connections.
certificate-management.purpose.BOSHBASED_IDENTITYSTORE.title=Identity Store (BOSH/HTTP-bind)
certificate-management.purpose.BOSHBASED_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for BOSH (HTTP-bind) connections.
certificate-management.purpose.BOSHBASED_C2S_TRUSTSTORE.title=Client-to-Server Trust Store (BOSH/HTTP-bind)
certificate-management.purpose.BOSHBASED_C2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify XMPP clients. These certificates are used during mutual authentication via BOSH (HTTP-bind) connections.
certificate-management.purpose.ADMINISTRATIVE_IDENTITYSTORE.title=Administrative Identity Store
certificate-management.purpose.ADMINISTRATIVE_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for connections to administrative services (eg: user providers).
certificate-management.purpose.ADMINISTRATIVE_TRUSTSTORE.title=Administrative Trust Store
certificate-management.purpose.ADMINISTRATIVE_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify applications/servers that provide administrative functionality (eg: user providers).
certificate-management.purpose.WEBADMIN_IDENTITYSTORE.title=Admin Panel Identity Store
certificate-management.purpose.WEBADMIN_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used by the Web-Admin panel (when accessed via HTTPS).
certificate-management.purpose.WEBADMIN_TRUSTSTORE.title=Admin Panel Trust Store
certificate-management.purpose.WEBADMIN_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify parties that wish to interact with the Openfire Web-Admin.
certificate-management.connectionType.SOCKETBASED_IDENTITYSTORE.title=Identity Store (socket)
certificate-management.connectionType.SOCKETBASED_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for plain socket-based connections.
certificate-management.connectionType.SOCKETBASED_S2S_TRUSTSTORE.title=Server-to-Server Trust Store (socket)
certificate-management.connectionType.SOCKETBASED_S2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify other XMPP servers. These certificates are used during server-to-server federation via plain socket-based connections.
certificate-management.connectionType.SOCKETBASED_C2S_TRUSTSTORE.title=Client-to-Server Trust Store (socket)
certificate-management.connectionType.SOCKETBASED_C2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify XMPP clients. These certificates are used during mutual authentication via plain socket-based connections.
certificate-management.connectionType.BOSHBASED_IDENTITYSTORE.title=Identity Store (BOSH/HTTP-bind)
certificate-management.connectionType.BOSHBASED_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for BOSH (HTTP-bind) connections.
certificate-management.connectionType.BOSHBASED_C2S_TRUSTSTORE.title=Client-to-Server Trust Store (BOSH/HTTP-bind)
certificate-management.connectionType.BOSHBASED_C2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify XMPP clients. These certificates are used during mutual authentication via BOSH (HTTP-bind) connections.
certificate-management.connectionType.ADMINISTRATIVE_IDENTITYSTORE.title=Administrative Identity Store
certificate-management.connectionType.ADMINISTRATIVE_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for connections to administrative services (eg: user providers).
certificate-management.connectionType.ADMINISTRATIVE_TRUSTSTORE.title=Administrative Trust Store
certificate-management.connectionType.ADMINISTRATIVE_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify applications/servers that provide administrative functionality (eg: user providers).
certificate-management.connectionType.WEBADMIN_IDENTITYSTORE.title=Admin Panel Identity Store
certificate-management.connectionType.WEBADMIN_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used by the Web-Admin panel (when accessed via HTTPS).
certificate-management.connectionType.WEBADMIN_TRUSTSTORE.title=Admin Panel Trust Store
certificate-management.connectionType.WEBADMIN_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify parties that wish to interact with the Openfire Web-Admin.
# Restart HTTP server
......
......@@ -26,6 +26,7 @@ import java.security.cert.Certificate;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.xmpp.packet.Packet;
/**
......@@ -354,7 +355,7 @@ public interface Connection extends Closeable {
* otherwise a {@link org.jivesoftware.openfire.net.ServerTrustManager} will be used.
* @param authentication policy to use for authenticating the remote peer.
* @throws Exception if an error occured while securing the connection.
* @deprecated Use {@link #startTLS(boolean, boolean, ClientAuth)} instead, with isPeerClient = (remoteServer == null)
* @deprecated Use {@link #startTLS(boolean)} instead.
*/
@Deprecated
void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception;
......@@ -364,22 +365,15 @@ public interface Connection extends Closeable {
* connection the server requesting the TLS negotiation will be the client and the other server
* will be the server during the TLS negotiation. Therefore, the server requesting the TLS
* negotiation must pass <code>true</code> in the <tt>clientMode</tt> parameter and the server
* receiving the TLS request must pass <code>false</code> in the <tt>clientMode</tt> parameter.
* Both servers should specify the xmpp domain of the other server in the <tt>remoteServer</tt>
* parameter.<p>
* receiving the TLS request must pass <code>false</code> in the <tt>clientMode</tt> parameter.<p>
*
* In the case of client-2-server the XMPP server must pass <code>false</code> in the
* <tt>clientMode</tt> parameter since it will behave as the server in the TLS negotiation. The
* <tt>remoteServer</tt> parameter will always be <tt>null</tt>.
* <tt>clientMode</tt> parameter since it will behave as the server in the TLS negotiation.
*
* @param clientMode boolean indicating if this entity is a client or a server in the TLS negotiation.
* @param isPeerClient indicates if the remote party is a client. When true a
* {@link org.jivesoftware.openfire.net.ClientTrustManager} will be used for verifying certificates
* otherwise a {@link org.jivesoftware.openfire.net.ServerTrustManager} will be used.
* @param authentication policy to use for authenticating the remote peer.
* @throws Exception if an error occured while securing the connection.
*/
void startTLS(boolean clientMode, boolean isPeerClient, ClientAuth authentication) throws Exception;
void startTLS(boolean clientMode) throws Exception;
/**
* Adds the compression filter to the connection but only filter incoming traffic. Do not filter
......@@ -396,6 +390,15 @@ public interface Connection extends Closeable {
*/
void startCompression();
/**
* Returns a representation of the desired state for this connection. Note that this is different from the current
* state of the connection. For example, TLS can be required by configuration, but while the connection has yet to
* be fully initialized, the current state might not be TLS-encrypted.
*
* @return The desired configuration for the connection (never null).
*/
ConnectionConfiguration getConfiguration();
/**
* Enumeration of possible compression policies required to interact with the server.
*/
......@@ -434,7 +437,14 @@ public interface Connection extends Closeable {
* TLS is not available. Entities that request a TLS negotiation will get a stream
* error and their connections will be closed.
*/
disabled
disabled,
/**
* A policy that requires connections to be encrypted immediately (as opposed to the
* 'required' policy, that allows for an initially unencrypted connection to become
* encrypted through StartTLS.
*/
legacyMode
}
/**
......
......@@ -48,15 +48,27 @@ public interface ConnectionManager {
* The default XMPP port for external components.
*/
final int DEFAULT_COMPONENT_PORT = 5275;
/**
* The XMPP port for external components using SSL traffic.
*/
final int DEFAULT_COMPONENT_SSL_PORT = 5276;
/**
* The default XMPP port for server2server communication.
*/
final int DEFAULT_SERVER_PORT = 5269;
/**
* The default XMPP port for connection multiplex.
*/
final int DEFAULT_MULTIPLEX_PORT = 5262;
/**
* The default XMPP port for connection multiplex.
*/
final int DEFAULT_MULTIPLEX_SSL_PORT = 5263;
/**
* Returns an array of the ports managed by this connection manager.
*
......@@ -76,7 +88,9 @@ public interface ConnectionManager {
* @param useBlockingMode true means that the server will use a thread per connection.
* @return the created socket reader.
* @throws java.io.IOException when there is an error creating the socket reader.
* @deprecated This is part of the legacy blocking IO implementation. It should no longer be used in favor of NIO.
*/
@Deprecated
public SocketReader createSocketReader(Socket socket, boolean isSecure, ServerPort serverPort,
boolean useBlockingMode) throws IOException;
......
......@@ -30,7 +30,9 @@ import java.util.List;
* regarding the port while hiding implementation details.
*
* @author Iain Shigeoka
* @Deprecated
*/
@Deprecated
public class ServerPort {
private int port;
......
......@@ -38,17 +38,17 @@ import org.jivesoftware.openfire.filetransfer.DefaultFileTransferManager;
import org.jivesoftware.openfire.filetransfer.FileTransferManager;
import org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy;
import org.jivesoftware.openfire.handler.*;
import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.keystore.IdentityStore;
import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.mediaproxy.MediaProxyService;
import org.jivesoftware.openfire.muc.MultiUserChatManager;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.ServerTrafficCounter;
import org.jivesoftware.openfire.pep.IQPEPHandler;
import org.jivesoftware.openfire.pubsub.PubSubModule;
import org.jivesoftware.openfire.roster.RosterManager;
import org.jivesoftware.openfire.session.RemoteSessionLocator;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.openfire.spi.XMPPServerInfoImpl;
import org.jivesoftware.openfire.transport.TransportHandler;
import org.jivesoftware.openfire.update.UpdateManager;
......@@ -375,8 +375,8 @@ public class XMPPServer {
// Update certificates (if required)
try {
// Check if keystore already has certificates for current domain
final IdentityStoreConfig storeConfig = SSLConfig.getInstance().getIdentityStoreConfig( Purpose.SOCKET_C2S );
// Check if keystore (that out-of-the-box is a fallback for all keystores) already has certificates for current domain.
final IdentityStore storeConfig = CertificateStoreManager.getIdentityStore( ConnectionType.SOCKET_C2S );
storeConfig.ensureDomainCertificates( "DSA", "RSA" );
} catch (Exception e) {
logger.error("Error generating self-signed certificates", e);
......@@ -474,7 +474,6 @@ public class XMPPServer {
@SuppressWarnings("unchecked")
private void loadModules() {
File modulesXml = new File(JiveGlobals.getHomeDirectory(), "conf/modules.xml");
logger.info("Loading modules from " + modulesXml.getAbsolutePath());
SAXReader xmlReader = new SAXReader();
......
......@@ -69,6 +69,7 @@ import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.openfire.ConnectionManager;
import org.jivesoftware.openfire.IQRouter;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.XMPPServerInfo;
......@@ -86,6 +87,8 @@ import org.jivesoftware.openfire.muc.spi.MultiUserChatServiceImpl;
import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.session.ComponentSession;
import org.jivesoftware.openfire.session.LocalClientSession;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.CertificateEventListener;
......@@ -778,12 +781,13 @@ public class ClearspaceManager extends BasicModule implements ExternalComponentM
private void updateClearspaceClientSettings() {
String xmppBoshSslPort = "0";
String xmppBoshPort = "0";
String xmppPort = String.valueOf(XMPPServer.getInstance().getConnectionManager().getClientListenerPort());
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
String xmppPort = String.valueOf( connectionManager.getConfiguration( ConnectionType.SOCKET_C2S, false ).getPort() );
if (JiveGlobals.getBooleanProperty(HttpBindManager.HTTP_BIND_ENABLED, HttpBindManager.HTTP_BIND_ENABLED_DEFAULT)) {
int boshSslPort = HttpBindManager.getInstance().getHttpBindSecurePort();
int boshPort = HttpBindManager.getInstance().getHttpBindUnsecurePort();
try {
if (HttpBindManager.getInstance().isHttpsBindActive() && LocalClientSession.getTLSPolicy() != org.jivesoftware.openfire.Connection.TLSPolicy.disabled) {
if (HttpBindManager.getInstance().isHttpsBindActive() && connectionManager.getConfiguration( ConnectionType.SOCKET_C2S, false ).getTlsPolicy() != org.jivesoftware.openfire.Connection.TLSPolicy.disabled) {
xmppBoshSslPort = String.valueOf(boshSslPort);
}
}
......
......@@ -35,8 +35,8 @@ import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HttpClientError;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -63,19 +63,19 @@ public class SSLProtocolSocketFactory implements SecureProtocolSocketFactory {
private SSLContext createSSLContext(String host) {
try {
final SSLContext context = SSLConfig.getSSLContext( Purpose.ADMIN );
final SSLContext context = SSLContext.getInstance( "TLSv1" );
context.init(
null,
new TrustManager[] {
new ClearspaceX509TrustManager(
host,
manager.getProperties(),
SSLConfig.getTrustStore( Purpose.ADMIN ) )
CertificateStoreManager.getTrustStore( ConnectionType.ADMIN ).getStore() )
},
null);
return context;
} catch (Exception e) {
Log.error(e.getMessage(), e);
Log.error("An exception occurred while trying to create an SSL Context for host: '"+host+"'", e);
throw new HttpClientError(e.toString());
}
}
......
......@@ -47,11 +47,11 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
import org.jivesoftware.openfire.JMXManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.CertificateStoreConfig;
import org.jivesoftware.openfire.keystore.TrustStoreConfig;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.keystore.IdentityStore;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.CertificateEventListener;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
......@@ -141,19 +141,20 @@ public class AdminConsolePlugin implements Plugin {
// Create a connector for https traffic if it's enabled.
sslEnabled = false;
try {
final IdentityStoreConfig identityStoreConfig = SSLConfig.getInstance().getIdentityStoreConfig( Purpose.WEBADMIN );
if (adminSecurePort > 0 && identityStoreConfig.getStore().aliases().hasMoreElements() )
final IdentityStore identityStore = CertificateStoreManager.getIdentityStore( ConnectionType.WEBADMIN );
if (adminSecurePort > 0 && !identityStore.getAllCertificates().isEmpty() )
{
if ( !identityStoreConfig.containsDomainCertificate( "RSA" )) {
if ( !identityStore.containsDomainCertificate( "RSA" )) {
Log.warn("Admin console: Using RSA certificates but they are not valid for the hosted domain");
}
final SslContextFactory sslContextFactory = SSLConfig.getSslContextFactory( Purpose.WEBADMIN );
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
final ConnectionConfiguration configuration = connectionManager.getConfiguration( ConnectionType.WEBADMIN, true );
final SslContextFactory sslContextFactory = configuration.getSslContextFactory();
final ServerConnector httpsConnector;
if ("npn".equals(JiveGlobals.getXMLProperty("spdy.protocol", ""))) {
httpsConnector = new HTTPSPDYServerConnector(adminServer, sslContextFactory);
} else {
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.setSendServerVersion( false );
......
......@@ -61,12 +61,12 @@ import org.eclipse.jetty.webapp.WebAppContext;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.JMXManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.CertificateStoreConfig;
import org.jivesoftware.openfire.keystore.TrustStoreConfig;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.keystore.IdentityStore;
import org.jivesoftware.openfire.session.ConnectionSettings;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.CertificateEventListener;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
......@@ -251,16 +251,17 @@ public final class HttpBindManager {
private void createSSLConnector(int securePort, int bindThreads) {
httpsConnector = null;
try {
final IdentityStoreConfig identityStoreConfig = SSLConfig.getInstance().getIdentityStoreConfig( Purpose.BOSH_C2S );
final KeyStore keyStore = identityStoreConfig.getStore();
final IdentityStore identityStore = CertificateStoreManager.getIdentityStore( ConnectionType.BOSH_C2S );
if (securePort > 0 && identityStoreConfig.getStore().aliases().hasMoreElements() ) {
if ( !identityStoreConfig.containsDomainCertificate( "RSA" ) ) {
if (securePort > 0 && identityStore.getStore().aliases().hasMoreElements() ) {
if ( !identityStore.containsDomainCertificate( "RSA" ) ) {
Log.warn("HTTP binding: Using RSA certificates but they are not valid for " +
"the hosted domain");
}
final SslContextFactory sslContextFactory = SSLConfig.getSslContextFactory( Purpose.BOSH_C2S );
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
final ConnectionConfiguration configuration = connectionManager.getConfiguration( ConnectionType.BOSH_C2S, true );
final SslContextFactory sslContextFactory = configuration.getSslContextFactory();
final HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.setSecureScheme("https");
......
......@@ -45,6 +45,7 @@ import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.VirtualConnection;
import org.jivesoftware.openfire.session.LocalClientSession;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.TaskEngine;
......@@ -1118,6 +1119,7 @@ public class HttpSession extends LocalClientSession {
public static class HttpVirtualConnection extends VirtualConnection {
private InetAddress address;
private ConnectionConfiguration configuration;
public HttpVirtualConnection(InetAddress address) {
this.address = address;
......@@ -1158,6 +1160,11 @@ public class HttpSession extends LocalClientSession {
((HttpSession) session).deliver(text);
}
@Override
public ConnectionConfiguration getConfiguration() {
return session.getConnection().getConfiguration();
}
@Override
public Certificate[] getPeerCertificates() {
return ((HttpSession) session).getPeerCertificates();
......
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.util.CertificateEventListener;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -24,8 +23,8 @@ import java.util.*;
*
* A subclass of this class exists for each of the two distinct types of key store.
* <ul>
* <li>one that is used to provide credentials, an <em>identity store</em>, in {@link IdentityStoreConfig}</li>
* <li>one that is used to verify credentials, a <em>trust store</em>, in {@link TrustStoreConfig}</li>
* <li>one that is used to provide credentials, an <em>identity store</em>, in {@link IdentityStore}</li>
* <li>one that is used to verify credentials, a <em>trust store</em>, in {@link TrustStore}</li>
* </ul>
*
* Note that in Java terminology, an identity store is commonly referred to as a 'key store', while the same name is
......@@ -34,9 +33,9 @@ import java.util.*;
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public abstract class CertificateStoreConfig
public abstract class CertificateStore
{
private static final Logger Log = LoggerFactory.getLogger( CertificateStoreConfig.class );
private static final Logger Log = LoggerFactory.getLogger( CertificateStore.class );
protected static final Provider PROVIDER = new BouncyCastleProvider();
......@@ -47,39 +46,41 @@ public abstract class CertificateStoreConfig
}
protected final KeyStore store;
protected final char[] password;
protected final String canonicalPath;
protected final CertificateStoreConfiguration configuration;
public CertificateStoreConfig( String path, String password, String type, boolean createIfAbsent ) throws CertificateStoreConfigException
public CertificateStore( CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException
{
if (configuration == null)
{
throw new IllegalArgumentException( "Argument 'configuration' cannot be null." );
}
this.configuration = configuration;
try
{
this.canonicalPath = Purpose.canonicalize( path );
final File file = new File( canonicalPath );
final File file = configuration.getFile();
if ( createIfAbsent && !file.exists() )
{
try ( final FileOutputStream os = new FileOutputStream( canonicalPath ) )
try ( final FileOutputStream os = new FileOutputStream( file.getPath() ) )
{
store = KeyStore.getInstance( type );
store.load( null, password.toCharArray() );
store.store( os, password.toCharArray() );
this.password = password.toCharArray();
store = KeyStore.getInstance( configuration.getType() );
store.load( null, configuration.getPassword() );
store.store( os, configuration.getPassword() );
}
}
else
{
try ( final FileInputStream is = new FileInputStream( canonicalPath ) )
try ( final FileInputStream is = new FileInputStream( file ) )
{
store = KeyStore.getInstance( type );
store.load( is, password.toCharArray() );
this.password = password.toCharArray();
store = KeyStore.getInstance( configuration.getType() );
store.load( is, configuration.getPassword() );
}
}
}
catch ( IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException ex )
{
throw new CertificateStoreConfigException( "Unable to load store of type '" + type + "' from location '" + path + "'", ex );
throw new CertificateStoreConfigException( "Unable to load store of type '" + configuration.getType() + "' from file '" + configuration.getFile() + "'", ex );
}
}
......@@ -89,13 +90,13 @@ public abstract class CertificateStoreConfig
*/
public void reload() throws CertificateStoreConfigException
{
try ( final FileInputStream is = new FileInputStream( canonicalPath ) )
try ( final FileInputStream is = new FileInputStream( configuration.getFile() ) )
{
store.load( is, password );
store.load( is, configuration.getPassword() );
}
catch ( IOException | NoSuchAlgorithmException | CertificateException ex )
{
throw new CertificateStoreConfigException( "Unable to reload store in location '" + canonicalPath + "'", ex );
throw new CertificateStoreConfigException( "Unable to reload store in '" + configuration.getFile() + "'", ex );
}
}
......@@ -105,13 +106,13 @@ public abstract class CertificateStoreConfig
*/
public void persist() throws CertificateStoreConfigException
{
try ( final FileOutputStream os = new FileOutputStream( canonicalPath ) )
try ( final FileOutputStream os = new FileOutputStream( configuration.getFile() ) )
{
store.store( os, password );
store.store( os, configuration.getPassword() );
}
catch ( NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException ex )
{
throw new CertificateStoreConfigException( "Unable to save changes to store in location '" + canonicalPath + "'", ex );
throw new CertificateStoreConfigException( "Unable to save changes to store in '" + configuration.getFile() + "'", ex );
}
}
......@@ -177,31 +178,13 @@ public abstract class CertificateStoreConfig
// TODO: Notify listeners that a new certificate has been removed.
}
public String getType()
{
return store.getType();
}
public KeyStore getStore()
{
return store;
}
public String getPassword()
{
return String.valueOf( password );
}
public String getCanonicalPath()
{
return canonicalPath;
}
public String getPath()
public CertificateStoreConfiguration getConfiguration()
{
final Path path = Paths.get( canonicalPath );
final Path home = Paths.get( JiveGlobals.getHomeDirectory() );
final Path corrected = path.startsWith( home ) ? home.relativize( path ) : path;
return corrected.toString();
return configuration;
}
}
package org.jivesoftware.openfire.keystore;
import java.io.File;
import java.nio.file.Path;
import java.util.Arrays;
/**
* Certificate stores are configured using a defined set of properties. This is a wrapper class for all of them.
*
* Instances of this class are immutable and safe for use by multiple concurrent threads.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class CertificateStoreConfiguration
{
protected final String type;
protected final File file;
protected final char[] password;
/**
* Creates a new instance.
*
* @param type The store type (jks, jceks, pkcs12, etc). Cannot be null or an empty string.
* @param file The file-system based representation of the store (cannot be null).
* @param password the password used to check the integrity of the store, the password used to unlock the store, or null.
*/
public CertificateStoreConfiguration( String type, File file, char[] password )
{
if ( type == null || type.isEmpty() )
{
throw new IllegalArgumentException( "Argument 'type' cannot be null or an empty string." );
}
if ( file == null )
{
throw new IllegalArgumentException( "Argument 'file' cannot be null." );
}
this.type = type;
this.file = file;
this.password = password;
}
public String getType()
{
return type;
}
public File getFile()
{
return file;
}
public char[] getPassword()
{
return password;
}
@Override
public boolean equals( Object o )
{
if ( this == o )
{
return true;
}
if ( !( o instanceof CertificateStoreConfiguration ) )
{
return false;
}
CertificateStoreConfiguration that = (CertificateStoreConfiguration) o;
if ( !type.equals( that.type ) )
{
return false;
}
if ( !file.equals( that.file ) )
{
return false;
}
return Arrays.equals( password, that.password );
}
@Override
public int hashCode()
{
int result = type.hashCode();
result = 31 * result + file.hashCode();
result = 31 * result + ( password != null ? Arrays.hashCode( password ) : 0 );
return result;
}
@Override
public String toString()
{
return "CertificateStoreConfiguration{" +
"type='" + type + '\'' +
", file=" + file +
", password hashcode=" + password.hashCode() + // java.lang.Array.hashCode inherits from Object. As it is a reference, it should be safe to log and useful enough to compare against other passwords.
'}';
}
}
package org.jivesoftware.openfire.keystore;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.spi.ConnectionListener;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A manager of certificate stores.
*
*/
// TODO Code duplication should be reduced.
// TODO Allow changing the store type.
public class CertificateStoreManager
{
private final static Logger Log = LoggerFactory.getLogger( CertificateStoreManager.class );
private final ConcurrentMap<ConnectionType, CertificateStoreConfiguration> typeToTrustStore = new ConcurrentHashMap<>();
private final ConcurrentMap<ConnectionType, CertificateStoreConfiguration> typeToIdentityStore = new ConcurrentHashMap<>();
private final ConcurrentMap<CertificateStoreConfiguration, IdentityStore> identityStores = new ConcurrentHashMap<>();
private final ConcurrentMap<CertificateStoreConfiguration, TrustStore> trustStores = new ConcurrentHashMap<>();
private static CertificateStoreManager INSTANCE;
static synchronized CertificateStoreManager getInstance( ) {
if (INSTANCE == null) {
INSTANCE = new CertificateStoreManager();
}
return INSTANCE;
}
private CertificateStoreManager( )
{
for ( ConnectionType type : ConnectionType.values() )
{
try
{
final CertificateStoreConfiguration identityStoreConfiguration = getIdentityStoreConfiguration( type );
typeToIdentityStore.put( type, identityStoreConfiguration );
if ( !identityStores.containsKey( identityStoreConfiguration ) )
{
final IdentityStore store = new IdentityStore( identityStoreConfiguration, false );
identityStores.put( identityStoreConfiguration, store );
}
}
catch ( CertificateStoreConfigException | IOException e )
{
Log.warn( "Unable to instantiate identity store for type '" + type + "'", e );
}
try
{
final CertificateStoreConfiguration trustStoreConfiguration = getTrustStoreConfiguration( type );
typeToTrustStore.put( type, trustStoreConfiguration );
if ( !trustStores.containsKey( trustStoreConfiguration ) )
{
final TrustStore store = new TrustStore( trustStoreConfiguration, false );
trustStores.put( trustStoreConfiguration, store );
}
}
catch ( CertificateStoreConfigException | IOException e )
{
Log.warn( "Unable to instantiate trust store for type '" + type + "'", e );
}
}
}
public static IdentityStore getIdentityStore( ConnectionType type )
{
final CertificateStoreManager manager = getInstance();
final CertificateStoreConfiguration configuration = manager.typeToIdentityStore.get( type );
return manager.identityStores.get( configuration );
}
public static TrustStore getTrustStore( ConnectionType type )
{
final CertificateStoreManager manager = getInstance();
final CertificateStoreConfiguration configuration = manager.typeToTrustStore.get( type );
return manager.trustStores.get( configuration );
}
public static void replaceIdentityStore( ConnectionType type, CertificateStoreConfiguration configuration ) throws CertificateStoreConfigException
{
if ( type == null)
{
throw new IllegalArgumentException( "Argument 'type' cannot be null." );
}
if ( configuration == null)
{
throw new IllegalArgumentException( "Argument 'configuration' cannot be null." );
}
final CertificateStoreManager manager = getInstance();
final CertificateStoreConfiguration oldConfig = manager.typeToIdentityStore.get( type ); // can be null if persisted properties are invalid
if ( oldConfig == null || !oldConfig.equals( configuration ) )
{
// If the new store is not already being used by any other type, it'll need to be registered.
if ( !manager.identityStores.containsKey( configuration ) )
{
// This constructor can throw an exception. If it does, the state of the manager should not have already changed.
final IdentityStore store = new IdentityStore( configuration, true );
manager.identityStores.put( configuration, store );
}
manager.typeToIdentityStore.put( type, configuration );
// If the old store is not used by any other type, it can be shut down.
if ( oldConfig != null && !manager.typeToIdentityStore.containsValue( oldConfig ) )
{
manager.identityStores.remove( oldConfig );
}
// Update all connection listeners that were using the old configuration.
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
for ( ConnectionListener connectionListener : connectionManager.getListeners( type ) ) {
try {
connectionListener.setIdentityStoreConfiguration( configuration );
} catch ( RuntimeException e ) {
Log.warn( "An exception occurred while trying to update the identity store configuration for connection type '" + type + "'", e );
}
}
}
// Always store the new configuration in properties, to make sure that we override a potential fallback.
JiveGlobals.setProperty( type.getPrefix() + "keystore", configuration.getFile().getPath() ); // FIXME ensure that this is relative to Openfire home!
JiveGlobals.setProperty( type.getPrefix() + "keypass", new String( configuration.getPassword() ) );
}
public static void replaceTrustStore( ConnectionType type, CertificateStoreConfiguration configuration ) throws CertificateStoreConfigException
{
if ( type == null)
{
throw new IllegalArgumentException( "Argument 'type' cannot be null." );
}
if ( configuration == null)
{
throw new IllegalArgumentException( "Argument 'configuration' cannot be null." );
}
final CertificateStoreManager manager = getInstance();
final CertificateStoreConfiguration oldConfig = manager.typeToTrustStore.get( type ); // can be null if persisted properties are invalid
if ( oldConfig == null || !oldConfig.equals( configuration ) )
{
// If the new store is not already being used by any other type, it'll need to be registered.
if ( !manager.trustStores.containsKey( configuration ) )
{
// This constructor can throw an exception. If it does, the state of the manager should not have already changed.
final TrustStore store = new TrustStore( configuration, true );
manager.trustStores.put( configuration, store );
}
manager.typeToTrustStore.put( type, configuration );
// If the old store is not used by any other type, it can be shut down.
if ( oldConfig != null && !manager.typeToTrustStore.containsValue( oldConfig ) )
{
manager.trustStores.remove( oldConfig );
}
// Update all connection listeners that were using the old configuration.
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
for ( ConnectionListener connectionListener : connectionManager.getListeners( type ) ) {
try {
connectionListener.setTrustStoreConfiguration( configuration );
} catch ( RuntimeException e ) {
Log.warn( "An exception occurred while trying to update the trust store configuration for connection type '" + type + "'", e );
}
}
}
// Always store the new configuration in properties, to make sure that we override a potential fallback.
JiveGlobals.setProperty( type.getPrefix() + "truststore", configuration.getFile().getPath() ); // FIXME ensure that this is relative to Openfire home!
JiveGlobals.setProperty( type.getPrefix() + "trustpass", new String( configuration.getPassword() ) );
}
public static CertificateStoreConfiguration getIdentityStoreConfiguration( ConnectionType type ) throws IOException
{
// Getting individual properties might use fallbacks. It is assumed (but not asserted) that each property value
// is obtained from the same connectionType (which is either the argument to this method, or one of its
// fallbacks.
final String keyStoreType = getKeyStoreType( type );
final String password = getIdentityStorePassword( type );
final String location = getIdentityStoreLocation( type );
final File file = canonicalize( location );
return new CertificateStoreConfiguration( keyStoreType, file, password.toCharArray() );
}
public static CertificateStoreConfiguration getTrustStoreConfiguration( ConnectionType type ) throws IOException
{
// Getting individual properties might use fallbacks. It is assumed (but not asserted) that each property value
// is obtained from the same connectionType (which is either the argument to this method, or one of its
// fallbacks.
final String keyStoreType = getKeyStoreType( type );
final String password = getTrustStorePassword( type );
final String location = getTrustStoreLocation( type );
final File file = canonicalize( location );
return new CertificateStoreConfiguration( keyStoreType, file, password.toCharArray() );
}
/**
* The KeyStore type (jks, jceks, pkcs12, etc) for the identity and trust store for connections created by this
* listener.
*
* @return a store type (never null).
* @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyStore">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
*/
static String getKeyStoreType( ConnectionType type )
{
final String propertyName = type.getPrefix() + "storeType";
final String defaultValue = "jks";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getKeyStoreType( type.getFallback() ) ).trim();
}
}
static void setKeyStoreType( ConnectionType type, String keyStoreType )
{
// Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value).
JiveGlobals.setProperty( type.getPrefix() + "storeType", keyStoreType );
final String oldKeyStoreType = getKeyStoreType( type );
if ( oldKeyStoreType.equals( keyStoreType ) )
{
Log.debug( "Ignoring KeyStore type change request (to '{}'): listener already in this state.", keyStoreType );
return;
}
Log.debug( "Changing KeyStore type from '{}' to '{}'.", oldKeyStoreType, keyStoreType );
}
/**
* The password of the identity store for connection created by this listener.
*
* @return a password (never null).
*/
static String getIdentityStorePassword( ConnectionType type )
{
final String propertyName = type.getPrefix() + "keypass";
final String defaultValue = "changeit";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getIdentityStorePassword( type.getFallback() ) ).trim();
}
}
/**
* The password of the trust store for connections created by this listener.
*
* @return a password (never null).
*/
static String getTrustStorePassword( ConnectionType type )
{
final String propertyName = type.getPrefix() + "trustpass";
final String defaultValue = "changeit";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getTrustStorePassword( type.getFallback() ) ).trim();
}
}
/**
* The location (relative to OPENFIRE_HOME) of the identity store for connections created by this listener.
*
* @return a path (never null).
*/
static String getIdentityStoreLocation( ConnectionType type )
{
final String propertyName = type.getPrefix() + "keystore";
final String defaultValue = "resources" + File.separator + "security" + File.separator + "keystore";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getIdentityStoreLocation( type.getFallback() ) ).trim();
}
}
/**
* The location (relative to OPENFIRE_HOME) of the trust store for connections created by this listener.
*
* @return a path (never null).
*/
static String getTrustStoreLocation( ConnectionType type )
{
final String propertyName = type.getPrefix() + "truststore";
final String defaultValue = "resources" + File.separator + "security" + File.separator + "truststore";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getTrustStoreLocation( type.getFallback() ) ).trim();
}
}
/**
* Canonicalizes a path. When the provided path is a relative path, it is interpreted as to be relative to the home
* directory of Openfire.
*
* @param path A path (cannot be null)
* @return A canonical representation of the path.
*/
static File canonicalize( String path ) throws IOException
{
File file = new File( path );
if (!file.isAbsolute()) {
file = new File( JiveGlobals.getHomeDirectory() + File.separator + path );
}
return file;
}
}
......@@ -155,10 +155,10 @@ public class CertificateUtils
final Principal subject = certificate.getSubjectDN();
if ( byIssuer.put( issuer, certificate ) != null ) {
throw new CertificateException( "The provided input should not contain multiple certificates with identical issuerDN values." );
throw new CertificateException( "The provided input should not contain multiple certificates with identical issuerDN values. Offending value: " + issuer );
}
if ( bySubject.put( subject, certificate ) != null ) {
throw new CertificateException( "The provided input should not contain multiple certificates with identical subjectDN values." );
throw new CertificateException( "The provided input should not contain multiple certificates with identical subjectDN values. Offending value: " + subject );
}
}
......
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.DNSUtil;
import org.jivesoftware.util.CertificateManager;
......@@ -37,31 +36,30 @@ import java.util.List;
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class IdentityStoreConfig extends CertificateStoreConfig
public class IdentityStore extends CertificateStore
{
private static final Logger Log = LoggerFactory.getLogger( IdentityStoreConfig.class );
private static final Logger Log = LoggerFactory.getLogger( IdentityStore.class );
protected final KeyManagerFactory keyFactory;
// protected final KeyManagerFactory keyFactory;
public IdentityStoreConfig( String path, String password, String type, boolean createIfAbsent ) throws CertificateStoreConfigException
public IdentityStore( CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException
{
super( path, password, type, createIfAbsent );
try
{
keyFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
keyFactory.init( store, password.toCharArray() );
}
catch ( UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException ex )
{
throw new CertificateStoreConfigException( "Unable to load store of type '" + type + "' from location '" + path + "'", ex );
}
super( configuration, createIfAbsent );
// try
// {
// keyFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
// keyFactory.init( store, configuration.getPassword() );
// }
// catch ( UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException ex )
// {
// throw new CertificateStoreConfigException( "Unable to load store of type '" + configuration.getType() + "' from location '" + configuration.getFile() + "'", ex );
// }
}
public KeyManager[] getKeyManagers()
{
return keyFactory.getKeyManagers();
}
// public KeyManager[] getKeyManagers()
// {
// return keyFactory.getKeyManagers();
// }
/**
* Creates a Certificate Signing Request based on the private key and certificate identified by the provided alias.
......@@ -97,7 +95,7 @@ public class IdentityStoreConfig extends CertificateStoreConfig
throw new CertificateStoreConfigException( "Cannot generate CSR for alias '"+ alias +"': there is no corresponding certificate in the store, or it is not an X509 certificate." );
}
final Key key = store.getKey( alias, password );
final Key key = store.getKey( alias, configuration.getPassword() );
if ( key == null || (!(key instanceof PrivateKey) ) )
{
throw new CertificateStoreConfigException( "Cannot generate CSR for alias '"+ alias +"': there is no corresponding key in the store, or it is not a private key." );
......@@ -165,14 +163,14 @@ public class IdentityStoreConfig extends CertificateStoreConfig
}
// All appears to be in order. Update the existing entry in the store.
store.setKeyEntry( alias, store.getKey( alias, password ), password, ordered.toArray( new X509Certificate[ ordered.size() ] ) );
store.setKeyEntry( alias, store.getKey( alias, configuration.getPassword() ), configuration.getPassword(), ordered.toArray( new X509Certificate[ ordered.size() ] ) );
}
catch ( RuntimeException | IOException | CertificateException | UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e )
{
reload(); // reset state of the store.
throw new CertificateStoreConfigException( "Unable to install a singing reply into an identity store.", e );
}
// TODO notifiy listneers.
// TODO notifiy listeners.
}
protected boolean corresponds( String alias, List<X509Certificate> certificates ) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException
......@@ -181,7 +179,7 @@ public class IdentityStoreConfig extends CertificateStoreConfig
return false;
}
final Key key = store.getKey( alias, password );
final Key key = store.getKey( alias, configuration.getPassword() );
if ( key == null ) {
return false;
}
......@@ -266,7 +264,7 @@ public class IdentityStoreConfig extends CertificateStoreConfig
final PrivateKey privateKey = CertificateManager.parsePrivateKey( pemPrivateKey, passPhrase );
// All appears to be in order. Install in the store.
store.setKeyEntry( alias, privateKey, password, ordered.toArray( new X509Certificate[ ordered.size() ] ) );
store.setKeyEntry( alias, privateKey, configuration.getPassword(), ordered.toArray( new X509Certificate[ ordered.size() ] ) );
persist();
}
......@@ -298,8 +296,10 @@ public class IdentityStoreConfig extends CertificateStoreConfig
{
for ( String algorithm : algorithms )
{
Log.debug( "Verifying that a domain certificate ({} algorithm) is available in this store.", algorithm);
if ( !containsDomainCertificate( algorithm ) )
{
Log.debug( "Store does not contain a domain certificate ({} algorithm). A self-signed certificate will be generated.", algorithm);
addSelfSignedDomainCertificate( algorithm );
}
}
......@@ -371,19 +371,19 @@ public class IdentityStoreConfig extends CertificateStoreConfig
final String name = JiveGlobals.getProperty( "xmpp.domain" ).toLowerCase();
final String alias = name + "_" + algorithm.toLowerCase();
final String distinctName = "cn=" + name;
final String domain = "*." + name;
final int validityInDays = 60;
Log.info( "Generating a new private key and corresponding self-signed certificate for domain name '{}', using the {} algorithm (sign-algorithm: {} with a key size of {} bits). Certificate will be valid for {} days.", name, algorithm, signAlgorithm, keySize, validityInDays );
// Generate public and private keys
try
{
final KeyPair keyPair = generateKeyPair( algorithm.toUpperCase(), keySize );
// Create X509 certificate with keys and specified domain
final X509Certificate cert = CertificateManager.createX509V3Certificate( keyPair, validityInDays, distinctName, distinctName, domain, signAlgorithm );
final X509Certificate cert = CertificateManager.createX509V3Certificate( keyPair, validityInDays, distinctName, distinctName, name, signAlgorithm );
// Store new certificate and private key in the key store
store.setKeyEntry( alias, keyPair.getPrivate(), password, new X509Certificate[]{cert} );
store.setKeyEntry( alias, keyPair.getPrivate(), configuration.getPassword(), new X509Certificate[]{cert} );
// Persist the changes in the store to disk.
persist();
......
......@@ -16,7 +16,7 @@ import java.util.*;
*/
// TODO re-enable optional OCSP checking.
// TODO re-enable CRL checking.
public class OpenfireX509ExtendedTrustManager implements X509TrustManager
public class OpenfireX509TrustManager implements X509TrustManager
{
private static final Provider PROVIDER = new BouncyCastleProvider();
......@@ -43,7 +43,7 @@ public class OpenfireX509ExtendedTrustManager implements X509TrustManager
*/
protected final Set<X509Certificate> trustedIssuers;
public OpenfireX509ExtendedTrustManager( KeyStore trustStore, boolean acceptSelfSigned, boolean checkValidity ) throws NoSuchAlgorithmException, KeyStoreException
public OpenfireX509TrustManager( KeyStore trustStore, boolean acceptSelfSigned, boolean checkValidity ) throws NoSuchAlgorithmException, KeyStoreException
{
this.acceptSelfSigned = acceptSelfSigned;
this.checkValidity = checkValidity;
......
package org.jivesoftware.openfire.keystore;
import org.jivesoftware.util.JiveGlobals;
import java.io.File;
import java.io.IOException;
import java.util.Set;
/**
* Potential intended usages (for TLS connectivity).
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public enum Purpose
{
/**
* Socket-based server-to-server (XMPP federation) connectivity.
*/
SOCKET_S2S( "xmpp.socket.ssl.", null ),
/**
* Socket-based client connectivity.
*/
SOCKET_C2S( "xmpp.socket.ssl.client.", null ),
/**
* BOSH (HTTP-bind) based client connectivity.
*/
BOSH_C2S( "xmpp.bosh.ssl.client.", SOCKET_C2S),
/**
* Generic administrative services (eg: user providers).
*/
ADMIN( "admin.ssl.", SOCKET_S2S),
/**
* Openfire web-admin console.
*/
WEBADMIN( "admin.web.ssl.", ADMIN);
String prefix;
Purpose fallback;
Purpose( String prefix, Purpose fallback) {
this.prefix = prefix;
this.fallback = fallback;
}
public String getPrefix()
{
return prefix;
}
public Purpose getFallback()
{
return fallback;
}
public String getIdentityStoreType()
{
final String propertyName = prefix + "storeType";
final String defaultValue = "jks";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getIdentityStoreType() ).trim();
}
}
public String getTrustStoreType()
{
return getIdentityStoreType();
}
public String getIdentityStorePassword()
{
final String propertyName = prefix + "keypass";
final String defaultValue = "changeit";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getIdentityStorePassword() ).trim();
}
}
public String getTrustStorePassword()
{
final String propertyName = prefix + "trustpass";
final String defaultValue = "changeit";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getTrustStorePassword() ).trim();
}
}
public boolean acceptSelfSigned()
{
// TODO these are new properties! Deprecate (migrate?) all existing 'accept-selfsigned properties' (Eg: org.jivesoftware.openfire.session.ConnectionSettings.Server.TLS_ACCEPT_SELFSIGNED_CERTS )
final String propertyName = prefix + "certificate.accept-selfsigned";
final boolean defaultValue = false;
if ( fallback == null )
{
return JiveGlobals.getBooleanProperty( propertyName, defaultValue );
}
else
{
return JiveGlobals.getBooleanProperty( propertyName, fallback.acceptSelfSigned() );
}
}
public boolean verifyValidity()
{
// TODO these are new properties! Deprecate (migrate?) all existing 'verify / verify-validity properties' (Eg: org.jivesoftware.openfire.session.ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY_VALIDITY )
final String propertyName = prefix + "certificate.verify.validity";
final boolean defaultValue = true;
if ( fallback == null )
{
return JiveGlobals.getBooleanProperty( propertyName, defaultValue );
}
else
{
return JiveGlobals.getBooleanProperty( propertyName, fallback.acceptSelfSigned() );
}
}
public String getIdentityStoreLocation() throws IOException
{
return canonicalize( getIdentityStoreLocationNonCanonicalized() );
}
public String getIdentityStoreLocationNonCanonicalized()
{
final String propertyName = prefix + "keystore";
final String defaultValue = "resources" + File.separator + "security" + File.separator + "keystore";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getIdentityStoreLocationNonCanonicalized() ).trim();
}
}
public String getTrustStoreLocation() throws IOException
{
return canonicalize( getTrustStoreLocationNonCanonicalized() );
}
public String getTrustStoreLocationNonCanonicalized()
{
final String propertyName = prefix + "truststore";
final String defaultValue = "resources" + File.separator + "security" + File.separator + "truststore";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getTrustStoreLocationNonCanonicalized() ).trim();
}
}
public String getProtocolsEnabled()
{
final String propertyName = prefix + "protocols.enabled";
final String defaultValue = "TLSv1,TLSv1.1,TLSv1.2";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getProtocolsEnabled() ).trim();
}
}
public String getProtocolsDisabled()
{
final String propertyName = prefix + "protocols.disabled";
final String defaultValue = "SSLv1,SSLv2,SSLv2Hello,SSLv3";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getProtocolsDisabled() ).trim();
}
}
public String getCipherSuitesEnabled()
{
final String propertyName = prefix + "ciphersuites.enabled";
final String defaultValue = "";
final String result;
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue );
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getCipherSuitesEnabled() );
}
}
public String getCipherSuitesDisabled()
{
final String propertyName = prefix + "ciphersuites.disabled";
final String defaultValue = "";
if ( fallback == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, fallback.getCipherSuitesDisabled() ).trim();
}
}
public static String canonicalize( String path ) throws IOException
{
File file = new File( path );
if (!file.isAbsolute()) {
file = new File( JiveGlobals.getHomeDirectory() + File.separator + path );
}
return file.getCanonicalPath();
}
}
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jivesoftware.openfire.net.ClientTrustManager;
import org.jivesoftware.openfire.net.ServerTrustManager;
import org.jivesoftware.util.CertificateManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.cert.*;
import java.util.*;
......@@ -26,65 +20,13 @@ import java.util.*;
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class TrustStoreConfig extends CertificateStoreConfig
public class TrustStore extends CertificateStore
{
private static final Logger Log = LoggerFactory.getLogger( TrustStoreConfig.class );
private static final Logger Log = LoggerFactory.getLogger( TrustStore.class );
private transient TrustManager[] trustManagers;
private boolean acceptSelfSigned;
private boolean checkValidity;
public TrustStoreConfig( String path, String password, String type, boolean createIfAbsent, boolean acceptSelfSigned, boolean checkValidity ) throws CertificateStoreConfigException
{
super( path, password, type, createIfAbsent );
this.acceptSelfSigned = acceptSelfSigned;
this.checkValidity = checkValidity;
}
public synchronized TrustManager[] getTrustManagers() throws KeyStoreException, NoSuchAlgorithmException
{
if ( trustManagers == null ) {
trustManagers = new TrustManager[] { new OpenfireX509ExtendedTrustManager( this.getStore(), acceptSelfSigned, checkValidity ) };
}
return trustManagers;
}
public synchronized void reconfigure( boolean acceptSelfSigned, boolean checkValidity ) throws CertificateStoreConfigException
{
boolean needsReload = false;
if ( this.acceptSelfSigned != acceptSelfSigned )
{
this.acceptSelfSigned = acceptSelfSigned;
needsReload = true;
}
if ( this.checkValidity != checkValidity )
{
this.checkValidity = checkValidity;
needsReload = true;
}
if ( needsReload ) {
reload();
}
}
public boolean isAcceptSelfSigned()
{
return acceptSelfSigned;
}
public boolean isCheckValidity()
{
return checkValidity;
}
@Override
public synchronized void reload() throws CertificateStoreConfigException
public TrustStore( CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException
{
super.reload();
trustManagers = null;
super( configuration, createIfAbsent );
}
/**
......
......@@ -24,6 +24,9 @@ import org.dom4j.Element;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.VirtualConnection;
import org.jivesoftware.openfire.session.ConnectionMultiplexerSession;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.xmpp.packet.IQ;
import org.xmpp.packet.Packet;
......@@ -114,6 +117,14 @@ public class ClientSessionConnection extends VirtualConnection {
}
@Override
public ConnectionConfiguration getConfiguration()
{
// Here, a client-to-server configuration is mocked. It is likely not used, as actual connection handling takes
// place at the connection manager.
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
return connectionManager.getConfiguration( ConnectionType.SOCKET_C2S, false );
}
public byte[] getAddress() throws UnknownHostException {
if (hostAddress != null) {
return InetAddress.getByName(hostAddress).getAddress();
......
......@@ -35,7 +35,9 @@ import org.slf4j.LoggerFactory;
* Accepts new socket connections and uses a thread for each new connection.
*
* @author Gaston Dombiak
* @deprecated Old, pre NIO / MINA code. Should not be used as NIO offers better performance
*/
@Deprecated
class BlockingAcceptingMode extends SocketAcceptingMode {
private static final Logger Log = LoggerFactory.getLogger(BlockingAcceptingMode.class);
......
......@@ -45,8 +45,13 @@ import org.xmpp.packet.Presence;
*/
public class ClientStanzaHandler extends StanzaHandler {
public ClientStanzaHandler(PacketRouter router, Connection connection) {
super(router, connection);
}
@Deprecated
public ClientStanzaHandler(PacketRouter router, String serverName, Connection connection) {
super(router, serverName, connection);
super(router, connection);
}
/**
......@@ -110,12 +115,6 @@ public class ClientStanzaHandler extends StanzaHandler {
@Override
void startTLS() throws Exception {
Connection.ClientAuth policy;
try {
policy = Connection.ClientAuth.valueOf(JiveGlobals.getProperty(ConnectionSettings.Client.AUTH_PER_CLIENTCERT_POLICY, "disabled"));
} catch (IllegalArgumentException e) {
policy = Connection.ClientAuth.disabled;
}
connection.startTLS(false, true, policy);
connection.startTLS(false);
}
}
......@@ -22,6 +22,7 @@ package org.jivesoftware.openfire.net;
import org.dom4j.Element;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.PacketRouter;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.session.ComponentSession;
......@@ -51,8 +52,13 @@ public class ComponentStanzaHandler extends StanzaHandler {
private static final Logger Log = LoggerFactory.getLogger(ComponentStanzaHandler.class);
public ComponentStanzaHandler(PacketRouter router, Connection connection) {
super(router, connection);
}
@Deprecated
public ComponentStanzaHandler(PacketRouter router, String serverName, Connection connection) {
super(router, serverName, connection);
super(router, connection);
}
@Override
......@@ -98,7 +104,7 @@ public class ComponentStanzaHandler extends StanzaHandler {
try {
// Get the requested subdomain
String subdomain = extraDomain;
int index = extraDomain.indexOf(serverName);
int index = extraDomain.indexOf( XMPPServer.getInstance().getServerInfo().getXMPPDomain() );
if (index > -1) {
subdomain = extraDomain.substring(0, index -1);
}
......@@ -186,7 +192,7 @@ public class ComponentStanzaHandler extends StanzaHandler {
@Override
void startTLS() throws Exception {
connection.startTLS(false, false, Connection.ClientAuth.disabled);
connection.startTLS(false);
}
@Override
......
......@@ -46,6 +46,11 @@ public class MultiplexerStanzaHandler extends StanzaHandler {
*/
private MultiplexerPacketHandler packetHandler;
public MultiplexerStanzaHandler(PacketRouter router, Connection connection) {
super(router, connection);
}
@Deprecated
public MultiplexerStanzaHandler(PacketRouter router, String serverName, Connection connection) {
super(router, serverName, connection);
}
......@@ -151,6 +156,6 @@ public class MultiplexerStanzaHandler extends StanzaHandler {
@Override
void startTLS() throws Exception {
connection.startTLS(false, false, Connection.ClientAuth.disabled);
connection.startTLS(false);
}
}
......@@ -49,7 +49,7 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.auth.AuthToken;
import org.jivesoftware.openfire.auth.AuthorizationManager;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.ConnectionSettings;
......@@ -58,6 +58,7 @@ import org.jivesoftware.openfire.session.LocalClientSession;
import org.jivesoftware.openfire.session.LocalIncomingServerSession;
import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.StringUtils;
......@@ -88,7 +89,6 @@ public class SASLAuthentication {
// plus an extra regex alternative to catch a single equals sign ('=', see RFC 6120 6.4.2)
private static final Pattern BASE64_ENCODED = Pattern.compile("^(=|([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==))$");
private static final String SASL_NAMESPACE = "xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"";
private static Map<String, ElementType> typeMap = new TreeMap<>();
......@@ -195,8 +195,8 @@ public class SASLAuthentication {
// Server connections don't follow the same rules as clients
if (session.isSecure()) {
LocalIncomingServerSession svr = (LocalIncomingServerSession)session;
final KeyStore keyStore = SSLConfig.getIdentityStore( Purpose.SOCKET_S2S );
final KeyStore trustStore = SSLConfig.getTrustStore( Purpose.SOCKET_S2S );
final KeyStore keyStore = svr.getConnection().getConfiguration().getIdentityStore().getStore();
final KeyStore trustStore = svr.getConnection().getConfiguration().getTrustStore().getStore();
final X509Certificate trusted = CertificateManager.getEndEntityCertificate( svr.getConnection().getPeerCertificates(), keyStore, trustStore );
boolean haveTrustedCertificate = trusted != null;
......@@ -574,8 +574,9 @@ public class SASLAuthentication {
return Status.failed;
}
final KeyStore keyStore = SSLConfig.getIdentityStore( Purpose.SOCKET_C2S );
final KeyStore trustStore = SSLConfig.getTrustStore( Purpose.SOCKET_C2S );
final KeyStore keyStore = connection.getConfiguration().getIdentityStore().getStore();
final KeyStore trustStore = connection.getConfiguration().getTrustStore().getStore();
final X509Certificate trusted = CertificateManager.getEndEntityCertificate( connection.getPeerCertificates(), keyStore, trustStore );
if (trusted == null) {
......@@ -655,9 +656,9 @@ public class SASLAuthentication {
}
public static boolean verifyCertificates(Certificate[] chain, String hostname, boolean isS2S) {
final Purpose purpose = isS2S ? Purpose.SOCKET_S2S : Purpose.SOCKET_C2S;
final KeyStore keyStore = SSLConfig.getIdentityStore( purpose );
final KeyStore trustStore = SSLConfig.getTrustStore( purpose );
final ConnectionType connectionType = isS2S ? ConnectionType.SOCKET_S2S : ConnectionType.SOCKET_C2S;
final KeyStore keyStore = CertificateStoreManager.getIdentityStore( connectionType ).getStore();
final KeyStore trustStore = CertificateStoreManager.getTrustStore( connectionType ).getStore();
final X509Certificate trusted = CertificateManager.getEndEntityCertificate( chain, keyStore, trustStore );
if (trusted != null) {
return verifyCertificate(trusted, hostname);
......
/**
* $RCSfile$
* $Revision: 1217 $
* $Date: 2005-04-11 18:11:06 -0300 (Mon, 11 Apr 2005) $
* <p/>
* Copyright (C) 2005-2008 Jive Software. All rights reserved.
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.net;
import org.apache.mina.filter.ssl.SslFilter;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.keystore.*;
import org.jivesoftware.openfire.session.ConnectionSettings;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Utility functions for TLS / SSL.
*
* @author Iain Shigeoka
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class SSLConfig
{
private static final Logger Log = LoggerFactory.getLogger( SSLConfig.class );
private final ConcurrentMap<Purpose, String> identityStoreLocationByPurpose = new ConcurrentHashMap<>();
private final ConcurrentMap<String, IdentityStoreConfig> identityStoresByLocation = new ConcurrentHashMap<>();
private final ConcurrentMap<Purpose, String> trustStoreLocationByPurpose = new ConcurrentHashMap<>();
private final ConcurrentMap<String, TrustStoreConfig> trustStoresByLocation = new ConcurrentHashMap<>();
private static SSLConfig INSTANCE;
public static synchronized SSLConfig getInstance()
{
if (INSTANCE == null) {
try
{
INSTANCE = new SSLConfig();
}
catch ( CertificateStoreConfigException | NoSuchAlgorithmException | IOException ex )
{
Log.error( "Unable to instantiate SSL Configuration!", ex );
ex.printStackTrace();
}
}
return INSTANCE;
}
/**
* An utility method that is short-hand for getInstance().getIdentityStoreConfig(purpose).getStore();
* @param purpose The purpose for which to return a store.
* @return a store (never null).
*/
public static synchronized KeyStore getIdentityStore( Purpose purpose )
{
return getInstance().getIdentityStoreConfig( purpose ).getStore();
}
/**
* An utility method that is short-hand for getInstance().getTrustStoreConfig(purpose).getStore();
* @param purpose The purpose for which to return a store.
* @return a store (never null).
*/
public static synchronized KeyStore getTrustStore( Purpose purpose )
{
return getInstance().getTrustStoreConfig( purpose ).getStore();
}
// /**
// * Openfire allows a store to be re-used for multiple purposes. This method will find the store used for the
// * provided purpose, and based on that will return all <em>other</em> purposes for which the same store is used.
// *
// * @param purpose The purpose for which to find a store (cannot be null).
// * @return all <em>other</em> purposes for which the store is used (never null, but possibly an empty collection).
// * @throws IOException
// */
// public Set<Purpose> getOtherPurposesForSameStore( Type type, boolean isTrustStore ) throws IOException
// {
// if ( type == null )
// {
// throw new IllegalArgumentException( "Argument 'type' cannot be null." );
// }
//
// final Set<Purpose> results = new HashSet<>();
//
// for ( Map.Entry<Purpose, String> entry : locationByPurpose.entrySet() )
// {
// if ( entry.getValue().equalsIgnoreCase( location ) )
// {
// results.add( entry.getKey() );
// }
// }
//
// return results;
// }
// public static String getNonCanonicalizedLocation(Purpose purpose)
// {
// final String path;
// switch ( purpose )
// {
// // Identity store for socket-based IO (this is the default identity store)
// case SOCKETBASED_IDENTITYSTORE:
// path = JiveGlobals.getProperty( "xmpp.socket.ssl.keystore", "resources" + File.separator + "security" + File.separator + "keystore" );
// break;
//
// // Identity store for BOSH-based IO (falls back to the default identity store when not configured)
// case BOSHBASED_IDENTITYSTORE:
// path = JiveGlobals.getProperty( "xmpp.bosh.ssl.keystore", getNonCanonicalizedLocation( Purpose.SOCKETBASED_IDENTITYSTORE ) );
// break;
//
// // Identity store for administrative IO (falls back to the default identity store when not configured)
// case ADMINISTRATIVE_IDENTITYSTORE:
// path = JiveGlobals.getProperty( "admin.ssl.keystore", getNonCanonicalizedLocation( Purpose.SOCKETBASED_IDENTITYSTORE ) );
// break;
//
// // Identity store for admin panel (falls back to the administrative identity store when not configured)
// case WEBADMIN_IDENTITYSTORE:
// path = JiveGlobals.getProperty( "admin.web.ssl.keystore", getNonCanonicalizedLocation( Purpose.ADMINISTRATIVE_IDENTITYSTORE ) );
// break;
//
// // server-to-server trust store (This is the only / default S2S trust store. S2S over BOSH is unsupported by Openfire).
// case SOCKETBASED_S2S_TRUSTSTORE:
// path = JiveGlobals.getProperty( "xmpp.socket.ssl.truststore", "resources" + File.separator + "security" + File.separator + "truststore" );
// break;
//
// // client-to-server trust store for socket-based IO (This is the default C2S trust store).
// case SOCKETBASED_C2S_TRUSTSTORE:
// path = JiveGlobals.getProperty( "xmpp.socket.ssl.client.truststore", "resources" + File.separator + "security" + File.separator + "client.truststore" );
// break;
//
// // client-to-server trust store for BOSH-based IO (falls back to the default C2S trust store when not configured).
// case BOSHBASED_C2S_TRUSTSTORE:
// path = JiveGlobals.getProperty( "xmpp.bosh.ssl.client.truststore", getNonCanonicalizedLocation( Purpose.SOCKETBASED_C2S_TRUSTSTORE ) );
// break;
//
// // Administrative trust store (falls back to the default trust store when not configured)
// case ADMINISTRATIVE_TRUSTSTORE:
// path = JiveGlobals.getProperty( "admin.ssl.truststore", getNonCanonicalizedLocation( Purpose.SOCKETBASED_S2S_TRUSTSTORE ) );
// break;
//
// // Trust store for admin panel (falls back to the administrative trust store when not configured)
// case WEBADMIN_TRUSTSTORE:
// path = JiveGlobals.getProperty( "admin.web.ssl.truststore", getNonCanonicalizedLocation( Purpose.ADMINISTRATIVE_TRUSTSTORE ) );
// break;
//
// default:
// throw new IllegalStateException( "Unrecognized purpose: " + purpose );
// }
// return path;
// }
// public static String getLocation(Purpose purpose, boolean isTrustStore) throws IOException
// {
// final String location;
// if ( isTrustStore ) {
// location = purpose.getTrustStoreLocationNonCanonicalized();
// } else {
// location = purpose.getIdentityStoreLocationNonCanonicalized();
// }
// return canonicalize( location );
// }
// protected String getPassword( Type type, boolean isTrustStore )
// {
// switch ( purpose ) {
// case SOCKETBASED_IDENTITYSTORE:
// return JiveGlobals.getProperty( "xmpp.socket.ssl.keypass", "changeit" ).trim();
//
// case BOSHBASED_IDENTITYSTORE:
// return JiveGlobals.getProperty( "xmpp.bosh.ssl.keypass", getPassword( Purpose.SOCKETBASED_IDENTITYSTORE ) ).trim();
//
// case ADMINISTRATIVE_IDENTITYSTORE:
// return JiveGlobals.getProperty( "admin.ssl.keypass", getPassword( Purpose.SOCKETBASED_IDENTITYSTORE ) ).trim();
//
// case WEBADMIN_IDENTITYSTORE:
// return JiveGlobals.getProperty( "admin.web.ssl.keypass", getPassword( Purpose.ADMINISTRATIVE_IDENTITYSTORE ) ).trim();
//
// case SOCKETBASED_S2S_TRUSTSTORE:
// return JiveGlobals.getProperty( "xmpp.socket.ssl.trustpass", "changeit" ).trim();
//
// case SOCKETBASED_C2S_TRUSTSTORE:
// return JiveGlobals.getProperty( "xmpp.socket.ssl.client.trustpass", "changeit" ).trim();
//
// case BOSHBASED_C2S_TRUSTSTORE:
// return JiveGlobals.getProperty( "xmpp.bosh.ssl.client.trustpass", getPassword( Purpose.SOCKETBASED_C2S_TRUSTSTORE ) ).trim();
//
// case ADMINISTRATIVE_TRUSTSTORE:
// return JiveGlobals.getProperty( "admin.ssl.trustpass", getPassword( Purpose.SOCKETBASED_S2S_TRUSTSTORE ) ).trim();
//
// case WEBADMIN_TRUSTSTORE:
// return JiveGlobals.getProperty( "admin..web.ssl.trustpass", getPassword( Purpose.ADMINISTRATIVE_TRUSTSTORE ) ).trim();
//
// default:
// throw new IllegalStateException( "Unrecognized purpose: " + purpose );
// }
// }
// protected String getStoreType( Type type, boolean isTrustStore )
// {
// // FIXME All properties should be unique, instead of being re-used by different stores.
// switch ( purpose )
// {
// case SOCKETBASED_IDENTITYSTORE:
// return JiveGlobals.getProperty( "xmpp.socket.ssl.storeType", "jks" ).trim();
//
// case BOSHBASED_IDENTITYSTORE:
// return JiveGlobals.getProperty( "xmpp.bosh.ssl.storeType", getStoreType( Purpose.SOCKETBASED_IDENTITYSTORE ) ).trim();
//
// case ADMINISTRATIVE_IDENTITYSTORE:
// return JiveGlobals.getProperty( "admin.ssl.storeType", getStoreType( Purpose.SOCKETBASED_IDENTITYSTORE ) ).trim();
//
// case WEBADMIN_IDENTITYSTORE:
// return JiveGlobals.getProperty( "admin.web.ssl.storeType", getStoreType( Purpose.ADMINISTRATIVE_IDENTITYSTORE ) ).trim();
//
// case SOCKETBASED_S2S_TRUSTSTORE:
// return JiveGlobals.getProperty( "xmpp.socket.ssl.storeType", "jks" ).trim();
//
// case SOCKETBASED_C2S_TRUSTSTORE:
// return JiveGlobals.getProperty( "xmpp.socket.ssl.client.storeType", "jks" ).trim();
//
// case BOSHBASED_C2S_TRUSTSTORE:
// return JiveGlobals.getProperty( "xmpp.bosh.ssl.client.storeType", getStoreType( Purpose.SOCKETBASED_C2S_TRUSTSTORE ) ).trim();
//
// case ADMINISTRATIVE_TRUSTSTORE:
// return JiveGlobals.getProperty( "admin.ssl.storeType", getStoreType( Purpose.SOCKETBASED_S2S_TRUSTSTORE ) ).trim();
//
// case WEBADMIN_TRUSTSTORE:
// return JiveGlobals.getProperty( "admin.web.ssl.storeType", getStoreType( Purpose.ADMINISTRATIVE_TRUSTSTORE ) ).trim();
//
// default:
// throw new IllegalStateException( "Unrecognized purpose: " + purpose );
// }
// }
private SSLConfig() throws CertificateStoreConfigException, IOException, NoSuchAlgorithmException
{
for ( final Purpose purpose : Purpose.values() )
{
// Instantiate an identity store.
final String locationIdent = purpose.getIdentityStoreLocation();
identityStoreLocationByPurpose.put( purpose, locationIdent );
if ( !identityStoresByLocation.containsKey( locationIdent ) )
{
final IdentityStoreConfig storeConfig = new IdentityStoreConfig( purpose.getIdentityStoreLocation(), purpose.getIdentityStorePassword(), purpose.getIdentityStoreType(), false );
identityStoresByLocation.put( locationIdent, storeConfig );
}
// Instantiate trust store.
final String locationTrust = purpose.getTrustStoreLocation();
trustStoreLocationByPurpose.put( purpose, locationTrust );
if ( !trustStoresByLocation.containsKey( locationTrust ) )
{
final TrustStoreConfig storeConfig = new TrustStoreConfig( purpose.getTrustStoreLocation(), purpose.getTrustStorePassword(), purpose.getTrustStoreType(), false, purpose.acceptSelfSigned(), purpose.verifyValidity() );
trustStoresByLocation.put( locationTrust, storeConfig );
}
}
}
public IdentityStoreConfig getIdentityStoreConfig( Purpose purpose )
{
if ( purpose == null ) {
throw new IllegalArgumentException( "Argument 'purpose' cannot be null.");
}
final IdentityStoreConfig config = identityStoresByLocation.get( identityStoreLocationByPurpose.get( purpose ) );
if (config == null) {
throw new IllegalStateException( "Cannot retrieve identity store for " + purpose );
}
return config;
}
public TrustStoreConfig getTrustStoreConfig( Purpose purpose )
{
if ( purpose == null ) {
throw new IllegalArgumentException( "Argument 'purpose' cannot be null.");
}
final TrustStoreConfig config = trustStoresByLocation.get( trustStoreLocationByPurpose.get( purpose ) );
if (config == null) {
throw new IllegalStateException( "Cannot retrieve trust store for " + purpose );
}
return config;
}
// public void useStoreForPurpose( Purpose purpose, String location, String password, String storeType, boolean createIfAbsent ) throws IOException, CertificateStoreConfigException
// {
// final String newPath = canonicalize( location );
// final String oldPath = locationByPurpose.get( purpose );
// final CertificateStoreConfig oldConfig = storesByLocation.get( oldPath );
//
// // When this invocation does not change the current state, only trigger a reload.
// if (oldPath.equalsIgnoreCase( newPath ) && oldConfig.getPassword().equals( password ) && oldConfig.getType().equals( storeType ))
// {
// oldConfig.reload();
// return;
// }
//
// // Has a store already been loaded from this location?
// final boolean isKnown = storesByLocation.containsKey( newPath );
//
// final CertificateStoreConfig newConfig;
// if ( isKnown )
// {
// newConfig = storesByLocation.get( newPath );
// }
// else
// {
// if (purpose.isTrustStore()) {
// final boolean acceptSelfSigned = false; // TODO make configurable
// final boolean checkValidity = true; ; // TODO make configurable
// newConfig = new TrustStoreConfig( newPath, password, storeType, createIfAbsent, acceptSelfSigned, checkValidity );
// } else {
// newConfig = new IdentityStoreConfig( newPath, password, storeType, createIfAbsent );
// }
// }
//
// locationByPurpose.replace( purpose, newConfig.getCanonicalPath() );
// storesByLocation.replace( newConfig.getCanonicalPath(), newConfig );
//
// // Persist changes by modifying the Openfire properties.
// final Path locationToStore = Paths.get( newConfig.getPath() );
//
// switch ( purpose )
// {
// case SOCKETBASED_IDENTITYSTORE:
// JiveGlobals.setProperty( "xmpp.socket.ssl.keystore", locationToStore.toString() );
// JiveGlobals.setProperty( "xmpp.socket.ssl.keypass", password );
// JiveGlobals.setProperty( "xmpp.socket.ssl.storeType", storeType ); // FIXME also in use by SOCKETBASED_S2S_TRUSTSTORE
// break;
//
// case BOSHBASED_IDENTITYSTORE:
// JiveGlobals.setProperty( "xmpp.bosh.ssl.keystore", locationToStore.toString() );
// JiveGlobals.setProperty( "xmpp.bosh.ssl.keypass", password );
// JiveGlobals.setProperty( "xmpp.bosh.ssl.storeType", storeType );
// break;
//
// case ADMINISTRATIVE_IDENTITYSTORE:
// JiveGlobals.setProperty( "admin.ssl.keystore", locationToStore.toString() );
// JiveGlobals.setProperty( "admin.ssl.keypass", password );
// JiveGlobals.setProperty( "admin.ssl.storeType", storeType ); // FIXME also in use by ADMINISTRATIVE_TRUSTSTORE
// break;
//
// case WEBADMIN_IDENTITYSTORE:
// JiveGlobals.setProperty( "admin.web.ssl.keystore", locationToStore.toString() );
// JiveGlobals.setProperty( "admin.web.ssl.keypass", password );
// JiveGlobals.setProperty( "admin.web.ssl.storeType", storeType ); // FIXME also in use by WEBADMIN_TRUSTSTORE
// break;
//
// case SOCKETBASED_S2S_TRUSTSTORE:
// JiveGlobals.setProperty( "xmpp.socket.ssl.truststore", locationToStore.toString() );
// JiveGlobals.setProperty( "xmpp.socket.ssl.trustpass", password );
// JiveGlobals.setProperty( "xmpp.socket.ssl.storeType", storeType ); // FIXME also in use by SOCKETBASED_IDENTITYSTORE
// break;
//
// case SOCKETBASED_C2S_TRUSTSTORE:
// JiveGlobals.setProperty( "xmpp.socket.ssl.client.truststore", locationToStore.toString() );
// JiveGlobals.setProperty( "xmpp.socket.ssl.client.trustpass", password );
// JiveGlobals.setProperty( "xmpp.socket.ssl.client.storeType", storeType );
// break;
//
// case BOSHBASED_C2S_TRUSTSTORE:
// JiveGlobals.setProperty( "xmpp.bosh.ssl.client.truststore", locationToStore.toString() );
// JiveGlobals.setProperty( "xmpp.bosh.ssl.client.trustpass", password );
// JiveGlobals.setProperty( "xmpp.bosh.ssl.storeType", storeType );
// break;
//
// case ADMINISTRATIVE_TRUSTSTORE:
// JiveGlobals.setProperty( "admin.ssl.truststore", locationToStore.toString() );
// JiveGlobals.setProperty( "admin.ssl.trustpass", password );
// JiveGlobals.setProperty( "admin.ssl.storeType", storeType ); // FIXME also in use by ADMINISTRATIVE_IDENTITYSTORE
//
// case WEBADMIN_TRUSTSTORE:
// JiveGlobals.setProperty( "admin.web.ssl.truststore", locationToStore.toString() );
// JiveGlobals.setProperty( "admin.web.ssl.trustpass", password );
// JiveGlobals.setProperty( "admin.web.ssl.storeType", storeType ); // FIXME also in use by WEBADMIN_IDENTITYSTORE
//
// default:
// throw new IllegalStateException( "Unrecognized purpose: " + purpose );
// }
//
// // TODO notify listeners
// }
public static SSLContext getSSLContext( final Purpose purpose ) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException
{
final KeyManager[] keyManagers = getInstance().getIdentityStoreConfig( purpose ).getKeyManagers();
final TrustManager[] trustManagers = getInstance().getTrustStoreConfig( purpose ).getTrustManagers();
final SSLContext sslContext = SSLContext.getInstance( "TLSv1" );
sslContext.init( keyManagers, trustManagers, new SecureRandom() );
return sslContext;
}
/**
* Creates an SSL Engine that is configured to use server mode when handshaking.
*
* For Openfire, an engine is of this mode used for most purposes (as Openfire is a server by nature).
*
* @param purpose The type of connectivity for which to configure a new SSLEngine instance. Cannot be null.
* @param clientAuth indication of the desired level of client-sided authentication (mutual authentication). Cannot be null.
* @return An initialized SSLEngine instance (never null).
*/
public static SSLEngine getServerModeSSLEngine( Purpose purpose, Connection.ClientAuth clientAuth ) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException
{
final SSLEngine sslEngine = getSSLEngine( purpose );
sslEngine.setUseClientMode( false );
switch ( clientAuth )
{
case needed:
sslEngine.setNeedClientAuth( true );
break;
case wanted:
sslEngine.setWantClientAuth( true );
break;
case disabled:
sslEngine.setWantClientAuth( false );
break;
}
return sslEngine;
}
/**
* Creates an SSL Engine that is configured to use client mode when handshaking.
*
* For Openfire, an engine of this mode is typically used when the server tries to connect to another server.
*
* @param purpose The type of connectivity for which to configure a new SSLEngine instance. Cannot be null.
* @return An initialized SSLEngine instance (never null).
*/
public static SSLEngine getClientModeSSLEngine( Purpose purpose ) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException
{
final SSLEngine sslEngine = getSSLEngine( purpose );
sslEngine.setUseClientMode( true );
return sslEngine;
}
/**
* A utility method that implements the shared functionality of getClientModeSSLEngine and getServerModeSSLEngine.
*
* This method is used to initialize and pre-configure an instance of SSLEngine for a particular connection type.
* The returned value lacks further configuration. In most cases, developers will want to use getClientModeSSLEngine
* or getServerModeSSLEngine instead of this method.
*
* @param purpose The type of connectivity for which to pre-configure a new SSLEngine instance. Cannot be null.
* @return A pre-configured SSLEngine (never null).
*/
private static SSLEngine getSSLEngine( final Purpose purpose ) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException
{
final SSLContext sslContext = getSSLContext( purpose );
final SSLEngine sslEngine = sslContext.createSSLEngine();
// Configure protocol support.
if ( purpose.getProtocolsEnabled() != null && !purpose.getProtocolsEnabled().isEmpty() )
{
// When an explicit list of enabled protocols is defined, use only those.
sslEngine.setEnabledProtocols( purpose.getProtocolsEnabled().split( "," ) );
}
else if ( purpose.getProtocolsDisabled() != null && !purpose.getProtocolsDisabled().isEmpty() )
{
// Otherwise, use all supported protocols (except for the ones that are explicitly disabled).
final List<String> disabled = Arrays.asList( purpose.getProtocolsDisabled() );
final ArrayList<String> supported = new ArrayList<>( );
for ( final String candidate : sslEngine.getSupportedProtocols() ) {
if ( !disabled.contains( candidate ) ) {
supported.add( candidate );
}
}
sslEngine.setEnabledProtocols( supported.toArray( new String[ supported.size()] ) );
}
// Configure cipher suite support.
if ( purpose.getCipherSuitesEnabled() != null && !purpose.getCipherSuitesEnabled().isEmpty() )
{
// When an explicit list of enabled protocols is defined, use only those.
sslEngine.setEnabledCipherSuites( purpose.getCipherSuitesEnabled().split( "," ) );
}
else if ( purpose.getCipherSuitesDisabled() != null && !purpose.getCipherSuitesDisabled().isEmpty() )
{
// Otherwise, use all supported cipher suites (except for the ones that are explicitly disabled).
final List<String> disabled = Arrays.asList( purpose.getCipherSuitesDisabled() );
final ArrayList<String> supported = new ArrayList<>( );
for ( final String candidate : sslEngine.getSupportedCipherSuites() ) {
if ( !disabled.contains( candidate ) ) {
supported.add( candidate );
}
}
sslEngine.setEnabledCipherSuites( supported.toArray( new String[ supported.size() ] ) );
}
// TODO: Set policy for checking client certificates
return sslEngine;
}
public static SslContextFactory getSslContextFactory( final Purpose purpose )
{
final SslContextFactory sslContextFactory = new SslContextFactory();
final TrustStoreConfig trustStoreConfig = SSLConfig.getInstance().getTrustStoreConfig( purpose );
sslContextFactory.setTrustStore( trustStoreConfig.getStore() );
sslContextFactory.setTrustStorePassword( trustStoreConfig.getPassword() );
final IdentityStoreConfig identityStoreConfig = SSLConfig.getInstance().getIdentityStoreConfig( purpose );
sslContextFactory.setKeyStore( identityStoreConfig.getStore() );
sslContextFactory.setKeyStorePassword( identityStoreConfig.getPassword() );
// Configure protocol and cipher suite support.
if ( purpose.getProtocolsEnabled() != null ) {
sslContextFactory.setIncludeProtocols( purpose.getProtocolsEnabled().split( "," ) );
}
if ( purpose.getProtocolsDisabled() != null ) {
sslContextFactory.setExcludeProtocols( purpose.getProtocolsDisabled().split( "," ) );
}
if ( purpose.getCipherSuitesEnabled() != null) {
sslContextFactory.setIncludeCipherSuites( purpose.getCipherSuitesEnabled().split( "," ) );
}
if ( purpose.getCipherSuitesDisabled() != null ) {
sslContextFactory.setExcludeCipherSuites( purpose.getCipherSuitesDisabled().split( "," ) );
}
// TODO: Set policy for checking client certificates
// String certPol = JiveGlobals.getProperty(HTTP_BIND_AUTH_PER_CLIENTCERT_POLICY, "disabled");
// if(certPol.equals("needed")) {
// sslContextFactory.setNeedClientAuth(true);
// sslContextFactory.setWantClientAuth(true);
// } else if(certPol.equals("wanted")) {
// sslContextFactory.setNeedClientAuth(false);
// sslContextFactory.setWantClientAuth(true);
// } else {
// sslContextFactory.setNeedClientAuth(false);
// sslContextFactory.setWantClientAuth(false);
// }
return sslContextFactory;
}
/**
* Enables a specific set of protocols in an SSLEngine instance.
*
* To determine what protocols to enable, this implementation first looks at a type-specific property. This property
* can contain a comma-separated list of protocols that are to be enabled.
*
* When the property is not set (or when the property value is empty), the protocols that will be enabled are all
* protocols supported by the SSLEngine for which the protocol name starts with "TLS".
*
* Note that the selection strategy is a different strategy than with cipher suites in configureCipherSuites(),
* where the SSLEngine default gets filtered but not replaced.
*
* @param sslEngine The instance to configure. Cannot be null.
* @param purpose The type of configuration to use (used to select the relevent property). Cannot be null.
private static void configureProtocols( SSLEngine sslEngine, Purpose purpose )
{
// Find configuration, using fallback where applicable.
String enabledProtocols = JiveGlobals.getProperty( purpose.getPrefix() + "enabled.protocols" );
while (enabledProtocols == null && purpose.getFallback() != null)
{
purpose = purpose.getFallback();
enabledProtocols = JiveGlobals.getProperty( purpose.getPrefix() + "enabled.protocols" );
}
if (enabledProtocols != null )
{
final String[] protocols = enabledProtocols.split( "," );
if (protocols != null && protocols.length > 0)
{
sslEngine.setEnabledProtocols( protocols );
}
}
else
{
// When no user-based configuration is available, the SSL Engine will use a default. Instead of this default,
// we want all of the TLS protocols that are supported (which will exclude all of the older, insecure SSL
// protocols).
final ArrayList<String> defaultEnabled = new ArrayList<>();
for ( String supported : sslEngine.getSupportedProtocols() )
{
// Include only TLS protocols.
if ( supported.toUpperCase().startsWith( "TLS" ) )
{
defaultEnabled.add( supported );
}
}
sslEngine.setEnabledProtocols( defaultEnabled.toArray( new String[ defaultEnabled.size()] ) );
}
}
*/
/**
* Enables a specific set of cipher suites in an SSLEngine instance.
*
* To determine what suites to enable, this implementation first looks at a type-specific property. This property
* can contain a comma-separated list of suites that are to be enabled.
*
* When the property is not set (or when the property value is empty), the suites that will be enabled are all
* suites that are enabled by default in the SSLEngine, with the exclusion of a number of known weak suites.
*
* Note that the selection strategy is a different strategy than with protocols in configureProtocols(), where the
* entire SSLEngine default gets replaced.
*
* @param sslEngine The instance to configure. Cannot be null.
* @param purpose The type of configuration to use (used to select the relevent property). Cannot be null.
private static void configureCipherSuites( SSLEngine sslEngine, Purpose purpose )
{
String enabledCipherSuites = JiveGlobals.getProperty( purpose.getPrefix() + "enabled.ciphersuites" );
while (enabledCipherSuites == null && purpose.getFallback() != null)
{
purpose = purpose.getFallback();
enabledCipherSuites = JiveGlobals.getProperty( purpose.getPrefix() + "enabled.ciphersuites" );
}
if (enabledCipherSuites != null )
{
final String[] suites = enabledCipherSuites.split( "," );
if (suites != null && suites.length > 0)
{
sslEngine.setEnabledCipherSuites( suites );
}
}
else
{
// When no user-based configuration is available, the SSL Engine will use a default. From this default, we
// want to filter out a couple of insecure ciphers.
final ArrayList<String> defaultEnabled = new ArrayList<>();
for ( String supported : sslEngine.getSupportedCipherSuites() )
{
// A number of weaknesses in SHA-1 are known. It is no longer recommended to be used.
if ( supported.toUpperCase().endsWith( "SHA" ) )
{
continue;
}
// Due to problems with collision resistance MD5 is no longer safe to use.
if ( supported.toUpperCase().endsWith( "MD5" ) )
{
continue;
}
defaultEnabled.add( supported );
}
sslEngine.setEnabledCipherSuites( defaultEnabled.toArray( new String[ defaultEnabled.size()] ) );
}
}
*/
/**
* Creates an Apache MINA SslFilter that is configured to use server mode when handshaking.
*
* For Openfire, an engine is of this mode used for most purposes (as Openfire is a server by nature).
*
* Instead of an SSLContext or SSLEngine, Apache MINA uses an SslFilter instance. It is generally not needed to
* create both SSLContext/SSLEngine as well as SslFilter instances.
*
* @param purpose Communication type (used to select the relevant property). Cannot be null.
* @param clientAuth indication of the desired level of client-sided authentication (mutual authentication). Cannot be null.
* @return An initialized SslFilter instance (never null)
*/
public static SslFilter getServerModeSslFilter( Purpose purpose, Connection.ClientAuth clientAuth ) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException
{
final SSLContext sslContext = SSLConfig.getSSLContext( purpose );
final SSLEngine sslEngine = SSLConfig.getServerModeSSLEngine( purpose, clientAuth );
return getSslFilter( sslContext, sslEngine );
}
/**
* Creates an Apache MINA SslFilter that is configured to use client mode when handshaking.
*
* For Openfire, a filter of this mode is typically used when the server tries to connect to another server.
*
* Instead of an SSLContext or SSLEngine, Apache MINA uses an SslFilter instance. It is generally not needed to
* create both SSLContext/SSLEngine as well as SslFilter instances.
*
* @param purpose Communication type (used to select the relevant property). Cannot be null.
* @return An initialized SslFilter instance (never null)
*/
public static SslFilter getClientModeSslFilter( Purpose purpose ) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException
{
final SSLContext sslContext = SSLConfig.getSSLContext( purpose );
final SSLEngine sslEngine = SSLConfig.getClientModeSSLEngine( purpose );
return getSslFilter( sslContext, sslEngine );
}
/**
* A utility method that implements the shared functionality of getServerModeSslFilter and getClientModeSslFilter.
*
* This method is used to initialize and configure an instance of SslFilter for a particular pre-configured
* SSLContext and SSLEngine. In most cases, developers will want to use getServerModeSslFilter or
* getClientModeSslFilter instead of this method.
*
* @param sslContext a pre-configured SSL Context instance (cannot be null).
* @param sslEngine a pre-configured SSL Engine instance (cannot be null).
* @return A SslFilter instance (never null).
*/
private static SslFilter getSslFilter( SSLContext sslContext, SSLEngine sslEngine ) {
final SslFilter filter = new SslFilter( sslContext );
// Copy configuration from the SSL Engine into the filter.
filter.setUseClientMode( sslEngine.getUseClientMode() );
filter.setEnabledProtocols( sslEngine.getEnabledProtocols() );
filter.setEnabledCipherSuites( sslEngine.getEnabledCipherSuites() );
// Note that the setters for 'need' and 'want' influence each-other. Invoke only one of them!
if ( sslEngine.getNeedClientAuth() ) {
filter.setNeedClientAuth( true );
} else if ( sslEngine.getWantClientAuth() ) {
filter.setWantClientAuth( true );
}
return filter;
}
}
......@@ -55,8 +55,13 @@ public class ServerStanzaHandler extends StanzaHandler {
private static final Logger Log = LoggerFactory.getLogger(ServerStanzaHandler.class);
public ServerStanzaHandler(PacketRouter router, Connection connection) {
super(router, connection);
}
@Deprecated
public ServerStanzaHandler(PacketRouter router, String serverName, Connection connection) {
super(router, serverName, connection);
super(router, connection);
}
@Override
......@@ -110,7 +115,8 @@ public class ServerStanzaHandler extends StanzaHandler {
boolean needed = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true) &&
JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_CHAIN_VERIFY, true) &&
!JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ACCEPT_SELFSIGNED_CERTS, false);
connection.startTLS(false, false, needed ? Connection.ClientAuth.needed : Connection.ClientAuth.wanted);
//needed ? Connection.ClientAuth.needed : Connection.ClientAuth.wanted
connection.startTLS(false);
}
@Override
protected void processIQ(IQ packet) throws UnauthorizedException {
......
......@@ -35,7 +35,9 @@ import java.net.InetAddress;
* changes to the system property.
*
* @author Gaston Dombiak
* @deprecated Old, pre NIO / MINA code. Should not be used as NIO offers better performance
*/
@Deprecated
public class SocketAcceptThread extends Thread {
/**
......
......@@ -30,7 +30,9 @@ import java.net.ServerSocket;
* Abstract class for {@link BlockingAcceptingMode}.
*
* @author Gaston Dombiak
* @deprecated Old, pre NIO / MINA code. Should not be used as NIO offers better performance
*/
@Deprecated
abstract class SocketAcceptingMode {
/**
......
......@@ -39,14 +39,14 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLPeerUnverifiedException;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.ConnectionCloseListener;
import org.jivesoftware.openfire.PacketDeliverer;
import org.jivesoftware.openfire.PacketException;
import org.jivesoftware.openfire.*;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.session.IncomingServerSession;
import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.slf4j.Logger;
......@@ -62,6 +62,7 @@ import com.jcraft.jzlib.ZOutputStream;
* client and server.
*
* @author Iain Shigeoka
* @deprecated Old, pre NIO / MINA code. Should not be used as NIO offers better performance. Currently only in use for s2s.
*/
public class SocketConnection implements Connection {
......@@ -168,13 +169,13 @@ public class SocketConnection implements Connection {
@Deprecated
public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception {
final boolean isPeerClient = ( remoteServer == null );
startTLS( clientMode, isPeerClient, authentication );
startTLS( clientMode );
}
public void startTLS(boolean clientMode, boolean isPeerClient, ClientAuth authentication) throws IOException {
public void startTLS(boolean clientMode) throws IOException {
if (!secure) {
secure = true;
// Prepare for TLS
final ClientAuth clientAuth;
if (session instanceof IncomingServerSession)
......@@ -185,7 +186,7 @@ public class SocketConnection implements Connection {
{
clientAuth = ClientAuth.wanted;
}
tlsStreamHandler = new TLSStreamHandler(socket, clientMode, isPeerClient, clientAuth);
tlsStreamHandler = new TLSStreamHandler(socket, getConfiguration(), clientMode);
if (!clientMode) {
// Indicate the client that the server is ready to negotiate TLS
deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
......@@ -193,7 +194,7 @@ public class SocketConnection implements Connection {
// Start handshake
tlsStreamHandler.start();
// Use new wrapped writers
writer = new BufferedWriter(new OutputStreamWriter(tlsStreamHandler.getOutputStream(), StandardCharsets.UTF_8));
writer = new BufferedWriter(new OutputStreamWriter(tlsStreamHandler.getOutputStream(), CHARSET));
xmlSerializer = new XMLSocketWriter(writer, this);
}
}
......@@ -230,6 +231,15 @@ public class SocketConnection implements Connection {
}
@Override
public ConnectionConfiguration getConfiguration()
{
// This is an ugly hack to get backwards compatibility with the pre-MINA era. As this implementation is being
// removed (it is marked as deprecated - at the time of writing, it is only used for S2S). The ugly hack: assume
// S2S:
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
return connectionManager.getConfiguration( ConnectionType.SOCKET_S2S, false );
}
public boolean validate() {
if (isClosed()) {
return false;
......@@ -582,7 +592,7 @@ public class SocketConnection implements Connection {
private void release() {
writeStarted = -1;
instances.remove(this);
}
}
private void closeConnection() {
release();
......
......@@ -82,7 +82,7 @@ abstract class SocketReadingMode {
// Client requested to secure the connection using TLS. Negotiate TLS.
try {
// This code is only used for s2s
socketReader.connection.startTLS(false, false, Connection.ClientAuth.disabled);
socketReader.connection.startTLS(false);
}
catch (IOException e) {
Log.error("Error while negotiating TLS: " + socketReader.connection, e);
......
......@@ -78,10 +78,6 @@ public abstract class StanzaHandler {
* Session associated with the socket reader.
*/
protected LocalSession session;
/**
* Server name for which we are attending clients.
*/
protected String serverName;
/**
* Router used to route incoming packets to the correct channels.
......@@ -92,11 +88,15 @@ public abstract class StanzaHandler {
* Creates a dedicated reader for a socket.
*
* @param router the router for sending packets that were read.
* @param serverName the name of the server this socket is working for.
* @param connection the connection being read.
*/
public StanzaHandler(PacketRouter router, Connection connection) {
this.router = router;
this.connection = connection;
}
@Deprecated
public StanzaHandler(PacketRouter router, String serverName, Connection connection) {
this.serverName = serverName;
this.router = router;
this.connection = connection;
}
......@@ -576,7 +576,7 @@ public abstract class StanzaHandler {
sb.append("xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"");
sb.append(getNamespace());
sb.append("\" from=\"");
sb.append(serverName);
sb.append(XMPPServer.getInstance().getServerInfo().getXMPPDomain());
sb.append("\" id=\"");
sb.append(session.getStreamID());
sb.append("\" xml:lang=\"");
......@@ -617,6 +617,8 @@ public abstract class StanzaHandler {
eventType = xpp.next();
}
final String serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
// Check that the TO attribute of the stream header matches the server name or a valid
// subdomain. If the value of the 'to' attribute is not valid then return a host-unknown
// error and close the underlying connection.
......@@ -676,7 +678,7 @@ public abstract class StanzaHandler {
// have a TO attribute
return false;
}
if (serverName.equals(host)) {
if (XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals( host )) {
// requested host matched the server name
return false;
}
......
......@@ -22,6 +22,7 @@ package org.jivesoftware.openfire.net;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.session.ConnectionSettings;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.util.JiveGlobals;
import javax.net.ssl.SSLEngine;
......@@ -88,7 +89,7 @@ public class TLSStreamHandler {
private static ByteBuffer hsBB = ByteBuffer.allocate(0);
/**
* @deprecated Use the other constructor. There's no functional change.
* @deprecated Use the other constructor.
*/
@Deprecated
public TLSStreamHandler(Connection connection, Socket socket, boolean clientMode, String remoteServer,
......@@ -96,9 +97,8 @@ public class TLSStreamHandler {
{
this(
socket,
clientMode,
remoteServer==null,
needClientAuth?Connection.ClientAuth.needed: Connection.ClientAuth.wanted
connection.getConfiguration(),
clientMode
);
}
......@@ -110,12 +110,10 @@ public class TLSStreamHandler {
*
* @param socket the plain socket connection to secure
* @param clientMode boolean indicating if this entity is a client or a server.
* @param isPeerClient indicates if the remote party is a client (or server).
* @param clientAuth indicates if client should authenticate during the TLS negotiation.
* @throws java.io.IOException
*/
public TLSStreamHandler(Socket socket, boolean clientMode, boolean isPeerClient, Connection.ClientAuth clientAuth) throws IOException {
wrapper = new TLSWrapper(clientMode, clientAuth, isPeerClient);
public TLSStreamHandler(Socket socket, ConnectionConfiguration configuration, boolean clientMode) throws IOException {
wrapper = new TLSWrapper(configuration, clientMode);
tlsEngine = wrapper.getTlsEngine();
reader = new TLSStreamReader(wrapper, socket);
writer = new TLSStreamWriter(wrapper, socket);
......@@ -148,7 +146,7 @@ public class TLSStreamHandler {
initialHSStatus = HandshakeStatus.NEED_WRAP;
tlsEngine.beginHandshake();
}
else if (clientAuth == Connection.ClientAuth.needed) {
else if (configuration.getClientAuth() == Connection.ClientAuth.needed) {
// Only REQUIRE client authentication if we are fully verifying certificates
if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true) &&
JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_CHAIN_VERIFY, true) &&
......
......@@ -31,7 +31,8 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -59,31 +60,29 @@ public class TLSWrapper {
private int appBuffSize;
/**
* @deprecated Use the other constructor. There's no functional change.
* @deprecated Use the other constructor.
*/
@Deprecated
public TLSWrapper(Connection connection, boolean clientMode, boolean needClientAuth, String remoteServer)
{
this(
clientMode,
needClientAuth?Connection.ClientAuth.needed:Connection.ClientAuth.wanted,
remoteServer == null
connection.getConfiguration(),
clientMode
);
}
public TLSWrapper(boolean clientMode, Connection.ClientAuth clientAuth, boolean isPeerClient) {
public TLSWrapper(ConnectionConfiguration configuration, boolean clientMode ) {
try
{
final SSLEngine sslEngine;
if ( clientMode )
{
sslEngine = SSLConfig.getClientModeSSLEngine( Purpose.SOCKET_S2S );
sslEngine = configuration.createClientModeSSLEngine();
}
else
{
final Purpose purpose = isPeerClient ? Purpose.SOCKET_C2S : Purpose.SOCKET_S2S;
sslEngine = SSLConfig.getServerModeSSLEngine( purpose, clientAuth );
sslEngine = configuration.createServerModeSSLEngine();
}
final SSLSession sslSession = sslEngine.getSession();
......@@ -91,7 +90,7 @@ public class TLSWrapper {
netBuffSize = sslSession.getPacketBufferSize();
appBuffSize = sslSession.getApplicationBufferSize();
}
catch ( NoSuchAlgorithmException | KeyManagementException | KeyStoreException ex )
catch ( NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException ex )
{
Log.error("TLSHandler startup problem. SSLContext initialisation failed.", ex );
}
......
......@@ -30,6 +30,7 @@ import org.jivesoftware.openfire.ConnectionCloseListener;
import org.jivesoftware.openfire.PacketDeliverer;
import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.util.LocaleUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -154,12 +155,15 @@ public abstract class VirtualConnection implements Connection {
return null;
}
@Deprecated
@Override
public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception {
//Ignore
}
@Override
public void startTLS(boolean clientMode) throws Exception {
//Ignore
}
public void addCompression() {
//Ignore
......
......@@ -27,6 +27,7 @@ import org.jivesoftware.openfire.handler.IQPingHandler;
import org.jivesoftware.openfire.net.ClientStanzaHandler;
import org.jivesoftware.openfire.net.StanzaHandler;
import org.jivesoftware.openfire.session.ConnectionSettings;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -44,18 +45,18 @@ public class ClientConnectionHandler extends ConnectionHandler {
private static final Logger Log = LoggerFactory.getLogger(ClientConnectionHandler.class);
public ClientConnectionHandler(String serverName) {
super(serverName);
public ClientConnectionHandler(ConnectionConfiguration configuration) {
super(configuration);
}
@Override
NIOConnection createNIOConnection(IoSession session) {
return new NIOConnection(session, new OfflinePacketDeliverer());
return new NIOConnection(session, new OfflinePacketDeliverer(), configuration );
}
@Override
StanzaHandler createStanzaHandler(NIOConnection connection) {
return new ClientStanzaHandler(XMPPServer.getInstance().getPacketRouter(), serverName, connection);
return new ClientStanzaHandler(XMPPServer.getInstance().getPacketRouter(), connection);
}
@Override
......@@ -97,7 +98,7 @@ public class ClientConnectionHandler extends ConnectionHandler {
final IQ pingRequest = new IQ(Type.get);
pingRequest.setChildElement("ping",
IQPingHandler.NAMESPACE);
pingRequest.setFrom(serverName);
pingRequest.setFrom( XMPPServer.getInstance().getServerInfo().getXMPPDomain() );
pingRequest.setTo(entity);
// Get the connection for this session
......
......@@ -23,6 +23,7 @@ import org.apache.mina.core.session.IoSession;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.ComponentStanzaHandler;
import org.jivesoftware.openfire.net.StanzaHandler;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.util.JiveGlobals;
/**
......@@ -32,18 +33,19 @@ import org.jivesoftware.util.JiveGlobals;
* @author Gaston Dombiak
*/
public class ComponentConnectionHandler extends ConnectionHandler {
public ComponentConnectionHandler(String serverName) {
super(serverName);
public ComponentConnectionHandler(ConnectionConfiguration configuration) {
super(configuration);
}
@Override
NIOConnection createNIOConnection(IoSession session) {
return new NIOConnection(session, XMPPServer.getInstance().getPacketDeliverer());
return new NIOConnection(session, XMPPServer.getInstance().getPacketDeliverer(), configuration );
}
@Override
StanzaHandler createStanzaHandler(NIOConnection connection) {
return new ComponentStanzaHandler(XMPPServer.getInstance().getPacketRouter(), serverName, connection);
return new ComponentStanzaHandler(XMPPServer.getInstance().getPacketRouter(), connection);
}
@Override
......
......@@ -24,9 +24,11 @@ import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.net.ServerTrafficCounter;
import org.jivesoftware.openfire.net.StanzaHandler;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParserException;
......@@ -49,7 +51,6 @@ public abstract class ConnectionHandler extends IoHandlerAdapter {
protected static final String HANDLER = "HANDLER";
protected static final String CONNECTION = "CONNECTION";
protected String serverName;
private static final ThreadLocal<XMPPPacketReader> PARSER_CACHE = new ThreadLocal<XMPPPacketReader>()
{
@Override
......@@ -75,8 +76,13 @@ public abstract class ConnectionHandler extends IoHandlerAdapter {
}
}
protected ConnectionHandler(String serverName) {
this.serverName = serverName;
/**
* The configuration for new connections.
*/
protected final ConnectionConfiguration configuration;
protected ConnectionHandler( ConnectionConfiguration configuration ) {
this.configuration = configuration;
}
@Override
......
......@@ -20,6 +20,7 @@
package org.jivesoftware.openfire.nio;
import org.apache.mina.core.session.IoSession;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.multiplex.MultiplexerPacketDeliverer;
......@@ -34,18 +35,18 @@ import org.jivesoftware.openfire.net.StanzaHandler;
*/
public class MultiplexerConnectionHandler extends ConnectionHandler {
public MultiplexerConnectionHandler(String serverName) {
super(serverName);
public MultiplexerConnectionHandler(ConnectionConfiguration configuration) {
super(configuration);
}
@Override
NIOConnection createNIOConnection(IoSession session) {
return new NIOConnection(session, new MultiplexerPacketDeliverer());
return new NIOConnection(session, new MultiplexerPacketDeliverer(), configuration );
}
@Override
StanzaHandler createStanzaHandler(NIOConnection connection) {
return new MultiplexerStanzaHandler(XMPPServer.getInstance().getPacketRouter(), serverName, connection);
return new MultiplexerStanzaHandler(XMPPServer.getInstance().getPacketRouter(), connection);
}
@Override
......
......@@ -50,6 +50,8 @@ import org.jivesoftware.openfire.keystore.*;
import org.jivesoftware.openfire.net.*;
import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -64,6 +66,7 @@ import org.xmpp.packet.Packet;
public class NIOConnection implements Connection {
private static final Logger Log = LoggerFactory.getLogger(NIOConnection.class);
private ConnectionConfiguration configuration;
public enum State { RUNNING, CLOSING, CLOSED }
......@@ -87,7 +90,6 @@ public class NIOConnection implements Connection {
private int minorVersion = 0;
private String language = null;
// TODO Uso el #checkHealth????
/**
* TLS policy currently in use for this connection.
*/
......@@ -119,9 +121,10 @@ public class NIOConnection implements Connection {
*/
private final ReentrantLock ioSessionLock = new ReentrantLock(true);
public NIOConnection(IoSession session, PacketDeliverer packetDeliverer) {
public NIOConnection( IoSession session, PacketDeliverer packetDeliverer, ConnectionConfiguration configuration ) {
this.ioSession = session;
this.backupDeliverer = packetDeliverer;
this.configuration = configuration;
state = State.RUNNING;
}
......@@ -366,20 +369,19 @@ public class NIOConnection implements Connection {
@Deprecated
@Override
public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception {
final boolean isPeerClient = ( remoteServer == null );
startTLS( clientMode, isPeerClient, authentication );
startTLS( clientMode );
}
public void startTLS(boolean clientMode, boolean isPeerClient, ClientAuth authentication) throws Exception {
public void startTLS(boolean clientMode) throws Exception {
final SslFilter filter;
if ( clientMode ) {
filter = SSLConfig.getClientModeSslFilter( Purpose.SOCKET_S2S );
if ( clientMode )
{
filter = configuration.createClientModeSslFilter();
}
else
{
final Purpose purpose = isPeerClient ? Purpose.SOCKET_C2S : Purpose.SOCKET_S2S;
filter = SSLConfig.getServerModeSslFilter( purpose, authentication );
filter = configuration.createServerModeSslFilter();
}
ioSession.getFilterChain().addBefore(EXECUTOR_FILTER_NAME, TLS_FILTER_NAME, filter);
......@@ -408,6 +410,11 @@ public class NIOConnection implements Connection {
}
@Override
public ConnectionConfiguration getConfiguration()
{
return configuration;
}
public boolean isFlashClient() {
return flashClient;
}
......
package org.jivesoftware.openfire.nio;
import org.apache.mina.core.session.IoSession;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.ServerStanzaHandler;
import org.jivesoftware.openfire.net.StanzaHandler;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.util.JiveGlobals;
/**
* ConnectionHandler that knows which subclass of {@link StanzaHandler} should be created and how to build and configure
* a {@link NIOConnection}.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class ServerConnectionHandler extends ConnectionHandler
{
public ServerConnectionHandler( ConnectionConfiguration configuration )
{
super( configuration );
}
@Override
NIOConnection createNIOConnection( IoSession session )
{
return new NIOConnection( session, XMPPServer.getInstance().getPacketDeliverer(), configuration );
}
@Override
StanzaHandler createStanzaHandler( NIOConnection connection )
{
return new ServerStanzaHandler( XMPPServer.getInstance().getPacketRouter(), connection );
}
@Override
int getMaxIdleTime()
{
return JiveGlobals.getIntProperty( "xmpp.server.idle", 6 * 60 * 1000 ) / 1000;
}
}
......@@ -27,9 +27,6 @@ public final class ConnectionSettings {
public static final String MAX_THREADS_SSL = "xmpp.client_ssl.processing.threads";
public static final String MAX_READ_BUFFER_SSL = "xmpp.client_ssl.maxReadBufferSize";
public static final String TLS_ALGORITHM = "xmpp.socket.ssl.algorithm";
private Client() {
}
}
public static final class Server {
......@@ -43,7 +40,14 @@ public final class ConnectionSettings {
public static final String QUEUE_SIZE = "xmpp.server.outgoing.queue";
public static final String DIALBACK_ENABLED = "xmpp.server.dialback.enabled";
public static final String TLS_POLICY = "xmpp.server.tls.policy";
/**
* @deprecated Replaced by #TLS_POLICY
*/
@Deprecated
public static final String TLS_ENABLED = "xmpp.server.tls.enabled";
public static final String TLS_ACCEPT_SELFSIGNED_CERTS = "xmpp.server.certificate.accept-selfsigned";
public static final String TLS_CERTIFICATE_VERIFY = "xmpp.server.certificate.verify";
public static final String TLS_CERTIFICATE_VERIFY_VALIDITY = "xmpp.server.certificate.verify.validity";
......@@ -53,9 +57,7 @@ public final class ConnectionSettings {
public static final String COMPRESSION_SETTINGS = "xmpp.server.compression.policy";
public static final String PERMISSION_SETTINGS = "xmpp.server.permission";
private Server() {
}
public static final String AUTH_PER_CLIENTCERT_POLICY = "xmpp.server.cert.policy";
}
public static final class Multiplex {
......@@ -65,13 +67,21 @@ public final class ConnectionSettings {
public static final String TLS_POLICY = "xmpp.multiplex.tls.policy";
public static final String COMPRESSION_SETTINGS = "xmpp.multiplex.compression.policy";
private Multiplex() {
}
public static final String OLD_SSLPORT = "xmpp.multiplex.ssl.port";
public static final String ENABLE_OLD_SSLPORT = "xmpp.multiplex.ssl.active";
public static final String MAX_THREADS ="xmpp.multiplex.processing.threads";
public static final String MAX_THREADS_SSL = "xmpp.multiplex.ssl.processing.threads";
public static final String AUTH_PER_CLIENTCERT_POLICY = "xmpp.multiplex.cert.policy" ;
}
public static final class Component {
public static final String SOCKET_ACTIVE = "xmpp.component.socket.active";
public static final String PORT = "xmpp.component.socket.port";
public static final String OLD_SSLPORT = "xmpp.component.ssl.port";
public static final String ENABLE_OLD_SSLPORT = "xmpp.component.ssl.active";
public static final String MAX_THREADS = "xmpp.component.processing.threads";
public static final String MAX_THREADS_SSL = "xmpp.component.ssl.processing.threads";
public static final String AUTH_PER_CLIENTCERT_POLICY = "xmpp.component.cert.policy";
public static final String TLS_POLICY = "xmpp.component.tls.policy";
}
}
......@@ -34,9 +34,7 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthToken;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.privacy.PrivacyList;
import org.jivesoftware.openfire.privacy.PrivacyListManager;
......@@ -256,12 +254,12 @@ public class LocalClientSession extends LocalSession implements ClientSession {
if (!connection.isSecure()) {
boolean hasCertificates = false;
try {
hasCertificates = SSLConfig.getIdentityStore( Purpose.SOCKET_C2S ).size() > 0;
hasCertificates = connection.getConfiguration().getIdentityStore().getAllCertificates().size() > 0;
}
catch (Exception e) {
Log.error(e.getMessage(), e);
}
Connection.TLSPolicy tlsPolicy = getTLSPolicy();
Connection.TLSPolicy tlsPolicy = connection.getConfiguration().getTlsPolicy();
if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) {
Log.error("Client session rejected. TLS is required but no certificates " +
"were created.");
......@@ -412,46 +410,13 @@ public class LocalClientSession extends LocalSession implements ClientSession {
}
}
/**
* Returns whether TLS is mandatory, optional or is disabled for clients. When TLS is
* mandatory clients are required to secure their connections or otherwise their connections
* will be closed. On the other hand, when TLS is disabled clients are not allowed to secure
* their connections using TLS. Their connections will be closed if they try to secure the
* connection. in this last case.
*
* @return whether TLS is mandatory, optional or is disabled.
*/
public static SocketConnection.TLSPolicy getTLSPolicy() {
// Set the TLS policy stored as a system property
String policyName = JiveGlobals.getProperty(ConnectionSettings.Client.TLS_POLICY, Connection.TLSPolicy.optional.toString());
SocketConnection.TLSPolicy tlsPolicy;
try {
tlsPolicy = Connection.TLSPolicy.valueOf(policyName);
} catch (IllegalArgumentException e) {
Log.error("Error parsing xmpp.client.tls.policy: " + policyName, e);
tlsPolicy = Connection.TLSPolicy.optional;
}
return tlsPolicy;
}
/**
* Sets whether TLS is mandatory, optional or is disabled for clients. When TLS is
* mandatory clients are required to secure their connections or otherwise their connections
* will be closed. On the other hand, when TLS is disabled clients are not allowed to secure
* their connections using TLS. Their connections will be closed if they try to secure the
* connection. in this last case.
*
* @param policy whether TLS is mandatory, optional or is disabled.
*/
public static void setTLSPolicy(SocketConnection.TLSPolicy policy) {
JiveGlobals.setProperty(ConnectionSettings.Client.TLS_POLICY, policy.toString());
}
/**
* Returns whether compression is optional or is disabled for clients.
*
* @return whether compression is optional or is disabled.
*/
// TODO Move this to ConnectionConfiguration.
public static SocketConnection.CompressionPolicy getCompressionPolicy() {
// Set the Compression policy stored as a system property
String policyName = JiveGlobals
......@@ -471,6 +436,7 @@ public class LocalClientSession extends LocalSession implements ClientSession {
*
* @param policy whether compression is optional or is disabled.
*/
// TODO Move this to ConnectionConfiguration.
public static void setCompressionPolicy(SocketConnection.CompressionPolicy policy) {
JiveGlobals.setProperty(ConnectionSettings.Client.COMPRESSION_SETTINGS, policy.toString());
}
......
......@@ -49,6 +49,7 @@ import org.xmpp.packet.StreamError;
*
* @author Gaston Dombiak
*/
// TODO implement TLS and observe org.jivesoftware.openfire.session.ConnectionSettings.Component.TLS_POLICY
public class LocalComponentSession extends LocalSession implements ComponentSession {
private static final Logger Log = LoggerFactory.getLogger(LocalComponentSession.class);
......
......@@ -29,6 +29,9 @@ import org.jivesoftware.openfire.multiplex.ConnectionMultiplexerManager;
import org.jivesoftware.openfire.multiplex.MultiplexerPacketDeliverer;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.spi.ConnectionConfiguration;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -261,13 +264,17 @@ public class LocalConnectionMultiplexerSession extends LocalSession implements C
* </ul
*/
private void sendClientOptions() {
final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
final ConnectionConfiguration configuration = connectionManager.getConfiguration( ConnectionType.SOCKET_C2S, false );
IQ options = new IQ(IQ.Type.set);
Element child = options.setChildElement("configuration",
"http://jabber.org/protocol/connectionmanager");
// Add info about TLS
if (LocalClientSession.getTLSPolicy() != Connection.TLSPolicy.disabled) {
if (configuration.getTlsPolicy() != Connection.TLSPolicy.disabled) {
Element tls = child.addElement("starttls", "urn:ietf:params:xml:ns:xmpp-tls");
if (LocalClientSession.getTLSPolicy() == Connection.TLSPolicy.required) {
if (configuration.getTlsPolicy() == Connection.TLSPolicy.required) {
tls.addElement("required");
}
......
......@@ -35,11 +35,11 @@ import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.StreamID;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.server.ServerDialback;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
......@@ -153,7 +153,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
Connection.TLSPolicy.required;
boolean hasCertificates = false;
try {
hasCertificates = SSLConfig.getIdentityStore( Purpose.SOCKET_S2S ).size() > 0;
hasCertificates = CertificateStoreManager.getIdentityStore( ConnectionType.SOCKET_S2S ).getStore().size() > 0;
}
catch (Exception e) {
Log.error(e.getMessage(), e);
......@@ -374,7 +374,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
usingSelfSigned = true;
} else {
try {
final KeyStore keyStore = SSLConfig.getIdentityStore( Purpose.SOCKET_S2S );
final KeyStore keyStore = CertificateStoreManager.getIdentityStore( ConnectionType.SOCKET_S2S ).getStore();
usingSelfSigned = CertificateManager.isSelfSignedCertificate(keyStore, (X509Certificate) chain[0]);
} catch (KeyStoreException ex) {
Log.warn("Exception occurred while trying to determine whether local certificate is self-signed. Proceeding as if it is.", ex);
......
......@@ -379,10 +379,10 @@ public class LocalOutgoingServerSession extends LocalServerSession implements Ou
if (proceed != null && proceed.getName().equals("proceed")) {
log.debug("Negotiating TLS...");
try {
boolean needed = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true) &&
JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_CHAIN_VERIFY, true) &&
!JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ACCEPT_SELFSIGNED_CERTS, false);
connection.startTLS(true, false, needed ? Connection.ClientAuth.needed : Connection.ClientAuth.wanted);
// boolean needed = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true) &&
// JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_CHAIN_VERIFY, true) &&
// !JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ACCEPT_SELFSIGNED_CERTS, false);
connection.startTLS(true);
} catch(Exception e) {
log.debug("Got an exception whilst negotiating TLS: " + e.getMessage());
throw e;
......
package org.jivesoftware.openfire.spi;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.service.IoService;
import org.apache.mina.core.service.IoServiceListener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.ssl.SslFilter;
import org.apache.mina.integration.jmx.IoServiceMBean;
import org.apache.mina.integration.jmx.IoSessionMBean;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.JMXManager;
import org.jivesoftware.openfire.net.StalledSessionsFilter;
import org.jivesoftware.openfire.nio.*;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This class is responsible for accepting new (socket) connections.
*
* The configuration (but not the state) of an instance of this class is immutable. When configuration changes are
* needed, an instance needs to be replaced by a new instance.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
class ConnectionAcceptor
{
private final Logger Log;
private final String name;
private final ConnectionHandler connectionHandler;
// Configuration
private final ConnectionConfiguration configuration;
private NioSocketAcceptor socketAcceptor;
/**
* Instantiates, but not starts, a new instance.
*
*/
public ConnectionAcceptor( ConnectionConfiguration configuration )
{
if (configuration == null) {
throw new IllegalArgumentException( "Argument 'configuation' cannot be null" );
}
this.configuration = configuration;
this.name = configuration.getType().toString().toLowerCase() + ( configuration.getTlsPolicy() == Connection.TLSPolicy.legacyMode ? "_ssl" : "" );
Log = LoggerFactory.getLogger( ConnectionAcceptor.class.getName() + "[" + name + "]" );
// FIXME implement missing switch branches.
switch ( configuration.getType() )
{
case SOCKET_S2S:
connectionHandler = new ServerConnectionHandler( configuration );
break;
case SOCKET_C2S:
connectionHandler = new ClientConnectionHandler( configuration );
break;
// case BOSH_C2S:
// break;
// case ADMIN:
// break;
// case WEBADMIN:
// break;
case COMPONENT:
connectionHandler = new ComponentConnectionHandler( configuration );
break;
case CONNECTION_MANAGER:
connectionHandler = new MultiplexerConnectionHandler( configuration );
break;
default:
throw new IllegalStateException( "Cannot determine 'connection handler' for connection type : " + configuration.getType() );
}
}
/**
* Starts this connection by binding the socket acceptor. When the acceptor is already started, a warning will be
* logged and the method invocation is otherwise ignored.
*/
public synchronized void start()
{
if ( socketAcceptor != null )
{
Log.warn( "Unable to start acceptor (it is already started!)" );
return;
}
try
{
// Configure the thread pool that is to be used.
final int initialSize = ( configuration.getMaxThreadPoolSize() / 4 ) + 1;
final ExecutorFilter executorFilter = new ExecutorFilter( initialSize, configuration.getMaxThreadPoolSize(), 60, TimeUnit.SECONDS );
final ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor) executorFilter.getExecutor();
final ThreadFactory threadFactory = new DelegatingThreadFactory( name + "-thread-", eventExecutor.getThreadFactory() );
eventExecutor.setThreadFactory( threadFactory );
// Construct a new socket acceptor, and configure it.
socketAcceptor = buildSocketAcceptor();
if ( JMXManager.isEnabled() )
{
configureJMX( socketAcceptor, name );
}
final DefaultIoFilterChainBuilder filterChain = socketAcceptor.getFilterChain();
filterChain.addFirst( ConnectionManagerImpl.EXECUTOR_FILTER_NAME, executorFilter );
// Add the XMPP codec filter
filterChain.addAfter( ConnectionManagerImpl.EXECUTOR_FILTER_NAME, ConnectionManagerImpl.XMPP_CODEC_FILTER_NAME, new ProtocolCodecFilter( new XMPPCodecFactory() ) );
// Kill sessions whose outgoing queues keep growing and fail to send traffic
filterChain.addAfter( ConnectionManagerImpl.XMPP_CODEC_FILTER_NAME, ConnectionManagerImpl.CAPACITY_FILTER_NAME, new StalledSessionsFilter() );
// Ports can be configured to start connections in SSL (as opposed to upgrade a non-encrypted socket to an encrypted one, typically using StartTLS)
if ( configuration.getTlsPolicy() == Connection.TLSPolicy.legacyMode )
{
final SslFilter sslFilter = configuration.createServerModeSslFilter();
filterChain.addAfter( ConnectionManagerImpl.EXECUTOR_FILTER_NAME, ConnectionManagerImpl.TLS_FILTER_NAME, sslFilter );
}
// Throttle sessions who send data too fast
if ( configuration.getMaxBufferSize() > 0 )
{
socketAcceptor.getSessionConfig().setMaxReadBufferSize( configuration.getMaxBufferSize() );
Log.debug( "Throttling read buffer for connections to max={} bytes", configuration.getMaxBufferSize() );
}
// Start accepting connections
socketAcceptor.setHandler( connectionHandler );
socketAcceptor.bind( new InetSocketAddress( configuration.getBindAddress(), configuration.getPort() ) );
}
catch ( Exception e )
{
System.err.println( "Error starting " + configuration.getPort() + ": " + e.getMessage() );
Log.error( "Error starting: " + configuration.getPort(), e );
}
}
/**
* Stops this connection by unbinding the socket acceptor. Does nothing when the instance is not started.
*/
public synchronized void stop()
{
if ( socketAcceptor != null )
{
socketAcceptor.unbind();
socketAcceptor = null;
}
}
/**
* Determines if this instance is currently in a state where it is actively serving connections.
*
* @return false when this instance is started and is currently being used to serve connections (otherwise true)
*/
public boolean isIdle()
{
return this.socketAcceptor != null && this.socketAcceptor.getManagedSessionCount() == 0;
}
public synchronized int getPort()
{
return configuration.getPort();
}
// TODO see if we can avoid exposing MINA internals.
public synchronized NioSocketAcceptor getSocketAcceptor()
{
return socketAcceptor;
}
private static NioSocketAcceptor buildSocketAcceptor()
{
// Create SocketAcceptor with correct number of processors
final int processorCount = JiveGlobals.getIntProperty( "xmpp.processor.count", Runtime.getRuntime().availableProcessors() );
final NioSocketAcceptor socketAcceptor = new NioSocketAcceptor( processorCount );
// Set that it will be possible to bind a socket if there is a connection in the timeout state.
socketAcceptor.setReuseAddress( true );
// Set the listen backlog (queue) length. Default is 50.
socketAcceptor.setBacklog( JiveGlobals.getIntProperty( "xmpp.socket.backlog", 50 ) );
// Set default (low level) settings for new socket connections
final SocketSessionConfig socketSessionConfig = socketAcceptor.getSessionConfig();
//socketSessionConfig.setKeepAlive();
final int receiveBuffer = JiveGlobals.getIntProperty( "xmpp.socket.buffer.receive", -1 );
if ( receiveBuffer > 0 )
{
socketSessionConfig.setReceiveBufferSize( receiveBuffer );
}
final int sendBuffer = JiveGlobals.getIntProperty( "xmpp.socket.buffer.send", -1 );
if ( sendBuffer > 0 )
{
socketSessionConfig.setSendBufferSize( sendBuffer );
}
final int linger = JiveGlobals.getIntProperty( "xmpp.socket.linger", -1 );
if ( linger > 0 )
{
socketSessionConfig.setSoLinger( linger );
}
socketSessionConfig.setTcpNoDelay( JiveGlobals.getBooleanProperty( "xmpp.socket.tcp-nodelay", socketSessionConfig.isTcpNoDelay() ) );
return socketAcceptor;
}
private void configureJMX( NioSocketAcceptor acceptor, String suffix )
{
final String prefix = IoServiceMBean.class.getPackage().getName();
// monitor the IoService
try
{
final IoServiceMBean mbean = new IoServiceMBean( acceptor );
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
final ObjectName name = new ObjectName( prefix + ":type=SocketAcceptor,name=" + suffix );
mbs.registerMBean( mbean, name );
// mbean.startCollectingStats(JiveGlobals.getIntProperty("xmpp.socket.jmx.interval", 60000));
}
catch ( JMException ex )
{
Log.warn( "Failed to register MINA acceptor mbean (JMX): " + ex );
}
// optionally register IoSession mbeans (one per session)
if ( JiveGlobals.getBooleanProperty( "xmpp.socket.jmx.sessions", false ) )
{
acceptor.addListener( new IoServiceListener()
{
private ObjectName getObjectNameForSession( IoSession session ) throws MalformedObjectNameException
{
return new ObjectName( prefix + ":type=IoSession,name=" + session.getRemoteAddress().toString().replace( ':', '/' ) );
}
public void sessionCreated( IoSession session )
{
try
{
ManagementFactory.getPlatformMBeanServer().registerMBean(
new IoSessionMBean( session ),
getObjectNameForSession( session )
);
}
catch ( JMException ex )
{
Log.warn( "Failed to register MINA session mbean (JMX): " + ex );
}
}
public void sessionDestroyed( IoSession session )
{
try
{
ManagementFactory.getPlatformMBeanServer().unregisterMBean(
getObjectNameForSession( session )
);
}
catch ( JMException ex )
{
Log.warn( "Failed to unregister MINA session mbean (JMX): " + ex );
}
}
public void serviceActivated( IoService service ) throws Exception {}
public void serviceDeactivated( IoService service ) throws Exception {}
public void serviceIdle( IoService service, IdleStatus idleStatus ) throws Exception {}
} );
}
}
// TODO this is a utility class that can be pulled out. There are several similar implementations throughout the codebase.
private static class DelegatingThreadFactory implements ThreadFactory {
private final AtomicInteger threadId;
private final ThreadFactory originalThreadFactory;
private String threadNamePrefix;
public DelegatingThreadFactory(String threadNamePrefix, ThreadFactory originalThreadFactory) {
this.originalThreadFactory = originalThreadFactory;
threadId = new AtomicInteger(0);
this.threadNamePrefix = threadNamePrefix;
}
public Thread newThread(Runnable runnable)
{
Thread t = originalThreadFactory.newThread(runnable);
t.setName(threadNamePrefix + threadId.incrementAndGet());
t.setDaemon(true);
return t;
}
}
}
package org.jivesoftware.openfire.spi;
import org.apache.mina.filter.ssl.SslFilter;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.keystore.*;
import javax.net.ssl.*;
import java.net.InetAddress;
import java.security.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
/**
* Configuration for a socket connection.
*
* Instances of this class are thread-safe, with the exception of the internal state of the #bindAddress property.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class ConnectionConfiguration
{
private final ConnectionType type;
private final int maxThreadPoolSize;
private final int maxBufferSize;
private final Connection.ClientAuth clientAuth;
private final InetAddress bindAddress;
private final int port;
private final Connection.TLSPolicy tlsPolicy;
private final CertificateStoreConfiguration identityStoreConfiguration;
private final CertificateStoreConfiguration trustStoreConfiguration;
private final boolean acceptSelfSignedCertificates;
private final boolean verifyCertificateValidity;
private final Set<String> encryptionProtocolsEnabled;
private final Set<String> encryptionProtocolsDisabled;
private final Set<String> cipherSuitesEnabled;
private final Set<String> cipherSuitesDisabled;
// derived
private final IdentityStore identityStore;
private final TrustStore trustStore;
// derived & lazy loaded factory objects. These re-usable objects should be lazy loaded, preventing initialization in configurations where they're never going to be used.
private transient KeyManagerFactory keyManagerFactory;
private transient SSLContext sslContext;
private transient SslContextFactory sslContextFactory;
public synchronized KeyManager[] getKeyManagers() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException
{
try
{
if ( keyManagerFactory == null )
{
keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
keyManagerFactory.init( getIdentityStore().getStore(), identityStoreConfiguration.getPassword() );
}
return keyManagerFactory.getKeyManagers();
}
catch ( UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | RuntimeException ex )
{
// Allow initialization to restart upon next iteration.
keyManagerFactory = null;
throw ex;
}
}
public synchronized TrustManager[] getTrustManagers() throws KeyStoreException, NoSuchAlgorithmException
{
return new TrustManager[] { new OpenfireX509TrustManager( trustStore.getStore(), isAcceptSelfSignedCertificates(), isVerifyCertificateValidity() ) };
}
public synchronized SSLContext getSSLContext( ) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
{
if ( sslContext == null )
{
sslContext = SSLContext.getInstance( "TLSv1" );
try
{
sslContext.init( getKeyManagers(), getTrustManagers(), new SecureRandom() );
}
catch ( UnrecoverableKeyException | RuntimeException ex )
{
// Allow initialization to restart upon next iteration.
sslContext = null;
throw ex;
}
}
return sslContext;
}
/**
* A utility method that implements the shared functionality of getClientModeSSLEngine and getServerModeSSLEngine.
*
* This method is used to initialize and pre-configure an instance of SSLEngine for a particular connection type.
* The returned value lacks further configuration. In most cases, developers will want to use getClientModeSSLEngine
* or getServerModeSSLEngine instead of this method.
*
* @return A new pre-configured SSLEngine instance (never null).
*/
private SSLEngine createSSLEngine( ) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException
{
final SSLContext sslContext = getSSLContext();
final SSLEngine sslEngine = sslContext.createSSLEngine();
// Configure protocol support.
final Set<String> protocolsEnabled = getEncryptionProtocolsEnabled();
if ( !protocolsEnabled.isEmpty() )
{
// When an explicit list of enabled protocols is defined, use only those.
sslEngine.setEnabledProtocols( protocolsEnabled.toArray( new String[ protocolsEnabled.size() ] ) );
}
else
{
// Otherwise, use all supported protocols (except for the ones that are explicitly disabled).
final Set<String> disabled = getEncryptionProtocolsDisabled();
final ArrayList<String> supported = new ArrayList<>();
for ( final String candidate : sslEngine.getSupportedProtocols() )
{
if ( !disabled.contains( candidate ) )
{
supported.add( candidate );
}
}
sslEngine.setEnabledProtocols( supported.toArray( new String[ supported.size()] ) );
}
// Configure cipher suite support.
final Set<String> cipherSuitesEnabled = getCipherSuitesEnabled();
if ( !cipherSuitesEnabled.isEmpty() )
{
// When an explicit list of enabled protocols is defined, use only those.
sslEngine.setEnabledCipherSuites( cipherSuitesEnabled.toArray( new String[ cipherSuitesEnabled.size() ] ) );
}
else
{
// Otherwise, use all supported cipher suites (except for the ones that are explicitly disabled).
final Set<String> disabled = getCipherSuitesDisabled();
final ArrayList<String> supported = new ArrayList<>();
for ( final String candidate : sslEngine.getSupportedCipherSuites() )
{
if ( !disabled.contains( candidate ) )
{
supported.add( candidate );
}
}
sslEngine.setEnabledCipherSuites( supported.toArray( new String[ supported.size() ] ) );
}
// TODO: Set policy for checking client certificates
return sslEngine;
}
/**
* Creates a new SSL Engine that is configured to use server mode when handshaking.
*
* For Openfire, an engine is of this mode used for most purposes (as Openfire is a server by nature).
*
* @return A new, initialized SSLEngine instance (never null).
*/
public SSLEngine createServerModeSSLEngine() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException
{
final SSLEngine sslEngine = createSSLEngine();
sslEngine.setUseClientMode( false );
switch ( getClientAuth() )
{
case needed:
sslEngine.setNeedClientAuth( true );
break;
case wanted:
sslEngine.setWantClientAuth( true );
break;
case disabled:
sslEngine.setWantClientAuth( false );
break;
}
return sslEngine;
}
/**
* Creates an SSL Engine that is configured to use client mode when handshaking.
*
* For Openfire, an engine of this mode is typically used when the server tries to connect to another server.
*
* @return An initialized SSLEngine instance (never null).
*/
public SSLEngine createClientModeSSLEngine( ) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException
{
final SSLEngine sslEngine = createSSLEngine();
sslEngine.setUseClientMode( true );
return sslEngine;
}
public synchronized SslContextFactory getSslContextFactory()
{
if ( sslContextFactory != null )
{
return sslContextFactory;
}
try
{
sslContextFactory = new SslContextFactory();
sslContextFactory.setTrustStore( trustStore.getStore() );
sslContextFactory.setTrustStorePassword( new String( trustStoreConfiguration.getPassword() ) );
sslContextFactory.setKeyStore( identityStore.getStore() );
sslContextFactory.setKeyStorePassword( new String( identityStoreConfiguration.getPassword() ) );
// Configure protocol and cipher suite support.
sslContextFactory.setIncludeProtocols( getEncryptionProtocolsEnabled().toArray( new String[ getEncryptionProtocolsEnabled().size() ] ) );
sslContextFactory.setExcludeProtocols( getEncryptionProtocolsDisabled().toArray( new String[ getEncryptionProtocolsDisabled().size() ] ) );
sslContextFactory.setIncludeCipherSuites( getCipherSuitesEnabled().toArray( new String[ getCipherSuitesEnabled().size() ] ) );
sslContextFactory.setExcludeCipherSuites( getCipherSuitesDisabled().toArray( new String[ getCipherSuitesDisabled().size() ] ) );
//Set policy for checking client certificates
switch ( clientAuth )
{
case disabled:
sslContextFactory.setNeedClientAuth( false );
sslContextFactory.setWantClientAuth( false );
break;
case wanted:
sslContextFactory.setNeedClientAuth( false );
sslContextFactory.setWantClientAuth( true );
break;
case needed:
sslContextFactory.setNeedClientAuth( true );
break;
}
return sslContextFactory;
}
catch ( RuntimeException ex )
{
// Allow initialization to restart upon next iteration.
sslContextFactory = null;
throw ex;
}
}
/**
* Creates an Apache MINA SslFilter that is configured to use server mode when handshaking.
*
* For Openfire, an engine is of this mode used for most purposes (as Openfire is a server by nature).
*
* Instead of an SSLContext or SSLEngine, Apache MINA uses an SslFilter instance. It is generally not needed to
* create both SSLContext/SSLEngine as well as SslFilter instances.
*
* @return An initialized SslFilter instance (never null)
*/
public SslFilter createServerModeSslFilter() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException
{
final SSLContext sslContext = getSSLContext();
final SSLEngine sslEngine = createServerModeSSLEngine();
return createSslFilter( sslContext, sslEngine );
}
/**
* Creates an Apache MINA SslFilter that is configured to use client mode when handshaking.
*
* For Openfire, a filter of this mode is typically used when the server tries to connect to another server.
*
* Instead of an SSLContext or SSLEngine, Apache MINA uses an SslFilter instance. It is generally not needed to
* create both SSLContext/SSLEngine as well as SslFilter instances.
*
* @return An initialized SslFilter instance (never null)
*/
public SslFilter createClientModeSslFilter() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException
{
final SSLContext sslContext = getSSLContext();
final SSLEngine sslEngine = createClientModeSSLEngine();
return createSslFilter( sslContext, sslEngine );
}
/**
* A utility method that implements the shared functionality of getServerModeSslFilter and getClientModeSslFilter.
*
* This method is used to initialize and configure an instance of SslFilter for a particular pre-configured
* SSLContext and SSLEngine. In most cases, developers will want to use getServerModeSslFilter or
* getClientModeSslFilter instead of this method.
*
* @param sslContext a pre-configured SSL Context instance (cannot be null).
* @param sslEngine a pre-configured SSL Engine instance (cannot be null).
* @return A SslFilter instance (never null).
*/
private static SslFilter createSslFilter( SSLContext sslContext, SSLEngine sslEngine ) {
final SslFilter filter = new SslFilter( sslContext );
// Copy configuration from the SSL Engine into the filter.
filter.setUseClientMode( sslEngine.getUseClientMode() );
filter.setEnabledProtocols( sslEngine.getEnabledProtocols() );
filter.setEnabledCipherSuites( sslEngine.getEnabledCipherSuites() );
// Note that the setters for 'need' and 'want' influence each-other. Invoke only one of them!
if ( sslEngine.getNeedClientAuth() )
{
filter.setNeedClientAuth( true );
}
else if ( sslEngine.getWantClientAuth() )
{
filter.setWantClientAuth( true );
}
return filter;
}
/**
* @param type
* @param maxThreadPoolSize The maximum number of threads that are to be used to processing network activity. Must be equal to or larger than one.
* @param maxBufferSize The maximum amount of bytes of the read buffer that I/O processor allocates per each read, or a non-positive value to configure no maximum.
* @param clientAuth specification if peers should be authenticated ('mutual authentication') (cannot be null).
* @param bindAddress The network address on which connections are accepted, or null when any local address can be used.
* @param port The TCP port number on which connections are accepted (must be a valid TCP port number).
* @param tlsPolicy The TLS policy that is applied to connections (cannot be null).
*/
// TODO input validation
public ConnectionConfiguration( ConnectionType type, int maxThreadPoolSize, int maxBufferSize, Connection.ClientAuth clientAuth, InetAddress bindAddress, int port, Connection.TLSPolicy tlsPolicy, CertificateStoreConfiguration identityStoreConfiguration, CertificateStoreConfiguration trustStoreConfiguration, boolean acceptSelfSignedCertificates, boolean verifyCertificateValidity, Set<String> encryptionProtocolsEnabled, Set<String> encryptionProtocolsDisabled, Set<String> cipherSuitesEnabled, Set<String> cipherSuitesDisabled )
{
if ( maxThreadPoolSize <= 0 ) {
throw new IllegalArgumentException( "Argument 'maxThreadPoolSize' must be equal to or greater than one." );
}
if ( clientAuth == null ) {
throw new IllegalArgumentException( "Argument 'clientAuth' cannot be null." );
}
this.tlsPolicy = tlsPolicy;
this.type = type;
this.maxThreadPoolSize = maxThreadPoolSize;
this.maxBufferSize = maxBufferSize;
this.clientAuth = clientAuth;
this.bindAddress = bindAddress;
this.port = port;
this.identityStoreConfiguration = identityStoreConfiguration;
this.trustStoreConfiguration = trustStoreConfiguration;
this.acceptSelfSignedCertificates = acceptSelfSignedCertificates;
this.verifyCertificateValidity = verifyCertificateValidity;
this.encryptionProtocolsEnabled = Collections.unmodifiableSet( encryptionProtocolsEnabled );
this.encryptionProtocolsDisabled = Collections.unmodifiableSet( encryptionProtocolsDisabled );
this.cipherSuitesEnabled = Collections.unmodifiableSet( cipherSuitesEnabled );
this.cipherSuitesDisabled = Collections.unmodifiableSet( cipherSuitesDisabled );
this.identityStore = CertificateStoreManager.getIdentityStore( type );
this.trustStore = CertificateStoreManager.getTrustStore( type );
}
public Connection.TLSPolicy getTlsPolicy()
{
return tlsPolicy;
}
public ConnectionType getType()
{
return type;
}
public int getMaxThreadPoolSize()
{
return maxThreadPoolSize;
}
public int getMaxBufferSize()
{
return maxBufferSize;
}
public Connection.ClientAuth getClientAuth()
{
return clientAuth;
}
public InetAddress getBindAddress()
{
return bindAddress;
}
public int getPort()
{
return port;
}
public CertificateStoreConfiguration getIdentityStoreConfiguration()
{
return identityStoreConfiguration;
}
public CertificateStoreConfiguration getTrustStoreConfiguration()
{
return trustStoreConfiguration;
}
/**
* A boolean that indicates if self-signed peer certificates can be used to establish an encrypted connection.
*
* @return true when self-signed certificates are accepted, otherwise false.
*/
public boolean isAcceptSelfSignedCertificates()
{
return acceptSelfSignedCertificates;
}
/**
* A boolean that indicates if the current validity of certificates (based on their 'notBefore' and 'notAfter'
* property values) is used when they are used to establish an encrypted connection..
*
* @return true when certificates are required to be valid to establish a secured connection, otherwise false.
*/
public boolean isVerifyCertificateValidity()
{
return verifyCertificateValidity;
}
/**
* A collection of protocol names that can be used for encryption of connections.
*
* When non-empty, the list is intended to specify those protocols (from a larger collection of implementation-
* supported protocols) that can be used to establish encryption.
*
* Values returned by {@link #getEncryptionProtocolsDisabled()} are not included in the result of this method.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string. This can, but is not guaranteed to, indicate preference.
*
* @return An (ordered) set of protocols, never null but possibly empty.
*/
public Set<String> getEncryptionProtocolsEnabled()
{
return encryptionProtocolsEnabled;
}
/**
* A collection of protocols that must not be used for encryption of connections.
*
* When non-empty, the list is intended to specify those protocols (from a larger collection of implementation-
* supported protocols) that must not be used to establish encryption.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string.
*
* @return An (ordered) set of protocols, never null but possibly empty.
*/
public Set<String> getEncryptionProtocolsDisabled()
{
return encryptionProtocolsDisabled;
}
/**
* A collection of cipher suite names that can be used for encryption of connections.
*
* When non-empty, the list is intended to specify those cipher suites (from a larger collection of implementation-
* supported cipher suties) that can be used to establish encryption.
*
* Values returned by {@link #getCipherSuitesDisabled()} are not included in the result of this method.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string. This can, but is not guaranteed to, indicate preference.
*
* @return An (ordered) set of cipher suites, never null but possibly empty.
*/
public Set<String> getCipherSuitesEnabled()
{
return cipherSuitesEnabled;
}
/**
* A collection of cipher suites that must not be used for encryption of connections.
*
* When non-empty, the list is intended to specify those cipher suites (from a larger collection of implementation-
* supported cipher suites) that must not be used to establish encryption.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string.
*
* @return An (ordered) set of cipher suites, never null but possibly empty.
*/
public Set<String> getCipherSuitesDisabled()
{
return cipherSuitesDisabled;
}
public IdentityStore getIdentityStore()
{
return identityStore;
}
public TrustStore getTrustStore()
{
return trustStore;
}
}
package org.jivesoftware.openfire.spi;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.ServerPort;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.keystore.CertificateStore;
import org.jivesoftware.openfire.keystore.CertificateStoreConfiguration;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* As a server, Openfire accepts connection requests from other network entities. The exact functionality is subject to
* configuration details (eg: TCP port on which connections are accepted, TLS policy that is applied, etc). An instance
* of this class is used to manage this configuration for one type of connection (on one TCP port), and is responsible
* for managing the lifecycle of the entity that implements the acceptance of new socket connections (as implemented by
* {@link ConnectionAcceptor}.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
// TODO most getters in this class assume that the ConnectionAcceptor property value match the property values of JiveGlobals. This should be the case, but should be asserted.
public class ConnectionListener
{
private Logger Log;
// Connection characteristics
private final ConnectionType type;
private final int defaultPort;
private final InetAddress bindAddress; // if null, represents any local address (typically 0.0.0.0 or ::0)
private CertificateStoreConfiguration identityStoreConfiguration;
private CertificateStoreConfiguration trustStoreConfiguration;
// Name of properties used to configure the acceptor.
private final String tcpPortPropertyName;
private final String isEnabledPropertyName;
private final String maxPoolSizePropertyName; // Max threads
private final String maxReadBufferPropertyName; // Max buffer size
private final String tlsPolicyPropertyName;
private final String clientAuthPolicyPropertyName;
// The entity that performs the acceptance of new (socket) connections.
private ConnectionAcceptor connectionAcceptor;
ConnectionListener getConnectionListener( ConnectionType type ) {
ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
try
{
return connectionManager.getListener( type, getTLSPolicy().equals( Connection.TLSPolicy.legacyMode ) );
} catch ( RuntimeException ex ) {
// TODO This entire catch-block is a hack, and should be removed. Listeners for all types should be available (but pending implementation of some, this hack was added).
Log.warn( "A connection listener for '{}' is not available. Using fallback: '{}'.", type, type.getFallback() );
return getConnectionListener( type.getFallback() );
}
}
/**
* Instantiates a new connection listener.
*/
public ConnectionListener( ConnectionType type, String tcpPortPropertyName, int defaultPort, String isEnabledPropertyName, String maxPoolSizePropertyName, String maxReadBufferPropertyName, String tlsPolicyPropertyName, String clientAuthPolicyPropertyName, InetAddress bindAddress, CertificateStoreConfiguration identityStoreConfiguration, CertificateStoreConfiguration trustStoreConfiguration )
{
this.type = type;
this.tcpPortPropertyName = tcpPortPropertyName;
this.defaultPort = defaultPort;
this.isEnabledPropertyName = isEnabledPropertyName;
this.maxPoolSizePropertyName = maxPoolSizePropertyName;
this.maxReadBufferPropertyName = maxReadBufferPropertyName;
this.tlsPolicyPropertyName = tlsPolicyPropertyName;
this.clientAuthPolicyPropertyName = clientAuthPolicyPropertyName;
this.bindAddress = bindAddress;
this.identityStoreConfiguration = identityStoreConfiguration;
this.trustStoreConfiguration = trustStoreConfiguration;
// A listener cannot be changed into or from legacy mode. That fact is safe to use in the name of the logger..
final String name = getType().toString().toLowerCase() + ( getTLSPolicy().equals( Connection.TLSPolicy.legacyMode ) ? "-legacyMode" : "" );
this.Log = LoggerFactory.getLogger( ConnectionListener.class.getName() + "[" + name + "]" );
}
/**
* Return if the configuration allows this listener to be enabled (but does not verify that the listener is
* indeed active).
*
* @return true if configuration allows this listener to be enabled, otherwise false.
*/
public boolean isEnabled()
{
// TODO if this is an SSL connection, legacy code required the existence of at least one certificate in the identity store in addition to the property value (although no such requirement is enforced for a TLS connection that might or might not be elevated to encrypted).
return JiveGlobals.getBooleanProperty( isEnabledPropertyName, true );
}
/**
* Activates or deactivates the listener, and changes the configuration accordingly. This configuration change is
* persisted. An invocation of this method has no effect if the listener is already in the provided state.
*/
public synchronized void enable( boolean enable )
{
final boolean isRunning = connectionAcceptor != null;
if ( enable == isRunning )
{
// This is likely to be caused by a cadence of property changes and harmless / safe to ignore.
Log.debug( "Ignoring enable({}): listener already in this state.", enable );
return;
}
JiveGlobals.setProperty( isEnabledPropertyName, Boolean.toString( enable ) );
if ( isRunning )
{
start();
}
else
{
stop();
}
}
/**
* Attempts to start the connection acceptor, creating a new instance when needed.
*
* An invocation of this method does not change the configuration for this connection. As a result, an acceptor will
* <em>not</em> be started when the listener is not enabled (in such cases, an invocation of this method has no
* effect).
*
* In order to start this listener and persist this as the desired state for this connection, use #enable(true).
*
* This method should not be called when an acceptor has already been started (instead, {@link #restart()} should be
* used to explicitly define the need to stop a previous connection). The current implementation of this method will
* stop a pre-existing acceptor, but only when it is currently not serving connections. When the acceptor is not
* idle, this method has no effect. This behavior might change in the future.
*/
public synchronized void start()
{
if ( !isEnabled() )
{
Log.debug( "Not starting: disabled by configuration." );
return;
}
if ( connectionAcceptor != null )
{
// This might indicate an illegal state. Legacy code allows for this, so we won't throw a runtime exception (for now).
if ( !connectionAcceptor.isIdle() )
{
Log.warn( "Unable to start: it appears to have already been started (and it is currently serving connections)! To restart, first stop this listener explicitly." );
return;
}
else
{
Log.warn( "Stopping (in order to restart) an instance that has already been started, but is idle. This start would have failed if the listener was not idle. The implementation should have called stop() or restart() first, to ensure a clean restart!" );
connectionAcceptor.stop();
}
}
Log.debug( "Starting..." );
connectionAcceptor = new ConnectionAcceptor( generateConnectionConfiguration() );
connectionAcceptor.start();
Log.info( "Started." );
}
/**
* Generates an immutable ConnectionConfiguration based on the current state.
*
* @return an immutable configuration, never null.
*/
public ConnectionConfiguration generateConnectionConfiguration()
{
final int maxThreadPoolSize = JiveGlobals.getIntProperty( maxPoolSizePropertyName, 16 );
final int maxBufferSize;
if ( maxReadBufferPropertyName != null )
{
maxBufferSize = JiveGlobals.getIntProperty( maxReadBufferPropertyName, 10 * 1024 * 1024 );
}
else
{
maxBufferSize = -1; // No upper bound. Should be used for high-volume & trusted connections only (if at all).
}
Connection.ClientAuth clientAuth;
if ( clientAuthPolicyPropertyName == null )
{
clientAuth = Connection.ClientAuth.wanted;
}
else
{
try
{
final String value = JiveGlobals.getProperty( clientAuthPolicyPropertyName, Connection.ClientAuth.wanted.name() );
clientAuth = Connection.ClientAuth.valueOf( value );
}
catch ( IllegalArgumentException e )
{
Log.warn( "Invalid client auth value. A default will be used.", e );
clientAuth = Connection.ClientAuth.wanted;
}
}
// Take the current state of this instance, and create a new configuration.
return new ConnectionConfiguration(
getType(),
maxThreadPoolSize,
maxBufferSize,
clientAuth,
getBindAddress(),
getPort(),
getTLSPolicy(),
identityStoreConfiguration,
trustStoreConfiguration,
acceptSelfSignedCertificates(),
verifyCertificateValidity(),
getEncryptionProtocolsEnabled(),
getEncryptionProtocolsDisabled(),
getCipherSuitesEnabled(),
getCipherSuitesDisabled()
);
}
/**
* Attempts to stop the connection acceptor. If the connection acceptor has not been started, an invocation of this
* method has no effect.
*
* An invocation of this method does not change the configuration for this connection. As a result, the acceptor for
* this connection can be restarted when this ConnectionListener instance is replaced.
*
* In order to stop this listener (and persist this as the desired state for this connection, use #enable(false).
*/
protected synchronized void stop()
{
if ( connectionAcceptor == null )
{
Log.debug( "Not stopping: it hasn't been started." );
return;
}
Log.debug( "Stopping..." );
try
{
connectionAcceptor.stop();
}
finally
{
connectionAcceptor = null;
}
Log.info( "Stopped." );
}
/**
* Starts or restarts this instance (typically used to put into effect a configuration change).
*
* A connection that was started, but is disabled by configuration will be stopped but not restarted by an
* invocation of this method.
*/
public synchronized void restart()
{
Log.debug( "Restarting..." );
try
{
if ( connectionAcceptor != null )
{
stop();
}
}
finally
{
start(); // won't actually start anything if not enabled.
}
Log.debug( "Done restarting..." );
}
/**
* Returns the acceptor that is managed by the instance.
*
* @return A socket acceptor, or null when this listener is disabled.
*/
// TODO see if we can avoid exposing MINA internals.
public NioSocketAcceptor getSocketAcceptor()
{
if ( connectionAcceptor == null )
{
return null;
}
return connectionAcceptor.getSocketAcceptor();
}
/**
* Returns the network address on which connections are accepted when this listener is enabled.
*
* This method can return null, which indicates that connections are accepted on any local address (typically
* 0.0.0.0 or ::0).
*
* @return A network address or null.
*/
public InetAddress getBindAddress()
{
return bindAddress;
}
/**
* Returns the type of connection that is accepted by this listener.
*
* @return A connection type (never null).
*/
public ConnectionType getType()
{
return type;
}
/**
* The TCP port number on which connections will be accepted when this listener is enabled.
*
* @return A port number.
*/
public int getPort()
{
if ( tcpPortPropertyName != null )
{
return JiveGlobals.getIntProperty( tcpPortPropertyName, defaultPort );
}
else
{
return defaultPort;
}
}
/**
* Changes the TCP port on which connections are accepted, This configuration change is persisted.
*
* If the listener is currently enabled, this configuration change will be applied immediately (which will cause a
* restart of the underlying connection acceptor).
*
* An invocation of this method has no effect if the new port value is equal to the existing value.
*
* @param port A port number.
*/
public void setPort( int port )
{
final long oldPort = getPort();
if (port == oldPort ) {
Log.debug( "Ignoring port change request (to '{}'): listener already in this state.", port );
return;
}
Log.debug( "Changing port from '{}' to '{}'.", oldPort, port );
if ( tcpPortPropertyName != null )
{
JiveGlobals.setProperty( tcpPortPropertyName, String.valueOf( port ) );
}
restart();
}
/**
* Returns whether TLS is mandatory, optional, disabled or mandatory immediately for new connections. When TLS is
* mandatory connections are required to be encrypted or otherwise will be closed.
*
* When TLS is disabled connections are not allowed to be (or become) encrypted. In this case, connections will be
* closed when encryption is attempted.
*
* @return An encryption policy, never null.
*/
public Connection.TLSPolicy getTLSPolicy()
{
if ( tlsPolicyPropertyName.equals( Connection.TLSPolicy.legacyMode.name() ) )
{
return Connection.TLSPolicy.legacyMode;
}
final String policyName = JiveGlobals.getProperty( tlsPolicyPropertyName, Connection.TLSPolicy.optional.toString() );
Connection.TLSPolicy tlsPolicy;
try
{
tlsPolicy = Connection.TLSPolicy.valueOf(policyName);
}
catch ( IllegalArgumentException e )
{
Log.error( "Error parsing property value of '{}' into a valid TLS_POLICY. Offending value: '{}'.", policyName, tlsPolicyPropertyName, e );
tlsPolicy = Connection.TLSPolicy.optional;
}
return tlsPolicy;
}
/**
* Sets whether TLS is mandatory, optional, disabled or mandatory immediately for new connections. When TLS is
* mandatory connections are required to be encrypted or otherwise will be closed. This configuration change is
* persisted.
*
* If the listener is currently enabled, this configuration change will be applied immediately (which will cause a
* restart of the underlying connection acceptor).
*
* When TLS is disabled connections are not allowed to be (or become) encrypted. In this case, connections will be
* closed when encryption is attempted.
*
* This method disallows changing the policy from or into legacy mode. Such a change is logged but otherwise
* ignored.
*
* An invocation of this method has no effect if the new policy value is equal to the existing value.
*
* @param policy an encryption policy (not null).
*/
public void setTLSPolicy( SocketConnection.TLSPolicy policy )
{
final Connection.TLSPolicy oldPolicy = getTLSPolicy();
if ( oldPolicy.equals( policy ) )
{
Log.debug( "Ignoring TLS Policy change request (to '{}'): listener already in this state.", policy );
return;
}
if ( Connection.TLSPolicy.legacyMode.equals( policy ) )
{
Log.warn( "Ignoring TLS Policy change request (to '{}'): You cannot reconfigure an existing connection (from '{}') into legacy mode!", policy, oldPolicy );
return;
}
if ( Connection.TLSPolicy.legacyMode.equals( oldPolicy ) )
{
Log.warn( "Ignoring TLS Policy change request (to '{}'): You cannot reconfigure an existing connection that is in legacy mode!", policy );
return;
}
Log.debug( "Changing TLS Policy from '{}' to '{}'.", oldPolicy, policy );
JiveGlobals.setProperty( tlsPolicyPropertyName, policy.toString() );
restart();
}
/**
* Returns the configuration for the identity store that identifies this instance of Openfire to the peer
* on connections created by this listener.
*
* @return The configuration of the identity store (not null)
*/
public CertificateStoreConfiguration getIdentityStoreConfiguration() {
return this.identityStoreConfiguration;
}
/**
* Replaces the configuration for the identity store that identifies this instance of Openfire to the peer
* on connections created by this listener.
*
* If the listener is currently enabled, this configuration change will be applied immediately (which will cause a
* restart of the underlying connection acceptor).
*
* @param configuration The identity store configuration (not null)
*/
public void setIdentityStoreConfiguration( CertificateStoreConfiguration configuration )
{
if ( this.identityStoreConfiguration.equals( configuration ) )
{
Log.debug( "Ignoring identity store configuration change request (to '{}'): listener already in this state.", configuration );
return;
}
Log.debug( "Changing identity store configuration from '{}' to '{}'.", this.identityStoreConfiguration, configuration );
this.identityStoreConfiguration = configuration;
restart();
}
/**
* Returns the configuration for the trust store that is used to identify/trust peers on connections created by this
* listener.
*
* @return The configuration of the identity store (not null)
*/
public CertificateStoreConfiguration getTrustStoreConfiguration() {
return this.trustStoreConfiguration;
}
/**
* Replaces the configuration for the trust store that is used to identify/trust peers on connections created by
* this listener.
*
* If the listener is currently enabled, this configuration change will be applied immediately (which will cause a
* restart of the underlying connection acceptor).
*
* @return The configuration of the identity store (not null)
*/
public void setTrustStoreConfiguration( CertificateStoreConfiguration configuration )
{
if ( this.trustStoreConfiguration.equals( configuration ) )
{
Log.debug( "Ignoring trust store configuration change request (to '{}'): listener already in this state.", configuration );
return;
}
Log.debug( "Changing trust store configuration from '{}' to '{}'.", this.trustStoreConfiguration, configuration );
this.trustStoreConfiguration = configuration;
restart();
}
// /**
// * The KeyStore type (jks, jceks, pkcs12, etc) for the identity and trust store for connections created by this
// * listener.
// *
// * @return a store type (never null).
// * @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyStore">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
// */
// public String getKeyStoreType()
// {
// final String propertyName = type.getPrefix() + "storeType";
// final String defaultValue = "jks";
//
// if ( type.getFallback() == null )
// {
// return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
// }
// else
// {
// return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getKeyStoreType() ).trim();
// }
// }
//
// public void setKeyStoreType( String keyStoreType )
// {
// // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value).
// JiveGlobals.setProperty( type.getPrefix() + "storeType", keyStoreType );
//
// final String oldKeyStoreType = getKeyStoreType();
// if ( oldKeyStoreType.equals( keyStoreType ) )
// {
// Log.debug( "Ignoring KeyStore type change request (to '{}'): listener already in this state.", keyStoreType );
// return;
// }
//
// Log.debug( "Changing KeyStore type from '{}' to '{}'.", oldKeyStoreType, keyStoreType );
// restart();
// }
//
// /**
// * The password of the identity store for connection created by this listener.
// *
// * @return a password (never null).
// */
// public String getIdentityStorePassword()
// {
// final String propertyName = type.getPrefix() + "keypass";
// final String defaultValue = "changeit";
//
// if ( type.getFallback() == null )
// {
// return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
// }
// else
// {
// return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getIdentityStorePassword() ).trim();
// }
// }
//
// public void setIdentityStorePassword( String password )
// {
// // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value).
// JiveGlobals.setProperty( type.getPrefix() + "keypass", password );
//
// final String oldPassword = getIdentityStorePassword();
// if ( oldPassword.equals( password ) )
// {
// Log.debug( "Ignoring identity store password change request: listener already in this state." ); // Do not put passwords in a logfile.
// return;
// }
//
// Log.debug( "Changing identity store password." ); // Do not put passwords in a logfile.
// restart();
// }
//
// /**
// * The password of the trust store for connections created by this listener.
// *
// * @return a password (never null).
// */
// public String getTrustStorePassword()
// {
// final String propertyName = type.getPrefix() + "trustpass";
// final String defaultValue = "changeit";
//
// if ( type.getFallback() == null )
// {
// return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
// }
// else
// {
// return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getTrustStorePassword() ).trim();
// }
// }
//
// public void setTrustStorePassword( String password )
// {
// // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value).
// JiveGlobals.setProperty( type.getPrefix() + "trustpass", password );
//
// final String oldPassword = getTrustStorePassword();
// if ( oldPassword.equals( password ) )
// {
// Log.debug( "Ignoring trust store password change request: listener already in this state." ); // Do not put passwords in a logfile.
// return;
// }
//
// Log.debug( "Changing trust store password." ); // Do not put passwords in a logfile.
// restart();
// }
//
// /**
// * The location (relative to OPENFIRE_HOME) of the identity store for connections created by this listener.
// *
// * @return a path (never null).
// */
// public String getIdentityStoreLocation()
// {
// final String propertyName = type.getPrefix() + "keystore";
// final String defaultValue = "resources" + File.separator + "security" + File.separator + "keystore";
//
// if ( type.getFallback() == null )
// {
// return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
// }
// else
// {
// return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getIdentityStoreLocation() ).trim();
// }
// }
//
// public void setIdentityStoreLocation( String location )
// {
// // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value).
// JiveGlobals.setProperty( type.getPrefix() + "keystore", location );
//
// final String oldLocation = getIdentityStoreLocation();
// if ( oldLocation.equals( location ) )
// {
// Log.debug( "Ignoring identity store location change request (to '{}'): listener already in this state.", location );
// return;
// }
//
// Log.debug( "Changing identity store location from '{}' to '{}'.", oldLocation, location );
// restart();
// }
//
// /**
// * The location (relative to OPENFIRE_HOME) of the trust store for connections created by this listener.
// *
// * @return a path (never null).
// */
// public String getTrustStoreLocation()
// {
// final String propertyName = type.getPrefix() + "truststore";
// final String defaultValue = "resources" + File.separator + "security" + File.separator + "truststore";
//
// if ( type.getFallback() == null )
// {
// return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
// }
// else
// {
// return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getTrustStoreLocation() ).trim();
// }
// }
//
// public void setTrustStoreLocation( String location )
// {
// // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value).
// JiveGlobals.setProperty( type.getPrefix() + "truststore", location );
//
// final String oldLocation = getTrustStoreLocation();
// if ( oldLocation.equals( location ) )
// {
// Log.debug( "Ignoring trust store location change request (to '{}'): listener already in this state.", location );
// return;
// }
//
// Log.debug( "Changing trust store location from '{}' to '{}'.", oldLocation, location );
// restart();
// }
/**
* A boolean that indicates if self-signed peer certificates can be used to establish an encrypted connection.
*
* @return true when self-signed certificates are accepted, otherwise false.
*/
// TODO add setter!
public boolean acceptSelfSignedCertificates()
{
// TODO these are new properties! Deprecate (migrate?) all existing 'accept-selfsigned properties' (Eg: org.jivesoftware.openfire.session.ConnectionSettings.Server.TLS_ACCEPT_SELFSIGNED_CERTS )
final String propertyName = type.getPrefix() + "certificate.accept-selfsigned";
final boolean defaultValue = false;
if ( type.getFallback() == null )
{
return JiveGlobals.getBooleanProperty( propertyName, defaultValue );
}
else
{
return JiveGlobals.getBooleanProperty( propertyName, getConnectionListener( type.getFallback() ).acceptSelfSignedCertificates() );
}
}
/**
* A boolean that indicates if the current validity of certificates (based on their 'notBefore' and 'notAfter'
* property values) is used when they are used to establish an encrypted connection..
*
* @return true when certificates are required to be valid to establish a secured connection, otherwise false.
*/
// TODO add setter!
public boolean verifyCertificateValidity()
{
// TODO these are new properties! Deprecate (migrate?) all existing 'verify / verify-validity properties' (Eg: org.jivesoftware.openfire.session.ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY_VALIDITY )
final String propertyName = type.getPrefix() + "certificate.verify.validity";
final boolean defaultValue = true;
if ( type.getFallback() == null )
{
return JiveGlobals.getBooleanProperty( propertyName, defaultValue );
}
else
{
return JiveGlobals.getBooleanProperty( propertyName, getConnectionListener( type.getFallback() ).acceptSelfSignedCertificates() );
}
}
/**
* A collection of protocol names that can be used for encryption of connections.
*
* When non-empty, the list is intended to specify those protocols (from a larger collection of implementation-
* supported protocols) that can be used to establish encryption.
*
* Values returned by {@link #getEncryptionProtocolsDisabled()} are not included in the result of this method.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string. This can, but is not guaranteed to, indicate preference.
*
* @return An (ordered) set of protocols, never null but possibly empty.
*/
// TODO add setter!
public Set<String> getEncryptionProtocolsEnabled()
{
final Set<String> result = new LinkedHashSet<>();
final String csv = getEncryptionProtocolsEnabledCommaSeparated();
result.addAll( Arrays.asList( csv.split( "\\s*,\\s*" ) ) );
result.removeAll( getEncryptionProtocolsDisabled() );
return result;
}
protected String getEncryptionProtocolsEnabledCommaSeparated()
{
final String propertyName = type.getPrefix() + "protocols.enabled";
final String defaultValue = "TLSv1,TLSv1.1,TLSv1.2";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getEncryptionProtocolsEnabledCommaSeparated() ).trim();
}
}
/**
* A collection of protocols that must not be used for encryption of connections.
*
* When non-empty, the list is intended to specify those protocols (from a larger collection of implementation-
* supported protocols) that must not be used to establish encryption.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string.
*
* @return An (ordered) set of protocols, never null but possibly empty.
*/
// TODO add setter!
public Set<String> getEncryptionProtocolsDisabled()
{
final Set<String> result = new LinkedHashSet<>();
final String csv = getEncryptionProtocolsDisabledCommaSeparated();
result.addAll( Arrays.asList( csv.split( "\\s*,\\s*" ) ) );
return result;
}
protected String getEncryptionProtocolsDisabledCommaSeparated()
{
final String propertyName = type.getPrefix() + "protocols.disabled";
final String defaultValue = "SSLv1,SSLv2,SSLv2Hello,SSLv3";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getEncryptionProtocolsDisabledCommaSeparated() ).trim();
}
}
/**
* A collection of cipher suite names that can be used for encryption of connections.
*
* When non-empty, the list is intended to specify those cipher suites (from a larger collection of implementation-
* supported cipher suties) that can be used to establish encryption.
*
* Values returned by {@link #getCipherSuitesDisabled()} are not included in the result of this method.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string. This can, but is not guaranteed to, indicate preference.
*
* @return An (ordered) set of cipher suites, never null but possibly empty.
*/
// TODO add setter!
public Set<String> getCipherSuitesEnabled()
{
final Set<String> result = new LinkedHashSet<>();
final String csv = getCipherSuitesEnabledCommaSeparated();
result.addAll( Arrays.asList( csv.split( "\\s*,\\s*" ) ) );
result.removeAll( getCipherSuitesDisabled() );
return result;
}
protected String getCipherSuitesEnabledCommaSeparated()
{
final String propertyName = type.getPrefix() + "ciphersuites.enabled";
final String defaultValue = "";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue );
}
else
{
return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getCipherSuitesEnabledCommaSeparated() );
}
}
/**
* A collection of cipher suites that must not be used for encryption of connections.
*
* When non-empty, the list is intended to specify those cipher suites (from a larger collection of implementation-
* supported cipher suites) that must not be used to establish encryption.
*
* The order over which values are iterated in the result is equal to the order of values in the comma-separated
* configuration string.
*
* @return An (ordered) set of cipher suites, never null but possibly empty.
*/
// TODO add setter!
public Set<String> getCipherSuitesDisabled()
{
final Set<String> result = new LinkedHashSet<>();
final String csv = getCipherSuitesDisabledCommaSeparated();
result.addAll( Arrays.asList( csv.split( "\\s*,\\s*" ) ) );
return result;
}
protected String getCipherSuitesDisabledCommaSeparated()
{
final String propertyName = type.getPrefix() + "ciphersuites.disabled";
final String defaultValue = "";
if ( type.getFallback() == null )
{
return JiveGlobals.getProperty( propertyName, defaultValue ).trim();
}
else
{
return JiveGlobals.getProperty( propertyName, getConnectionListener( type.getFallback() ).getCipherSuitesDisabledCommaSeparated() ).trim();
}
}
/**
* Constructs and returns a ServerPort instance that reflects the state of this listener.
*
* @return A ServerPort instance, or null when the listener is not enabled.
* @deprecated To obtain the state of this instance, use corresponding getters instead.
*/
@Deprecated
public ServerPort getServerPort()
{
if ( connectionAcceptor == null )
{
return null;
}
final int port = getPort();
final String name = getBindAddress().getHostName();
final String address = getBindAddress().getHostAddress();
final boolean isSecure = getTLSPolicy() != Connection.TLSPolicy.disabled;
final String algorithm = null;
switch ( type ) {
case SOCKET_C2S:
return new ServerPort( port, name, address, isSecure, algorithm, ServerPort.Type.client );
case SOCKET_S2S:
return new ServerPort( port, name, address, isSecure, algorithm, ServerPort.Type.server );
case COMPONENT:
return new ServerPort( port, name, address, isSecure, algorithm, ServerPort.Type.component );
case CONNECTION_MANAGER:
return new ServerPort( port, name, address, isSecure, algorithm, ServerPort.Type.connectionManager );
default:
throw new IllegalStateException( "Unrecognized type: " + type );
}
}
@Override
public String toString()
{
final String name = getType().toString().toLowerCase() + ( getTLSPolicy().equals( Connection.TLSPolicy.legacyMode ) ? "-legacyMode" : "" );
return "ConnectionListener{" +
"name=" + name +
'}';
}
}
......@@ -21,121 +21,173 @@
package org.jivesoftware.openfire.spi;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.util.*;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.buffer.SimpleBufferAllocator;
import org.apache.mina.core.service.IoService;
import org.apache.mina.core.service.IoServiceListener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.ssl.SslFilter;
import org.apache.mina.integration.jmx.IoServiceMBean;
import org.apache.mina.integration.jmx.IoSessionMBean;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.jivesoftware.openfire.*;
import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.container.PluginManagerListener;
import org.jivesoftware.openfire.http.HttpBindManager;
import org.jivesoftware.openfire.keystore.*;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.net.*;
import org.jivesoftware.openfire.nio.ClientConnectionHandler;
import org.jivesoftware.openfire.nio.ComponentConnectionHandler;
import org.jivesoftware.openfire.nio.MultiplexerConnectionHandler;
import org.jivesoftware.openfire.nio.XMPPCodecFactory;
import org.jivesoftware.openfire.session.ConnectionSettings;
import org.jivesoftware.util.*;
import org.jivesoftware.util.CertificateEventListener;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.PropertyEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConnectionManagerImpl extends BasicModule implements ConnectionManager, CertificateEventListener, PropertyEventListener {
private static final int MB = 1024 * 1024;
public class ConnectionManagerImpl extends BasicModule implements ConnectionManager, CertificateEventListener, PropertyEventListener
{
public static final String EXECUTOR_FILTER_NAME = "threadModel";
public static final String TLS_FILTER_NAME = "tls";
public static final String COMPRESSION_FILTER_NAME = "compression";
public static final String XMPP_CODEC_FILTER_NAME = "xmpp";
public static final String CAPACITY_FILTER_NAME = "outCap";
private static final String CLIENT_SOCKET_ACCEPTOR_NAME = "client";
private static final String CLIENT_SSL_SOCKET_ACCEPTOR_NAME = "client_ssl";
private static final String COMPONENT_SOCKET_ACCEPTOR_NAME = "component";
private static final String MULTIPLEXER_SOCKET_ACCEPTOR_NAME = "multiplexer";
private static final Logger Log = LoggerFactory.getLogger(ConnectionManagerImpl.class);
private NioSocketAcceptor socketAcceptor;
private NioSocketAcceptor sslSocketAcceptor;
private NioSocketAcceptor componentAcceptor;
private SocketAcceptThread serverSocketThread;
private NioSocketAcceptor multiplexerSocketAcceptor;
private ArrayList<ServerPort> ports;
private final ConnectionListener clientListener;
private final ConnectionListener clientSslListener;
private final ConnectionListener serverListener;
private final ConnectionListener componentListener;
private final ConnectionListener componentSslListener;
private final ConnectionListener connectionManagerListener; // Also known as 'multiplexer'
private final ConnectionListener connectionManagerSslListener; // Also known as 'multiplexer'
private SessionManager sessionManager;
private PacketDeliverer deliverer;
private PacketRouter router;
private RoutingTable routingTable;
private String serverName;
private String localIPAddress = null;
// Used to know if the sockets have been started
private boolean isSocketStarted = false;
public ConnectionManagerImpl() {
/**
* Instantiates a new connection manager.
*/
public ConnectionManagerImpl() throws IOException
{
super("Connection Manager");
ports = new ArrayList<>(4);
}
private synchronized void createListeners() {
if (isSocketStarted || sessionManager == null || deliverer == null || router == null || serverName == null) {
return;
}
// Create the port listener for s2s communication
createServerListener(localIPAddress);
// Create the port listener for Connections Multiplexers
createConnectionManagerListener();
// Create the port listener for external components
createComponentListener();
// Create the port listener for clients
createClientListeners();
// Create the port listener for secured clients
createClientSSLListeners();
}
private synchronized void startListeners() {
if (isSocketStarted || sessionManager == null || deliverer == null || router == null || serverName == null) {
return;
InetAddress bindAddress = null;
try
{
bindAddress = getListenAddress();
}
catch ( UnknownHostException e )
{
Log.warn( "Unable to resolve bind address: ", e );
}
// client-to-server
clientListener = new ConnectionListener(
ConnectionType.SOCKET_C2S,
ConnectionSettings.Client.PORT,
DEFAULT_PORT,
ConnectionSettings.Client.SOCKET_ACTIVE,
ConnectionSettings.Client.MAX_THREADS,
ConnectionSettings.Client.MAX_READ_BUFFER,
ConnectionSettings.Client.TLS_POLICY,
ConnectionSettings.Client.AUTH_PER_CLIENTCERT_POLICY,
bindAddress,
CertificateStoreManager.getIdentityStoreConfiguration( ConnectionType.SOCKET_C2S ),
CertificateStoreManager.getTrustStoreConfiguration( ConnectionType.SOCKET_C2S )
);
clientSslListener = new ConnectionListener(
ConnectionType.SOCKET_C2S,
ConnectionSettings.Client.OLD_SSLPORT,
DEFAULT_SSL_PORT,
ConnectionSettings.Client.ENABLE_OLD_SSLPORT,
ConnectionSettings.Client.MAX_THREADS_SSL,
ConnectionSettings.Client.MAX_READ_BUFFER_SSL,
Connection.TLSPolicy.legacyMode.name(), // force legacy mode
ConnectionSettings.Client.AUTH_PER_CLIENTCERT_POLICY,
bindAddress,
CertificateStoreManager.getIdentityStoreConfiguration( ConnectionType.SOCKET_C2S ),
CertificateStoreManager.getTrustStoreConfiguration( ConnectionType.SOCKET_C2S )
);
// server-to-server (federation)
serverListener = new ConnectionListener(
ConnectionType.SOCKET_S2S,
ConnectionSettings.Server.PORT,
DEFAULT_SERVER_PORT,
ConnectionSettings.Server.SOCKET_ACTIVE,
"xmpp.server.processing.threads",
null,
ConnectionSettings.Server.TLS_POLICY,
ConnectionSettings.Server.AUTH_PER_CLIENTCERT_POLICY,
bindAddress,
CertificateStoreManager.getIdentityStoreConfiguration( ConnectionType.SOCKET_S2S ),
CertificateStoreManager.getTrustStoreConfiguration( ConnectionType.SOCKET_S2S )
);
// external components (XEP 0114)
componentListener = new ConnectionListener(
ConnectionType.COMPONENT,
ConnectionSettings.Component.PORT,
DEFAULT_COMPONENT_PORT,
ConnectionSettings.Component.SOCKET_ACTIVE,
ConnectionSettings.Component.MAX_THREADS,
null,
ConnectionSettings.Component.TLS_POLICY,
ConnectionSettings.Component.AUTH_PER_CLIENTCERT_POLICY,
bindAddress,
CertificateStoreManager.getIdentityStoreConfiguration( ConnectionType.COMPONENT ),
CertificateStoreManager.getTrustStoreConfiguration( ConnectionType.COMPONENT )
);
componentSslListener = new ConnectionListener(
ConnectionType.COMPONENT,
ConnectionSettings.Component.OLD_SSLPORT,
DEFAULT_COMPONENT_SSL_PORT,
ConnectionSettings.Component.ENABLE_OLD_SSLPORT,
ConnectionSettings.Component.MAX_THREADS_SSL,
null,
Connection.TLSPolicy.legacyMode.name(), // force legacy mode
ConnectionSettings.Component.AUTH_PER_CLIENTCERT_POLICY,
bindAddress,
CertificateStoreManager.getIdentityStoreConfiguration( ConnectionType.COMPONENT ),
CertificateStoreManager.getTrustStoreConfiguration( ConnectionType.COMPONENT )
);
// Multiplexers (our propertietary connection manager implementation)
connectionManagerListener = new ConnectionListener(
ConnectionType.CONNECTION_MANAGER,
ConnectionSettings.Multiplex.PORT,
DEFAULT_MULTIPLEX_PORT,
ConnectionSettings.Multiplex.SOCKET_ACTIVE,
ConnectionSettings.Multiplex.MAX_THREADS,
null,
ConnectionSettings.Multiplex.TLS_POLICY,
ConnectionSettings.Multiplex.AUTH_PER_CLIENTCERT_POLICY,
bindAddress,
CertificateStoreManager.getIdentityStoreConfiguration( ConnectionType.CONNECTION_MANAGER ),
CertificateStoreManager.getTrustStoreConfiguration( ConnectionType.CONNECTION_MANAGER )
);
connectionManagerSslListener = new ConnectionListener(
ConnectionType.CONNECTION_MANAGER,
ConnectionSettings.Multiplex.OLD_SSLPORT,
DEFAULT_MULTIPLEX_SSL_PORT,
ConnectionSettings.Multiplex.ENABLE_OLD_SSLPORT,
ConnectionSettings.Multiplex.MAX_THREADS_SSL,
null,
Connection.TLSPolicy.legacyMode.name(), // force legacy mode
ConnectionSettings.Multiplex.AUTH_PER_CLIENTCERT_POLICY,
bindAddress,
CertificateStoreManager.getIdentityStoreConfiguration( ConnectionType.CONNECTION_MANAGER ),
CertificateStoreManager.getTrustStoreConfiguration( ConnectionType.CONNECTION_MANAGER )
);
}
/**
* Starts all listeners. This ensures that all those that are enabled will start accept connections.
*/
private synchronized void startListeners()
{
// Check if plugins have been loaded
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
if (!pluginManager.isExecuted()) {
pluginManager.addPluginManagerListener(new PluginManagerListener() {
@Override
public void pluginsMonitored() {
// Stop listening for plugin events
XMPPServer.getInstance().getPluginManager().removePluginManagerListener(this);
......@@ -146,411 +198,350 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
return;
}
isSocketStarted = true;
// Setup port info
try {
localIPAddress = InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException e) {
if (localIPAddress == null) {
localIPAddress = "Unknown";
for ( final ConnectionListener listener : getListeners() )
{
try
{
listener.start();
}
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while starting listener " + listener, ex );
}
// Start the port listener for s2s communication
startServerListener();
// Start the port listener for Connections Multiplexers
startConnectionManagerListener(localIPAddress);
// Start the port listener for external components
startComponentListener();
// Start the port listener for clients
startClientListeners(localIPAddress);
// Start the port listener for secured clients
startClientSSLListeners(localIPAddress);
// Start the HTTP client listener
startHTTPBindListeners();
}
private void createServerListener(String localIPAddress) {
// Start servers socket unless it's been disabled.
if (isServerListenerEnabled()) {
int port = getServerListenerPort();
try {
serverSocketThread = new SocketAcceptThread(this, new ServerPort(port, serverName,
localIPAddress, false, null, ServerPort.Type.server));
ports.add(serverSocketThread.getServerPort());
serverSocketThread.setDaemon(true);
serverSocketThread.setPriority(Thread.MAX_PRIORITY);
}
catch (Exception e) {
System.err.println("Error creating server listener on port " + port + ": " +
e.getMessage());
Log.error(LocaleUtils.getLocalizedString("admin.error.socket-setup"), e);
// Start the HTTP client listener.
try
{
HttpBindManager.getInstance().start();
}
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while starting HTTP Bind listener ", ex );
}
}
private void startServerListener() {
// Start servers socket unless it's been disabled.
if (isServerListenerEnabled()) {
int port = getServerListenerPort();
try {
serverSocketThread.start();
List<String> params = new ArrayList<>();
params.add(Integer.toString(serverSocketThread.getPort()));
Log.info(LocaleUtils.getLocalizedString("startup.server", params));
}
catch (Exception e) {
System.err.println("Error starting server listener on port " + port + ": " +
e.getMessage());
Log.error(LocaleUtils.getLocalizedString("admin.error.socket-setup"), e);
/**
* Stops all listeners. This ensures no listener will accept new connections.
*/
private synchronized void stopListeners()
{
for ( final ConnectionListener listener : getListeners() )
{
// TODO determine by purpose exactly what needs and what need not be restarted.
try
{
listener.stop();
}
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while stopping listener " + listener, ex );
}
}
private void stopServerListener() {
if (serverSocketThread != null) {
serverSocketThread.shutdown();
ports.remove(serverSocketThread.getServerPort());
serverSocketThread = null;
}
// Stop the HTTP client listener.
try
{
HttpBindManager.getInstance().stop();
}
private void createConnectionManagerListener() {
// Start multiplexers socket unless it's been disabled.
if (isConnectionManagerListenerEnabled()) {
// Create SocketAcceptor with correct number of processors
multiplexerSocketAcceptor = buildSocketAcceptor(MULTIPLEXER_SOCKET_ACCEPTOR_NAME);
// Customize Executor that will be used by processors to process incoming stanzas
int maxPoolSize = JiveGlobals.getIntProperty("xmpp.multiplex.processing.threads", 16);
ExecutorFilter executorFilter = new ExecutorFilter(getCorePoolSize(maxPoolSize), maxPoolSize, 60, TimeUnit.SECONDS);
ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor)executorFilter.getExecutor();
ThreadFactory threadFactory = eventExecutor.getThreadFactory();
threadFactory = new DelegatingThreadFactory("Multiplexer-Thread-", threadFactory);
eventExecutor.setThreadFactory(threadFactory);
multiplexerSocketAcceptor.getFilterChain().addFirst(EXECUTOR_FILTER_NAME, executorFilter);
// Add the XMPP codec filter
multiplexerSocketAcceptor.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, XMPP_CODEC_FILTER_NAME, new ProtocolCodecFilter(new XMPPCodecFactory()));
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while stopping HTTP Bind listener ", ex );
}
}
private void startConnectionManagerListener(String localIPAddress) {
// Start multiplexers socket unless it's been disabled.
if (isConnectionManagerListenerEnabled()) {
int port = getConnectionManagerListenerPort();
try {
// Listen on a specific network interface if it has been set.
String interfaceName = JiveGlobals.getXMLProperty("network.interface");
/**
* Returns the specific network interface on which Openfire is configured to listen, or null when no such preference
* has been configured.
*
* @return A network interface or null.
* @throws UnknownHostException When the configured network name cannot be resolved.
*/
public InetAddress getListenAddress() throws UnknownHostException
{
String interfaceName = JiveGlobals.getXMLProperty( "network.interface" );
InetAddress bindInterface = null;
if (interfaceName != null) {
if (interfaceName.trim().length() > 0) {
bindInterface = InetAddress.getByName(interfaceName);
}
}
// Start accepting connections
multiplexerSocketAcceptor.setHandler(new MultiplexerConnectionHandler(serverName));
multiplexerSocketAcceptor.bind(new InetSocketAddress(bindInterface, port));
ports.add(new ServerPort(port, serverName, localIPAddress, false, null, ServerPort.Type.connectionManager));
List<String> params = new ArrayList<String>();
params.add(Integer.toString(port));
Log.info(LocaleUtils.getLocalizedString("startup.multiplexer", params));
}
catch (Exception e) {
System.err.println("Error starting multiplexer listener on port " + port + ": " +
e.getMessage());
Log.error(LocaleUtils.getLocalizedString("admin.error.socket-setup"), e);
}
}
return bindInterface;
}
private void stopConnectionManagerListener() {
if (multiplexerSocketAcceptor != null) {
multiplexerSocketAcceptor.unbind();
for (ServerPort port : ports) {
if (port.isConnectionManagerPort()) {
ports.remove(port);
break;
}
}
multiplexerSocketAcceptor = null;
}
/**
* Returns all connection listeners.
*
* @return All connection listeners (never null).
*/
public Set<ConnectionListener> getListeners() {
final Set<ConnectionListener> listeners = new LinkedHashSet<>();
listeners.add( clientListener );
listeners.add( clientSslListener );
listeners.add( serverListener );
listeners.add( componentListener );
listeners.add( componentSslListener );
listeners.add( connectionManagerListener );
listeners.add( connectionManagerSslListener );
return listeners;
}
/**
* Returns a connection listener.
*
* The #startInSslMode parameter is used to distinguish between listeners that expect to receive SSL encrypted data
* immediately, as opposed to connections that initially accept plain text data (the latter are typically subject to
* StartTLS for in-band encryption configuration). When for a particular connection type only one of these options
* is implemented, the parameter value is ignored.
*
* @param type The connection type for which a listener is to be configured.
* @param startInSslMode true when the listener to be configured is in legacy SSL mode, otherwise false.
* @return The connection listener (never null).
*/
public ConnectionListener getListener( ConnectionType type, boolean startInSslMode )
{
switch ( type )
{
case SOCKET_C2S:
if (startInSslMode) {
return clientSslListener;
} else {
return clientListener;
}
private void createComponentListener() {
// Start components socket unless it's been disabled.
if (isComponentListenerEnabled() && componentAcceptor == null) {
// Create SocketAcceptor with correct number of processors
componentAcceptor = buildSocketAcceptor(COMPONENT_SOCKET_ACCEPTOR_NAME);
int maxPoolSize = JiveGlobals.getIntProperty("xmpp.component.processing.threads", 16);
ExecutorFilter executorFilter = new ExecutorFilter(getCorePoolSize(maxPoolSize), maxPoolSize, 60, TimeUnit.SECONDS);
ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor)executorFilter.getExecutor();
ThreadFactory threadFactory = eventExecutor.getThreadFactory();
threadFactory = new DelegatingThreadFactory("Component-Thread-", threadFactory);
eventExecutor.setThreadFactory(threadFactory);
componentAcceptor.getFilterChain().addFirst(EXECUTOR_FILTER_NAME, executorFilter);
// Add the XMPP codec filter
componentAcceptor.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, XMPP_CODEC_FILTER_NAME, new ProtocolCodecFilter(new XMPPCodecFactory()));
case SOCKET_S2S:
return serverListener; // there's no legacy-mode server listener.
case COMPONENT:
if (startInSslMode) {
return componentSslListener;
} else {
return componentListener;
}
case CONNECTION_MANAGER:
if (startInSslMode) {
return connectionManagerSslListener;
} else {
return connectionManagerListener;
}
private void startComponentListener() {
// Start components socket unless it's been disabled.
if (isComponentListenerEnabled() && componentAcceptor != null &&
componentAcceptor.getManagedSessionCount() == 0) {
int port = getComponentListenerPort();
try {
// Listen on a specific network interface if it has been set.
String interfaceName = JiveGlobals.getXMLProperty("network.interface");
InetAddress bindInterface = null;
if (interfaceName != null) {
if (interfaceName.trim().length() > 0) {
bindInterface = InetAddress.getByName(interfaceName);
default:
throw new IllegalStateException( "Unknown connection type: "+ type );
}
}
// Start accepting connections
componentAcceptor.setHandler(new ComponentConnectionHandler(serverName));
componentAcceptor.bind(new InetSocketAddress(bindInterface, port));
ports.add(new ServerPort(port, serverName, localIPAddress, false, null, ServerPort.Type.component));
/**
* Returns al connection listeners for the provided type.
*
* @param type The connection type for which a listener is to be configured.
* @return The connection listener (never null).
*/
public Set<ConnectionListener> getListeners( ConnectionType type )
{
final Set<ConnectionListener> result = new HashSet<>();
switch ( type )
{
case SOCKET_C2S:
result.add( clientListener );
result.add( clientSslListener );
break;
List<String> params = new ArrayList<String>();
params.add(Integer.toString(port));
Log.info(LocaleUtils.getLocalizedString("startup.component", params));
}
catch (Exception e) {
System.err.println("Error starting component listener on port " + port + ": " +
e.getMessage());
Log.error(LocaleUtils.getLocalizedString("admin.error.socket-setup"), e);
}
}
}
case SOCKET_S2S:
result.add( serverListener ); // there's no legacy-mode server listener.
break;
private void stopComponentListener() {
if (componentAcceptor != null) {
componentAcceptor.unbind();
for (ServerPort port : ports) {
if (port.isComponentPort()) {
ports.remove(port);
case COMPONENT:
result.add( componentListener );
result.add( componentSslListener );
break;
}
}
componentAcceptor = null;
}
}
private void createClientListeners() {
// Start clients plain socket unless it's been disabled.
if (isClientListenerEnabled()) {
// Create SocketAcceptor with correct number of processors
socketAcceptor = buildSocketAcceptor(CLIENT_SOCKET_ACCEPTOR_NAME);
// Customize Executor that will be used by processors to process incoming stanzas
int maxPoolSize = JiveGlobals.getIntProperty(ConnectionSettings.Client.MAX_THREADS, 16);
ExecutorFilter executorFilter = new ExecutorFilter(getCorePoolSize(maxPoolSize), maxPoolSize, 60, TimeUnit.SECONDS);
ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor)executorFilter.getExecutor();
ThreadFactory threadFactory = eventExecutor.getThreadFactory();
threadFactory = new DelegatingThreadFactory("C2S-Thread-", threadFactory);
eventExecutor.setThreadFactory(threadFactory);
case CONNECTION_MANAGER:
result.add( connectionManagerListener );
result.add( connectionManagerSslListener );
break;
// Add the XMPP codec filter
socketAcceptor.getFilterChain().addFirst(EXECUTOR_FILTER_NAME, executorFilter);
socketAcceptor.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, XMPP_CODEC_FILTER_NAME, new ProtocolCodecFilter(new XMPPCodecFactory()));
// Kill sessions whose outgoing queues keep growing and fail to send traffic
socketAcceptor.getFilterChain().addAfter(XMPP_CODEC_FILTER_NAME, CAPACITY_FILTER_NAME, new StalledSessionsFilter());
// Throttle sessions who send data too fast
int maxBufferSize = JiveGlobals.getIntProperty(ConnectionSettings.Client.MAX_READ_BUFFER, 10 * MB);
socketAcceptor.getSessionConfig().setMaxReadBufferSize(maxBufferSize);
Log.debug("Throttling read buffer for connections from socketAcceptor={} to max={} bytes",
socketAcceptor, maxBufferSize);
}
default:
throw new IllegalStateException( "Unknown connection type: "+ type );
}
private void startClientListeners(String localIPAddress) {
// Start clients plain socket unless it's been disabled.
if (isClientListenerEnabled()) {
int port = getClientListenerPort();
try {
// Listen on a specific network interface if it has been set.
String interfaceName = JiveGlobals.getXMLProperty("network.interface");
InetAddress bindInterface = null;
if (interfaceName != null) {
if (interfaceName.trim().length() > 0) {
bindInterface = InetAddress.getByName(interfaceName);
}
return result;
}
// Start accepting connections
socketAcceptor.setHandler(new ClientConnectionHandler(serverName));
socketAcceptor.bind(new InetSocketAddress(bindInterface, port));
ports.add(new ServerPort(port, serverName, localIPAddress, false, null, ServerPort.Type.client));
List<String> params = new ArrayList<String>();
params.add(Integer.toString(port));
Log.info(LocaleUtils.getLocalizedString("startup.plain", params));
}
catch (Exception e) {
System.err.println("Error starting XMPP listener on port " + port + ": " +
e.getMessage());
Log.error(LocaleUtils.getLocalizedString("admin.error.socket-setup"), e);
}
}
/**
* Returns connection configuration that under the current settings is applicable.
*
* The #startInSslMode parameter is used to distinguish between listeners that expect to receive SSL encrypted data
* immediately, as opposed to connections that initially accept plain text data (the latter are typically subject to
* StartTLS for in-band encryption configuration). When for a particular connection type only one of these options
* is implemented, the parameter value is ignored.
*
* @param type The connection type for which a listener is to be configured.
* @param startInSslMode true when the listener to be configured is in legacy SSL mode, otherwise false.
* @return The applicable connection configuration (never null).
*/
public ConnectionConfiguration getConfiguration( ConnectionType type, boolean startInSslMode ) {
return getListener( type, startInSslMode ).generateConnectionConfiguration();
}
private void stopClientListeners() {
if (socketAcceptor != null) {
socketAcceptor.unbind();
for (ServerPort port : ports) {
if (port.isClientPort() && !port.isSecure()) {
ports.remove(port);
break;
}
}
socketAcceptor = null;
}
/**
* Return if the configuration allows this listener to be enabled (but does not verify that the listener is
* indeed active)
*
* The #startInSslMode parameter is used to distinguish between listeners that expect to receive SSL encrypted data
* immediately, as opposed to connections that initially accept plain text data (the latter are typically subject to
* StartTLS for in-band encryption configuration). When for a particular connection type only one of these options
* is implemented, the parameter value is ignored.
*
* @param type The connection type for which a listener is to be configured.
* @param startInSslMode true when the listener to be configured is in legacy SSL mode, otherwise false.
* @return true if configuration allows this listener to be enabled, otherwise false.
*/
public boolean isEnabled( ConnectionType type, boolean startInSslMode )
{
return getListener( type, startInSslMode ).isEnabled();
}
private void createClientSSLListeners() {
// Start clients SSL unless it's been disabled.
if (isClientSSLListenerEnabled()) {
int port = getClientSSLListenerPort();
try {
// Customize Executor that will be used by processors to process incoming stanzas
int maxPoolSize = JiveGlobals.getIntProperty(ConnectionSettings.Client.MAX_THREADS_SSL, 16);
ExecutorFilter executorFilter = new ExecutorFilter(getCorePoolSize(maxPoolSize), maxPoolSize, 60, TimeUnit.SECONDS);
ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor)executorFilter.getExecutor();
ThreadFactory threadFactory = eventExecutor.getThreadFactory();
threadFactory = new DelegatingThreadFactory("LegacySSL-Thread-", threadFactory);
eventExecutor.setThreadFactory(threadFactory);
// Create SocketAcceptor with correct number of processors
sslSocketAcceptor = buildSocketAcceptor(CLIENT_SSL_SOCKET_ACCEPTOR_NAME);
sslSocketAcceptor.getFilterChain().addFirst(EXECUTOR_FILTER_NAME, executorFilter);
// Add the XMPP codec filter
sslSocketAcceptor.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, XMPP_CODEC_FILTER_NAME, new ProtocolCodecFilter(new XMPPCodecFactory()));
// Kill sessions whose outgoing queues keep growing and fail to send traffic
sslSocketAcceptor.getFilterChain().addAfter(XMPP_CODEC_FILTER_NAME, CAPACITY_FILTER_NAME, new StalledSessionsFilter());
// Throttle sessions who send data too fast
int maxBufferSize = JiveGlobals.getIntProperty(ConnectionSettings.Client.MAX_READ_BUFFER_SSL, 10 * MB);
sslSocketAcceptor.getSessionConfig().setMaxReadBufferSize(maxBufferSize);
Log.debug("Throttling read buffer for connections from sslSocketAcceptor={} to max={} bytes",
sslSocketAcceptor, maxBufferSize);
// Add the SSL filter now since sockets are "born" encrypted when using the legacy SSL port. On the non-legacy port, a non-encrypted socket is elevated to an encrypted one after negotiation.
Connection.ClientAuth clientAuth;
try {
clientAuth = Connection.ClientAuth.valueOf(JiveGlobals.getProperty(ConnectionSettings.Client.AUTH_PER_CLIENTCERT_POLICY, "disabled"));
} catch (IllegalArgumentException e) {
clientAuth = Connection.ClientAuth.disabled;
/**
* Enables or disables a connection listener. Does nothing if the particular listener is already in the requested
* state.
*
* The #startInSslMode parameter is used to distinguish between listeners that expect to receive SSL encrypted data
* immediately, as opposed to connections that initially accept plain text data (the latter are typically subject to
* StartTLS for in-band encryption configuration). When for a particular connection type only one of these options
* is implemented, the parameter value is ignored.
*
* @param type The connection type for which a listener is to be configured.
* @param startInSslMode true when the listener to be configured is in legacy SSL mode, otherwise false.
* @param enabled true if the listener is to be enabled, otherwise false.
*/
public void enable( ConnectionType type, boolean startInSslMode, boolean enabled )
{
getListener( type, startInSslMode ).enable( enabled );
}
final SslFilter sslFilter = SSLConfig.getServerModeSslFilter( Purpose.SOCKET_C2S, clientAuth );
sslSocketAcceptor.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, TLS_FILTER_NAME, sslFilter);
}
catch (Exception e) {
System.err.println("Error starting SSL XMPP listener on port " + port + ": " +
e.getMessage());
Log.error(LocaleUtils.getLocalizedString("admin.error.ssl"), e);
}
}
/**
* Retrieves the configured TCP port on which a listener accepts connections.
*
* @param type The connection type for which a listener is to be configured.
* @param startInSslMode true when the listener to be configured is in legacy SSL mode, otherwise false.
* @return a port number.
*/
public int getPort( ConnectionType type, boolean startInSslMode )
{
return getListener( type, startInSslMode ).getPort();
}
private void startClientSSLListeners(String localIPAddress) {
// Start clients SSL unless it's been disabled.
if (isClientSSLListenerEnabled()) {
int port = getClientSSLListenerPort();
try {
// Listen on a specific network interface if it has been set.
String interfaceName = JiveGlobals.getXMLProperty("network.interface");
InetAddress bindInterface = null;
if (interfaceName != null) {
if (interfaceName.trim().length() > 0) {
bindInterface = InetAddress.getByName(interfaceName);
/**
* Sets the TCP port on which a listener accepts connections.
*
* @param type The connection type for which a listener is to be configured.
* @param startInSslMode true when the listener to be configured is in legacy SSL mode, otherwise false.
* @param port a port number.
*/
public void setPort( ConnectionType type, boolean startInSslMode, int port )
{
getListener( type, startInSslMode ).setPort( port );
}
// TODO see if we can avoid exposing MINA internals.
public NioSocketAcceptor getSocketAcceptor( ConnectionType type, boolean startInSslMode )
{
return getListener( type, startInSslMode ).getSocketAcceptor();
}
// Start accepting connections
sslSocketAcceptor.setHandler(new ClientConnectionHandler(serverName));
sslSocketAcceptor.bind(new InetSocketAddress(bindInterface, port));
ports.add(new ServerPort(port, serverName, localIPAddress, true, null, ServerPort.Type.client));
// #####################################################################
// Certificates events
// #####################################################################
List<String> params = new ArrayList<String>();
params.add(Integer.toString(port));
Log.info(LocaleUtils.getLocalizedString("startup.ssl", params));
public void certificateCreated(KeyStore keyStore, String alias, X509Certificate cert) {
// Note that all non-SSL listeners can be using TLS - these also need to be restarted.
for ( final ConnectionListener listener : getListeners() )
{
// TODO determine by purpose exactly what needs and what need not be restarted.
try
{
listener.restart();
}
catch (Exception e) {
System.err.println("Error starting SSL XMPP listener on port " + port + ": " +
e.getMessage());
Log.error(LocaleUtils.getLocalizedString("admin.error.ssl"), e);
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while restarting listener " + listener + ". The reason for restart was a certificate store change.", ex );
}
}
}
private void stopClientSSLListeners() {
if (sslSocketAcceptor != null) {
sslSocketAcceptor.unbind();
for (ServerPort port : ports) {
if (port.isClientPort() && port.isSecure()) {
ports.remove(port);
break;
public void certificateDeleted(KeyStore keyStore, String alias) {
// Note that all non-SSL listeners can be using TLS - these also need to be restarted.
for ( final ConnectionListener listener : getListeners() )
{
// TODO determine by purpose exactly what needs and what need not be restarted.
try
{
listener.restart();
}
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while restarting listener " + listener + ". The reason for restart was a certificate store change.", ex );
}
sslSocketAcceptor = null;
}
}
private void restartClientSSLListeners() {
if (!isSocketStarted) {
return;
public void certificateSigned(KeyStore keyStore, String alias, List<X509Certificate> certificates) {
// Note that all non-SSL listeners can be using TLS - these also need to be restarted.
for ( final ConnectionListener listener : getListeners() )
{
// TODO determine by purpose exactly what needs and what need not be restarted.
try
{
listener.restart();
}
// Setup port info
try {
localIPAddress = InetAddress.getLocalHost().getHostAddress();
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while restarting listener " + listener + ". The reason for restart was a certificate store change.", ex );
}
catch (UnknownHostException e) {
if (localIPAddress == null) {
localIPAddress = "Unknown";
}
}
stopClientSSLListeners();
createClientSSLListeners();
startClientSSLListeners(localIPAddress);
// #####################################################################
// Property events
// #####################################################################
@Override
public void propertySet( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
}
@Override
public Collection<ServerPort> getPorts() {
return ports;
public void propertyDeleted( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
}
@Override
public SocketReader createSocketReader(Socket sock, boolean isSecure, ServerPort serverPort,
boolean useBlockingMode) throws IOException {
if (serverPort.isServerPort()) {
SocketConnection conn = new SocketConnection(deliverer, sock, isSecure);
return new ServerSocketReader(router, routingTable, serverName, sock, conn,
useBlockingMode);
public void xmlPropertySet( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
}
return null;
@Override
public void xmlPropertyDeleted( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
}
private void startHTTPBindListeners() {
HttpBindManager.getInstance().start();
private void processPropertyValueChange( String property, Map<String, Object> params ) {
Log.debug( "Processing property value change for '"+property +"'. Params: " + params );
// TODO there are more properties on which a restart is required (and this also applies to other listeners)!
if ("xmpp.client.cert.policy".equalsIgnoreCase( property )) {
clientSslListener.restart();
}
}
// #####################################################################
// Module management
// #####################################################################
@Override
public void initialize(XMPPServer server) {
super.initialize(server);
serverName = server.getServerInfo().getXMPPDomain();
router = server.getPacketRouter();
routingTable = server.getRoutingTable();
deliverer = server.getPacketDeliverer();
sessionManager = server.getSessionManager();
// Check if we need to configure MINA to use Direct or Heap Buffers
// Note: It has been reported that heap buffers are 50% faster than direct buffers
if (JiveGlobals.getBooleanProperty("xmpp.socket.heapBuffer", true)) {
......@@ -560,429 +551,283 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
}
@Override
public void enableClientListener(boolean enabled) {
if (enabled == isClientListenerEnabled()) {
// Ignore new setting
return;
}
if (enabled) {
JiveGlobals.setProperty(ConnectionSettings.Client.SOCKET_ACTIVE, "true");
// Start the port listener for clients
createClientListeners();
startClientListeners(localIPAddress);
}
else {
JiveGlobals.setProperty(ConnectionSettings.Client.SOCKET_ACTIVE, "false");
// Stop the port listener for clients
stopClientListeners();
}
public void start() {
super.start();
startListeners();
SocketSendingTracker.getInstance().start();
CertificateManager.addListener(this);
}
@Override
public boolean isClientListenerEnabled() {
return JiveGlobals.getBooleanProperty(ConnectionSettings.Client.SOCKET_ACTIVE, true);
public void stop() {
CertificateManager.removeListener(this);
SocketSendingTracker.getInstance().shutdown();
stopListeners();
super.stop();
}
@Override
public void enableClientSSLListener(boolean enabled) {
if (enabled == isClientSSLListenerEnabled()) {
// Ignore new setting
return;
}
if (enabled) {
JiveGlobals.setProperty(ConnectionSettings.Client.ENABLE_OLD_SSLPORT, "true");
// Start the port listener for secured clients
createClientSSLListeners();
startClientSSLListeners(localIPAddress);
}
else {
JiveGlobals.setProperty(ConnectionSettings.Client.ENABLE_OLD_SSLPORT, "false");
// Stop the port listener for secured clients
stopClientSSLListeners();
}
}
// #####################################################################
// Deprecated delegation methods to individual listeners (as dictated by legacy API design).
// #####################################################################
@Override
public boolean isClientSSLListenerEnabled() {
try {
return JiveGlobals.getBooleanProperty(ConnectionSettings.Client.ENABLE_OLD_SSLPORT, false) && SSLConfig.getIdentityStore( Purpose.SOCKET_C2S ).size() > 0;
} catch (KeyStoreException e) {
return false;
}
// Client
@Deprecated
public void enableClientListener( boolean enabled )
{
enable( ConnectionType.SOCKET_C2S, false, enabled);
}
@Override
public void enableComponentListener(boolean enabled) {
if (enabled == isComponentListenerEnabled()) {
// Ignore new setting
return;
}
if (enabled) {
JiveGlobals.setProperty(ConnectionSettings.Component.SOCKET_ACTIVE, "true");
// Start the port listener for external components
createComponentListener();
startComponentListener();
}
else {
JiveGlobals.setProperty(ConnectionSettings.Component.SOCKET_ACTIVE, "false");
// Stop the port listener for external components
stopComponentListener();
}
@Deprecated
public boolean isClientListenerEnabled()
{
return isEnabled( ConnectionType.SOCKET_C2S, false );
}
@Override
public boolean isComponentListenerEnabled() {
return JiveGlobals.getBooleanProperty(ConnectionSettings.Component.SOCKET_ACTIVE, false);
@Deprecated
public NioSocketAcceptor getSocketAcceptor()
{
return getSocketAcceptor( ConnectionType.SOCKET_C2S, false );
}
@Override
public void enableServerListener(boolean enabled) {
if (enabled == isServerListenerEnabled()) {
// Ignore new setting
return;
}
if (enabled) {
JiveGlobals.setProperty(ConnectionSettings.Server.SOCKET_ACTIVE, "true");
// Start the port listener for s2s communication
createServerListener(localIPAddress);
startServerListener();
}
else {
JiveGlobals.setProperty(ConnectionSettings.Server.SOCKET_ACTIVE, "false");
// Stop the port listener for s2s communication
stopServerListener();
}
@Deprecated
public void setClientListenerPort( int port )
{
setPort( ConnectionType.SOCKET_C2S, false, port );
}
@Override
public boolean isServerListenerEnabled() {
return JiveGlobals.getBooleanProperty(ConnectionSettings.Server.SOCKET_ACTIVE, true);
@Deprecated
public int getClientListenerPort()
{
return getPort( ConnectionType.SOCKET_C2S, false );
}
@Override
public void enableConnectionManagerListener(boolean enabled) {
if (enabled == isConnectionManagerListenerEnabled()) {
// Ignore new setting
return;
}
if (enabled) {
JiveGlobals.setProperty(ConnectionSettings.Multiplex.SOCKET_ACTIVE, "true");
// Start the port listener for s2s communication
createConnectionManagerListener();
startConnectionManagerListener(localIPAddress);
}
else {
JiveGlobals.setProperty(ConnectionSettings.Multiplex.SOCKET_ACTIVE, "false");
// Stop the port listener for s2s communication
stopConnectionManagerListener();
}
// Client in legacy mode
@Deprecated
public void enableClientSSLListener( boolean enabled )
{
enable( ConnectionType.SOCKET_C2S, true, enabled );
}
@Override
public boolean isConnectionManagerListenerEnabled() {
return JiveGlobals.getBooleanProperty(ConnectionSettings.Multiplex.SOCKET_ACTIVE, false);
@Deprecated
public boolean isClientSSLListenerEnabled()
{
return isEnabled( ConnectionType.SOCKET_C2S, true );
}
@Override
public void setClientListenerPort(int port) {
if (port == getClientListenerPort()) {
// Ignore new setting
return;
}
JiveGlobals.setProperty(ConnectionSettings.Client.PORT, String.valueOf(port));
// Stop the port listener for clients
stopClientListeners();
if (isClientListenerEnabled()) {
// Start the port listener for clients
createClientListeners();
startClientListeners(localIPAddress);
}
@Deprecated
public NioSocketAcceptor getSSLSocketAcceptor()
{
return getSocketAcceptor( ConnectionType.SOCKET_C2S, true );
}
public NioSocketAcceptor getSocketAcceptor() {
return socketAcceptor;
@Deprecated
public void setClientSSLListenerPort( int port )
{
setPort( ConnectionType.SOCKET_C2S, true, port );
}
@Override
public int getClientListenerPort() {
return JiveGlobals.getIntProperty(ConnectionSettings.Client.PORT, DEFAULT_PORT);
@Deprecated
public int getClientSSLListenerPort()
{
return getPort( ConnectionType.SOCKET_C2S, true );
}
public NioSocketAcceptor getSSLSocketAcceptor() {
return sslSocketAcceptor;
// Component
@Deprecated
public void enableComponentListener( boolean enabled )
{
enable( ConnectionType.COMPONENT, false, enabled );
}
@Override
public void setClientSSLListenerPort(int port) {
if (port == getClientSSLListenerPort()) {
// Ignore new setting
return;
}
JiveGlobals.setProperty(ConnectionSettings.Client.OLD_SSLPORT, String.valueOf(port));
// Stop the port listener for secured clients
stopClientSSLListeners();
if (isClientSSLListenerEnabled()) {
// Start the port listener for secured clients
createClientSSLListeners();
startClientSSLListeners(localIPAddress);
}
@Deprecated
public boolean isComponentListenerEnabled()
{
return isEnabled( ConnectionType.COMPONENT, false );
}
@Override
public int getClientSSLListenerPort() {
return JiveGlobals.getIntProperty(ConnectionSettings.Client.OLD_SSLPORT, DEFAULT_SSL_PORT);
@Deprecated
public NioSocketAcceptor getComponentAcceptor()
{
return getSocketAcceptor( ConnectionType.COMPONENT, false );
}
@Override
public void setComponentListenerPort(int port) {
if (port == getComponentListenerPort()) {
// Ignore new setting
return;
}
JiveGlobals.setProperty(ConnectionSettings.Component.PORT, String.valueOf(port));
// Stop the port listener for external components
stopComponentListener();
if (isComponentListenerEnabled()) {
// Start the port listener for external components
createComponentListener();
startComponentListener();
}
@Deprecated
public void setComponentListenerPort( int port )
{
setPort( ConnectionType.COMPONENT, false, port );
}
public NioSocketAcceptor getComponentAcceptor() {
return componentAcceptor;
@Deprecated
public int getComponentListenerPort()
{
return getPort( ConnectionType.COMPONENT, false );
}
@Override
public int getComponentListenerPort() {
return JiveGlobals.getIntProperty(ConnectionSettings.Component.PORT, DEFAULT_COMPONENT_PORT);
// Component in legacy mode
@Deprecated
public void enableComponentSslListener( boolean enabled )
{
enable( ConnectionType.COMPONENT, true, enabled );
}
@Override
public void setServerListenerPort(int port) {
if (port == getServerListenerPort()) {
// Ignore new setting
return;
}
JiveGlobals.setProperty(ConnectionSettings.Server.PORT, String.valueOf(port));
// Stop the port listener for s2s communication
stopServerListener();
if (isServerListenerEnabled()) {
// Start the port listener for s2s communication
createServerListener(localIPAddress);
startServerListener();
}
@Deprecated
public boolean isComponentSslListenerEnabled()
{
return isEnabled( ConnectionType.COMPONENT, true );
}
@Override
public int getServerListenerPort() {
return JiveGlobals.getIntProperty(ConnectionSettings.Server.PORT, DEFAULT_SERVER_PORT);
@Deprecated
public NioSocketAcceptor getComponentSslAcceptor()
{
return getSocketAcceptor( ConnectionType.COMPONENT, true);
}
public NioSocketAcceptor getMultiplexerSocketAcceptor() {
return multiplexerSocketAcceptor;
@Deprecated
public void setComponentSslListenerPort( int port )
{
setPort( ConnectionType.COMPONENT, true, port );
}
@Override
public void setConnectionManagerListenerPort(int port) {
if (port == getConnectionManagerListenerPort()) {
// Ignore new setting
return;
}
JiveGlobals.setProperty(ConnectionSettings.Multiplex.PORT, String.valueOf(port));
// Stop the port listener for connection managers
stopConnectionManagerListener();
if (isConnectionManagerListenerEnabled()) {
// Start the port listener for connection managers
createConnectionManagerListener();
startConnectionManagerListener(localIPAddress);
}
@Deprecated
public int getComponentSslListenerPort()
{
return getPort( ConnectionType.COMPONENT, true );
}
@Override
public int getConnectionManagerListenerPort() {
return JiveGlobals.getIntProperty(ConnectionSettings.Multiplex.PORT, DEFAULT_MULTIPLEX_PORT);
// Server
@Deprecated
public void enableServerListener( boolean enabled )
{
enable( ConnectionType.SOCKET_S2S, false, enabled );
}
// #####################################################################
// Certificates events
// #####################################################################
@Override
public void certificateCreated(KeyStore keyStore, String alias, X509Certificate cert) {
restartClientSSLListeners();
@Deprecated
public boolean isServerListenerEnabled()
{
return isEnabled( ConnectionType.SOCKET_S2S, false );
}
@Override
public void certificateDeleted(KeyStore keyStore, String alias) {
restartClientSSLListeners();
@Deprecated
public NioSocketAcceptor getServerListenerSocketAcceptor()
{
return getSocketAcceptor( ConnectionType.SOCKET_S2S, false );
}
@Override
public void certificateSigned(KeyStore keyStore, String alias, List<X509Certificate> certificates) {
restartClientSSLListeners();
@Deprecated
public void setServerListenerPort( int port )
{
setPort( ConnectionType.SOCKET_S2S, false, port );
}
// #####################################################################
// Property events
// #####################################################################
@Override
public void propertySet( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
@Deprecated
public int getServerListenerPort()
{
return getPort( ConnectionType.SOCKET_S2S, false );
}
@Override
public void propertyDeleted( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
// Connection Manager
@Deprecated
public void enableConnectionManagerListener( boolean enabled )
{
enable( ConnectionType.CONNECTION_MANAGER, false, enabled );
}
@Override
public void xmlPropertySet( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
@Deprecated
public boolean isConnectionManagerListenerEnabled()
{
return isEnabled( ConnectionType.CONNECTION_MANAGER, false );
}
@Override
public void xmlPropertyDeleted( String property, Map<String, Object> params ) {
processPropertyValueChange( property, params );
/**
* @deprecated Replaced by #getConnectionManagerSocketAcceptor
*/
@Deprecated
public NioSocketAcceptor getMultiplexerSocketAcceptor()
{
return getSocketAcceptor( ConnectionType.CONNECTION_MANAGER, false );
}
private void processPropertyValueChange( String property, Map<String, Object> params ) {
Log.debug( "Processing property value change for '"+property +"'." );
@Deprecated
public NioSocketAcceptor getConnectionManagerSocketAcceptor()
{
return getSocketAcceptor( ConnectionType.CONNECTION_MANAGER, false );
}
if ("xmpp.client.cert.policy".equalsIgnoreCase( property )) {
restartClientSSLListeners();
}
}
private NioSocketAcceptor buildSocketAcceptor(String name) {
NioSocketAcceptor socketAcceptor;
// Create SocketAcceptor with correct number of processors
int processorCount = JiveGlobals.getIntProperty("xmpp.processor.count", Runtime.getRuntime().availableProcessors());
socketAcceptor = new NioSocketAcceptor(processorCount);
// Set that it will be possible to bind a socket if there is a connection in the timeout state
socketAcceptor.setReuseAddress(true);
// Set the listen backlog (queue) length. Default is 50.
socketAcceptor.setBacklog(JiveGlobals.getIntProperty("xmpp.socket.backlog", 50));
// Set default (low level) settings for new socket connections
SocketSessionConfig socketSessionConfig = socketAcceptor.getSessionConfig();
//socketSessionConfig.setKeepAlive();
int receiveBuffer = JiveGlobals.getIntProperty("xmpp.socket.buffer.receive", -1);
if (receiveBuffer > 0 ) {
socketSessionConfig.setReceiveBufferSize(receiveBuffer);
}
int sendBuffer = JiveGlobals.getIntProperty("xmpp.socket.buffer.send", -1);
if (sendBuffer > 0 ) {
socketSessionConfig.setSendBufferSize(sendBuffer);
}
int linger = JiveGlobals.getIntProperty("xmpp.socket.linger", -1);
if (linger > 0 ) {
socketSessionConfig.setSoLinger(linger);
}
socketSessionConfig.setTcpNoDelay(
JiveGlobals.getBooleanProperty("xmpp.socket.tcp-nodelay", socketSessionConfig.isTcpNoDelay()));
if (JMXManager.isEnabled()) {
configureJMX(socketAcceptor, name);
}
return socketAcceptor;
}
private void configureJMX(NioSocketAcceptor acceptor, String suffix) {
final String prefix = IoServiceMBean.class.getPackage().getName();
// monitor the IoService
try {
IoServiceMBean mbean = new IoServiceMBean(acceptor);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName(prefix + ":type=SocketAcceptor,name=" + suffix);
mbs.registerMBean( mbean, name );
// mbean.startCollectingStats(JiveGlobals.getIntProperty("xmpp.socket.jmx.interval", 60000));
} catch (JMException ex) {
Log.warn("Failed to register MINA acceptor mbean (JMX): " + ex);
}
// optionally register IoSession mbeans (one per session)
if (JiveGlobals.getBooleanProperty("xmpp.socket.jmx.sessions", false)) {
acceptor.addListener(new IoServiceListener() {
@Override
public void sessionCreated(IoSession session) {
try {
IoSessionMBean mbean = new IoSessionMBean(session);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName(prefix + ":type=IoSession,name=" +
session.getRemoteAddress().toString().replace(':', '/'));
mbs.registerMBean(mbean, name);
} catch(JMException ex) {
Log.warn("Failed to register MINA session mbean (JMX): " + ex);
@Deprecated
public void setConnectionManagerListenerPort( int port )
{
setPort( ConnectionType.CONNECTION_MANAGER, false, port );
}
@Deprecated
public int getConnectionManagerListenerPort()
{
return getPort( ConnectionType.CONNECTION_MANAGER, false );
}
@Override
public void sessionDestroyed(IoSession session) {
try {
ObjectName name = new ObjectName(prefix + ":type=IoSession,name=" +
session.getRemoteAddress().toString().replace(':', '/'));
ManagementFactory.getPlatformMBeanServer().unregisterMBean(name);
} catch(JMException ex) {
Log.warn("Failed to unregister MINA session mbean (JMX): " + ex);
// Connection Manager in legacy mode
@Deprecated
public void enableConnectionManagerSslListener( boolean enabled )
{
enable( ConnectionType.CONNECTION_MANAGER, true, enabled );
}
@Deprecated
public boolean isConnectionManagerSslListenerEnabled()
{
return isEnabled( ConnectionType.CONNECTION_MANAGER, true );
}
@Override
public void serviceActivated(IoService service) throws Exception { }
@Override
public void serviceDeactivated(IoService service) throws Exception { }
@Override
public void serviceIdle(IoService service, IdleStatus idleStatus) throws Exception { }
});
@Deprecated
public NioSocketAcceptor getConnectionManagerSslSocketAcceptor()
{
return getSocketAcceptor( ConnectionType.CONNECTION_MANAGER, true );
}
@Deprecated
public void setConnectionManagerSslListenerPort( int port )
{
setPort( ConnectionType.CONNECTION_MANAGER, true, port );
}
private int getCorePoolSize(int maxPoolSize) {
return (maxPoolSize/4)+1;
@Deprecated
public int getConnectionManagerSslListenerPort()
{
return getPort( ConnectionType.CONNECTION_MANAGER, true );
}
// #####################################################################
// Module management
// Other deprecated implementations.
// #####################################################################
@Override
public void start() {
super.start();
createListeners();
startListeners();
SocketSendingTracker.getInstance().start();
CertificateManager.addListener(this);
/**
* @deprecated use #getListeners
*/
@Deprecated
public Collection<ServerPort> getPorts() {
final Set<ServerPort> result = new LinkedHashSet<>();
for ( ConnectionListener listener : getListeners() )
{
if (listener.getServerPort() != null)
{
result.add( listener.getServerPort() );
}
@Override
public void stop() {
super.stop();
stopClientListeners();
stopClientSSLListeners();
stopComponentListener();
stopConnectionManagerListener();
stopServerListener();
HttpBindManager.getInstance().stop();
SocketSendingTracker.getInstance().shutdown();
CertificateManager.removeListener(this);
serverName = null;
}
private static class DelegatingThreadFactory implements ThreadFactory {
private final AtomicInteger threadId;
private final ThreadFactory originalThreadFactory;
private String threadNamePrefix;
public DelegatingThreadFactory(String threadNamePrefix, ThreadFactory originalThreadFactory) {
this.originalThreadFactory = originalThreadFactory;
threadId = new AtomicInteger(0);
this.threadNamePrefix = threadNamePrefix;
return result;
}
@Override
public Thread newThread(Runnable runnable)
{
Thread t = originalThreadFactory.newThread(runnable);
t.setName(threadNamePrefix + threadId.incrementAndGet());
t.setDaemon(true);
return t;
// Old, pre NIO / MINA code. Should not be used as NIO offers better performance
@Deprecated
public SocketReader createSocketReader(Socket sock, boolean isSecure, ServerPort serverPort, boolean useBlockingMode) throws IOException {
if (serverPort.isServerPort()) {
final XMPPServer server = XMPPServer.getInstance();
final String serverName = server.getServerInfo().getXMPPDomain();
final PacketRouter router = server.getPacketRouter();
final RoutingTable routingTable = server.getRoutingTable();
final PacketDeliverer deliverer = server.getPacketDeliverer();
final SocketConnection conn = new SocketConnection(deliverer, sock, isSecure);
return new ServerSocketReader(router, routingTable, serverName, sock, conn, useBlockingMode);
}
return null;
}
}
package org.jivesoftware.openfire.spi;
import org.jivesoftware.openfire.keystore.IdentityStore;
import org.jivesoftware.openfire.keystore.TrustStore;
import org.jivesoftware.util.JiveGlobals;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* Types of (socket-based, including HTTP) connections.
*
* This is an enumeration of the connections that are expected to be terminated by an instance of the Openfire instance,
* and is used to define type-specific characteristics, including but not limited to:
* <ul>
* <li>Property-name definition</li>
* <li>Applicable encryption policies</li>
* <li>Identity & trust store configuration</li>
* </ul>
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public enum ConnectionType
{
/**
* Socket-based server-to-server (XMPP federation) connectivity.
*/
SOCKET_S2S( "xmpp.socket.ssl.", null ),
/**
* Socket-based client connectivity.
*/
SOCKET_C2S( "xmpp.socket.ssl.client.", null ),
/**
* BOSH (HTTP-bind) based client connectivity.
*/
BOSH_C2S( "xmpp.bosh.ssl.client.", SOCKET_C2S ),
/**
* Generic administrative services (eg: user providers).
*/
ADMIN( "admin.ssl.", SOCKET_S2S ),
/**
* Openfire web-admin console.
*/
WEBADMIN( "admin.web.ssl.", ADMIN ),
/**
* Openfire External Component connectivity.
*/
COMPONENT( "xmpp.component.", ADMIN ),
/**
* Openfire Connection Manager (multiplexer) connectivity.
*/
CONNECTION_MANAGER( "xmpp.multiplex.", ADMIN );
String prefix;
ConnectionType fallback;
ConnectionType( String prefix, ConnectionType fallback )
{
this.prefix = prefix;
this.fallback = fallback;
}
/**
* Returns the prefix used for the name of properties that are used to configure connections of this type.
* @return A property name prefix (never null or an empty string).
*/
public String getPrefix()
{
return prefix;
}
/**
* Returns a type from which configuration can be used, when configuration specific for this type is missing.
* @return A configuration fallback, or null if no such fallback exists.
*/
public ConnectionType getFallback()
{
return fallback;
}
}
......@@ -61,7 +61,7 @@ import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.jivesoftware.openfire.keystore.CertificateStoreConfig;
import org.jivesoftware.openfire.keystore.CertificateStore;
import org.jivesoftware.openfire.keystore.CertificateStoreConfigException;
import org.jivesoftware.openfire.keystore.CertificateUtils;
import org.jivesoftware.util.cert.CertificateIdentityMapping;
......@@ -145,10 +145,10 @@ public class CertificateManager {
}
/**
* @Deprecated Use {@link CertificateStoreConfig#delete(String)} instead.
* @Deprecated Use {@link CertificateStore#delete(String)} instead.
*/
@Deprecated
public static void deleteCertificate(CertificateStoreConfig storeConfig, String alias) throws GeneralSecurityException, IOException, CertificateStoreConfigException
public static void deleteCertificate(CertificateStore storeConfig, String alias) throws GeneralSecurityException, IOException, CertificateStoreConfigException
{
final KeyStore store = storeConfig.getStore();
if (!store.containsAlias( alias ) )
......@@ -342,7 +342,7 @@ public class CertificateManager {
* @return true if an RSA certificate was found in the specified keystore for the specified domain.
* @throws KeyStoreException
*/
public static boolean isRSACertificate(CertificateStoreConfig storeConfig, String domain) throws KeyStoreException {
public static boolean isRSACertificate(CertificateStore storeConfig, String domain) throws KeyStoreException {
return isCertificate(storeConfig, domain, "RSA");
}
......@@ -354,7 +354,7 @@ public class CertificateManager {
* @return true if an DSA certificate was found in the specified keystore for the specified domain.
* @throws KeyStoreException
*/
public static boolean isDSACertificate(CertificateStoreConfig storeConfig, String domain) throws KeyStoreException {
public static boolean isDSACertificate(CertificateStore storeConfig, String domain) throws KeyStoreException {
return isCertificate( storeConfig, domain, "DSA" );
}
......@@ -380,7 +380,7 @@ public class CertificateManager {
* @return true if a certificate with the specified configuration was found in the key store.
* @throws KeyStoreException
*/
private static boolean isCertificate(CertificateStoreConfig storeConfig, String domain, String algorithm) throws KeyStoreException {
private static boolean isCertificate(CertificateStore storeConfig, String domain, String algorithm) throws KeyStoreException {
for (Enumeration<String> aliases = storeConfig.getStore().aliases(); aliases.hasMoreElements();) {
X509Certificate certificate = (X509Certificate) storeConfig.getStore().getCertificate(aliases.nextElement());
......
......@@ -40,8 +40,6 @@ import java.security.cert.X509Certificate;
import java.util.Comparator;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.session.ConnectionSettings;
/**
* SSLSocketFactory that accepts any certificate chain and also accepts expired
......
......@@ -6,7 +6,7 @@ Fonthead Design End User License Agreement
By downloading and/or installing Fonthead Design fonts (&quot;software&quot;) you agree to the following user license terms:
1) Grant of License: The purchase of this software grants to you (&quot;user&quot;) as licensee, the non-exclusive right to use and display the software at a single home or business location on a maximum of 5 CPUs. You also may give a copy of this software to any service bureau which you hire to output your film, paper or color proofs, provided that they do not use the font software for any purpose other than outputing your work. They may keep the font software on file for use with future jobs on your behalf.
1) Grant of License: The purchase of this software grants to you (&quot;user&quot;) as licensee, the non-exclusive right to use and display the software at a single home or business location on a maximum of 5 CPUs. You also may give a copy of this software to any service bureau which you hire to output your film, paper or color proofs, provided that they do not use the font software for any connectionType other than outputing your work. They may keep the font software on file for use with future jobs on your behalf.
2) Backup: One copy of the software may be made for backup.
......
......@@ -109,7 +109,7 @@ class RegisterProcessing {
// }
/*
* else{ SipURI uri = (SipURI) address.getURI();
* scheduleReRegistration(uri.getHost(), uri.getPort(),
* scheduleReRegistration(uri.getHost(), uri.getServerPort(),
* uri.getTransportParam(), expires); }
*/
sipManCallback.fireRegistered(address.toString());
......
......@@ -15,7 +15,7 @@ import java.util.Collections;
import java.util.List;
/**
* Unit tests that verify the functionality of {@link OpenfireX509ExtendedTrustManager#checkChainTrusted(CertSelector, X509Certificate...)}.
* Unit tests that verify the functionality of {@link OpenfireX509TrustManager#checkChainTrusted(CertSelector, X509Certificate...)}.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
......@@ -27,14 +27,14 @@ public class CheckChainTrustedTest
* iterable returned here (its values are passed to the constructor of this class). This allows us to execute the
* same set of tests against a different configuration of the system under test.
*/
@Parameterized.Parameters(name = "acceptSelfSigned={0},checkValidity={1}" )
@Parameterized.Parameters(name = "acceptSelfSignedCertificates={0},checkValidity={1}" )
public static Iterable<Object[]> constructorArguments()
{
final List<Object[]> constructorArguments = new ArrayList<>();
constructorArguments.add( new Object[] { false, true } ); // acceptSelfSigned = false, check validity = true
constructorArguments.add( new Object[] { false, false } ); // acceptSelfSigned = false, check validity = false
constructorArguments.add( new Object[] { true, true } ); // acceptSelfSigned = true, check validity = true
constructorArguments.add( new Object[] { true, false } ); // acceptSelfSigned = true, check validity = false
constructorArguments.add( new Object[] { false, true } ); // acceptSelfSignedCertificates = false, check validity = true
constructorArguments.add( new Object[] { false, false } ); // acceptSelfSignedCertificates = false, check validity = false
constructorArguments.add( new Object[] { true, true } ); // acceptSelfSignedCertificates = true, check validity = true
constructorArguments.add( new Object[] { true, false } ); // acceptSelfSignedCertificates = true, check validity = false
return constructorArguments;
}
......@@ -79,7 +79,7 @@ public class CheckChainTrustedTest
/**
* The system under test (refreshed before every test invocation).
*/
private OpenfireX509ExtendedTrustManager trustManager;
private OpenfireX509TrustManager trustManager;
public CheckChainTrustedTest( boolean acceptSelfSigned, boolean checkValidity )
{
......@@ -106,7 +106,7 @@ public class CheckChainTrustedTest
trustStore.setCertificateEntry( getLast( expiredRootChain ).getSubjectDN().getName(), getLast( expiredRootChain ) );
// Reset the system under test before each test.
trustManager = new OpenfireX509ExtendedTrustManager( trustStore, acceptSelfSigned, checkValidity );
trustManager = new OpenfireX509TrustManager( trustStore, acceptSelfSigned, checkValidity );
}
/**
......
......@@ -14,16 +14,16 @@ import java.security.cert.*;
import java.util.*;
/**
* Unit tests that verify the functionality of {@link OpenfireX509ExtendedTrustManager}.
* Unit tests that verify the functionality of {@link OpenfireX509TrustManager}.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class OpenfireX509ExtendedTrustManagerTest
public class OpenfireX509TrustManagerTest
{
/**
* An instance that is freshly recreated before each test.
*/
private OpenfireX509ExtendedTrustManager systemUnderTest;
private OpenfireX509TrustManager systemUnderTest;
/**
* The keystore that contains the certificates used by the system under test (refreshed before every test invocation).
......@@ -63,7 +63,7 @@ public class OpenfireX509ExtendedTrustManagerTest
}
// Create the Trust Manager that is subject of these tests.
systemUnderTest = null; // TODO FIXME // new OpenfireX509ExtendedTrustManager( trustStore );
systemUnderTest = null; // TODO FIXME // new OpenfireX509TrustManager( trustStore );
}
/**
......@@ -91,7 +91,7 @@ public class OpenfireX509ExtendedTrustManagerTest
}
/**
* This test verifies that {@link OpenfireX509ExtendedTrustManager#getAcceptedIssuers()} does not return expired
* This test verifies that {@link OpenfireX509TrustManager#getAcceptedIssuers()} does not return expired
* certificates.
*/
@Test
......
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.jivesoftware.util.Base64;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import sun.security.provider.X509Factory;
import java.io.File;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.*;
/**
* Unit tests that verify the functionality of {@link TrustStoreConfig}.
* Unit tests that verify the functionality of {@link TrustStore}.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
......@@ -30,7 +10,7 @@ public class TrustStoreConfigTest
// /**
// * An instance that is freshly recreated before each test.
// */
// private TrustStoreConfig trustStoreConfig;
// private TrustStore trustStoreConfig;
//
// @Before
// public void createFixture() throws Exception
......@@ -57,7 +37,7 @@ public class TrustStoreConfigTest
// }
//
// // Use the new keystore file to create a fresh trust store, which will be used as a fixture by the tests.
// trustStoreConfig = new TrustStoreConfig( location, password, keyStore.getType(), false );
// trustStoreConfig = new TrustStore( location, password, keyStore.getType(), false );
// }
//
// @After
......@@ -74,7 +54,7 @@ public class TrustStoreConfigTest
// /**
// * The store in the fixture contains two certificates - one that is valid, and one that is invalid.
// *
// * This test verifies that {@link TrustStoreConfig#getAllCertificates()} returns both.
// * This test verifies that {@link TrustStore#getAllCertificates()} returns both.
// */
// @Test
// public void testGetAll() throws Exception
......@@ -91,7 +71,7 @@ public class TrustStoreConfigTest
// /**
// * The store in the fixture contains two certificates - one that is valid, and one that is invalid.
// *
// * This test verifies that {@link TrustStoreConfig#getAllValidTrustAnchors()} returns only the valid one.
// * This test verifies that {@link TrustStore#getAllValidTrustAnchors()} returns only the valid one.
// */
// @Test
// public void testGetValid() throws Exception
......@@ -142,7 +122,7 @@ public class TrustStoreConfigTest
//
// /**
// * This test verifies that when a certificate is installed in the store using
// * {@link TrustStoreConfig#installCertificate(String, String)} a certificate chain of which the anchor is that same
// * {@link TrustStore#installCertificate(String, String)} a certificate chain of which the anchor is that same
// * certificate is successfully verified.
// */
// @Test
......
<%@ page errorPage="error.jsp" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStoreConfig" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreManager" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStore" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
......@@ -18,20 +18,20 @@
final String privateKey = ParamUtils.getParameter(request, "private-key");
final String passPhrase = ParamUtils.getParameter(request, "passPhrase");
final String certificate = ParamUtils.getParameter(request, "certificate");
final String storePurposeText = ParamUtils.getParameter(request, "storePurpose");
final String storePurposeText = ParamUtils.getParameter(request, "storeConnectionType");
final Map<String, String> errors = new HashMap<String, String>();
Purpose storePurpose;
ConnectionType storeConnectionType;
try
{
storePurpose = Purpose.valueOf( storePurposeText );
storeConnectionType = ConnectionType.valueOf( storePurposeText );
} catch (RuntimeException ex) {
errors.put( "storePurpose", ex.getMessage() );
storePurpose = null;
errors.put( "storeConnectionType", ex.getMessage() );
storeConnectionType = null;
}
pageContext.setAttribute( "storePurpose", storePurpose );
pageContext.setAttribute( "storeConnectionType", storeConnectionType );
if (save) {
if (privateKey == null || "".equals(privateKey)) {
......@@ -42,24 +42,24 @@
}
if (errors.isEmpty()) {
try {
final IdentityStoreConfig identityStoreConfig = SSLConfig.getInstance().getIdentityStoreConfig( storePurpose );
final IdentityStore identityStore = CertificateStoreManager.getIdentityStore( storeConnectionType );
// Create an alias for the signed certificate
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
int index = 1;
String alias = domain + "_" + index;
while ( identityStoreConfig.getStore().containsAlias( alias )) {
while ( identityStore.getStore().containsAlias( alias )) {
index = index + 1;
alias = domain + "_" + index;
}
// Import certificate
identityStoreConfig.installCertificate( alias, privateKey, passPhrase, certificate );
identityStore.installCertificate( alias, privateKey, passPhrase, certificate );
// Log the event
webManager.logEvent("imported SSL certificate in identity store "+ storePurposeText, "alias = "+alias);
response.sendRedirect("security-keystore.jsp?storePurpose="+storePurposeText);
response.sendRedirect("security-keystore.jsp?storeConnectionType="+storePurposeText);
return;
}
catch (Exception e) {
......@@ -72,8 +72,8 @@
<html>
<head>
<title><fmt:message key="ssl.import.certificate.keystore.${storePurpose}.title"/></title>
<meta name="pageID" content="security-keystore-${storePurpose}"/>
<title><fmt:message key="ssl.import.certificate.keystore.${storeConnectionType}.title"/></title>
<meta name="pageID" content="security-keystore-${storeConnectionType}"/>
</head>
<body>
......@@ -115,7 +115,7 @@
<!-- BEGIN 'Import Private Key and Certificate' -->
<form action="import-keystore-certificate.jsp" method="post" name="f">
<input type="hidden" name="storePurpose" value="${storePurpose}"/>
<input type="hidden" name="storeConnectionType" value="${storeConnectionType}"/>
<div class="jive-contentBoxHeader">
<fmt:message key="ssl.import.certificate.keystore.boxtitle" />
</div>
......
<%@ page errorPage="error.jsp"%>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreManager"%>
<%@ page import="org.jivesoftware.openfire.keystore.TrustStore"%>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType"%>
<%@ page import="org.jivesoftware.util.ParamUtils"%>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.TrustStoreConfig" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
......@@ -16,24 +16,24 @@
<% final boolean save = ParamUtils.getParameter(request, "save") != null;
final String alias = ParamUtils.getParameter(request, "alias");
final String certificate = ParamUtils.getParameter(request, "certificate");
final String storePurposeText = ParamUtils.getParameter(request, "storePurpose");
final String storePurposeText = ParamUtils.getParameter(request, "storeConnectionType");
final Map<String, String> errors = new HashMap<String, String>();
Purpose storePurpose;
ConnectionType storeConnectionType;
try
{
storePurpose = Purpose.valueOf( storePurposeText );
storeConnectionType = ConnectionType.valueOf( storePurposeText );
} catch (RuntimeException ex) {
errors.put( "storePurpose", ex.getMessage() );
storePurpose = null;
errors.put( "storeConnectionType", ex.getMessage() );
storeConnectionType = null;
}
pageContext.setAttribute( "storePurpose", storePurpose );
pageContext.setAttribute( "storeConnectionType", storeConnectionType );
if (save && errors.isEmpty())
{
final TrustStoreConfig trustStoreConfig = SSLConfig.getInstance().getTrustStoreConfig( storePurpose );
final TrustStore trustStoreConfig = CertificateStoreManager.getTrustStore( storeConnectionType );
if (alias == null || "".equals(alias))
{
......@@ -59,7 +59,7 @@
// Log the event
webManager.logEvent("imported SSL certificate in trust store "+ storePurposeText, "alias = "+alias);
response.sendRedirect( "security-truststore.jsp?storePurpose=" + storePurposeText + "&importsuccess=true" );
response.sendRedirect( "security-truststore.jsp?storeConnectionType=" + storePurposeText + "&importsuccess=true" );
return;
}
catch (Throwable e)
......@@ -74,9 +74,9 @@
<html>
<head>
<title>
<fmt:message key="ssl.import.certificate.keystore.${storePurpose}.title"/> - <fmt:message key="ssl.certificates.truststore.${param.type}-title"/>
<fmt:message key="ssl.import.certificate.keystore.${storeConnectionType}.title"/> - <fmt:message key="ssl.certificates.truststore.${param.type}-title"/>
</title>
<meta name="pageID" content="security-truststore-${storePurpose}-${param.type}"/>
<meta name="pageID" content="security-truststore-${storeConnectionType}-${param.type}"/>
</head>
<body>
......@@ -124,7 +124,7 @@
<!-- BEGIN 'Import Certificate' -->
<form action="import-truststore-certificate.jsp?type=${param.type}" method="post" name="f">
<input type="hidden" name="connectivityType" value="${storePurpose}"/>
<input type="hidden" name="connectivityType" value="${storeConnectionType}"/>
<div class="jive-contentBoxHeader">
<fmt:message key="ssl.import.certificate.keystore.boxtitle"/>
</div>
......
<%--
<%--
- $Revision$
- $Date$
......@@ -23,29 +24,31 @@
%>
<%@ page import="com.sun.syndication.fetcher.impl.FeedFetcherCache"%>
<%@ page import="com.sun.syndication.fetcher.impl.HashMapFeedInfoCache"%>
<%@ page import="org.apache.mina.transport.socket.nio.NioSocketAcceptor"%>
<%@ page import="org.jivesoftware.admin.AdminConsole"%>
<%@ page import="org.jivesoftware.openfire.*" %>
<%@ page import="org.jivesoftware.openfire.Connection"%>
<%@ page import="org.jivesoftware.openfire.FlashCrossDomainHandler" %>
<%@ page import="org.jivesoftware.openfire.JMXManager" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="org.jivesoftware.openfire.container.AdminConsolePlugin" %>
<%@ page import="org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy" %>
<%@ page import="org.jivesoftware.openfire.http.HttpBindManager" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreManager" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStore" %>
<%@ page import="org.jivesoftware.openfire.mediaproxy.MediaProxyService" %>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig" %>
<%@ page import="org.jivesoftware.openfire.session.LocalClientSession" %>
<%@ page import="org.jivesoftware.openfire.session.LocalConnectionMultiplexerSession" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionListener" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionManagerImpl" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ page import="org.jivesoftware.openfire.update.Update" %>
<%@ page import="org.jivesoftware.openfire.update.UpdateManager" %>
<%@ page import="org.jivesoftware.util.*" %>
<%@ page import="java.net.InetSocketAddress" %>
<%@ page import="java.net.SocketAddress" %>
<%@ page import="org.jivesoftware.util.HttpClientWithTimeoutFeedFetcher" %>
<%@ page import="org.jivesoftware.util.JiveGlobals" %>
<%@ page import="org.jivesoftware.util.LocaleUtils" %>
<%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="org.slf4j.LoggerFactory" %>
<%@ page import="java.net.URL" %>
<%@ page import="java.text.DecimalFormat" %>
<%@ page import="java.util.Arrays" %>
<%@ page import="java.util.List" %>
<%@ page import="org.slf4j.LoggerFactory" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreConfig" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStoreConfig" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
......@@ -75,13 +78,9 @@
<% // Get parameters //
boolean serverOn = (webManager.getXMPPServer() != null);
String interfaceName = JiveGlobals.getXMLProperty("network.interface");
ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
NioSocketAcceptor socketAcceptor = connectionManager.getSocketAcceptor();
NioSocketAcceptor sslSocketAcceptor = connectionManager.getSSLSocketAcceptor();
NioSocketAcceptor multiplexerSocketAcceptor = connectionManager.getMultiplexerSocketAcceptor();
ServerPort serverPort = null;
ServerPort componentPort = null;
// Network interface (if any) is configured for all ports on the server
AdminConsolePlugin adminConsolePlugin =
(AdminConsolePlugin) XMPPServer.getInstance().getPluginManager().getPlugin("admin");
......@@ -90,15 +89,6 @@
MediaProxyService mediaProxyService = XMPPServer.getInstance().getMediaProxyService();
FlashCrossDomainHandler flashCrossDomainHandler = XMPPServer.getInstance().getFlashCrossDomainHandler();
// Search for s2s and external component ports info
for (ServerPort port : XMPPServer.getInstance().getServerInfo().getServerPorts()) {
if (port.getType() == ServerPort.Type.server) {
serverPort = port;
} else if (port.getType() == ServerPort.Type.component) {
componentPort = port;
}
}
boolean rssEnabled = JiveGlobals.getBooleanProperty("rss.enabled", true);
%>
......@@ -253,9 +243,9 @@
<fmt:message key="index.server_name" />
</td>
<td class="c2">
<% final IdentityStoreConfig storeConfig = SSLConfig.getInstance().getIdentityStoreConfig( Purpose.SOCKET_C2S ); %>
<% final IdentityStore identityStore = CertificateStoreManager.getIdentityStore( ConnectionType.SOCKET_C2S ); %>
<% try { %>
<% if (!storeConfig.containsDomainCertificate( "RSA" )) {%>
<% if (!identityStore.containsDomainCertificate( "RSA" )) {%>
<img src="images/warning-16x16.gif" width="16" height="16" border="0" alt="<fmt:message key="index.certificate-warning" />" title="<fmt:message key="index.certificate-warning" />">&nbsp;
<% } %>
<% } catch (Exception e) { %>
......@@ -393,8 +383,8 @@
lastRSSFetch = nowTime;
}
catch (Exception ioe) {
LoggerFactory.getLogger("index.jsp").warn("Failed to fetch RSS feed: " + ioe);
catch (Throwable throwable) {
LoggerFactory.getLogger("index.jsp").warn("Failed to fetch RSS feed:", throwable);
}
}
......@@ -439,7 +429,7 @@
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<th width="80"><fmt:message key="ports.interface" /></th>
<th width="100"><fmt:message key="ports.interface" /></th>
<th width="1"><fmt:message key="ports.port" /></th>
<th width="1">&nbsp;</th>
<th width="130"><fmt:message key="ports.type" /></th>
......@@ -447,103 +437,95 @@
</tr>
</thead>
<tbody>
<% if (socketAcceptor != null) {
for (SocketAddress socketAddress : socketAcceptor.getLocalAddresses()) {
InetSocketAddress address = (InetSocketAddress) socketAddress;
%>
<tr>
<td><%= "0.0.0.0".equals(address.getHostName()) ? LocaleUtils.getLocalizedString("ports.all_ports") : address.getHostName() %></td>
<td><%= address.getPort() %></td>
<% try { %>
<% if (!storeConfig.containsDomainCertificate( "RSA" ) || LocalClientSession.getTLSPolicy() == org.jivesoftware.openfire.Connection.TLSPolicy.disabled) { %>
<td><img src="images/blank.gif" width="1" height="1" alt=""/></td>
<% } else { %>
<td><img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/></td>
<% } %>
<% } catch (Exception e) { %>
<td><img src="images/blank.gif" width="1" height="1" alt=""/></td>
<% } %>
<td><fmt:message key="ports.client_to_server" /></td>
<td><fmt:message key="ports.client_to_server.desc">
<fmt:param value="<a href='ssl-settings.jsp'>" />
<fmt:param value="</a>" />
</fmt:message>
</td>
</tr>
<% } } %>
<% if (sslSocketAcceptor != null) {
for (SocketAddress socketAddress : sslSocketAcceptor.getLocalAddresses()) {
InetSocketAddress address = (InetSocketAddress) socketAddress;
%>
<tr>
<td><%= "0.0.0.0".equals(address.getHostName()) ? LocaleUtils.getLocalizedString("ports.all_ports") : address.getHostName() %></td>
<td><%= address.getPort() %></td>
<td><img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/></td>
<td><fmt:message key="ports.client_to_server" /></td>
<td><fmt:message key="ports.client_to_server.desc_old_ssl">
<fmt:param value="<a href='ssl-settings.jsp'>" />
<fmt:param value="</a>" />
</fmt:message>
</td>
</tr>
<% } } %>
<%
if (serverPort != null) {
%>
<%
for ( ConnectionListener connectionListener : connectionManager.getListeners() )
{
if ( !connectionListener.isEnabled() )
{
continue;
}
final String interfaceName;
if (connectionListener.getBindAddress() == null || connectionListener.getBindAddress().isAnyLocalAddress() ) {
interfaceName = LocaleUtils.getLocalizedString("ports.all_ports");
} else {
interfaceName = connectionListener.getBindAddress().getHostName();
}
%>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : serverPort.getIPAddress() %></td>
<td><%= serverPort.getPort() %></td>
<% if (JiveGlobals.getBooleanProperty("xmpp.server.tls.enabled", true)) { %>
<td><img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/></td>
<td><%= interfaceName %></td>
<td><%= connectionListener.getPort() %></td>
<td>
<% if ( connectionListener.getTLSPolicy().equals( Connection.TLSPolicy.disabled ) ) { %>
<img src="images/blank.gif" width="1" height="1" alt=""/>
<% } else { %>
<td><img src="images/blank.gif" width="1" height="1" alt=""/></td>
<img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/>
<% } %>
<td><fmt:message key="ports.server_to_server" /></td>
<td><fmt:message key="ports.server_to_server.desc">
<fmt:param value="<a href='server2server-settings.jsp'>" />
<fmt:param value="</a>" />
</fmt:message>
</td>
<td>
</td>
</tr>
<% } %>
<% if (multiplexerSocketAcceptor != null) {
for (SocketAddress socketAddress : multiplexerSocketAcceptor.getLocalAddresses()) {
InetSocketAddress address = (InetSocketAddress) socketAddress;
<%
final String typeName;
switch ( connectionListener.getType() ) {
case SOCKET_C2S:
typeName = LocaleUtils.getLocalizedString("ports.client_to_server");
break;
case SOCKET_S2S:
typeName = LocaleUtils.getLocalizedString("ports.server_to_server");
break;
case COMPONENT:
typeName = LocaleUtils.getLocalizedString("ports.external_components");
break;
case CONNECTION_MANAGER:
typeName = LocaleUtils.getLocalizedString("ports.connection_manager");
break;
default:
typeName = "(unspecified)";
break;
}
%>
<tr>
<td><%= "0.0.0.0".equals(address.getHostName()) ? LocaleUtils.getLocalizedString("ports.all_ports") : address.getHostName() %></td>
<td><%= address.getPort() %></td>
<% if (LocalConnectionMultiplexerSession.getTLSPolicy() == org.jivesoftware.openfire.Connection.TLSPolicy.disabled) { %>
<td><img src="images/blank.gif" width="1" height="1" alt=""></td>
<% } else { %>
<td><img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/></td>
<% } %>
<td><fmt:message key="ports.connection_manager" /></td>
<td><fmt:message key="ports.connection_manager.desc">
<fmt:param value="<a href='connection-managers-settings.jsp'>" />
<fmt:param value="</a>" />
</fmt:message>
<%=typeName%>
</td>
</tr>
<% } } %>
<td>
<%
if (componentPort != null) {
final String description;
switch ( connectionListener.getType() ) {
case SOCKET_C2S:
if ( connectionListener.getTLSPolicy().equals( Connection.TLSPolicy.legacyMode ) )
{
description = LocaleUtils.getLocalizedString( "ports.client_to_server.desc_old_ssl", Arrays.asList( "<a href='ssl-settings.jsp'>", "</a>" ) );
} else {
description = LocaleUtils.getLocalizedString( "ports.client_to_server.desc", Arrays.asList( "<a href='ssl-settings.jsp'>", "</a>" ) );
}
break;
case SOCKET_S2S:
description = LocaleUtils.getLocalizedString( "ports.server_to_server.desc", Arrays.asList( "<a href='server2server-settings.jsp'>", "</a>" ) );
break;
case COMPONENT:
description = LocaleUtils.getLocalizedString( "ports.external_components.desc", Arrays.asList( "<a href='external-components-settings.jsp'>", "</a>" ) );
break;
case CONNECTION_MANAGER:
description = LocaleUtils.getLocalizedString( "ports.connection_manager.desc", Arrays.asList( "<a href='connection-managers-settings.jsp'>", "</a>" ) );
break;
default:
description = "";
break;
}
%>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : componentPort.getIPAddress() %></td>
<td><%= componentPort.getPort() %></td>
<td><img src="images/blank.gif" width="1" height="1" alt=""></td>
<td><fmt:message key="ports.external_components" /></td>
<td><fmt:message key="ports.external_components.desc">
<fmt:param value="<a href='external-components-settings.jsp'>" />
<fmt:param value="</a>" />
</fmt:message>
<%=description%>
</td>
</tr>
<% } %>
<%
}
final String interfaceName;
if (connectionManager.getListenAddress() == null || connectionManager.getListenAddress().isAnyLocalAddress() ) {
interfaceName = LocaleUtils.getLocalizedString("ports.all_ports");
} else {
interfaceName = connectionManager.getListenAddress().getHostName();
}
%>
<tr>
<td><%= adminConsolePlugin.getBindInterface() == null ? LocaleUtils.getLocalizedString("ports.all_ports") : adminConsolePlugin.getBindInterface() %></td>
<td><%= adminConsolePlugin.getAdminUnsecurePort() %></td>
......@@ -566,7 +548,7 @@
if (fileTransferProxy.isProxyEnabled()) {
%>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : interfaceName %></td>
<td><%= interfaceName %></td>
<td><%= fileTransferProxy.getProxyPort() %></td>
<td><img src="images/blank.gif" width="1" height="1" alt=""></td>
<td><fmt:message key="ports.file_proxy" /></td>
......@@ -580,7 +562,7 @@
if (httpBindManager.getHttpBindUnsecurePort() > 0) {
%>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : interfaceName %></td>
<td><%= interfaceName %></td>
<td><%= httpBindManager.getHttpBindUnsecurePort() %></td>
<td><img src="images/blank.gif" width="1" height="1" alt=""></td>
<td><fmt:message key="ports.http_bind" /></td>
......@@ -591,7 +573,7 @@
if (httpBindManager.isHttpsBindActive()) {
%>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : interfaceName %></td>
<td><%= interfaceName %></td>
<td><%= httpBindManager.getHttpBindSecurePort() %></td>
<td><img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/></td>
<td><fmt:message key="ports.http_bind" /></td>
......@@ -603,7 +585,7 @@
if (mediaProxyService.isEnabled()) {
%>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : interfaceName %></td>
<td><%= interfaceName %></td>
<td><%= mediaProxyService.getMinPort() %> - <%= mediaProxyService.getMaxPort() %></td>
<td><img src="images/blank.gif" width="1" height="1" alt=""></td>
<td><fmt:message key="ports.media_proxy" /></td>
......@@ -611,7 +593,7 @@
</tr>
<% } %>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : interfaceName %></td>
<td><%= interfaceName %></td>
<td><%= flashCrossDomainHandler.getPort() %></td>
<td><img src="images/blank.gif" width="1" height="1" alt=""></td>
<td><fmt:message key="ports.flash_cross_domain" /></td>
......@@ -621,7 +603,7 @@
if (JMXManager.isEnabled()) {
%>
<tr>
<td><%= interfaceName == null ? LocaleUtils.getLocalizedString("ports.all_ports") : interfaceName %></td>
<td><%= interfaceName %></td>
<td><%= JMXManager.getPort() %></td>
<td><% if (JMXManager.isSecure()) {
%><img src="images/user.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.jmx_console.alt" />" title="<fmt:message key="ports.jmx_console.alt" />"/><%
......
<%@ page errorPage="error.jsp"%>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStore"%>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreManager"%>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType"%>
<%@ page import="org.jivesoftware.util.ParamUtils"%>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="javax.xml.bind.DatatypeConverter" %>
<%@ page import="java.security.AlgorithmParameters" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreConfig" %>
<%@ page import="java.security.KeyStore" %>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
......@@ -21,21 +20,21 @@
<% webManager.init(request, response, session, application, out );
final String alias = ParamUtils.getParameter( request, "alias" );
final String storePurposeText = ParamUtils.getParameter( request, "storePurpose" );
final String storePurposeText = ParamUtils.getParameter( request, "storeConnectionType" );
final boolean isTrustStore = ParamUtils.getBooleanParameter( request, "isTrustStore" );
final Map<String, String> errors = new HashMap<String, String>();
Purpose storePurpose;
ConnectionType storeConnectionType;
try
{
storePurpose = Purpose.valueOf( storePurposeText );
storeConnectionType = ConnectionType.valueOf( storePurposeText );
} catch (RuntimeException ex) {
errors.put( "storePurpose", ex.getMessage() );
storePurpose = null;
errors.put( "storeConnectionType", ex.getMessage() );
storeConnectionType = null;
}
pageContext.setAttribute( "storePurpose", storePurpose );
pageContext.setAttribute( "storeConnectionType", storeConnectionType );
if (alias == null) {
errors.put("alias", "The alias has not been specified.");
......@@ -44,15 +43,15 @@
{
try
{
final KeyStore store;
final CertificateStore store;
if (isTrustStore) {
store = SSLConfig.getTrustStore( storePurpose );
store = CertificateStoreManager.getTrustStore( storeConnectionType );
} else {
store = SSLConfig.getIdentityStore( storePurpose );
store = CertificateStoreManager.getIdentityStore( storeConnectionType );
}
// Get the certificate
final X509Certificate certificate = (X509Certificate) store.getCertificate( alias );
final X509Certificate certificate = (X509Certificate) store.getStore().getCertificate( alias );
if ( certificate == null ) {
errors.put( "alias", "alias" );
......@@ -70,9 +69,9 @@
// Handle a "go back" click:
if ( request.getParameter( "back" ) != null ) {
if ( isTrustStore ) {
response.sendRedirect( "security-truststore.jsp?storePurpose=" + storePurpose );
response.sendRedirect( "security-truststore.jsp?storeConnectionType=" + storeConnectionType );
} else {
response.sendRedirect( "security-keystore.jsp?storePurpose=" + storePurpose );
response.sendRedirect( "security-keystore.jsp?storeConnectionType=" + storeConnectionType );
}
return;
}
......@@ -448,7 +447,7 @@
<br/>
<form action="security-certificate-details.jsp">
<input type="hidden" name="storePurpose" value="${storePurpose}"/>
<input type="hidden" name="storeConnectionType" value="${storeConnectionType}"/>
<div style="text-align: center;">
<input type="submit" name="back" value="<fmt:message key="session.details.back_button"/>">
</div>
......
<%@ page errorPage="error.jsp"%>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig"%>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
......@@ -94,17 +91,17 @@
<tr>
<td><label for="loc-key-socket">Identity Store:</label></td>
<td><input id="loc-key-socket" name="loc-key-socket" type="text" size="40" value="${locKeySocket}"/></td>
<td><a href="security-keystore.jsp?storePurpose=SOCKETBASED_IDENTITYSTORE">Manage Store Contents</a></td>
<td><a href="security-keystore.jsp?storeConnectionType=SOCKETBASED_IDENTITYSTORE">Manage Store Contents</a></td>
</tr>
<tr>
<td><label for="loc-trust-socket-s2s">Server Trust Store:</label></td>
<td><input id="loc-trust-socket-s2s" name="loc-trust-socket-s2s" type="text" size="40" value="${locTrustSocketS2S}"/></td>
<td><a href="security-truststore.jsp?storePurpose=SOCKETBASED_S2S_TRUSTSTORE">Manage Store Contents</a></td>
<td><a href="security-truststore.jsp?storeConnectionType=SOCKETBASED_S2S_TRUSTSTORE">Manage Store Contents</a></td>
</tr>
<tr>
<td><label for="loc-trust-socket-c2s">Client Trust Store:</label></td>
<td><input id="loc-trust-socket-c2s" name="loc-trust-socket-c2s" type="text" size="40" value="${locTrustSocketC2S}"/></td>
<td><a href="security-truststore.jsp?storePurpose=SOCKETBASED_C2S_TRUSTSTORE">Manage Store Contents</a></td>
<td><a href="security-truststore.jsp?storeConnectionType=SOCKETBASED_C2S_TRUSTSTORE">Manage Store Contents</a></td>
</tr>
</tbody>
</table>
......@@ -129,12 +126,12 @@
<tr>
<td><label for="loc-key-bosh">Identity Store:</label></td>
<td><input id="loc-key-bosh" name="loc-key-bosh" type="text" size="40" value="${locKeyBosh}"/></td>
<td><a href="security-keystore.jsp?storePurpose=BOSHBASED_IDENTITYSTORE">Manage Store Contents</a></td>
<td><a href="security-keystore.jsp?storeConnectionType=BOSHBASED_IDENTITYSTORE">Manage Store Contents</a></td>
</tr>
<tr>
<td><label for="loc-trust-bosh-c2s">Client Trust Store:</label></td>
<td><input id="loc-trust-bosh-c2s" name="loc-trust-bosh-c2s" type="text" size="40" value="${locTrustBoshC2S}"/></td>
<td><a href="security-truststore.jsp?storePurpose=BOSHBASED_C2S_TRUSTSTORE">Manage Store Contents</a></td>
<td><a href="security-truststore.jsp?storeConnectionType=BOSHBASED_C2S_TRUSTSTORE">Manage Store Contents</a></td>
</tr>
</tbody>
</table>
......@@ -155,12 +152,12 @@
<tr>
<td><label for="loc-key-webadmin">Identity Store:</label></td>
<td><input id="loc-key-webadmin" name="loc-key-webadmin" type="text" size="40" value="${locKeyWebadmin}"/></td>
<td><a href="security-keystore.jsp?storePurpose=WEBADMIN_IDENTITYSTORE">Manage Store Contents</a></td>
<td><a href="security-keystore.jsp?storeConnectionType=WEBADMIN_IDENTITYSTORE">Manage Store Contents</a></td>
</tr>
<tr>
<td><label for="loc-trust-webadmin">Trust Store:</label></td>
<td><input id="loc-trust-webadmin" name="loc-trust-webadmin" type="text" size="40" value="${locTrustWebadmin}"/></td>
<td><a href="security-keystore.jsp?storePurpose=WEBADMIN_TRUSTSTORE">Manage Store Contents</a></td>
<td><a href="security-keystore.jsp?storeConnectionType=WEBADMIN_TRUSTSTORE">Manage Store Contents</a></td>
</tr>
</tbody>
</table>
......@@ -180,12 +177,12 @@
<tr>
<td><label for="loc-key-administrative">Identity Store:</label></td>
<td><input id="loc-key-administrative" name="loc-key-administrative" type="text" size="40" value="${locKeyAdministrative}"/></td>
<td><a href="security-keystore.jsp?storePurpose=ADMINISTRATIVE_IDENTITYSTORE">Manage Store Contents</a></td>
<td><a href="security-keystore.jsp?storeConnectionType=ADMINISTRATIVE_IDENTITYSTORE">Manage Store Contents</a></td>
</tr>
<tr>
<td><label for="loc-trust-administrative">Trust Store:</label></td>
<td><input id="loc-trust-administrative" name="loc-trust-administrative" type="text" size="40" value="${locTrustAdministrative}"/></td>
<td><a href="security-truststore.jsp?storePurpose=ADMINISTRATIVE_TRUSTSTORE">Manage Store Contents</a></td>
<td><a href="security-truststore.jsp?storeConnectionType=ADMINISTRATIVE_TRUSTSTORE">Manage Store Contents</a></td>
</tr>
</tbody>
</table>
......
<%@ page errorPage="error.jsp" %>
<%@ page import="org.jivesoftware.util.CertificateManager" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig" %>
<%@ page import="java.io.ByteArrayInputStream" %>
<%@ page import="java.security.PrivateKey" %>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="org.jivesoftware.openfire.container.PluginManager" %>
<%@ page import="org.jivesoftware.openfire.container.AdminConsolePlugin" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStoreConfig" %>
<%@ page import="java.util.*" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreManager" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStore" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Set" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
......@@ -27,39 +25,39 @@
final boolean delete = ParamUtils.getBooleanParameter(request, "delete");
final boolean importReply = ParamUtils.getBooleanParameter(request, "importReply");
final String alias = ParamUtils.getParameter( request, "alias" );
final String storePurposeText = ParamUtils.getParameter( request, "storePurpose" );
final String storePurposeText = ParamUtils.getParameter( request, "storeConnectionType" );
final Map<String, String> errors = new HashMap<String, String>();
Purpose storePurpose = null;
IdentityStoreConfig storeConfig = null;
ConnectionType storeConnectionType = null;
IdentityStore identityStore = null;
try
{
storePurpose = Purpose.valueOf( storePurposeText );
storeConfig = SSLConfig.getInstance().getIdentityStoreConfig( storePurpose );
if ( storeConfig == null )
storeConnectionType = ConnectionType.valueOf( storePurposeText );
identityStore = CertificateStoreManager.getIdentityStore( storeConnectionType );
if ( identityStore == null )
{
errors.put( "storeConfig", "Unable to get an instance." );
errors.put( "identityStore", "Unable to get an instance." );
}
}
catch (RuntimeException ex)
{
errors.put( "storePurpose", ex.getMessage() );
errors.put( "storeConnectionType", ex.getMessage() );
}
if ( errors.isEmpty() )
{
pageContext.setAttribute( "storePurpose", storePurpose );
pageContext.setAttribute( "storeConfig", storeConfig );
pageContext.setAttribute( "storeConnectionType", storeConnectionType );
pageContext.setAttribute( "identityStore", identityStore );
final Set<Purpose> sameStorePurposes = Collections.EMPTY_SET; // TODO FIXME: SSLConfig.getInstance().getOtherPurposesForSameStore( storePurpose );
pageContext.setAttribute( "sameStorePurposes", sameStorePurposes );
final Set<ConnectionType> sameStoreConnectionTypes = Collections.EMPTY_SET; // TODO FIXME: SSLConfig.getInstance().getOtherPurposesForSameStore( storeConnectionType );
pageContext.setAttribute( "sameStoreConnectionTypes", sameStoreConnectionTypes );
final Map<String, X509Certificate> certificates = storeConfig.getAllCertificates();
final Map<String, X509Certificate> certificates = identityStore.getAllCertificates();
pageContext.setAttribute( "certificates", certificates );
pageContext.setAttribute( "validRSACert", storeConfig.containsDomainCertificate( "RSA" ) );
pageContext.setAttribute( "validDSACert", storeConfig.containsDomainCertificate( "DSA" ) );
pageContext.setAttribute( "validRSACert", identityStore.containsDomainCertificate( "RSA" ) );
pageContext.setAttribute( "validDSACert", identityStore.containsDomainCertificate( "DSA" ) );
if ( delete )
{
......@@ -71,11 +69,11 @@
{
try
{
storeConfig.delete( alias );
identityStore.delete( alias );
// Log the event
webManager.logEvent( "deleted SSL cert from " + storePurposeText + " with alias " + alias, null );
response.sendRedirect( "security-keystore.jsp?storePurpose=" + storePurposeText + "&deletesuccess=true" );
response.sendRedirect( "security-keystore.jsp?storeConnectionType=" + storePurposeText + "&deletesuccess=true" );
return;
}
catch ( Exception e )
......@@ -149,7 +147,7 @@
<c:if test="${restartNeeded}">
<admin:infobox type="warning">
<fmt:message key="ssl.certificates.keystore.restart_server">
<fmt:param value="<a href='server-restart.jsp?page=security-keystore.jsp&storePurpose=${storePurpose}'>"/>
<fmt:param value="<a href='server-restart.jsp?page=security-keystore.jsp&storeConnectionType=${storeConnectionType}'>"/>
<fmt:param value="</a>"/>
</fmt:message>
</admin:infobox>
......@@ -177,9 +175,9 @@
<c:if test="${not validDSACert or not validRSACert}">
<admin:infobox type="warning">
<fmt:message key="ssl.certificates.keystore.no_installed">
<fmt:param value="<a href='security-keystore.jsp?generate=true&storePurpose=${storePurpose}'>"/>
<fmt:param value="<a href='security-keystore.jsp?generate=true&storeConnectionType=${storeConnectionType}'>"/>
<fmt:param value="</a>"/>
<fmt:param value="<a href='import-keystore-certificate.jsp?storePurpose=${storePurpose}'>"/>
<fmt:param value="<a href='import-keystore-certificate.jsp?storeConnectionType=${storeConnectionType}'>"/>
<fmt:param value="</a>"/>
</fmt:message>
</admin:infobox>
......@@ -202,7 +200,7 @@
<p>
<fmt:message key="ssl.certificates.keystore.info">
<fmt:param value="<a href='import-keystore-certificate.jsp?storePurpose=${storePurpose}'>"/>
<fmt:param value="<a href='import-keystore-certificate.jsp?storeConnectionType=${storeConnectionType}'>"/>
<fmt:param value="</a>"/>
</fmt:message>
</p>
......@@ -230,13 +228,13 @@
</thead>
<tbody>
<c:choose>
<c:when test="${empty storeConfig.allCertificates}">
<c:when test="${empty certificates}">
<tr valign="top">
<td colspan="5"><em>(<fmt:message key="global.none"/>)</em></td>
</tr>
</c:when>
<c:otherwise>
<c:forEach var="certificateEntry" items="${storeConfig.allCertificates}">
<c:forEach var="certificateEntry" items="${certificates}">
<c:set var="certificate" value="${certificateEntry.value}"/>
<c:set var="alias" value="${certificateEntry.key}"/>
<c:set var="identities" value="${admin:serverIdentities(certificateEntry.value)}"/>
......@@ -276,7 +274,7 @@
%>
<tr valign="top">
<td>
<a href="security-certificate-details.jsp?storePurpose=${storePurpose}&alias=${alias}" title="<fmt:message key='session.row.cliked'/>">
<a href="security-certificate-details.jsp?storeConnectionType=${storeConnectionType}&alias=${alias}" title="<fmt:message key='session.row.cliked'/>">
<c:forEach items="${identities}" var="currentItem" varStatus="stat">
<c:out value="${stat.first ? '' : ','} ${currentItem}"/>
</c:forEach>
......@@ -328,7 +326,7 @@
<c:out value="${certificate.publicKey.algorithm}"/>
</td>
<td width="1" align="center">
<a href="security-keystore.jsp?alias=${alias}&storePurpose=${storePurpose}&delete=true"
<a href="security-keystore.jsp?alias=${alias}&storeConnectionType=${storeConnectionType}&delete=true"
title="<fmt:message key="global.click_delete"/>"
onclick="return confirm('<fmt:message key="ssl.certificates.confirm_delete"/>');"
><img src="images/delete-16x16.gif" width="16" height="16" border="0" alt=""></a>
......
<%@ page errorPage="error.jsp"%>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig"%>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreManager"%>
<%@ page import="org.jivesoftware.openfire.keystore.TrustStore"%>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType"%>
<%@ page import="org.jivesoftware.util.ParamUtils"%>
<%@ page import="java.security.cert.X509Certificate"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.TrustStoreConfig" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
......@@ -20,33 +20,36 @@
final boolean delete = ParamUtils.getBooleanParameter( request, "delete" );
final String alias = ParamUtils.getParameter( request, "alias" );
final String storePurposeText = ParamUtils.getParameter(request, "storePurpose");
final String storePurposeText = ParamUtils.getParameter(request, "storeConnectionType");
final Map<String, String> errors = new HashMap<>();
Purpose storePurpose = null;
TrustStoreConfig storeConfig = null;
ConnectionType storeConnectionType = null;
TrustStore trustStore = null;
try
{
storePurpose = Purpose.valueOf( storePurposeText );
storeConfig = SSLConfig.getInstance().getTrustStoreConfig( storePurpose );
if ( storeConfig == null )
storeConnectionType = ConnectionType.valueOf( storePurposeText );
trustStore = CertificateStoreManager.getTrustStore( storeConnectionType );
if ( trustStore == null )
{
errors.put( "storeConfig", "Unable to get an instance." );
errors.put( "trustStore", "Unable to get an instance." );
}
}
catch (RuntimeException ex)
{
errors.put( "storePurpose", ex.getMessage() );
errors.put( "storeConnectionType", ex.getMessage() );
}
if ( errors.isEmpty() )
{
pageContext.setAttribute( "storePurpose", storePurpose );
pageContext.setAttribute( "storeConfig", storeConfig );
pageContext.setAttribute( "storeConnectionType", storeConnectionType );
pageContext.setAttribute( "trustStore", trustStore );
final Set<ConnectionType> sameStoreConnectionTypes = Collections.EMPTY_SET; // TODO FIXME: SSLConfig.getInstance().getOtherPurposesForSameStore( storeConnectionType );
pageContext.setAttribute( "sameStoreConnectionTypes", sameStoreConnectionTypes );
final Set<Purpose> sameStorePurposes = Collections.EMPTY_SET; // TODO FIXME: SSLConfig.getInstance().getOtherPurposesForSameStore( storePurpose );
pageContext.setAttribute( "sameStorePurposes", sameStorePurposes );
final Map<String, X509Certificate> certificates = trustStore.getAllCertificates();
pageContext.setAttribute( "certificates", certificates );
if ( delete )
{
......@@ -58,11 +61,11 @@
{
try
{
storeConfig.delete( alias );
trustStore.delete( alias );
// Log the event
webManager.logEvent( "deleted SSL cert from " + storePurposeText + " with alias " + alias, null );
response.sendRedirect( "security-truststore.jsp?storePurpose=" + storePurposeText + "&deletesuccess=true" );
response.sendRedirect( "security-truststore.jsp?storeConnectionType=" + storePurposeText + "&deletesuccess=true" );
return;
}
catch ( Exception e )
......@@ -78,7 +81,7 @@
<html>
<head>
<title><fmt:message key="certificate-management.purpose.${storePurpose}.title"/></title>
<title><fmt:message key="certificate-management.connectionType.${storeConnectionType}.title"/></title>
<meta name="pageID" content="security-truststore"/>
<style>
.info-header {
......@@ -130,9 +133,9 @@
<admin:infobox type="success"><fmt:message key="ssl.certificates.added_updated"/></admin:infobox>
</c:if>
<c:if test="${storePurpose != null}">
<c:if test="${storeConnectionType != null}">
<p>
<fmt:message key="certificate-management.purpose.${storePurpose}.description"/>
<fmt:message key="certificate-management.connectionType.${storeConnectionType}.description"/>
</p>
<table border="0" width="100%">
......@@ -144,26 +147,26 @@
<tbody>
<tr>
<td class="c1">File location:</td>
<td class="c2"><c:out value="${storeConfig.path}"/></td>
<td class="c2"><c:out value="${trustStore.configuration.file}"/></td>
</tr>
<tr>
<td class="c1">Type:</td>
<td class="c2"><c:out value="${storeConfig.type}"/></td>
<td class="c2"><c:out value="${trustStore.configuration.type}"/></td>
</tr>
<tr>
<td class="c1">Password:</td>
<td class="c2"><c:out value="${storeConfig.password}"/></td>
<td class="c2"><c:out value="${trustStore.configuration.password}"/></td>
</tr>
</tbody>
</table>
</td>
<td valign="top" width="40%">
<c:if test="${not empty sameStorePurposes}">
<c:if test="${not empty sameStoreConnectionTypes}">
<admin:infobox type="info">
This store is re-used for these additional purposes. Any changes to this store will also affect that functionality!
<ul style="margin-top: 1em;">
<c:forEach var="sameStorePurpose" items="${sameStorePurposes}">
<li><fmt:message key="certificate-management.purpose.${sameStorePurpose}.title"/></li>
<c:forEach var="sameStorePurpose" items="${sameStoreConnectionTypes}">
<li><fmt:message key="certificate-management.connectionType.${sameStorePurpose}.title"/></li>
</c:forEach>
</ul>
</admin:infobox>
......@@ -174,7 +177,7 @@
<p>
<fmt:message key="ssl.certificates.truststore.link-to-import">
<fmt:param value="<a href='import-truststore-certificate.jsp?storePurpose=${storePurpose}'>"/>
<fmt:param value="<a href='import-truststore-certificate.jsp?storeConnectionType=${storeConnectionType}'>"/>
<fmt:param value="</a>"/>
</fmt:message>
</p>
......@@ -199,13 +202,13 @@
<tbody>
<c:choose>
<c:when test="${empty storeConfig.allCertificates}">
<c:when test="${empty certificates}">
<tr valign="top">
<td colspan="5"><em>(<fmt:message key="global.none"/>)</em></td>
</tr>
</c:when>
<c:otherwise>
<c:forEach var="certificateEntry" items="${storeConfig.allCertificates}">
<c:forEach var="certificateEntry" items="${certificates}">
<c:set var="certificate" value="${certificateEntry.value}"/>
<c:set var="alias" value="${certificateEntry.key}"/>
......@@ -227,7 +230,7 @@
<tr valign="top">
<td>
<a href="security-certificate-details.jsp?storePurpose=${storePurpose}&alias=${alias}" title="<fmt:message key='session.row.cliked'/>">
<a href="security-certificate-details.jsp?storeConnectionType=${storeConnectionType}&alias=${alias}" title="<fmt:message key='session.row.cliked'/>">
<c:choose>
<c:when test="${empty fn:trim(organization)}">
<c:out value="${commonname}"/>
......@@ -261,7 +264,7 @@
<c:out value="${certificate.publicKey.algorithm}"/>
</td>
<td width="1" align="center">
<a href="security-truststore.jsp?storePurpose=${storePurpose}&alias=${alias}&delete=true"
<a href="security-truststore.jsp?storeConnectionType=${storeConnectionType}&alias=${alias}&delete=true"
title="<fmt:message key="global.click_delete"/>"
onclick="return confirm('<fmt:message key="ssl.certificates.confirm_delete"/>');"
><img src="images/delete-16x16.gif" width="16" height="16" border="0" alt=""></a>
......
......@@ -83,7 +83,7 @@
verifyRoot = ParamUtils.getBooleanParameter(request, "verifyRoot", verifyRoot);
selfSigned = ParamUtils.getBooleanParameter(request, "selfSigned", selfSigned);
verifyIdentity = ParamUtils.getBooleanParameter(request, "verifyIdentity", verifyIdentity);
verifyValidity = ParamUtils.getBooleanParameter(request, "verifyValidity", verifyValidity);
verifyValidity = ParamUtils.getBooleanParameter(request, "verifyCertificateValidity", verifyValidity);
Map<String, String> settings = new HashMap<String, String>();
// If there are no errors check if there is a need to run a force test
......
......@@ -27,6 +27,7 @@
if (session == null || session.getAttribute("xmppSettings") == null || session.getAttribute("xmlSettings") == null) {
// Session appears to have timed out, send back to first page.
response.sendRedirect("index.jsp");
return;
}
// First, update with XMPPSettings
......
......@@ -27,6 +27,9 @@
%>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.openfire.session.ConnectionSettings" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionManagerImpl" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionListener" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
......@@ -50,75 +53,118 @@
String server_tls = ParamUtils.getParameter(request, "server_tls");
boolean selfSigned = ParamUtils.getBooleanParameter(request, "selfSigned");
if (update) {
if ("req".equals(clientSecurityRequired)) {
final ConnectionManagerImpl connectionManager = (ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager();
final ConnectionListener clientListener = connectionManager.getListener( ConnectionType.SOCKET_C2S, false );
final ConnectionListener clientListenerSsl = connectionManager.getListener( ConnectionType.SOCKET_C2S, true );
final ConnectionListener serverListener = connectionManager.getListener( ConnectionType.SOCKET_S2S, false );
final ConnectionListener serverListenerSsl = connectionManager.getListener( ConnectionType.SOCKET_S2S, true );
if (update)
{
// Client-to-server settings
if ("req".equals(clientSecurityRequired))
{
// User selected that security is required
// Enable 5222 port and make TLS required
XMPPServer.getInstance().getConnectionManager().enableClientListener(true);
LocalClientSession.setTLSPolicy(Connection.TLSPolicy.required);
clientListener.setTLSPolicy( Connection.TLSPolicy.required );
clientListener.enable( true );
// Enable 5223 port (old SSL port)
XMPPServer.getInstance().getConnectionManager().enableClientSSLListener(true);
} else if ("notreq".equals(clientSecurityRequired)) {
clientListenerSsl.enable( true );
}
else if ("notreq".equals(clientSecurityRequired))
{
// User selected that security is NOT required
// Enable 5222 port and make TLS optional
XMPPServer.getInstance().getConnectionManager().enableClientListener(true);
LocalClientSession.setTLSPolicy(Connection.TLSPolicy.optional);
clientListener.setTLSPolicy( Connection.TLSPolicy.optional );
clientListener.enable( true );
// Enable 5223 port (old SSL port)
XMPPServer.getInstance().getConnectionManager().enableClientSSLListener(true);
} else if ("custom".equals(clientSecurityRequired)) {
clientListenerSsl.enable( true );
}
else if ("custom".equals(clientSecurityRequired))
{
// User selected custom client authentication
// Enable or disable 5223 port (old SSL port)
XMPPServer.getInstance().getConnectionManager().enableClientSSLListener("available".equals(ssl));
// Enable port 5222 and configure TLS policy
XMPPServer.getInstance().getConnectionManager().enableClientListener(true);
if ("notavailable".equals(tls)) {
LocalClientSession.setTLSPolicy(Connection.TLSPolicy.disabled);
final Connection.TLSPolicy newPolicy;
if ("disabled".equals(tls)) {
newPolicy = Connection.TLSPolicy.disabled;
} else if ("optional".equals(tls)) {
LocalClientSession.setTLSPolicy(Connection.TLSPolicy.optional);
newPolicy = Connection.TLSPolicy.optional;
} else {
LocalClientSession.setTLSPolicy(Connection.TLSPolicy.required);
newPolicy = Connection.TLSPolicy.required;
}
clientListener.setTLSPolicy( newPolicy );
clientListener.enable( true );
// Enable or disable 5223 port (old SSL port)
clientListenerSsl.enable( "available".equals( ssl ) );
}
if ("req".equals(serverSecurityRequired)) {
// Server to Server settings
if ("req".equals(serverSecurityRequired))
{
// User selected that security for s2s is required
// Enable TLS and disable server dialback
XMPPServer.getInstance().getConnectionManager().enableServerListener(true);
JiveGlobals.setProperty(ConnectionSettings.Server.TLS_ENABLED, "true");
JiveGlobals.setProperty(ConnectionSettings.Server.DIALBACK_ENABLED, "false");
} else if ("notreq".equals(serverSecurityRequired)) {
serverListener.setTLSPolicy( Connection.TLSPolicy.required );
JiveGlobals.setProperty( ConnectionSettings.Server.TLS_ENABLED, "true" );
JiveGlobals.setProperty( ConnectionSettings.Server.DIALBACK_ENABLED, "false" );
serverListener.enable( true );
// Enable legacy SSL port
serverListenerSsl.enable( true );
}
else if ("notreq".equals(serverSecurityRequired))
{
// User selected that security for s2s is NOT required
// Enable TLS and enable server dialback
XMPPServer.getInstance().getConnectionManager().enableServerListener(true);
serverListener.setTLSPolicy( Connection.TLSPolicy.optional );
JiveGlobals.setProperty(ConnectionSettings.Server.TLS_ENABLED, "true");
JiveGlobals.setProperty(ConnectionSettings.Server.DIALBACK_ENABLED, "true");
} else if ("custom".equals(serverSecurityRequired)) {
serverListener.enable( true );
// Enable legacy SSL port
serverListenerSsl.enable( true );
}
else if ("custom".equals(serverSecurityRequired))
{
// User selected custom server authentication
boolean dialbackEnabled = "available".equals(dialback);
boolean tlsEnabled = "optional".equals(server_tls);
final boolean dialbackEnabled = "available".equals(dialback);
final boolean tlsEnabled = "optional".equals(server_tls) || "required".equals(server_tls);
if (dialbackEnabled || tlsEnabled) {
XMPPServer.getInstance().getConnectionManager().enableServerListener(true);
if (dialbackEnabled || tlsEnabled)
{
// Enable or disable TLS for s2s connections
final Connection.TLSPolicy newPolicy;
if ("disabled".equals(server_tls)) {
newPolicy = Connection.TLSPolicy.disabled;
} else if ("optional".equals(tls)) {
newPolicy = Connection.TLSPolicy.optional;
} else {
newPolicy = Connection.TLSPolicy.required;
}
serverListener.setTLSPolicy( newPolicy );
JiveGlobals.setProperty(ConnectionSettings.Server.TLS_ENABLED, tlsEnabled ? "true" : "false");
// Enable or disable server dialback
JiveGlobals.setProperty(ConnectionSettings.Server.DIALBACK_ENABLED, dialbackEnabled ? "true" : "false");
// Enable or disable TLS for s2s connections
JiveGlobals.setProperty(ConnectionSettings.Server.TLS_ENABLED, tlsEnabled ? "true" : "false");
} else {
XMPPServer.getInstance().getConnectionManager().enableServerListener(false);
// Disable server dialback
JiveGlobals.setProperty(ConnectionSettings.Server.DIALBACK_ENABLED, "false");
serverListener.enable( true );
// Disable TLS for s2s connections
JiveGlobals.setProperty(ConnectionSettings.Server.TLS_ENABLED, "false");
// Enable legacy SSL port
serverListenerSsl.enable( true );
}
else
{
serverListener.enable( false );
serverListenerSsl.enable( false );
}
}
ServerDialback.setEnabledForSelfSigned(selfSigned);
......@@ -131,51 +177,56 @@
webManager.logEvent("updated SSL configuration",
ConnectionSettings.Server.DIALBACK_ENABLED + " = " + JiveGlobals.getProperty(ConnectionSettings.Server.DIALBACK_ENABLED) + "\n" +
ConnectionSettings.Server.TLS_ENABLED + " = " + JiveGlobals.getProperty(ConnectionSettings.Server.TLS_ENABLED) + "\n" +
ConnectionSettings.Server.TLS_POLICY + " = " + JiveGlobals.getProperty(ConnectionSettings.Server.TLS_POLICY) + "\n" +
"xmpp.client.cert.policy = " + JiveGlobals.getProperty("xmpp.client.cert.policy") + "\n" +
"httpbind.client.cert.policy = " + JiveGlobals.getProperty("httpbind.client.cert.policy")
);
}
// Set page vars
ConnectionManager connectionManager = XMPPServer.getInstance().getConnectionManager();
if (connectionManager.isClientListenerEnabled() && connectionManager.isClientSSLListenerEnabled()) {
if (Connection.TLSPolicy.required.equals(LocalClientSession.getTLSPolicy())) {
// Set page vars (client-to-client)
if ( clientListener.isEnabled() && clientListenerSsl.isEnabled() )
{
switch ( clientListener.getTLSPolicy() )
{
case required:
clientSecurityRequired = "req";
ssl = "available";
tls = "required";
} else if (Connection.TLSPolicy.optional.equals(LocalClientSession.getTLSPolicy())) {
break;
case optional:
clientSecurityRequired = "notreq";
ssl = "available";
tls = "optional";
} else {
break;
default:
clientSecurityRequired = "custom";
ssl = "available";
tls = "notavailable";
tls = "disabled";
break;
}
} else {
}
else
{
clientSecurityRequired = "custom";
ssl = connectionManager.isClientSSLListenerEnabled() ? "available" : "notavailable";
tls = Connection.TLSPolicy.disabled.equals(LocalClientSession.getTLSPolicy()) ? "notavailable" :
LocalClientSession.getTLSPolicy().toString();
ssl = clientListenerSsl.isEnabled() ? "available" : "disabled";
tls = clientListener.getTLSPolicy().toString();
}
boolean tlsEnabled = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true);
boolean dialbackEnabled = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.DIALBACK_ENABLED, true);
if (tlsEnabled) {
if (dialbackEnabled) {
serverSecurityRequired = "notreq";
dialback = "available";
server_tls = "optional";
} else {
// Set page vars (client-to-server)
final Connection.TLSPolicy tlsEnabled = serverListener.getTLSPolicy();
final boolean dialbackEnabled = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.DIALBACK_ENABLED, true);
if (tlsEnabled.equals( Connection.TLSPolicy.required ) && !dialbackEnabled ) {
serverSecurityRequired = "req";
dialback = "notavailable";
server_tls = "optional";
}
} else if ( tlsEnabled.equals( Connection.TLSPolicy.optional ) && dialbackEnabled ) {
serverSecurityRequired = "notreq";
} else {
serverSecurityRequired = "custom";
dialback = dialbackEnabled ? "available" : "notavailable";
server_tls = "notavailable";
}
server_tls = tlsEnabled.name();
dialback = dialbackEnabled ? "available" : "disabled";
selfSigned = ServerDialback.isEnabledForSelfSigned();
clientMutualAuthenticationSocket = JiveGlobals.getProperty( "xmpp.client.cert.policy", "disabled" );
......@@ -291,7 +342,7 @@
<fmt:message key="ssl.settings.client.customSSL" />
</td>
<td width="99%">
<input type="radio" name="ssl" value="notavailable" id="rb04" <%= ("notavailable".equals(ssl) ? "checked" : "") %>
<input type="radio" name="ssl" value="disabled" id="rb04" <%= ("disabled".equals(ssl) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb04"><fmt:message key="ssl.settings.notavailable" /></label>&nbsp;&nbsp;
<input type="radio" name="ssl" value="available" id="rb05" <%= ("available".equals(ssl) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb05"><fmt:message key="ssl.settings.available" /></label>
......@@ -302,7 +353,7 @@
<fmt:message key="ssl.settings.client.customTLS" />
</td>
<td width="99%">
<input type="radio" name="tls" value="notavailable" id="rb06" <%= ("notavailable".equals(tls) ? "checked" : "") %>
<input type="radio" name="tls" value="disabled" id="rb06" <%= ("disabled".equals(tls) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb06"><fmt:message key="ssl.settings.notavailable" /></label>&nbsp;&nbsp;
<input type="radio" name="tls" value="optional" id="rb07" <%= ("optional".equals(tls) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb07"><fmt:message key="ssl.settings.optional" /></label>&nbsp;&nbsp;
......@@ -398,7 +449,7 @@
<fmt:message key="ssl.settings.server.dialback" />
</td>
<td width="99%">
<input type="radio" name="dialback" value="notavailable" id="rb12" <%= ("notavailable".equals(dialback) ? "checked" : "") %>
<input type="radio" name="dialback" value="disabled" id="rb12" <%= ("disabled".equals(dialback) ? "checked" : "") %>
onclick="this.form.serverSecurityRequired[2].checked=true;">&nbsp;<label for="rb12"><fmt:message key="ssl.settings.notavailable" /></label>&nbsp;&nbsp;
<input type="radio" name="dialback" value="available" id="rb13" <%= ("available".equals(dialback) ? "checked" : "") %>
onclick="this.form.serverSecurityRequired[2].checked=true;">&nbsp;<label for="rb13"><fmt:message key="ssl.settings.available" /></label>
......@@ -409,10 +460,12 @@
<fmt:message key="ssl.settings.server.customTLS" />
</td>
<td width="99%">
<input type="radio" name="server_tls" value="notavailable" id="rb14" <%= ("notavailable".equals(server_tls) ? "checked" : "") %>
<input type="radio" name="server_tls" value="disabled" id="rb14" <%= ("disabled".equals(server_tls) ? "checked" : "") %>
onclick="this.form.serverSecurityRequired[2].checked=true;">&nbsp;<label for="rb14"><fmt:message key="ssl.settings.notavailable" /></label>&nbsp;&nbsp;
<input type="radio" name="server_tls" value="optional" id="rb15" <%= ("optional".equals(server_tls) ? "checked" : "") %>
onclick="this.form.serverSecurityRequired[2].checked=true;">&nbsp;<label for="rb15"><fmt:message key="ssl.settings.optional" /></label>&nbsp;&nbsp;
<input type="radio" name="server_tls" value="required" id="rb22" <%= ("required".equals(server_tls) ? "checked" : "") %>
onclick="this.form.serverSecurityRequired[2].checked=true;">&nbsp;<label for="rb22"><fmt:message key="ssl.settings.required" /></label>&nbsp;&nbsp;
</td>
</tr>
</table>
......
<%@ page import="org.jivesoftware.util.CertificateManager" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStoreConfig" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
......@@ -25,20 +18,20 @@
final String city = ParamUtils.getParameter(request, "city");
final String state = ParamUtils.getParameter(request, "state");
final String countryCode = ParamUtils.getParameter(request, "country");
final String storePurposeText = ParamUtils.getParameter(request, "storePurpose");
final String storePurposeText = ParamUtils.getParameter(request, "storeConnectionType");
final Map<String, String> errors = new HashMap<String, String>();
Purpose storePurpose;
ConnectionType storeConnectionType;
try
{
storePurpose = Purpose.valueOf( storePurposeText );
storeConnectionType = ConnectionType.valueOf( storePurposeText );
} catch (RuntimeException ex) {
errors.put( "storePurpose", ex.getMessage() );
storePurpose = null;
errors.put( "storeConnectionType", ex.getMessage() );
storeConnectionType = null;
}
pageContext.setAttribute( "storePurpose", storePurpose );
pageContext.setAttribute( "storeConnectionType", storeConnectionType );
// if (save) {
//
......@@ -63,7 +56,7 @@
// }
// if (errors.size() == 0) {
// try {
// final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( storePurpose );
// final IdentityStore identityStoreConfig = (IdentityStore) SSLConfig.getInstance().getStoreConfig( storeConnectionType );
//
// identityStoreConfig.ensureSelfSignedDomainCertificates( name, organizationalUnit, organization, city, state, countryCode, "rsa", "dsa" );
// // Regenerate self-sign certs whose subjectDN matches the issuerDN and set the new issuerDN
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment