qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ai...@apache.org
Subject svn commit: r744113 - in /qpid/trunk/qpid/java: broker/etc/ broker/src/main/java/org/apache/qpid/server/management/ broker/src/main/java/org/apache/qpid/server/security/auth/rmi/ broker/src/test/java/org/apache/qpid/server/security/auth/rmi/ management...
Date Fri, 13 Feb 2009 14:00:11 GMT
Author: aidan
Date: Fri Feb 13 14:00:10 2009
New Revision: 744113

URL: http://svn.apache.org/viewvc?rev=744113&view=rev
Log:
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>

Added:
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java
    qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/
    qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java
Modified:
    qpid/trunk/qpid/java/broker/etc/config.xml
    qpid/trunk/qpid/java/broker/etc/persistent_config.xml
    qpid/trunk/qpid/java/broker/etc/transient_config.xml
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java
    qpid/trunk/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java
    qpid/trunk/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java
    qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini
    qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini
    qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini

Modified: qpid/trunk/qpid/java/broker/etc/config.xml
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/etc/config.xml?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/etc/config.xml (original)
+++ qpid/trunk/qpid/java/broker/etc/config.xml Fri Feb 13 14:00:10 2009
@@ -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"/>

Modified: qpid/trunk/qpid/java/broker/etc/persistent_config.xml
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/etc/persistent_config.xml?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/etc/persistent_config.xml (original)
+++ qpid/trunk/qpid/java/broker/etc/persistent_config.xml Fri Feb 13 14:00:10 2009
@@ -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"/>

Modified: qpid/trunk/qpid/java/broker/etc/transient_config.xml
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/etc/transient_config.xml?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/etc/transient_config.xml (original)
+++ qpid/trunk/qpid/java/broker/etc/transient_config.xml Fri Feb 13 14:00:10 2009
@@ -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"/>

Modified: qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java (original)
+++ qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java Fri Feb 13 14:00:10 2009
@@ -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.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.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 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 @@
             }
             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 @@
             //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 @@
         _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 @@
         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
     {

Modified: qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java (original)
+++ qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java Fri Feb 13 14:00:10 2009
@@ -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 @@
  */
 public interface ManagedObjectRegistry
 {
-    void start() throws IOException;
+    void start() throws IOException, ConfigurationException;
 
     void registerObject(ManagedObject managedObject) throws JMException;
 

Added: qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java?rev=744113&view=auto
==============================================================================
--- qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java (added)
+++ qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java Fri Feb 13 14:00:10 2009
@@ -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

Added: qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java?rev=744113&view=auto
==============================================================================
--- qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java (added)
+++ qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java Fri Feb 13 14:00:10 2009
@@ -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());
+        }
+    }
+
+}

Modified: qpid/trunk/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java (original)
+++ qpid/trunk/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java Fri Feb 13 14:00:10 2009
@@ -29,6 +29,7 @@
 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.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 @@
         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 @@
                 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 @@
                         {
                             /* 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 @@
         }
 	}
 	
-	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 @@
 
             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 @@
             }
             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 @@
             }
             else
             {
-                throw new Exception("Unknown authentication mechanism");
+                throw new Exception("Unknown JMXMP authentication mechanism");
             }
         }
         else

Modified: qpid/trunk/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java (original)
+++ qpid/trunk/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java Fri Feb 13 14:00:10 2009
@@ -24,6 +24,11 @@
 
 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 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 @@
         //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);

Modified: qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini (original)
+++ qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini Fri Feb 13 14:00:10 2009
@@ -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>
+
+

Modified: qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini (original)
+++ qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini Fri Feb 13 14:00:10 2009
@@ -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>
+

Modified: qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini?rev=744113&r1=744112&r2=744113&view=diff
==============================================================================
--- qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini (original)
+++ qpid/trunk/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini Fri Feb 13 14:00:10 2009
@@ -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>
+



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscribe@qpid.apache.org


Mime
View raw message