< prev index next >

src/java.management/share/classes/sun/management/jmxremote/ConnectorBootstrap.java

Print this page
@    rev 13068 : 6425769: jmx remote bind address
|\   Summary: Allow for binding to a specific address via custom socket factories.
| |  Reviewed-by: kvn
| \
| |\
| | \
| | |\
+-+---o  rev 11054 : Merge
| | |/
+---o  rev 11053 : 8049367: Modular Run-Time Images
| |    Reviewed-by: chegar, dfuchs, ihse, joehw, mullan, psandoz, wetmore
| |    Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, bradford.wetmore@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, james.laskey@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com, sundararajan.athijegannathan@oracle.com
| o  rev 11007 : 8048050: Agent NullPointerException when rmi.port in use
|/   Reviewed-by: jbachorik, dfuchs
o  rev 10469 : 8054834: Modular Source Code
|  Reviewed-by: alanb, chegar, ihse, mduigou
|  Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com

@@ -28,20 +28,24 @@
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.Serializable;
 import java.lang.management.ManagementFactory;
 import java.net.InetAddress;
 import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.ServerSocket;
 import java.net.UnknownHostException;
 import java.rmi.NoSuchObjectException;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
 import java.rmi.registry.Registry;
 import java.rmi.server.RMIClientSocketFactory;
 import java.rmi.server.RMIServerSocketFactory;
+import java.rmi.server.RMISocketFactory;
 import java.rmi.server.RemoteObject;
 import java.rmi.server.UnicastRemoteObject;
 import java.security.KeyStore;
 import java.security.Principal;
 import java.util.HashMap;

@@ -58,10 +62,12 @@
 import javax.management.remote.JMXConnectorServerFactory;
 import javax.management.remote.JMXServiceURL;
 import javax.management.remote.rmi.RMIConnectorServer;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManagerFactory;
 import javax.rmi.ssl.SslRMIClientSocketFactory;
 import javax.rmi.ssl.SslRMIServerSocketFactory;
 import javax.security.auth.Subject;
 

@@ -105,10 +111,12 @@
      **/
     public static interface PropertyNames {
 
         public static final String PORT =
                 "com.sun.management.jmxremote.port";
+        public static final String HOST =
+                "com.sun.management.jmxremote.host";
         public static final String RMI_PORT =
                 "com.sun.management.jmxremote.rmi.port";
         public static final String CONFIG_FILE_NAME =
                 "com.sun.management.config.file";
         public static final String USE_LOCAL_ONLY =

@@ -422,14 +430,18 @@
             accessFileName = props.getProperty(PropertyNames.ACCESS_FILE_NAME,
                     getDefaultFileName(DefaultValues.ACCESS_FILE_NAME));
             checkAccessFile(accessFileName);
         }
 
