summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan Skinner <aidan@apache.org>2009-02-13 14:00:10 +0000
committerAidan Skinner <aidan@apache.org>2009-02-13 14:00:10 +0000
commit448e6bff629c2d8d9b3cbd7c39d8eefd2b33c06e (patch)
treedf337797ff6438c5b09f208604fd4d54951ee312
parentbc5378a4b3220ec8c1e700c5fe705d983b4b0c7b (diff)
downloadqpid-python-448e6bff629c2d8d9b3cbd7c39d8eefd2b33c06e.tar.gz
QPID-1511 : Adds authentication and ssl encryption capabilities to the RMI based JMXConnectorServer in use, enforces use of the custom MBeanInvocationhandlerImp when using the RMI based JMX, and implements a customised RMI registry to prevent external changes being possible. Updated Management console accordingly.
Patch from Robbert Gemmell <gemmellr@dcs.gla.ac.uk> git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@744113 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--qpid/java/broker/etc/config.xml8
-rw-r--r--qpid/java/broker/etc/persistent_config.xml9
-rw-r--r--qpid/java/broker/etc/transient_config.xml9
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java312
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java5
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java182
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java267
-rw-r--r--qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java56
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java63
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini12
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini11
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini11
12 files changed, 846 insertions, 99 deletions
diff --git a/qpid/java/broker/etc/config.xml b/qpid/java/broker/etc/config.xml
index 6a8c011e77..b2b3625cca 100644
--- a/qpid/java/broker/etc/config.xml
+++ b/qpid/java/broker/etc/config.xml
@@ -43,9 +43,15 @@
<socketSendBuffer>32768</socketSendBuffer>
</connector>
<management>
- <enabled>false</enabled>
+ <enabled>true</enabled>
<jmxport>8999</jmxport>
<security-enabled>false</security-enabled>
+ <ssl>
+ <enabled>true</enabled>
+ <!-- Update below path to your keystore location, eg ${conf}/qpid.keystore -->
+ <keyStorePath>${prefix}/../test_resources/ssl/keystore.jks</keyStorePath>
+ <keyStorePassword>password</keyStorePassword>
+ </ssl>
</management>
<advanced>
<filterchain enableExecutorPool="true"/>
diff --git a/qpid/java/broker/etc/persistent_config.xml b/qpid/java/broker/etc/persistent_config.xml
index b7965a8af6..ed2c2cab1f 100644
--- a/qpid/java/broker/etc/persistent_config.xml
+++ b/qpid/java/broker/etc/persistent_config.xml
@@ -35,8 +35,15 @@
<socketSendBuffer>32768</socketSendBuffer>
</connector>
<management>
- <enabled>false</enabled>
+ <enabled>true</enabled>
<jmxport>8999</jmxport>
+ <security-enabled>false</security-enabled>
+ <ssl>
+ <enabled>true</enabled>
+ <!-- Update below path to your keystore location, eg ${conf}/qpid.keystore -->
+ <keyStorePath>${prefix}/../test_resources/ssl/keystore.jks</keyStorePath>
+ <keyStorePassword>password</keyStorePassword>
+ </ssl>
</management>
<advanced>
<filterchain enableExecutorPool="true"/>
diff --git a/qpid/java/broker/etc/transient_config.xml b/qpid/java/broker/etc/transient_config.xml
index 1dd693f60f..19ffeb9720 100644
--- a/qpid/java/broker/etc/transient_config.xml
+++ b/qpid/java/broker/etc/transient_config.xml
@@ -35,8 +35,15 @@
<socketSendBuffer>32768</socketSendBuffer>
</connector>
<management>
- <enabled>false</enabled>
+ <enabled>true</enabled>
<jmxport>8999</jmxport>
+ <security-enabled>false</security-enabled>
+ <ssl>
+ <enabled>true</enabled>
+ <!-- Update below path to your keystore location, eg ${conf}/qpid.keystore -->
+ <keyStorePath>${prefix}/../test_resources/ssl/keystore.jks</keyStorePath>
+ <keyStorePassword>password</keyStorePassword>
+ </ssl>
</management>
<advanced>
<filterchain enableExecutorPool="true"/>
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
index 659f806d58..a0d2c7dc66 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
@@ -20,6 +20,7 @@
*/
package org.apache.qpid.server.management;
+import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.registry.ApplicationRegistry;
@@ -27,6 +28,7 @@ import org.apache.qpid.server.registry.IApplicationRegistry;
import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator;
import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser;
import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser;
@@ -37,31 +39,45 @@ import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.MBeanServerForwarder;
+import javax.management.remote.rmi.RMIConnectorServer;
+import javax.management.remote.rmi.RMIJRMPServerImpl;
+import javax.management.remote.rmi.RMIServerImpl;
+import javax.rmi.ssl.SslRMIClientSocketFactory;
+import javax.rmi.ssl.SslRMIServerSocketFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.management.ManagementFactory;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
/**
- * This class starts up an MBeanserver. If out of the box agent is being used then there are no security features
- * implemented. To use the security features like user authentication, turn off the jmx options in the "QPID_OPTS" env
- * variable and use JMXMP connector server. If JMXMP connector is not available, then the standard JMXConnector will be
- * used, which again doesn't have user authentication.
+ * This class starts up an MBeanserver. If out of the box agent has been enabled then there are no
+ * security features implemented like user authentication and authorisation.
*/
public class JMXManagedObjectRegistry implements ManagedObjectRegistry
{
private static final Logger _log = Logger.getLogger(JMXManagedObjectRegistry.class);
+ private static final Logger _startupLog = Logger.getLogger("Qpid.Broker");
+
+ public static final String MANAGEMENT_PORT_CONFIG_PATH = "management.jmxport";
+ public static final int MANAGEMENT_PORT_DEFAULT = 8999;
+ public static final int PORT_EXPORT_OFFSET = 100;
private final MBeanServer _mbeanServer;
private Registry _rmiRegistry;
- private JMXServiceURL _jmxURL;
- public static final String MANAGEMENT_PORT_CONFIG_PATH = "management.jmxport";
- public static final int MANAGEMENT_PORT_DEFAULT = 8999;
public JMXManagedObjectRegistry() throws AMQException
{
@@ -77,44 +93,38 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
}
- public void start() throws IOException
+ public void start() throws IOException, ConfigurationException
{
- // Check if the "QPID_OPTS" is set to use Out of the Box JMXAgent
+ //check if system properties are set to use the JVM's out-of-the-box JMXAgent
if (areOutOfTheBoxJMXOptionsSet())
{
- _log.info("JMX: Using the out of the box JMX Agent");
+ _log.warn("JMX: Using the out of the box JMX Agent");
+ _startupLog.warn("JMX: Using the out of the box JMX Agent");
return;
}
IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
- boolean security = appRegistry.getConfiguration().getBoolean("management.security-enabled", false);
+ boolean jmxmpSecurity = appRegistry.getConfiguration().getBoolean("management.security-enabled", false);
int port = appRegistry.getConfiguration().getInt(MANAGEMENT_PORT_CONFIG_PATH, MANAGEMENT_PORT_DEFAULT);
- if (security)
- {
- // For SASL using JMXMP
- _jmxURL = new JMXServiceURL("jmxmp", null, port);
+ //retrieve the Principal Database assigned to JMX authentication duties
+ String jmxDatabaseName = appRegistry.getConfiguration().getString("security.jmx.principal-database");
+ Map<String, PrincipalDatabase> map = appRegistry.getDatabaseManager().getDatabases();
+ PrincipalDatabase db = map.get(jmxDatabaseName);
- Map env = new HashMap();
- Map<String, PrincipalDatabase> map = appRegistry.getDatabaseManager().getDatabases();
- PrincipalDatabase db = null;
+ final JMXConnectorServer cs;
+ HashMap<String,Object> env = new HashMap<String,Object>();
- for (Map.Entry<String, PrincipalDatabase> entry : map.entrySet())
- {
- if (entry.getValue() instanceof Base64MD5PasswordFilePrincipalDatabase)
- {
- db = entry.getValue();
- break;
- }
- else if (entry.getValue() instanceof PlainPasswordFilePrincipalDatabase)
- {
- db = entry.getValue();
- }
- }
+ if (jmxmpSecurity)
+ {
+ // For SASL using JMXMP
+ JMXServiceURL jmxURL = new JMXServiceURL("jmxmp", null, port);
+ String saslType = null;
if (db instanceof Base64MD5PasswordFilePrincipalDatabase)
{
+ saslType = "SASL/CRAM-MD5";
env.put("jmx.remote.profiles", "SASL/CRAM-MD5");
CRAMMD5HashedInitialiser initialiser = new CRAMMD5HashedInitialiser();
initialiser.initialise(db);
@@ -122,6 +132,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
}
else if (db instanceof PlainPasswordFilePrincipalDatabase)
{
+ saslType = "SASL/PLAIN";
PlainInitialiser initialiser = new PlainInitialiser();
initialiser.initialise(db);
env.put("jmx.remote.sasl.callback.handler", initialiser.getCallbackHandler());
@@ -131,43 +142,209 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
//workaround NPE generated from env map classloader issue when using Eclipse 3.4 to launch
env.put("jmx.remote.profile.provider.class.loader", this.getClass().getClassLoader());
- // Enable the SSL security and server authentication
- /*
- SslRMIClientSocketFactory csf = new SslRMIClientSocketFactory();
- SslRMIServerSocketFactory ssf = new SslRMIServerSocketFactory();
- env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
- env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);
- */
-
- JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(_jmxURL, env, _mbeanServer);
- MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance();
- cs.setMBeanServerForwarder(mbsf);
- cs.start();
- _log.warn("JMX: Started JMXConnector server on port '" + port + "' with SASL");
-
+ _log.warn("Starting JMXMP based JMX ConnectorServer on port '" + port + "' with " + saslType);
+ _startupLog.warn("Starting JMXMP based JMX ConnectorServer on port '" + port + "' with " + saslType);
+
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(jmxURL, env, _mbeanServer);
}
else
- {
- startJMXConnectorServer(port);
- _log.warn("JMX: Started JMXConnector server on port '" + port + "' with security disabled");
+ {
+ //Socket factories for the RMIConnectorServer, either default or SLL depending on configuration
+ RMIClientSocketFactory csf;
+ RMIServerSocketFactory ssf;
+
+ //check ssl enabled option in config, default to true if option is not set
+ boolean sslEnabled = appRegistry.getConfiguration().getBoolean("management.ssl.enabled", true);
+
+ if (sslEnabled)
+ {
+ //set the SSL related system properties used by the SSL RMI socket factories to the values
+ //given in the configuration file, unless command line settings have already been specified
+ String keyStorePath;
+
+ if(System.getProperty("javax.net.ssl.keyStore") != null)
+ {
+ keyStorePath = System.getProperty("javax.net.ssl.keyStore");
+ }
+ else{
+ keyStorePath = appRegistry.getConfiguration().getString("management.ssl.keyStorePath", null);
+ }
+
+ //check the keystore path value is valid
+ if (keyStorePath == null)
+ {
+ throw new ConfigurationException("JMX management SSL keystore path not defined, " +
+ "unable to start SSL protected JMX ConnectorServer");
+ }
+ else
+ {
+ //ensure the system property is set
+ System.setProperty("javax.net.ssl.keyStore", keyStorePath);
+
+ //check the file is usable
+ File ksf = new File(keyStorePath);
+
+ if (!ksf.exists())
+ {
+ throw new FileNotFoundException("Cannot find JMX management SSL keystore file " + ksf);
+ }
+ if (!ksf.canRead())
+ {
+ throw new FileNotFoundException("Cannot read JMX management SSL keystore file: "
+ + ksf + ". Check permissions.");
+ }
+
+ _log.info("JMX ConnectorServer using SSL keystore file " + ksf.getAbsolutePath());
+ _startupLog.info("JMX ConnectorServer using SSL keystore file " + ksf.getAbsolutePath());
+ }
+
+ //check the key store password is set
+ if (System.getProperty("javax.net.ssl.keyStorePassword") == null)
+ {
+
+ if (appRegistry.getConfiguration().getString("management.ssl.keyStorePassword") == null)
+ {
+ throw new ConfigurationException("JMX management SSL keystore password not defined, " +
+ "unable to start requested SSL protected JMX server");
+ }
+ else
+ {
+ System.setProperty("javax.net.ssl.keyStorePassword",
+ appRegistry.getConfiguration().getString("management.ssl.keyStorePassword"));
+ }
+ }
+
+ //create the SSL RMI socket factories
+ csf = new SslRMIClientSocketFactory();
+ ssf = new SslRMIServerSocketFactory();
+
+ _log.warn("Starting JMX ConnectorServer on port '"+ port + "' (+" +
+ (port +PORT_EXPORT_OFFSET) + ") with SSL");
+ _startupLog.warn("Starting JMX ConnectorServer on port '"+ port + "' (+" +
+ (port +PORT_EXPORT_OFFSET) + ") with SSL");
+ }
+ else
+ {
+ //Do not specify any specific RMI socket factories, resulting in use of the defaults.
+ csf = null;
+ ssf = null;
+
+ _log.warn("Starting JMX ConnectorServer on port '" + port + "' (+" + (port +PORT_EXPORT_OFFSET) + ")");
+ _startupLog.warn("Starting JMX ConnectorServer on port '" + port + "' (+" + (port +PORT_EXPORT_OFFSET) + ")");
+ }
+
+ //add a JMXAuthenticator implementation the env map to authenticate the RMI based JMX connector server
+ RMIPasswordAuthenticator rmipa = new RMIPasswordAuthenticator();
+ rmipa.setPrincipalDatabase(db);
+ env.put(JMXConnectorServer.AUTHENTICATOR, rmipa);
+
+ /*
+ * Start a RMI registry on the management port, to hold the JMX RMI ConnectorServer stub.
+ * Using custom socket factory to prevent anyone (including us unfortunately) binding to the registry using RMI.
+ * As a result, only binds made using the object reference will succeed, thus securing it from external change.
+ */
+ System.setProperty("java.rmi.server.randomIDs", "true");
+ _rmiRegistry = LocateRegistry.createRegistry(port, null, new CustomRMIServerSocketFactory());
+
+ /*
+ * We must now create the RMI ConnectorServer manually, as the JMX Factory methods use RMI calls
+ * to bind the ConnectorServer to the registry, which will now fail as for security we have
+ * locked it from any RMI based modifications, including our own. Instead, we will manually bind
+ * the RMIConnectorServer stub to the registry using its object reference, which will still succeed.
+ *
+ * The registry is exported on the defined management port 'port'. We will export the RMIConnectorServer
+ * on 'port +1'. Use of these two well-defined ports will ease any navigation through firewall's.
+ */
+ final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(port+PORT_EXPORT_OFFSET, csf, ssf, env);
+ final String hostname = InetAddress.getLocalHost().getHostName();
+ final JMXServiceURL externalUrl = new JMXServiceURL(
+ "service:jmx:rmi://"+hostname+":"+(port+PORT_EXPORT_OFFSET)+"/jndi/rmi://"+hostname+":"+port+"/jmxrmi");
+
+ final JMXServiceURL internalUrl = new JMXServiceURL("rmi", hostname, port+PORT_EXPORT_OFFSET);
+ cs = new RMIConnectorServer(internalUrl, env, rmiConnectorServerStub, _mbeanServer)
+ {
+ @Override
+ public synchronized void start() throws IOException
+ {
+ try
+ {
+ //manually bind the connector server to the registry at key 'jmxrmi', like the out-of-the-box agent
+ _rmiRegistry.bind("jmxrmi", rmiConnectorServerStub);
+ }
+ catch (AlreadyBoundException abe)
+ {
+ //key was already in use. shouldnt happen here as its a new registry, unbindable by normal means.
+
+ //IOExceptions are the only checked type throwable by the method, wrap and rethrow
+ IOException ioe = new IOException(abe.getMessage());
+ ioe.initCause(abe);
+ throw ioe;
+ }
+
+ //now do the normal tasks
+ super.start();
+ }
+
+ @Override
+ public JMXServiceURL getAddress()
+ {
+ //must return our pre-crafted url that includes the full details, inc JNDI details
+ return externalUrl;
+ }
+
+ };
}
+
+ //Add the custom invoker as an MBeanServerForwarder, and start the RMIConnectorServer.
+ MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance();
+ cs.setMBeanServerForwarder(mbsf);
+ cs.start();
}
- /**
- * Starts up an RMIRegistry at configured port and attaches a JMXConnectorServer to it.
- *
- * @param port
- *
- * @throws IOException
+ /*
+ * Custom RMIServerSocketFactory class, used to prevent updates to the RMI registry.
+ * Supplied to the registry at creation, this will prevent RMI-based operations on the
+ * registry such as attempting to bind a new object, thereby securing it from tampering.
+ * This is accomplished by always returning null when attempting to determine the address
+ * of the caller, thus ensuring the registry will refuse the attempt. Calls to bind etc
+ * made using the object reference will not be affected and continue to operate normally.
*/
- private void startJMXConnectorServer(int port) throws IOException
+
+ private class CustomRMIServerSocketFactory implements RMIServerSocketFactory
{
- startRMIRegistry(port);
- _jmxURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi");
- JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(_jmxURL, null, _mbeanServer);
- cs.start();
+
+ public ServerSocket createServerSocket(int port) throws IOException
+ {
+ return new NoLocalAddressServerSocket(port);
+ }
+
+ private class NoLocalAddressServerSocket extends ServerSocket
+ {
+ NoLocalAddressServerSocket(int port) throws IOException
+ {
+ super(port);
+ }
+
+ @Override
+ public Socket accept() throws IOException
+ {
+ Socket s = new NoLocalAddressSocket();
+ super.implAccept(s);
+ return s;
+ }
+ }
+
+ private class NoLocalAddressSocket extends Socket
+ {
+ @Override
+ public InetAddress getInetAddress()
+ {
+ return null;
+ }
+ }
}
+
public void registerObject(ManagedObject managedObject) throws JMException
{
_mbeanServer.registerMBean(managedObject, managedObject.getObjectName());
@@ -178,11 +355,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
_mbeanServer.unregisterMBean(managedObject.getObjectName());
}
- /**
- * Checks is the "QPID_OPTS" env variable is set to use the out of the box JMXAgent.
- *
- * @return
- */
+ // checks if the system properties are set which enable the JVM's out-of-the-box JMXAgent.
private boolean areOutOfTheBoxJMXOptionsSet()
{
if (System.getProperty("com.sun.management.jmxremote") != null)
@@ -198,19 +371,6 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
return false;
}
- /**
- * Starts the rmi registry at given port
- *
- * @param port
- *
- * @throws RemoteException
- */
- private void startRMIRegistry(int port) throws RemoteException
- {
- System.setProperty("java.rmi.server.randomIDs", "true");
- _rmiRegistry = LocateRegistry.createRegistry(port);
- }
-
// stops the RMIRegistry, if it was running and bound to a port
public void close() throws RemoteException
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java
index d8d87ef881..b58b17ba86 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java
@@ -21,6 +21,9 @@
package org.apache.qpid.server.management;
import javax.management.JMException;
+
+import org.apache.commons.configuration.ConfigurationException;
+
import java.rmi.RemoteException;
import java.io.IOException;
@@ -38,7 +41,7 @@ import java.io.IOException;
*/
public interface ManagedObjectRegistry
{
- void start() throws IOException;
+ void start() throws IOException, ConfigurationException;
void registerObject(ManagedObject managedObject) throws JMException;
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java
new file mode 100644
index 0000000000..378b17e733
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java
@@ -0,0 +1,182 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.qpid.server.security.auth.rmi;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collections;
+
+import javax.management.remote.JMXAuthenticator;
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+
+import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+
+public class RMIPasswordAuthenticator implements JMXAuthenticator
+{
+ static final String UNABLE_TO_LOOKUP = "The broker was unable to lookup the user details";
+ static final String SHOULD_BE_STRING_ARRAY = "User details should be String[]";
+ static final String SHOULD_HAVE_2_ELEMENTS = "User details should have 2 elements, username, password";
+ static final String SHOULD_BE_NON_NULL = "Supplied username and password should be non-null";
+ static final String INVALID_CREDENTIALS = "Invalid user details supplied";
+ static final String CREDENTIALS_REQUIRED = "User details are required. " +
+ "Please ensure you are using an up to date management console to connect.";
+
+ public static final String DEFAULT_ENCODING = "utf-8";
+ private PrincipalDatabase _db = null;
+
+ public RMIPasswordAuthenticator()
+ {
+ }
+
+ public void setPrincipalDatabase(PrincipalDatabase pd)
+ {
+ this._db = pd;
+ }
+
+ public Subject authenticate(Object credentials) throws SecurityException
+ {
+ // Verify that credential's are of type String[].
+ if (!(credentials instanceof String[]))
+ {
+ if (credentials == null)
+ {
+ throw new SecurityException(CREDENTIALS_REQUIRED);
+ }
+ else
+ {
+ throw new SecurityException(SHOULD_BE_STRING_ARRAY);
+ }
+ }
+
+ // Verify that required number of credential's.
+ final String[] userCredentials = (String[]) credentials;
+ if (userCredentials.length != 2)
+ {
+ throw new SecurityException(SHOULD_HAVE_2_ELEMENTS);
+ }
+
+ String username = (String) userCredentials[0];
+ String password = (String) userCredentials[1];
+
+ // Verify that all required credential's are actually present.
+ if (username == null || password == null)
+ {
+ throw new SecurityException(SHOULD_BE_NON_NULL);
+ }
+
+ boolean authenticated = false;
+
+ // Perform authentication
+ try
+ {
+ PasswordCallback pwCallback = new PasswordCallback("prompt",false);
+ UsernamePrincipal uname = new UsernamePrincipal(username);
+
+ if (_db instanceof Base64MD5PasswordFilePrincipalDatabase)
+ {
+ //retrieve the stored password for the given user
+ _db.setPassword(uname, pwCallback);
+
+ //compare the MD5Hash of the given password with the stored value
+ if (Arrays.equals(getMD5Hash(password), pwCallback.getPassword()))
+ {
+ authenticated = true;
+ }
+ }
+ else if (_db instanceof PlainPasswordFilePrincipalDatabase)
+ {
+ //retrieve the users stored password and compare with given value
+ _db.setPassword(uname, pwCallback);
+
+ if (password.equals(new String(pwCallback.getPassword())))
+ {
+ authenticated = true;
+ }
+ }
+ else
+ {
+ throw new SecurityException(UNABLE_TO_LOOKUP);
+ }
+ }
+ catch (AccountNotFoundException e)
+ {
+ throw new SecurityException(INVALID_CREDENTIALS);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new SecurityException(UNABLE_TO_LOOKUP);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new SecurityException(UNABLE_TO_LOOKUP);
+ }
+ catch (IOException e)
+ {
+ throw new SecurityException(UNABLE_TO_LOOKUP);
+ }
+
+ if (authenticated)
+ {
+ //credential's check out, return the appropriate JAAS Subject
+ return new Subject(true,
+ Collections.singleton(new JMXPrincipal(username)),
+ Collections.EMPTY_SET,
+ Collections.EMPTY_SET);
+ }
+ else
+ {
+ throw new SecurityException(INVALID_CREDENTIALS);
+ }
+ }
+
+ public static char[] getMD5Hash(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException
+ {
+ byte[] data = text.getBytes(DEFAULT_ENCODING);
+
+ MessageDigest md = MessageDigest.getInstance("MD5");
+
+ for (byte b : data)
+ {
+ md.update(b);
+ }
+
+ byte[] digest = md.digest();
+
+ char[] hash = new char[digest.length ];
+
+ int index = 0;
+ for (byte b : digest)
+ {
+ hash[index++] = (char) b;
+ }
+
+ return hash;
+ }
+} \ No newline at end of file
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java
new file mode 100644
index 0000000000..e8c24da68d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java
@@ -0,0 +1,267 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.qpid.server.security.auth.rmi;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Collections;
+
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.Subject;
+
+import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
+import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
+
+import junit.framework.TestCase;
+
+public class RMIPasswordAuthenticatorTest extends TestCase
+{
+ private final String USERNAME = "guest";
+ private final String PASSWORD = "guest";
+ private final String B64_MD5HASHED_PASSWORD = "CE4DQ6BIb/BVMN9scFyLtA==";
+ private RMIPasswordAuthenticator _rmipa;
+
+ private Base64MD5PasswordFilePrincipalDatabase _md5Pd;
+ private File _md5PwdFile;
+
+ private PlainPasswordFilePrincipalDatabase _plainPd;
+ private File _plainPwdFile;
+
+ private Subject testSubject;
+
+ protected void setUp() throws Exception
+ {
+ _rmipa = new RMIPasswordAuthenticator();
+
+ _md5Pd = new Base64MD5PasswordFilePrincipalDatabase();
+ _md5PwdFile = createTempPasswordFile(this.getClass().getName()+"md5pwd", USERNAME, B64_MD5HASHED_PASSWORD);
+ _md5Pd.setPasswordFile(_md5PwdFile.getAbsolutePath());
+
+ _plainPd = new PlainPasswordFilePrincipalDatabase();
+ _plainPwdFile = createTempPasswordFile(this.getClass().getName()+"plainpwd", USERNAME, PASSWORD);
+ _plainPd.setPasswordFile(_plainPwdFile.getAbsolutePath());
+
+ testSubject = new Subject(true,
+ Collections.singleton(new JMXPrincipal(USERNAME)),
+ Collections.EMPTY_SET,
+ Collections.EMPTY_SET);
+ }
+
+ private File createTempPasswordFile(String filenamePrefix, String user, String password)
+ {
+ try
+ {
+ File testFile = File.createTempFile(filenamePrefix,"tmp");
+ testFile.deleteOnExit();
+
+ BufferedWriter writer = new BufferedWriter(new FileWriter(testFile));
+
+ writer.write(user + ":" + password);
+ writer.newLine();
+
+ writer.flush();
+ writer.close();
+
+ return testFile;
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create temporary test password file." + e.getMessage());
+ }
+
+ return null;
+ }
+
+
+ //********** Test Methods *********//
+
+
+ public void testAuthenticate()
+ {
+ String[] credentials;
+ Subject newSubject;
+
+ // Test when no PD has been set
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to lack of principal database");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.UNABLE_TO_LOOKUP, se.getMessage());
+ }
+
+ //The PrincipalDatabase's are tested primarily by their own tests, but
+ //minimal tests are done here to exercise their usage in this area.
+
+ // Test correct passwords are verified with an MD5 PD
+ try
+ {
+ _rmipa.setPrincipalDatabase(_md5Pd);
+ credentials = new String[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ assertTrue("Returned subject does not equal expected value",
+ newSubject.equals(testSubject));
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected Exception:" + e.getMessage());
+ }
+
+ // Test incorrect passwords are not verified with an MD5 PD
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD+"incorrect"};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to incorrect password");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test non-existent accounts are not verified with an MD5 PD
+ try
+ {
+ credentials = new String[]{USERNAME+"invalid", PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to non-existant account");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test correct passwords are verified with a Plain PD
+ try
+ {
+ _rmipa.setPrincipalDatabase(_plainPd);
+ credentials = new String[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ assertTrue("Returned subject does not equal expected value",
+ newSubject.equals(testSubject));
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected Exception");
+ }
+
+ // Test incorrect passwords are not verified with a Plain PD
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD+"incorrect"};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to incorrect password");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test non-existent accounts are not verified with an Plain PD
+ try
+ {
+ credentials = new String[]{USERNAME+"invalid", PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to non existant account");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage());
+ }
+
+ // Test handling of non-string credential's
+ try
+ {
+ Object[] objCredentials = new Object[]{USERNAME, PASSWORD};
+ newSubject = _rmipa.authenticate(objCredentials);
+ fail("SecurityException expected due to non string[] credentials");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_BE_STRING_ARRAY, se.getMessage());
+ }
+
+ // Test handling of incorrect number of credential's
+ try
+ {
+ credentials = new String[]{USERNAME, PASSWORD, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to supplying wrong number of credentials");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_HAVE_2_ELEMENTS, se.getMessage());
+ }
+
+ // Test handling of null credential's
+ try
+ {
+ //send a null array
+ credentials = null;
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to not supplying an array of credentials");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.CREDENTIALS_REQUIRED, se.getMessage());
+ }
+
+ try
+ {
+ //send a null password
+ credentials = new String[]{USERNAME, null};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to sending a null password");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage());
+ }
+
+ try
+ {
+ //send a null username
+ credentials = new String[]{null, PASSWORD};
+ newSubject = _rmipa.authenticate(credentials);
+ fail("SecurityException expected due to sending a null username");
+ }
+ catch (SecurityException se)
+ {
+ assertEquals("Unexpected exception message",
+ RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage());
+ }
+ }
+
+}
diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java
index c9955329d0..f5831c9e28 100644
--- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java
+++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java
@@ -29,6 +29,7 @@ import java.util.Map;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
+import javax.net.ssl.SSLException;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.SaslClientFactory;
@@ -40,8 +41,13 @@ import org.apache.qpid.management.common.sasl.UserPasswordCallbackHandler;
import org.apache.qpid.management.common.sasl.UsernameHashedPasswordCallbackHandler;
public class JMXConnnectionFactory {
-
- public static JMXConnector getJMXConnection(long timeout, String host, int port, String username, String password) throws Exception
+
+ private static final String NON_JRMP_SERVER = "non-JRMP server at remote endpoint";
+ private static final String SERVER_SUPPORTED_PROFILES = "The server supported profiles";
+ private static final String CLIENT_REQUIRED_PROFILES = "do not match the client required profiles";
+
+ public static JMXConnector getJMXConnection(long timeout, String host, int port, String username, String password)
+ throws SSLException, IOException, Exception
{
//auto-negotiate an RMI or JMXMP (SASL/CRAM-MD5 or SASL/PLAIN) JMX connection to broker
try
@@ -51,11 +57,30 @@ public class JMXConnnectionFactory {
catch (IOException rmiIOE)
{
// check if the ioe was raised because we tried connecting to a non RMI-JRMP based JMX server
- boolean jrmpServer = !rmiIOE.getMessage().contains("non-JRMP server at remote endpoint");
+ boolean jrmpServer = !rmiIOE.getMessage().contains(NON_JRMP_SERVER);
if (jrmpServer)
{
- throw rmiIOE;
+ //it was an RMI-JRMP based JMX server, so something else went wrong. Check for SSL issues.
+ Throwable rmiIOECause = rmiIOE.getCause();
+ boolean isSSLException = false;
+ if (rmiIOECause != null)
+ {
+ isSSLException = rmiIOECause instanceof SSLException;
+ }
+
+ //if it was an SSLException based cause, throw it
+ if (isSSLException)
+ {
+ throw (SSLException) rmiIOECause;
+ }
+ else
+ {
+ //can't determine cause, throw new IOE citing the original as cause
+ IOException nioe = new IOException();
+ nioe.initCause(rmiIOE);
+ throw nioe;
+ }
}
else
{
@@ -67,8 +92,8 @@ public class JMXConnnectionFactory {
catch (IOException cramIOE)
{
// check if the IOE was raised because we tried connecting to a SASL/PLAIN server using SASL/CRAM-MD5
- boolean plainProfileServer = cramIOE.getMessage().contains("The server supported profiles [SASL/PLAIN]" +
- " do not match the client required profiles [SASL/CRAM-MD5]");
+ boolean plainProfileServer = cramIOE.getMessage().contains(SERVER_SUPPORTED_PROFILES +
+ " [" + Constants.SASL_PLAIN + "] " + CLIENT_REQUIRED_PROFILES + " [" + Constants.SASL_CRAMMD5 + "]");
if (!plainProfileServer)
{
@@ -87,7 +112,7 @@ public class JMXConnnectionFactory {
{
/* Out of options now. Check that the IOE was raised because we tried connecting to a server
* which didnt support SASL/PLAIN. If so, signal an unknown profile type. If not, raise the exception. */
- boolean unknownProfile = cramIOE.getMessage().contains("do not match the client required profiles [SASL/PLAIN]");
+ boolean unknownProfile = plainIOE.getMessage().contains(CLIENT_REQUIRED_PROFILES + " [" + Constants.SASL_PLAIN + "]");
if (unknownProfile)
{
@@ -106,18 +131,19 @@ public class JMXConnnectionFactory {
}
}
- private static JMXConnector createJMXconnector(String connectionType, long timeout, String host, int port, String userName, String password) throws IOException, Exception
+ private static JMXConnector createJMXconnector(String connectionType, long timeout, String host, int port,
+ String userName, String password) throws IOException, Exception
{
Map<String, Object> env = new HashMap<String, Object>();
- String securityMechanism = null;
JMXServiceURL jmxUrl = null;
if (connectionType == "RMI")
{
- securityMechanism = Constants.MECH_PLAIN;
-
jmxUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi");
- env = null;
+
+ //Add user credential's to environment map for RMIConnector startup.
+ //These will be used for authentication by the remote RMIConnectorServer if supported, or ignored otherwise.
+ env.put(JMXConnector.CREDENTIALS, new String[] {userName,password});
}
else if (connectionType.contains("JMXMP"))
{
@@ -143,8 +169,6 @@ public class JMXConnnectionFactory {
if (connectionType == "JMXMP_CRAM-MD5")
{
- securityMechanism = Constants.MECH_CRAMMD5;
-
Map<String, Class<? extends SaslClientFactory>> map = new HashMap<String, Class<? extends SaslClientFactory>>();
map.put("CRAM-MD5-HASHED", CRAMMD5HashedSaslClientFactory.class);
Security.addProvider(new JCAProvider(map));
@@ -156,8 +180,6 @@ public class JMXConnnectionFactory {
}
else if (connectionType == "JMXMP_PLAIN")
{
- securityMechanism = Constants.MECH_PLAIN;
-
Security.addProvider(new SaslProvider());
CallbackHandler handler = new UserPasswordCallbackHandler(userName, password);
env.put("jmx.remote.profiles", Constants.SASL_PLAIN);
@@ -165,7 +187,7 @@ public class JMXConnnectionFactory {
}
else
{
- throw new Exception("Unknown authentication mechanism");
+ throw new Exception("Unknown JMXMP authentication mechanism");
}
}
else
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java
index 202c6ea650..474e31cd8f 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java
@@ -24,6 +24,11 @@ import static org.apache.qpid.management.ui.Constants.ERROR_SERVER_CONNECTION;
import java.io.IOException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLKeyException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+
import org.apache.qpid.management.ui.ApplicationRegistry;
import org.apache.qpid.management.ui.ApplicationWorkbenchAdvisor;
import org.apache.qpid.management.ui.Constants;
@@ -47,6 +52,10 @@ public class AbstractAction
public static final String SERVER_UNAVAILABLE = "Unable to connect to the specified Qpid JMX server";
public static final String INVALID_PERSPECTIVE = "Invalid Perspective";
public static final String CHANGE_PERSPECTIVE = "Please use the Qpid Management Perspective";
+
+ private static final String SSL_EMPTY_TRUSTANCHORS = "the trustAnchors parameter must be non-empty";
+ private static final String SSL_UNABLE_TO_FIND_CERTPATH = "sun.security.provider.certpath.SunCertPathBuilderException: " +
+ "unable to find valid certification path to requested target";
/**
* We will cache window object in order to
@@ -93,9 +102,59 @@ public class AbstractAction
//determine the error message to display
if (msg == null)
{
- if (ex instanceof IOException)
+ if (ex instanceof SSLException)
+ {
+ if (ex instanceof SSLKeyException)
+ {
+ msg = "SSL key was invalid, please check the certificate configuration.";
+ //Display error dialogue and return
+ displayErrorDialogue(msg, title);
+ return;
+ }
+ else if (ex instanceof SSLPeerUnverifiedException)
+ {
+ msg = "SSL peer identity could not be verified, please ensure valid certificate configuration.";
+ //Display error dialogue and return
+ displayErrorDialogue(msg, title);
+ return;
+ }
+ else if (ex instanceof SSLHandshakeException)
+ {
+ if (ex.getMessage().contains(SSL_UNABLE_TO_FIND_CERTPATH))
+ {
+ msg = "Unable to certify the provided SSL certificate using the current SSL trust store.";
+ }
+ else
+ {
+ //cause unknown, provide a trace too
+ MBeanUtility.printStackTrace(ex);
+ msg = "SSL handhshake error.";
+ }
+ //Display error dialogue and return
+ displayErrorDialogue(msg, title);
+ return;
+ }
+ else
+ {
+ //general SSL Exception.
+ if (ex.getMessage().contains(SSL_EMPTY_TRUSTANCHORS))
+ {
+ msg = "Unable to locate the specified SSL certificate trust store, please check the configuration.";
+ }
+ else
+ {
+ //cause unknown, print stack trace
+ MBeanUtility.printStackTrace(ex);
+ msg = "SSL connection error.";
+ }
+ //Display error dialogue and return
+ displayErrorDialogue(msg, title);
+ return;
+ }
+ }
+ else if (ex instanceof IOException)
{
- //IOException, eg when trying to connect to a server/port with no JMX server running
+ //uncaught IOException, eg when trying to connect to a server/port with no JMX server running
msg = SERVER_UNAVAILABLE;
//Display error dialogue and return
displayErrorDialogue(msg, title);
diff --git a/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini b/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini
index 8fd70ba19f..19ceb6f717 100644
--- a/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini
+++ b/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini
@@ -23,3 +23,15 @@
-XX:MaxPermSize=256m
-Dosgi.requiredJavaVersion=1.5
-Declipse.consoleLog=true
+
+#===============================================
+# SSL trust store configuration options.
+#===============================================
+
+# Uncomment lines below to specify custom truststore for server SSL
+# certificate verification, eg when using self-signed server certs.
+#
+#-Djavax.net.ssl.trustStore=<path.to.truststore>
+#-Djavax.net.ssl.trustStorePassword=<truststore.password>
+
+
diff --git a/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini b/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini
index 231adf2d8b..2a31b9b2c7 100644
--- a/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini
+++ b/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini
@@ -29,3 +29,14 @@
-Dosgi.requiredJavaVersion=1.5
-Declipse.consoleLog=true
-Dorg.eclipse.swt.internal.carbon.smallFonts
+
+#===============================================
+# SSL trust store configuration options.
+#===============================================
+
+# Uncomment lines below to specify custom truststore for server SSL
+# certificate verification, eg when using self-signed server certs.
+#
+#-Djavax.net.ssl.trustStore=<path.to.truststore>
+#-Djavax.net.ssl.trustStorePassword=<truststore.password>
+
diff --git a/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini b/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini
index 9f3ad202ad..9e3de042d5 100644
--- a/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini
+++ b/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini
@@ -23,3 +23,14 @@
-XX:MaxPermSize=256m
-Dosgi.requiredJavaVersion=1.5
-Declipse.consoleLog=true
+
+#===============================================
+# SSL trust store configuration options.
+#===============================================
+
+# Uncomment lines below to specify custom truststore for server SSL
+# certificate verification, eg when using self-signed server certs.
+#
+#-Djavax.net.ssl.trustStore=<path.to.truststore>
+#-Djavax.net.ssl.trustStorePassword=<truststore.password>
+