0N/A/*
4823N/A * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.management.jmxremote;
0N/A
0N/Aimport java.io.BufferedInputStream;
0N/Aimport java.io.File;
0N/Aimport java.io.FileInputStream;
4823N/Aimport java.io.IOException;
0N/Aimport java.io.InputStream;
4823N/Aimport java.lang.management.ManagementFactory;
0N/Aimport java.net.InetAddress;
0N/Aimport java.net.MalformedURLException;
0N/Aimport java.net.UnknownHostException;
0N/Aimport java.rmi.NoSuchObjectException;
0N/Aimport java.rmi.Remote;
0N/Aimport java.rmi.RemoteException;
0N/Aimport java.rmi.registry.Registry;
0N/Aimport java.rmi.server.RMIClientSocketFactory;
0N/Aimport java.rmi.server.RMIServerSocketFactory;
4823N/Aimport java.rmi.server.RemoteObject;
0N/Aimport java.rmi.server.UnicastRemoteObject;
0N/Aimport java.security.KeyStore;
0N/Aimport java.security.Principal;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.HashSet;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.Map;
0N/Aimport java.util.Properties;
0N/Aimport java.util.Set;
0N/Aimport java.util.StringTokenizer;
0N/A
0N/Aimport javax.management.MBeanServer;
0N/Aimport javax.management.remote.JMXAuthenticator;
0N/Aimport javax.management.remote.JMXConnectorServer;
0N/Aimport javax.management.remote.JMXConnectorServerFactory;
0N/Aimport javax.management.remote.JMXServiceURL;
0N/Aimport javax.management.remote.rmi.RMIConnectorServer;
4823N/Aimport javax.net.ssl.KeyManagerFactory;
4823N/Aimport javax.net.ssl.SSLContext;
4823N/Aimport javax.net.ssl.TrustManagerFactory;
0N/Aimport javax.rmi.ssl.SslRMIClientSocketFactory;
0N/Aimport javax.rmi.ssl.SslRMIServerSocketFactory;
0N/Aimport javax.security.auth.Subject;
0N/A
4823N/Aimport com.sun.jmx.remote.internal.RMIExporter;
4823N/Aimport com.sun.jmx.remote.security.JMXPluggableAuthenticator;
4823N/Aimport com.sun.jmx.remote.util.ClassLogger;
0N/A
0N/Aimport sun.management.Agent;
0N/Aimport sun.management.AgentConfigurationError;
0N/Aimport static sun.management.AgentConfigurationError.*;
29N/Aimport sun.management.ConnectorAddressLink;
0N/Aimport sun.management.FileSystem;
4823N/Aimport sun.rmi.server.UnicastRef;
4823N/Aimport sun.rmi.server.UnicastServerRef;
4823N/Aimport sun.rmi.server.UnicastServerRef2;
0N/A
0N/A/**
0N/A * This class initializes and starts the RMIConnectorServer for JSR 163
0N/A * JMX Monitoring.
0N/A **/
0N/Apublic final class ConnectorBootstrap {
0N/A
0N/A /**
0N/A * Default values for JMX configuration properties.
0N/A **/
0N/A public static interface DefaultValues {
29N/A
29N/A public static final String PORT = "0";
29N/A public static final String CONFIG_FILE_NAME = "management.properties";
29N/A public static final String USE_SSL = "true";
672N/A public static final String USE_LOCAL_ONLY = "true";
29N/A public static final String USE_REGISTRY_SSL = "false";
29N/A public static final String USE_AUTHENTICATION = "true";
29N/A public static final String PASSWORD_FILE_NAME = "jmxremote.password";
29N/A public static final String ACCESS_FILE_NAME = "jmxremote.access";
29N/A public static final String SSL_NEED_CLIENT_AUTH = "false";
0N/A }
0N/A
0N/A /**
0N/A * Names of JMX configuration properties.
0N/A **/
0N/A public static interface PropertyNames {
29N/A
0N/A public static final String PORT =
0N/A "com.sun.management.jmxremote.port";
4823N/A public static final String RMI_PORT =
4823N/A "com.sun.management.jmxremote.rmi.port";
0N/A public static final String CONFIG_FILE_NAME =
0N/A "com.sun.management.config.file";
671N/A public static final String USE_LOCAL_ONLY =
671N/A "com.sun.management.jmxremote.local.only";
0N/A public static final String USE_SSL =
0N/A "com.sun.management.jmxremote.ssl";
0N/A public static final String USE_REGISTRY_SSL =
0N/A "com.sun.management.jmxremote.registry.ssl";
0N/A public static final String USE_AUTHENTICATION =
0N/A "com.sun.management.jmxremote.authenticate";
0N/A public static final String PASSWORD_FILE_NAME =
0N/A "com.sun.management.jmxremote.password.file";
0N/A public static final String ACCESS_FILE_NAME =
0N/A "com.sun.management.jmxremote.access.file";
0N/A public static final String LOGIN_CONFIG_NAME =
0N/A "com.sun.management.jmxremote.login.config";
0N/A public static final String SSL_ENABLED_CIPHER_SUITES =
0N/A "com.sun.management.jmxremote.ssl.enabled.cipher.suites";
0N/A public static final String SSL_ENABLED_PROTOCOLS =
0N/A "com.sun.management.jmxremote.ssl.enabled.protocols";
0N/A public static final String SSL_NEED_CLIENT_AUTH =
0N/A "com.sun.management.jmxremote.ssl.need.client.auth";
0N/A public static final String SSL_CONFIG_FILE_NAME =
0N/A "com.sun.management.jmxremote.ssl.config.file";
0N/A }
0N/A
0N/A /**
29N/A * JMXConnectorServer associated data.
29N/A */
29N/A private static class JMXConnectorServerData {
29N/A
29N/A public JMXConnectorServerData(
29N/A JMXConnectorServer jmxConnectorServer,
29N/A JMXServiceURL jmxRemoteURL) {
29N/A this.jmxConnectorServer = jmxConnectorServer;
29N/A this.jmxRemoteURL = jmxRemoteURL;
29N/A }
29N/A JMXConnectorServer jmxConnectorServer;
29N/A JMXServiceURL jmxRemoteURL;
29N/A }
29N/A
29N/A /**
0N/A * <p>Prevents our RMI server objects from keeping the JVM alive.</p>
0N/A *
0N/A * <p>We use a private interface in Sun's JMX Remote API implementation
0N/A * that allows us to specify how to export RMI objects. We do so using
0N/A * UnicastServerRef, a class in Sun's RMI implementation. This is all
0N/A * non-portable, of course, so this is only valid because we are inside
0N/A * Sun's JRE.</p>
0N/A *
0N/A * <p>Objects are exported using {@link
0N/A * UnicastServerRef#exportObject(Remote, Object, boolean)}. The
0N/A * boolean parameter is called <code>permanent</code> and means
0N/A * both that the object is not eligible for Distributed Garbage
0N/A * Collection, and that its continued existence will not prevent
0N/A * the JVM from exiting. It is the latter semantics we want (we
0N/A * already have the former because of the way the JMX Remote API
0N/A * works). Hence the somewhat misleading name of this class.</p>
0N/A */
0N/A private static class PermanentExporter implements RMIExporter {
29N/A
0N/A public Remote exportObject(Remote obj,
0N/A int port,
0N/A RMIClientSocketFactory csf,
0N/A RMIServerSocketFactory ssf)
0N/A throws RemoteException {
0N/A
0N/A synchronized (this) {
29N/A if (firstExported == null) {
0N/A firstExported = obj;
29N/A }
0N/A }
0N/A
0N/A final UnicastServerRef ref;
29N/A if (csf == null && ssf == null) {
0N/A ref = new UnicastServerRef(port);
29N/A } else {
0N/A ref = new UnicastServerRef2(port, csf, ssf);
29N/A }
0N/A return ref.exportObject(obj, null, true);
0N/A }
0N/A
0N/A // Nothing special to be done for this case
0N/A public boolean unexportObject(Remote obj, boolean force)
29N/A throws NoSuchObjectException {
0N/A return UnicastRemoteObject.unexportObject(obj, force);
0N/A }
0N/A Remote firstExported;
0N/A }
0N/A
0N/A /**
0N/A * This JMXAuthenticator wraps the JMXPluggableAuthenticator and verifies
0N/A * that at least one of the principal names contained in the authenticated
0N/A * Subject is present in the access file.
0N/A */
0N/A private static class AccessFileCheckerAuthenticator
0N/A implements JMXAuthenticator {
0N/A
0N/A public AccessFileCheckerAuthenticator(Map<String, Object> env) throws IOException {
0N/A environment = env;
0N/A accessFile = (String) env.get("jmx.remote.x.access.file");
0N/A properties = propertiesFromFile(accessFile);
0N/A }
0N/A
0N/A public Subject authenticate(Object credentials) {
0N/A final JMXAuthenticator authenticator =
0N/A new JMXPluggableAuthenticator(environment);
0N/A final Subject subject = authenticator.authenticate(credentials);
0N/A checkAccessFileEntries(subject);
0N/A return subject;
0N/A }
0N/A
0N/A private void checkAccessFileEntries(Subject subject) {
29N/A if (subject == null) {
0N/A throw new SecurityException(
0N/A "Access denied! No matching entries found in " +
0N/A "the access file [" + accessFile + "] as the " +
0N/A "authenticated Subject is null");
29N/A }
0N/A final Set principals = subject.getPrincipals();
29N/A for (Iterator i = principals.iterator(); i.hasNext();) {
0N/A final Principal p = (Principal) i.next();
29N/A if (properties.containsKey(p.getName())) {
0N/A return;
29N/A }
0N/A }
0N/A final Set<String> principalsStr = new HashSet<String>();
29N/A for (Iterator i = principals.iterator(); i.hasNext();) {
0N/A final Principal p = (Principal) i.next();
0N/A principalsStr.add(p.getName());
0N/A }
0N/A throw new SecurityException(
0N/A "Access denied! No entries found in the access file [" +
0N/A accessFile + "] for any of the authenticated identities " +
0N/A principalsStr);
0N/A }
0N/A
0N/A private static Properties propertiesFromFile(String fname)
29N/A throws IOException {
0N/A Properties p = new Properties();
29N/A if (fname == null) {
0N/A return p;
29N/A }
0N/A FileInputStream fin = new FileInputStream(fname);
0N/A p.load(fin);
0N/A fin.close();
0N/A return p;
0N/A }
0N/A private final Map<String, Object> environment;
0N/A private final Properties properties;
0N/A private final String accessFile;
0N/A }
0N/A
4823N/A // The variable below is here to support stop functionality
4823N/A // It would be overriten if you call startRemoteCommectionServer second
4823N/A // time. It's OK for now as logic in Agent.java forbids mutiple agents
4823N/A private static Registry registry = null;
4823N/A
4823N/A public static void unexportRegistry() {
4823N/A // Remove the entry from registry
4823N/A try {
4823N/A if (registry != null) {
4823N/A UnicastRemoteObject.unexportObject(registry, true);
4823N/A registry = null;
4823N/A }
4823N/A } catch(NoSuchObjectException ex) {
4823N/A // This exception can appears only if we attempt
4823N/A // to unexportRegistry second time. So it's safe
4823N/A // to ignore it without additional messages.
4823N/A }
4823N/A }
0N/A
4823N/A /**
4823N/A * Initializes and starts the JMX Connector Server.
4823N/A * If the com.sun.management.jmxremote.port property is not defined,
4823N/A * simply return. Otherwise, attempts to load the config file, and
4823N/A * then calls {@link #startRemoteConnectorServer
4823N/A * (java.lang.String, java.util.Properties)}.
4823N/A *
4823N/A * This method is used by some jtreg tests.
4823N/A **/
4823N/A public static synchronized JMXConnectorServer initialize() {
0N/A
4823N/A // Load a new management properties
4823N/A final Properties props = Agent.loadManagementProperties();
4823N/A if (props == null) {
4823N/A return null;
4823N/A }
0N/A
4823N/A final String portStr = props.getProperty(PropertyNames.PORT);
4823N/A return startRemoteConnectorServer(portStr, props);
4823N/A }
4823N/A
4823N/A /**
4823N/A * This method is used by some jtreg tests.
4823N/A *
4823N/A * @see #startRemoteConnectorServer
4823N/A * (String portStr, Properties props)
4823N/A */
4823N/A public static synchronized JMXConnectorServer initialize(String portStr, Properties props) {
4823N/A return startRemoteConnectorServer(portStr, props);
0N/A }
0N/A
0N/A /**
0N/A * Initializes and starts a JMX Connector Server for remote
0N/A * monitoring and management.
0N/A **/
4823N/A public static synchronized JMXConnectorServer startRemoteConnectorServer(String portStr, Properties props) {
0N/A
0N/A // Get port number
0N/A final int port;
0N/A try {
0N/A port = Integer.parseInt(portStr);
0N/A } catch (NumberFormatException x) {
0N/A throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, x, portStr);
0N/A }
0N/A if (port < 0) {
0N/A throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, portStr);
0N/A }
0N/A
4823N/A // User can specify a port to be used to export rmi object,
4823N/A // in order to simplify firewall rules
4823N/A // if port is not specified random one will be allocated.
4823N/A int rmiPort = 0;
4823N/A String rmiPortStr = props.getProperty(PropertyNames.RMI_PORT);
4823N/A try {
4823N/A if (rmiPortStr != null) {
4823N/A rmiPort = Integer.parseInt(rmiPortStr);
4823N/A }
4823N/A } catch (NumberFormatException x) {
4823N/A throw new AgentConfigurationError(INVALID_JMXREMOTE_RMI_PORT, x, rmiPortStr);
4823N/A }
4823N/A if (rmiPort < 0) {
4823N/A throw new AgentConfigurationError(INVALID_JMXREMOTE_RMI_PORT, rmiPortStr);
4823N/A }
4823N/A
0N/A // Do we use authentication?
29N/A final String useAuthenticationStr =
0N/A props.getProperty(PropertyNames.USE_AUTHENTICATION,
0N/A DefaultValues.USE_AUTHENTICATION);
0N/A final boolean useAuthentication =
0N/A Boolean.valueOf(useAuthenticationStr).booleanValue();
0N/A
0N/A // Do we use SSL?
29N/A final String useSslStr =
0N/A props.getProperty(PropertyNames.USE_SSL,
0N/A DefaultValues.USE_SSL);
0N/A final boolean useSsl =
0N/A Boolean.valueOf(useSslStr).booleanValue();
0N/A
0N/A // Do we use RMI Registry SSL?
29N/A final String useRegistrySslStr =
0N/A props.getProperty(PropertyNames.USE_REGISTRY_SSL,
0N/A DefaultValues.USE_REGISTRY_SSL);
0N/A final boolean useRegistrySsl =
0N/A Boolean.valueOf(useRegistrySslStr).booleanValue();
0N/A
0N/A final String enabledCipherSuites =
0N/A props.getProperty(PropertyNames.SSL_ENABLED_CIPHER_SUITES);
0N/A String enabledCipherSuitesList[] = null;
0N/A if (enabledCipherSuites != null) {
0N/A StringTokenizer st = new StringTokenizer(enabledCipherSuites, ",");
0N/A int tokens = st.countTokens();
0N/A enabledCipherSuitesList = new String[tokens];
29N/A for (int i = 0; i < tokens; i++) {
0N/A enabledCipherSuitesList[i] = st.nextToken();
0N/A }
0N/A }
0N/A
0N/A final String enabledProtocols =
0N/A props.getProperty(PropertyNames.SSL_ENABLED_PROTOCOLS);
0N/A String enabledProtocolsList[] = null;
0N/A if (enabledProtocols != null) {
0N/A StringTokenizer st = new StringTokenizer(enabledProtocols, ",");
0N/A int tokens = st.countTokens();
0N/A enabledProtocolsList = new String[tokens];
29N/A for (int i = 0; i < tokens; i++) {
0N/A enabledProtocolsList[i] = st.nextToken();
0N/A }
0N/A }
0N/A
29N/A final String sslNeedClientAuthStr =
0N/A props.getProperty(PropertyNames.SSL_NEED_CLIENT_AUTH,
0N/A DefaultValues.SSL_NEED_CLIENT_AUTH);
0N/A final boolean sslNeedClientAuth =
0N/A Boolean.valueOf(sslNeedClientAuthStr).booleanValue();
0N/A
0N/A // Read SSL config file name
0N/A final String sslConfigFileName =
0N/A props.getProperty(PropertyNames.SSL_CONFIG_FILE_NAME);
0N/A
0N/A String loginConfigName = null;
0N/A String passwordFileName = null;
0N/A String accessFileName = null;
0N/A
0N/A // Initialize settings when authentication is active
0N/A if (useAuthentication) {
0N/A
0N/A // Get non-default login configuration
0N/A loginConfigName =
0N/A props.getProperty(PropertyNames.LOGIN_CONFIG_NAME);
0N/A
0N/A if (loginConfigName == null) {
0N/A // Get password file
0N/A passwordFileName =
0N/A props.getProperty(PropertyNames.PASSWORD_FILE_NAME,
0N/A getDefaultFileName(DefaultValues.PASSWORD_FILE_NAME));
0N/A checkPasswordFile(passwordFileName);
0N/A }
0N/A
0N/A // Get access file
0N/A accessFileName = props.getProperty(PropertyNames.ACCESS_FILE_NAME,
0N/A getDefaultFileName(DefaultValues.ACCESS_FILE_NAME));
0N/A checkAccessFile(accessFileName);
0N/A }
0N/A
671N/A if (log.debugOn()) {
4823N/A log.debug("startRemoteConnectorServer",
4823N/A Agent.getText("jmxremote.ConnectorBootstrap.starting") +
0N/A "\n\t" + PropertyNames.PORT + "=" + port +
4823N/A "\n\t" + PropertyNames.RMI_PORT + "=" + rmiPort +
0N/A "\n\t" + PropertyNames.USE_SSL + "=" + useSsl +
0N/A "\n\t" + PropertyNames.USE_REGISTRY_SSL + "=" + useRegistrySsl +
0N/A "\n\t" + PropertyNames.SSL_CONFIG_FILE_NAME + "=" + sslConfigFileName +
0N/A "\n\t" + PropertyNames.SSL_ENABLED_CIPHER_SUITES + "=" +
0N/A enabledCipherSuites +
0N/A "\n\t" + PropertyNames.SSL_ENABLED_PROTOCOLS + "=" +
0N/A enabledProtocols +
0N/A "\n\t" + PropertyNames.SSL_NEED_CLIENT_AUTH + "=" +
0N/A sslNeedClientAuth +
0N/A "\n\t" + PropertyNames.USE_AUTHENTICATION + "=" +
0N/A useAuthentication +
29N/A (useAuthentication ? (loginConfigName == null ? ("\n\t" + PropertyNames.PASSWORD_FILE_NAME + "=" +
29N/A passwordFileName) : ("\n\t" + PropertyNames.LOGIN_CONFIG_NAME + "=" +
0N/A loginConfigName)) : "\n\t" +
4823N/A Agent.getText("jmxremote.ConnectorBootstrap.noAuthentication")) +
29N/A (useAuthentication ? ("\n\t" + PropertyNames.ACCESS_FILE_NAME + "=" +
0N/A accessFileName) : "") +
0N/A "");
0N/A }
0N/A
0N/A final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
0N/A JMXConnectorServer cs = null;
29N/A JMXServiceURL url = null;
0N/A try {
29N/A final JMXConnectorServerData data = exportMBeanServer(
4823N/A mbs, port, rmiPort, useSsl, useRegistrySsl,
0N/A sslConfigFileName, enabledCipherSuitesList,
0N/A enabledProtocolsList, sslNeedClientAuth,
0N/A useAuthentication, loginConfigName,
0N/A passwordFileName, accessFileName);
29N/A cs = data.jmxConnectorServer;
29N/A url = data.jmxRemoteURL;
4823N/A log.config("startRemoteConnectorServer",
4823N/A Agent.getText("jmxremote.ConnectorBootstrap.ready",
29N/A url.toString()));
0N/A } catch (Exception e) {
0N/A throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
0N/A }
29N/A try {
29N/A // Export remote connector address and associated configuration
29N/A // properties to the instrumentation buffer.
29N/A Map<String, String> properties = new HashMap<String, String>();
29N/A properties.put("remoteAddress", url.toString());
29N/A properties.put("authenticate", useAuthenticationStr);
29N/A properties.put("ssl", useSslStr);
29N/A properties.put("sslRegistry", useRegistrySslStr);
29N/A properties.put("sslNeedClientAuth", sslNeedClientAuthStr);
29N/A ConnectorAddressLink.exportRemote(properties);
29N/A } catch (Exception e) {
29N/A // Remote connector server started but unable to export remote
29N/A // connector address and associated configuration properties to
29N/A // the instrumentation buffer - non-fatal error.
4823N/A log.debug("startRemoteConnectorServer", e);
29N/A }
0N/A return cs;
0N/A }
0N/A
0N/A /*
0N/A * Creates and starts a RMI Connector Server for "local" monitoring
0N/A * and management.
0N/A */
0N/A public static JMXConnectorServer startLocalConnectorServer() {
0N/A // Ensure cryptographically strong random number generater used
0N/A // to choose the object number - see java.rmi.server.ObjID
0N/A System.setProperty("java.rmi.server.randomIDs", "true");
0N/A
0N/A // This RMI server should not keep the VM alive
0N/A Map<String, Object> env = new HashMap<String, Object>();
0N/A env.put(RMIExporter.EXPORTER_ATTRIBUTE, new PermanentExporter());
0N/A
0N/A // The local connector server need only be available via the
0N/A // loopback connection.
0N/A String localhost = "localhost";
0N/A InetAddress lh = null;
0N/A try {
0N/A lh = InetAddress.getByName(localhost);
0N/A localhost = lh.getHostAddress();
0N/A } catch (UnknownHostException x) {
0N/A }
0N/A
0N/A // localhost unknown or (somehow) didn't resolve to
0N/A // a loopback address.
0N/A if (lh == null || !lh.isLoopbackAddress()) {
0N/A localhost = "127.0.0.1";
0N/A }
0N/A
0N/A MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
0N/A try {
0N/A JMXServiceURL url = new JMXServiceURL("rmi", localhost, 0);
671N/A // Do we accept connections from local interfaces only?
671N/A Properties props = Agent.getManagementProperties();
671N/A if (props == null) {
671N/A props = new Properties();
671N/A }
671N/A String useLocalOnlyStr = props.getProperty(
671N/A PropertyNames.USE_LOCAL_ONLY, DefaultValues.USE_LOCAL_ONLY);
671N/A boolean useLocalOnly = Boolean.valueOf(useLocalOnlyStr).booleanValue();
671N/A if (useLocalOnly) {
671N/A env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
671N/A new LocalRMIServerSocketFactory());
671N/A }
0N/A JMXConnectorServer server =
0N/A JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
0N/A server.start();
0N/A return server;
0N/A } catch (Exception e) {
0N/A throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
0N/A }
0N/A }
0N/A
0N/A private static void checkPasswordFile(String passwordFileName) {
29N/A if (passwordFileName == null || passwordFileName.length() == 0) {
0N/A throw new AgentConfigurationError(PASSWORD_FILE_NOT_SET);
0N/A }
0N/A File file = new File(passwordFileName);
0N/A if (!file.exists()) {
0N/A throw new AgentConfigurationError(PASSWORD_FILE_NOT_FOUND, passwordFileName);
0N/A }
0N/A
0N/A if (!file.canRead()) {
0N/A throw new AgentConfigurationError(PASSWORD_FILE_NOT_READABLE, passwordFileName);
0N/A }
0N/A
0N/A FileSystem fs = FileSystem.open();
0N/A try {
0N/A if (fs.supportsFileSecurity(file)) {
0N/A if (!fs.isAccessUserOnly(file)) {
4823N/A final String msg = Agent.getText("jmxremote.ConnectorBootstrap.password.readonly",
0N/A passwordFileName);
4823N/A log.config("startRemoteConnectorServer", msg);
0N/A throw new AgentConfigurationError(PASSWORD_FILE_ACCESS_NOT_RESTRICTED,
0N/A passwordFileName);
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A throw new AgentConfigurationError(PASSWORD_FILE_READ_FAILED,
0N/A e, passwordFileName);
0N/A }
0N/A }
0N/A
0N/A private static void checkAccessFile(String accessFileName) {
29N/A if (accessFileName == null || accessFileName.length() == 0) {
0N/A throw new AgentConfigurationError(ACCESS_FILE_NOT_SET);
0N/A }
0N/A File file = new File(accessFileName);
0N/A if (!file.exists()) {
0N/A throw new AgentConfigurationError(ACCESS_FILE_NOT_FOUND, accessFileName);
0N/A }
0N/A
0N/A if (!file.canRead()) {
0N/A throw new AgentConfigurationError(ACCESS_FILE_NOT_READABLE, accessFileName);
0N/A }
0N/A }
0N/A
0N/A private static void checkRestrictedFile(String restrictedFileName) {
0N/A if (restrictedFileName == null || restrictedFileName.length() == 0) {
0N/A throw new AgentConfigurationError(FILE_NOT_SET);
0N/A }
0N/A File file = new File(restrictedFileName);
0N/A if (!file.exists()) {
0N/A throw new AgentConfigurationError(FILE_NOT_FOUND, restrictedFileName);
0N/A }
0N/A if (!file.canRead()) {
0N/A throw new AgentConfigurationError(FILE_NOT_READABLE, restrictedFileName);
0N/A }
0N/A FileSystem fs = FileSystem.open();
0N/A try {
0N/A if (fs.supportsFileSecurity(file)) {
0N/A if (!fs.isAccessUserOnly(file)) {
0N/A final String msg = Agent.getText(
4823N/A "jmxremote.ConnectorBootstrap.file.readonly",
0N/A restrictedFileName);
4823N/A log.config("startRemoteConnectorServer", msg);
0N/A throw new AgentConfigurationError(
0N/A FILE_ACCESS_NOT_RESTRICTED, restrictedFileName);
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A throw new AgentConfigurationError(
0N/A FILE_READ_FAILED, e, restrictedFileName);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Compute the full path name for a default file.
0N/A * @param basename basename (with extension) of the default file.
0N/A * @return ${JRE}/lib/management/${basename}
0N/A **/
0N/A private static String getDefaultFileName(String basename) {
0N/A final String fileSeparator = File.separator;
0N/A return System.getProperty("java.home") + fileSeparator + "lib" +
0N/A fileSeparator + "management" + fileSeparator +
0N/A basename;
0N/A }
0N/A
0N/A private static SslRMIServerSocketFactory createSslRMIServerSocketFactory(
0N/A String sslConfigFileName,
0N/A String[] enabledCipherSuites,
0N/A String[] enabledProtocols,
0N/A boolean sslNeedClientAuth) {
0N/A if (sslConfigFileName == null) {
0N/A return new SslRMIServerSocketFactory(
0N/A enabledCipherSuites,
0N/A enabledProtocols,
0N/A sslNeedClientAuth);
0N/A } else {
0N/A checkRestrictedFile(sslConfigFileName);
0N/A try {
0N/A // Load the SSL keystore properties from the config file
0N/A Properties p = new Properties();
0N/A InputStream in = new FileInputStream(sslConfigFileName);
0N/A try {
0N/A BufferedInputStream bin = new BufferedInputStream(in);
0N/A p.load(bin);
0N/A } finally {
0N/A in.close();
0N/A }
0N/A String keyStore =
0N/A p.getProperty("javax.net.ssl.keyStore");
0N/A String keyStorePassword =
0N/A p.getProperty("javax.net.ssl.keyStorePassword", "");
0N/A String trustStore =
0N/A p.getProperty("javax.net.ssl.trustStore");
0N/A String trustStorePassword =
0N/A p.getProperty("javax.net.ssl.trustStorePassword", "");
0N/A
0N/A char[] keyStorePasswd = null;
0N/A if (keyStorePassword.length() != 0) {
0N/A keyStorePasswd = keyStorePassword.toCharArray();
0N/A }
0N/A
0N/A char[] trustStorePasswd = null;
0N/A if (trustStorePassword.length() != 0) {
0N/A trustStorePasswd = trustStorePassword.toCharArray();
0N/A }
0N/A
0N/A KeyStore ks = null;
0N/A if (keyStore != null) {
0N/A ks = KeyStore.getInstance(KeyStore.getDefaultType());
0N/A FileInputStream ksfis = new FileInputStream(keyStore);
0N/A try {
0N/A ks.load(ksfis, keyStorePasswd);
0N/A } finally {
0N/A ksfis.close();
0N/A }
0N/A }
0N/A KeyManagerFactory kmf = KeyManagerFactory.getInstance(
0N/A KeyManagerFactory.getDefaultAlgorithm());
0N/A kmf.init(ks, keyStorePasswd);
0N/A
0N/A KeyStore ts = null;
0N/A if (trustStore != null) {
0N/A ts = KeyStore.getInstance(KeyStore.getDefaultType());
0N/A FileInputStream tsfis = new FileInputStream(trustStore);
0N/A try {
0N/A ts.load(tsfis, trustStorePasswd);
0N/A } finally {
0N/A tsfis.close();
0N/A }
0N/A }
0N/A TrustManagerFactory tmf = TrustManagerFactory.getInstance(
0N/A TrustManagerFactory.getDefaultAlgorithm());
0N/A tmf.init((KeyStore) ts);
0N/A
0N/A SSLContext ctx = SSLContext.getInstance("SSL");
0N/A ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
0N/A
0N/A return new SslRMIServerSocketFactory(
0N/A ctx,
0N/A enabledCipherSuites,
0N/A enabledProtocols,
0N/A sslNeedClientAuth);
0N/A } catch (Exception e) {
0N/A throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
0N/A }
0N/A }
0N/A }
0N/A
29N/A private static JMXConnectorServerData exportMBeanServer(
0N/A MBeanServer mbs,
0N/A int port,
4823N/A int rmiPort,
0N/A boolean useSsl,
0N/A boolean useRegistrySsl,
0N/A String sslConfigFileName,
0N/A String[] enabledCipherSuites,
0N/A String[] enabledProtocols,
0N/A boolean sslNeedClientAuth,
0N/A boolean useAuthentication,
0N/A String loginConfigName,
0N/A String passwordFileName,
0N/A String accessFileName)
0N/A throws IOException, MalformedURLException {
0N/A
0N/A /* Make sure we use non-guessable RMI object IDs. Otherwise
0N/A * attackers could hijack open connections by guessing their
0N/A * IDs. */
0N/A System.setProperty("java.rmi.server.randomIDs", "true");
0N/A
4823N/A JMXServiceURL url = new JMXServiceURL("rmi", null, rmiPort);
0N/A
0N/A Map<String, Object> env = new HashMap<String, Object>();
0N/A
0N/A PermanentExporter exporter = new PermanentExporter();
0N/A
0N/A env.put(RMIExporter.EXPORTER_ATTRIBUTE, exporter);
0N/A
0N/A if (useAuthentication) {
0N/A if (loginConfigName != null) {
0N/A env.put("jmx.remote.x.login.config", loginConfigName);
0N/A }
0N/A if (passwordFileName != null) {
0N/A env.put("jmx.remote.x.password.file", passwordFileName);
0N/A }
0N/A
0N/A env.put("jmx.remote.x.access.file", accessFileName);
0N/A
0N/A if (env.get("jmx.remote.x.password.file") != null ||
0N/A env.get("jmx.remote.x.login.config") != null) {
0N/A env.put(JMXConnectorServer.AUTHENTICATOR,
0N/A new AccessFileCheckerAuthenticator(env));
0N/A }
0N/A }
0N/A
0N/A RMIClientSocketFactory csf = null;
0N/A RMIServerSocketFactory ssf = null;
0N/A
0N/A if (useSsl || useRegistrySsl) {
0N/A csf = new SslRMIClientSocketFactory();
0N/A ssf = createSslRMIServerSocketFactory(
0N/A sslConfigFileName, enabledCipherSuites,
0N/A enabledProtocols, sslNeedClientAuth);
0N/A }
0N/A
0N/A if (useSsl) {
0N/A env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
0N/A csf);
0N/A env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
0N/A ssf);
0N/A }
0N/A
0N/A JMXConnectorServer connServer = null;
0N/A try {
0N/A connServer =
0N/A JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
0N/A connServer.start();
0N/A } catch (IOException e) {
0N/A if (connServer == null) {
0N/A throw new AgentConfigurationError(CONNECTOR_SERVER_IO_ERROR,
0N/A e, url.toString());
0N/A } else {
0N/A throw new AgentConfigurationError(CONNECTOR_SERVER_IO_ERROR,
0N/A e, connServer.getAddress().toString());
0N/A }
0N/A }
0N/A
29N/A if (useRegistrySsl) {
0N/A registry =
0N/A new SingleEntryRegistry(port, csf, ssf,
0N/A "jmxrmi", exporter.firstExported);
29N/A } else {
0N/A registry =
0N/A new SingleEntryRegistry(port,
0N/A "jmxrmi", exporter.firstExported);
29N/A }
29N/A
4823N/A
4823N/A int registryPort =
4823N/A ((UnicastRef) ((RemoteObject) registry).getRef()).getLiveRef().getPort();
4823N/A String jmxUrlStr = String.format("service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi",
4823N/A url.getHost(), registryPort);
4823N/A JMXServiceURL remoteURL = new JMXServiceURL(jmxUrlStr);
0N/A
0N/A /* Our exporter remembers the first object it was asked to
29N/A export, which will be an RMIServerImpl appropriate for
29N/A publication in our special registry. We could
29N/A alternatively have constructed the RMIServerImpl explicitly
29N/A and then constructed an RMIConnectorServer passing it as a
29N/A parameter, but that's quite a bit more verbose and pulls in
29N/A lots of knowledge of the RMI connector. */
0N/A
29N/A return new JMXConnectorServerData(connServer, remoteURL);
0N/A }
0N/A
0N/A /**
0N/A * This class cannot be instantiated.
0N/A **/
0N/A private ConnectorBootstrap() {
0N/A }
0N/A
671N/A private static final ClassLogger log =
671N/A new ClassLogger(ConnectorBootstrap.class.getPackage().getName(),
671N/A "ConnectorBootstrap");
0N/A}