+        final String bindAddress =
+                props.getProperty(PropertyNames.HOST);
+
         if (log.debugOn()) {
             log.debug("startRemoteConnectorServer",
                     Agent.getText("jmxremote.ConnectorBootstrap.starting") +
                     "\n\t" + PropertyNames.PORT + "=" + port +
+                    (bindAddress == null ? "" : "\n\t" + PropertyNames.HOST + "=" + bindAddress) +
                     "\n\t" + PropertyNames.RMI_PORT + "=" + rmiPort +
                     "\n\t" + PropertyNames.USE_SSL + "=" + useSsl +
                     "\n\t" + PropertyNames.USE_REGISTRY_SSL + "=" + useRegistrySsl +
                     "\n\t" + PropertyNames.SSL_CONFIG_FILE_NAME + "=" + sslConfigFileName +
                     "\n\t" + PropertyNames.SSL_ENABLED_CIPHER_SUITES + "=" +

@@ -456,11 +468,11 @@
             final JMXConnectorServerData data = exportMBeanServer(
                     mbs, port, rmiPort, useSsl, useRegistrySsl,
                     sslConfigFileName, enabledCipherSuitesList,
                     enabledProtocolsList, sslNeedClientAuth,
                     useAuthentication, loginConfigName,
-                    passwordFileName, accessFileName);
+                    passwordFileName, accessFileName, bindAddress);
             cs = data.jmxConnectorServer;
             url = data.jmxRemoteURL;
             log.config("startRemoteConnectorServer",
                     Agent.getText("jmxremote.ConnectorBootstrap.ready",
                     url.toString()));

@@ -626,16 +638,17 @@
 
     private static SslRMIServerSocketFactory createSslRMIServerSocketFactory(
             String sslConfigFileName,
             String[] enabledCipherSuites,
             String[] enabledProtocols,
-            boolean sslNeedClientAuth) {
+            boolean sslNeedClientAuth,
+            String bindAddress) {
         if (sslConfigFileName == null) {
-            return new SslRMIServerSocketFactory(
+            return new HostAwareSslSocketFactory(
                     enabledCipherSuites,
                     enabledProtocols,
-                    sslNeedClientAuth);
+                    sslNeedClientAuth, bindAddress);
         } else {
             checkRestrictedFile(sslConfigFileName);
             try {
                 // Load the SSL keystore properties from the config file
                 Properties p = new Properties();

@@ -685,15 +698,15 @@
                 tmf.init(ts);
 
                 SSLContext ctx = SSLContext.getInstance("SSL");
                 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 
-                return new SslRMIServerSocketFactory(
+                return new HostAwareSslSocketFactory(
                         ctx,
                         enabledCipherSuites,
                         enabledProtocols,
-                        sslNeedClientAuth);
+                        sslNeedClientAuth, bindAddress);
             } catch (Exception e) {
                 throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
             }
         }
     }

@@ -709,26 +722,29 @@
             String[] enabledProtocols,
             boolean sslNeedClientAuth,
             boolean useAuthentication,
             String loginConfigName,
             String passwordFileName,
-            String accessFileName)
+            String accessFileName,
+            String bindAddress)
             throws IOException, MalformedURLException {
 
         /* Make sure we use non-guessable RMI object IDs.  Otherwise
          * attackers could hijack open connections by guessing their
          * IDs.  */
         System.setProperty("java.rmi.server.randomIDs", "true");
 
-        JMXServiceURL url = new JMXServiceURL("rmi", null, rmiPort);
+        JMXServiceURL url = new JMXServiceURL("rmi", bindAddress, rmiPort);
 
         Map<String, Object> env = new HashMap<>();
 
         PermanentExporter exporter = new PermanentExporter();
 
         env.put(RMIExporter.EXPORTER_ATTRIBUTE, exporter);
 
+        boolean useSocketFactory = bindAddress != null && !useSsl;
+
         if (useAuthentication) {
             if (loginConfigName != null) {
                 env.put("jmx.remote.x.login.config", loginConfigName);
             }
             if (passwordFileName != null) {

@@ -749,20 +765,26 @@
 
         if (useSsl || useRegistrySsl) {
             csf = new SslRMIClientSocketFactory();
             ssf = createSslRMIServerSocketFactory(
                     sslConfigFileName, enabledCipherSuites,
-                    enabledProtocols, sslNeedClientAuth);
+                    enabledProtocols, sslNeedClientAuth, bindAddress);
         }
 
         if (useSsl) {
             env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
                     csf);
             env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
                     ssf);
         }
 
+        if (useSocketFactory) {
+            ssf = new HostAwareSocketFactory(bindAddress);
+            env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
+                    ssf);
+        }
+
         JMXConnectorServer connServer = null;
         try {
             connServer =
                     JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
             connServer.start();

@@ -778,10 +800,14 @@
 
         if (useRegistrySsl) {
             registry =
                     new SingleEntryRegistry(port, csf, ssf,
                     "jmxrmi", exporter.firstExported);
+        } else if (useSocketFactory) {
+            registry =
+                    new SingleEntryRegistry(port, csf, ssf,
+                    "jmxrmi", exporter.firstExported);
         } else {
             registry =
                     new SingleEntryRegistry(port,
                     "jmxrmi", exporter.firstExported);
         }

@@ -811,6 +837,174 @@
     }
 
     private static final ClassLogger log =
         new ClassLogger(ConnectorBootstrap.class.getPackage().getName(),
                         "ConnectorBootstrap");
