< prev index next >

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

Print this page
@  rev 11403 : 6425769: Allow specifying an address to bind JMX remote connector
|  Reviewed-by: jbachorik, dfuchs

*** 28,47 **** --- 28,51 ---- 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,67 **** --- 62,73 ---- 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,114 **** --- 111,122 ---- **/ 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,435 **** --- 430,447 ---- 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,466 **** final JMXConnectorServerData data = exportMBeanServer( mbs, port, rmiPort, useSsl, useRegistrySsl, sslConfigFileName, enabledCipherSuitesList, enabledProtocolsList, sslNeedClientAuth, useAuthentication, loginConfigName, ! passwordFileName, accessFileName); cs = data.jmxConnectorServer; url = data.jmxRemoteURL; log.config("startRemoteConnectorServer", Agent.getText("jmxremote.ConnectorBootstrap.ready", url.toString())); --- 468,478 ---- final JMXConnectorServerData data = exportMBeanServer( mbs, port, rmiPort, useSsl, useRegistrySsl, sslConfigFileName, enabledCipherSuitesList, enabledProtocolsList, sslNeedClientAuth, useAuthentication, loginConfigName, ! passwordFileName, accessFileName, bindAddress); cs = data.jmxConnectorServer; url = data.jmxRemoteURL; log.config("startRemoteConnectorServer", Agent.getText("jmxremote.ConnectorBootstrap.ready", url.toString()));
*** 626,641 **** private static SslRMIServerSocketFactory createSslRMIServerSocketFactory( String sslConfigFileName, String[] enabledCipherSuites, String[] enabledProtocols, ! boolean sslNeedClientAuth) { if (sslConfigFileName == null) { ! return new SslRMIServerSocketFactory( enabledCipherSuites, enabledProtocols, ! sslNeedClientAuth); } else { checkRestrictedFile(sslConfigFileName); try { // Load the SSL keystore properties from the config file Properties p = new Properties(); --- 638,654 ---- private static SslRMIServerSocketFactory createSslRMIServerSocketFactory( String sslConfigFileName, String[] enabledCipherSuites, String[] enabledProtocols, ! boolean sslNeedClientAuth, ! String bindAddress) { if (sslConfigFileName == null) { ! return new HostAwareSslSocketFactory( enabledCipherSuites, enabledProtocols, ! sslNeedClientAuth, bindAddress); } else { checkRestrictedFile(sslConfigFileName); try { // Load the SSL keystore properties from the config file Properties p = new Properties();
*** 685,699 **** tmf.init(ts); SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); ! return new SslRMIServerSocketFactory( ctx, enabledCipherSuites, enabledProtocols, ! sslNeedClientAuth); } catch (Exception e) { throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString()); } } } --- 698,712 ---- tmf.init(ts); SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); ! return new HostAwareSslSocketFactory( ctx, enabledCipherSuites, enabledProtocols, ! sslNeedClientAuth, bindAddress); } catch (Exception e) { throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString()); } } }
*** 709,734 **** String[] enabledProtocols, boolean sslNeedClientAuth, boolean useAuthentication, String loginConfigName, String passwordFileName, ! String accessFileName) 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); Map<String, Object> env = new HashMap<>(); PermanentExporter exporter = new PermanentExporter(); env.put(RMIExporter.EXPORTER_ATTRIBUTE, exporter); if (useAuthentication) { if (loginConfigName != null) { env.put("jmx.remote.x.login.config", loginConfigName); } if (passwordFileName != null) { --- 722,750 ---- String[] enabledProtocols, boolean sslNeedClientAuth, boolean useAuthentication, String loginConfigName, String passwordFileName, ! 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", 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,768 **** if (useSsl || useRegistrySsl) { csf = new SslRMIClientSocketFactory(); ssf = createSslRMIServerSocketFactory( sslConfigFileName, enabledCipherSuites, ! enabledProtocols, sslNeedClientAuth); } if (useSsl) { env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf); env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf); } JMXConnectorServer connServer = null; try { connServer = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); connServer.start(); --- 765,790 ---- if (useSsl || useRegistrySsl) { csf = new SslRMIClientSocketFactory(); ssf = createSslRMIServerSocketFactory( sslConfigFileName, enabledCipherSuites, ! 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,787 **** --- 800,813 ---- 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,816 **** --- 837,1010 ---- } 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 >