Author: ritchiem Date: Mon Apr 13 11:59:29 2009 New Revision: 764422 URL: http://svn.apache.org/viewvc?rev=764422&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 <[email protected]> merged from trunk r744113 Added: qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/ - copied from r744113, qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/ qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java - copied unchanged from r744113, qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java qpid/branches/0.5-fix/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/ - copied from r744113, qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/ qpid/branches/0.5-fix/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java - copied unchanged from r744113, qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java Modified: qpid/branches/0.5-fix/qpid/ (props changed) qpid/branches/0.5-fix/qpid/java/broker/etc/config.xml qpid/branches/0.5-fix/qpid/java/broker/etc/persistent_config.xml qpid/branches/0.5-fix/qpid/java/broker/etc/transient_config.xml qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java qpid/branches/0.5-fix/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini Propchange: qpid/branches/0.5-fix/qpid/ ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Mon Apr 13 11:59:29 2009 @@ -1 +1 @@ -/qpid/trunk/qpid:742626,743015,743028-743029,743304,743306,743311,743357 +/qpid/trunk/qpid:742626,743015,743028-743029,743304,743306,743311,743357,744113 Modified: qpid/branches/0.5-fix/qpid/java/broker/etc/config.xml URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/broker/etc/config.xml?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/broker/etc/config.xml (original) +++ qpid/branches/0.5-fix/qpid/java/broker/etc/config.xml Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/broker/etc/persistent_config.xml URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/broker/etc/persistent_config.xml?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/broker/etc/persistent_config.xml (original) +++ qpid/branches/0.5-fix/qpid/java/broker/etc/persistent_config.xml Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/broker/etc/transient_config.xml URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/broker/etc/transient_config.xml?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/broker/etc/transient_config.xml (original) +++ qpid/branches/0.5-fix/qpid/java/broker/etc/transient_config.xml Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java (original) +++ qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java (original) +++ qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java Mon Apr 13 11:59:29 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; Modified: qpid/branches/0.5-fix/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java (original) +++ qpid/branches/0.5-fix/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/JMXConnnectionFactory.java Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java (original) +++ qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini (original) +++ qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/linux-gtk-x86/qpidmc.ini Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini (original) +++ qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Contents/MacOS/qpidmc.ini Mon Apr 13 11:59:29 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/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini?rev=764422&r1=764421&r2=764422&view=diff ============================================================================== --- qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini (original) +++ qpid/branches/0.5-fix/qpid/java/management/eclipse-plugin/src/main/resources/win32-win32-x86/qpidmc.ini Mon Apr 13 11:59:29 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:[email protected]