+
+    private static class HostAwareSocketFactory implements RMIServerSocketFactory {
+
+        private final String bindAddress;
+
+        private HostAwareSocketFactory(String bindAddress) {
+             this.bindAddress = bindAddress;
+        }
+
+        @Override
+        public ServerSocket createServerSocket(int port) throws IOException {
+            if (bindAddress == null) {
+                return new ServerSocket(port);
+            } else {
+                try {
+                    InetAddress addr = InetAddress.getByName(bindAddress);
+                    return new ServerSocket(port, 0, addr);
+                } catch (UnknownHostException e) {
+                    return new ServerSocket(port);
+                }
+            }
+        }
+    }
+
+    private static class HostAwareSslSocketFactory extends SslRMIServerSocketFactory {
+
+        private final String bindAddress;
+        private final String[] enabledCipherSuites;
+        private final String[] enabledProtocols;
+        private final boolean needClientAuth;
+        private final SSLContext context;
+
+        private HostAwareSslSocketFactory(String[] enabledCipherSuites,
+                                          String[] enabledProtocols,
+                                          boolean sslNeedClientAuth,
+                                          String bindAddress) throws IllegalArgumentException {
+            this(null, enabledCipherSuites, enabledProtocols, sslNeedClientAuth, bindAddress);
+        }
+
+        private HostAwareSslSocketFactory(SSLContext ctx,
+                                          String[] enabledCipherSuites,
+                                          String[] enabledProtocols,
+                                          boolean sslNeedClientAuth,
+                                          String bindAddress) throws IllegalArgumentException {
+            this.context = ctx;
+            this.bindAddress = bindAddress;
+            this.enabledProtocols = enabledProtocols;
+            this.enabledCipherSuites = enabledCipherSuites;
+            this.needClientAuth = sslNeedClientAuth;
+            checkValues(ctx, enabledCipherSuites, enabledProtocols);
+        }
+
+        @Override
+        public ServerSocket createServerSocket(int port) throws IOException {
+            if (bindAddress != null) {
+                try {
+                    InetAddress addr = InetAddress.getByName(bindAddress);
+                    return new SslServerSocket(port, 0, addr, context,
+                                               enabledCipherSuites, enabledProtocols, needClientAuth);
+                } catch (UnknownHostException e) {
+                    return new SslServerSocket(port, context,
+                                               enabledCipherSuites, enabledProtocols, needClientAuth);
+                }
+            } else {
+                return new SslServerSocket(port, context,
+                                           enabledCipherSuites, enabledProtocols, needClientAuth);
+            }
+        }
+
+        private static void checkValues(SSLContext context,
+                                        String[] enabledCipherSuites,
+                                        String[] enabledProtocols) throws IllegalArgumentException {
+            // Force the initialization of the default at construction time,
+            // rather than delaying it to the first time createServerSocket()
+            // is called.
+            //
+            final SSLSocketFactory sslSocketFactory =
+                    context == null ?
+                        (SSLSocketFactory)SSLSocketFactory.getDefault() : context.getSocketFactory();
+            SSLSocket sslSocket = null;
+            if (enabledCipherSuites != null || enabledProtocols != null) {
+                try {
+                    sslSocket = (SSLSocket) sslSocketFactory.createSocket();
+                } catch (Exception e) {
+                    final String msg = "Unable to check if the cipher suites " +
+                            "and protocols to enable are supported";
+                    throw (IllegalArgumentException)
+                    new IllegalArgumentException(msg).initCause(e);
+                }
+            }
+
+            // Check if all the cipher suites and protocol versions to enable
+            // are supported by the underlying SSL/TLS implementation and if
+            // true create lists from arrays.
+            //
+            if (enabledCipherSuites != null) {
+                sslSocket.setEnabledCipherSuites(enabledCipherSuites);
+            }
+            if (enabledProtocols != null) {
+                sslSocket.setEnabledProtocols(enabledProtocols);
+            }
+        }
+    }
+
+    private static class SslServerSocket extends ServerSocket {
+
+        private static SSLSocketFactory defaultSSLSocketFactory;
+        private final String[] enabledCipherSuites;
+        private final String[] enabledProtocols;
+        private final boolean needClientAuth;
+        private final SSLContext context;
+
+        private SslServerSocket(int port,
+                                SSLContext ctx,
+                                String[] enabledCipherSuites,
+                                String[] enabledProtocols,
+                                boolean needClientAuth) throws IOException {
+            super(port);
+            this.enabledProtocols = enabledProtocols;
+            this.enabledCipherSuites = enabledCipherSuites;
+            this.needClientAuth = needClientAuth;
+            this.context = ctx;
+        }
+
+        private SslServerSocket(int port,
+                                int backlog,
+                                InetAddress bindAddr,
+                                SSLContext ctx,
+                                String[] enabledCipherSuites,
+                                String[] enabledProtocols,
+                                boolean needClientAuth) throws IOException {
+            super(port, backlog, bindAddr);
+            this.enabledProtocols = enabledProtocols;
+            this.enabledCipherSuites = enabledCipherSuites;
+            this.needClientAuth = needClientAuth;
+            this.context = ctx;
+        }
+
+        @Override
+        public Socket accept() throws IOException {
+            final SSLSocketFactory sslSocketFactory =
+                    context == null ?
+                        getDefaultSSLSocketFactory() : context.getSocketFactory();
+            Socket socket = super.accept();
+            SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
+                    socket, socket.getInetAddress().getHostName(),
+                    socket.getPort(), true);
+            sslSocket.setUseClientMode(false);
+            if (enabledCipherSuites != null) {
+                sslSocket.setEnabledCipherSuites(enabledCipherSuites);
+            }
+            if (enabledProtocols != null) {
+                sslSocket.setEnabledProtocols(enabledProtocols);
+            }
+            sslSocket.setNeedClientAuth(needClientAuth);
+            return sslSocket;
+        }
+
+        private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
+            if (defaultSSLSocketFactory == null) {
+                defaultSSLSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
+                return defaultSSLSocketFactory;
+            } else {
+                return defaultSSLSocketFactory;
+            }
+        }
+
+    }
 }
< prev index next >