1 /* 2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.management.jmxremote; 27 28 import java.io.BufferedInputStream; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.lang.management.ManagementFactory; 34 import java.net.InetAddress; 35 import java.net.MalformedURLException; 36 import java.net.UnknownHostException; 37 import java.rmi.NoSuchObjectException; 38 import java.rmi.Remote; 39 import java.rmi.RemoteException; 40 import java.rmi.registry.Registry; 41 import java.rmi.server.RMIClientSocketFactory; 42 import java.rmi.server.RMIServerSocketFactory; 43 import java.rmi.server.RemoteObject; 44 import java.rmi.server.UnicastRemoteObject; 45 import java.security.KeyStore; 46 import java.security.Principal; 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.Iterator; 50 import java.util.Map; 51 import java.util.Properties; 52 import java.util.Set; 53 import java.util.StringTokenizer; 54 55 import javax.management.MBeanServer; 56 import javax.management.remote.JMXAuthenticator; 57 import javax.management.remote.JMXConnectorServer; 58 import javax.management.remote.JMXConnectorServerFactory; 59 import javax.management.remote.JMXServiceURL; 60 import javax.management.remote.rmi.RMIConnectorServer; 61 import javax.net.ssl.KeyManagerFactory; 62 import javax.net.ssl.SSLContext; 63 import javax.net.ssl.TrustManagerFactory; 64 import javax.rmi.ssl.SslRMIClientSocketFactory; 65 import javax.rmi.ssl.SslRMIServerSocketFactory; 66 import javax.security.auth.Subject; 67 68 import com.sun.jmx.remote.internal.RMIExporter; 69 import com.sun.jmx.remote.security.JMXPluggableAuthenticator; 70 import com.sun.jmx.remote.util.ClassLogger; 71 72 import sun.management.Agent; 73 import sun.management.AgentConfigurationError; 74 import static sun.management.AgentConfigurationError.*; 75 import sun.management.ConnectorAddressLink; 76 import sun.management.FileSystem; 77 import sun.rmi.server.UnicastRef; 78 import sun.rmi.server.UnicastServerRef; 79 import sun.rmi.server.UnicastServerRef2; 80 81 /** 82 * This class initializes and starts the RMIConnectorServer for JSR 163 83 * JMX Monitoring. 84 **/ 85 public final class ConnectorBootstrap { 86 87 /** 88 * Default values for JMX configuration properties. 89 **/ 90 public static interface DefaultValues { 91 92 public static final String PORT = "0"; 93 public static final String CONFIG_FILE_NAME = "management.properties"; 94 public static final String USE_SSL = "true"; 95 public static final String USE_LOCAL_ONLY = "true"; 96 public static final String USE_REGISTRY_SSL = "false"; 97 public static final String USE_AUTHENTICATION = "true"; 98 public static final String PASSWORD_FILE_NAME = "jmxremote.password"; 99 public static final String ACCESS_FILE_NAME = "jmxremote.access"; 100 public static final String SSL_NEED_CLIENT_AUTH = "false"; 101 } 102 103 /** 104 * Names of JMX configuration properties. 105 **/ 106 public static interface PropertyNames { 107 108 public static final String PORT = 109 "com.sun.management.jmxremote.port"; 110 public static final String RMI_PORT = 111 "com.sun.management.jmxremote.rmi.port"; 112 public static final String CONFIG_FILE_NAME = 113 "com.sun.management.config.file"; 114 public static final String USE_LOCAL_ONLY = 115 "com.sun.management.jmxremote.local.only"; 116 public static final String USE_SSL = 117 "com.sun.management.jmxremote.ssl"; 118 public static final String USE_REGISTRY_SSL = 119 "com.sun.management.jmxremote.registry.ssl"; 120 public static final String USE_AUTHENTICATION = 121 "com.sun.management.jmxremote.authenticate"; 122 public static final String PASSWORD_FILE_NAME = 123 "com.sun.management.jmxremote.password.file"; 124 public static final String ACCESS_FILE_NAME = 125 "com.sun.management.jmxremote.access.file"; 126 public static final String LOGIN_CONFIG_NAME = 127 "com.sun.management.jmxremote.login.config"; 128 public static final String SSL_ENABLED_CIPHER_SUITES = 129 "com.sun.management.jmxremote.ssl.enabled.cipher.suites"; 130 public static final String SSL_ENABLED_PROTOCOLS = 131 "com.sun.management.jmxremote.ssl.enabled.protocols"; 132 public static final String SSL_NEED_CLIENT_AUTH = 133 "com.sun.management.jmxremote.ssl.need.client.auth"; 134 public static final String SSL_CONFIG_FILE_NAME = 135 "com.sun.management.jmxremote.ssl.config.file"; 136 } 137 138 /** 139 * JMXConnectorServer associated data. 140 */ 141 private static class JMXConnectorServerData { 142 143 public JMXConnectorServerData( 144 JMXConnectorServer jmxConnectorServer, 145 JMXServiceURL jmxRemoteURL) { 146 this.jmxConnectorServer = jmxConnectorServer; 147 this.jmxRemoteURL = jmxRemoteURL; 148 } 149 JMXConnectorServer jmxConnectorServer; 150 JMXServiceURL jmxRemoteURL; 151 } 152 153 /** 154 * <p>Prevents our RMI server objects from keeping the JVM alive.</p> 155 * 156 * <p>We use a private interface in Sun's JMX Remote API implementation 157 * that allows us to specify how to export RMI objects. We do so using 158 * UnicastServerRef, a class in Sun's RMI implementation. This is all 159 * non-portable, of course, so this is only valid because we are inside 160 * Sun's JRE.</p> 161 * 162 * <p>Objects are exported using {@link 163 * UnicastServerRef#exportObject(Remote, Object, boolean)}. The 164 * boolean parameter is called <code>permanent</code> and means 165 * both that the object is not eligible for Distributed Garbage 166 * Collection, and that its continued existence will not prevent 167 * the JVM from exiting. It is the latter semantics we want (we 168 * already have the former because of the way the JMX Remote API 169 * works). Hence the somewhat misleading name of this class.</p> 170 */ 171 private static class PermanentExporter implements RMIExporter { 172 173 public Remote exportObject(Remote obj, 174 int port, 175 RMIClientSocketFactory csf, 176 RMIServerSocketFactory ssf) 177 throws RemoteException { 178 179 synchronized (this) { 180 if (firstExported == null) { 181 firstExported = obj; 182 } 183 } 184 185 final UnicastServerRef ref; 186 if (csf == null && ssf == null) { 187 ref = new UnicastServerRef(port); 188 } else { 189 ref = new UnicastServerRef2(port, csf, ssf); 190 } 191 return ref.exportObject(obj, null, true); 192 } 193 194 // Nothing special to be done for this case 195 public boolean unexportObject(Remote obj, boolean force) 196 throws NoSuchObjectException { 197 return UnicastRemoteObject.unexportObject(obj, force); 198 } 199 Remote firstExported; 200 } 201 202 /** 203 * This JMXAuthenticator wraps the JMXPluggableAuthenticator and verifies 204 * that at least one of the principal names contained in the authenticated 205 * Subject is present in the access file. 206 */ 207 private static class AccessFileCheckerAuthenticator 208 implements JMXAuthenticator { 209 210 public AccessFileCheckerAuthenticator(Map<String, Object> env) throws IOException { 211 environment = env; 212 accessFile = (String) env.get("jmx.remote.x.access.file"); 213 properties = propertiesFromFile(accessFile); 214 } 215 216 public Subject authenticate(Object credentials) { 217 final JMXAuthenticator authenticator = 218 new JMXPluggableAuthenticator(environment); 219 final Subject subject = authenticator.authenticate(credentials); 220 checkAccessFileEntries(subject); 221 return subject; 222 } 223 224 private void checkAccessFileEntries(Subject subject) { 225 if (subject == null) { 226 throw new SecurityException( 227 "Access denied! No matching entries found in " + 228 "the access file [" + accessFile + "] as the " + 229 "authenticated Subject is null"); 230 } 231 final Set<Principal> principals = subject.getPrincipals(); 232 for (Principal p1: principals) { 233 if (properties.containsKey(p1.getName())) { 234 return; 235 } 236 } 237 238 final Set<String> principalsStr = new HashSet<>(); 239 for (Principal p2: principals) { 240 principalsStr.add(p2.getName()); 241 } 242 throw new SecurityException( 243 "Access denied! No entries found in the access file [" + 244 accessFile + "] for any of the authenticated identities " + 245 principalsStr); 246 } 247 248 private static Properties propertiesFromFile(String fname) 249 throws IOException { 250 Properties p = new Properties(); 251 if (fname == null) { 252 return p; 253 } 254 try (FileInputStream fin = new FileInputStream(fname)) { 255 p.load(fin); 256 } 257 return p; 258 } 259 private final Map<String, Object> environment; 260 private final Properties properties; 261 private final String accessFile; 262 } 263 264 // The variable below is here to support stop functionality 265 // It would be overriten if you call startRemoteCommectionServer second 266 // time. It's OK for now as logic in Agent.java forbids mutiple agents 267 private static Registry registry = null; 268 269 public static void unexportRegistry() { 270 // Remove the entry from registry 271 try { 272 if (registry != null) { 273 UnicastRemoteObject.unexportObject(registry, true); 274 registry = null; 275 } 276 } catch(NoSuchObjectException ex) { 277 // This exception can appears only if we attempt 278 // to unexportRegistry second time. So it's safe 279 // to ignore it without additional messages. 280 } 281 } 282 283 /** 284 * Initializes and starts the JMX Connector Server. 285 * If the com.sun.management.jmxremote.port property is not defined, 286 * simply return. Otherwise, attempts to load the config file, and 287 * then calls {@link #startRemoteConnectorServer 288 * (java.lang.String, java.util.Properties)}. 289 * 290 * This method is used by some jtreg tests. 291 **/ 292 public static synchronized JMXConnectorServer initialize() { 293 294 // Load a new management properties 295 final Properties props = Agent.loadManagementProperties(); 296 if (props == null) { 297 return null; 298 } 299 300 final String portStr = props.getProperty(PropertyNames.PORT); 301 return startRemoteConnectorServer(portStr, props); 302 } 303 304 /** 305 * This method is used by some jtreg tests. 306 * 307 * @see #startRemoteConnectorServer 308 * (String portStr, Properties props) 309 */ 310 public static synchronized JMXConnectorServer initialize(String portStr, Properties props) { 311 return startRemoteConnectorServer(portStr, props); 312 } 313 314 /** 315 * Initializes and starts a JMX Connector Server for remote 316 * monitoring and management. 317 **/ 318 public static synchronized JMXConnectorServer startRemoteConnectorServer(String portStr, Properties props) { 319 320 // Get port number 321 final int port; 322 try { 323 port = Integer.parseInt(portStr); 324 } catch (NumberFormatException x) { 325 throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, x, portStr); 326 } 327 if (port < 0) { 328 throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, portStr); 329 } 330 331 // User can specify a port to be used to export rmi object, 332 // in order to simplify firewall rules 333 // if port is not specified random one will be allocated. 334 int rmiPort = 0; 335 String rmiPortStr = props.getProperty(PropertyNames.RMI_PORT); 336 try { 337 if (rmiPortStr != null) { 338 rmiPort = Integer.parseInt(rmiPortStr); 339 } 340 } catch (NumberFormatException x) { 341 throw new AgentConfigurationError(INVALID_JMXREMOTE_RMI_PORT, x, rmiPortStr); 342 } 343 if (rmiPort < 0) { 344 throw new AgentConfigurationError(INVALID_JMXREMOTE_RMI_PORT, rmiPortStr); 345 } 346 347 // Do we use authentication? 348 final String useAuthenticationStr = 349 props.getProperty(PropertyNames.USE_AUTHENTICATION, 350 DefaultValues.USE_AUTHENTICATION); 351 final boolean useAuthentication = 352 Boolean.valueOf(useAuthenticationStr).booleanValue(); 353 354 // Do we use SSL? 355 final String useSslStr = 356 props.getProperty(PropertyNames.USE_SSL, 357 DefaultValues.USE_SSL); 358 final boolean useSsl = 359 Boolean.valueOf(useSslStr).booleanValue(); 360 361 // Do we use RMI Registry SSL? 362 final String useRegistrySslStr = 363 props.getProperty(PropertyNames.USE_REGISTRY_SSL, 364 DefaultValues.USE_REGISTRY_SSL); 365 final boolean useRegistrySsl = 366 Boolean.valueOf(useRegistrySslStr).booleanValue(); 367 368 final String enabledCipherSuites = 369 props.getProperty(PropertyNames.SSL_ENABLED_CIPHER_SUITES); 370 String enabledCipherSuitesList[] = null; 371 if (enabledCipherSuites != null) { 372 StringTokenizer st = new StringTokenizer(enabledCipherSuites, ","); 373 int tokens = st.countTokens(); 374 enabledCipherSuitesList = new String[tokens]; 375 for (int i = 0; i < tokens; i++) { 376 enabledCipherSuitesList[i] = st.nextToken(); 377 } 378 } 379 380 final String enabledProtocols = 381 props.getProperty(PropertyNames.SSL_ENABLED_PROTOCOLS); 382 String enabledProtocolsList[] = null; 383 if (enabledProtocols != null) { 384 StringTokenizer st = new StringTokenizer(enabledProtocols, ","); 385 int tokens = st.countTokens(); 386 enabledProtocolsList = new String[tokens]; 387 for (int i = 0; i < tokens; i++) { 388 enabledProtocolsList[i] = st.nextToken(); 389 } 390 } 391 392 final String sslNeedClientAuthStr = 393 props.getProperty(PropertyNames.SSL_NEED_CLIENT_AUTH, 394 DefaultValues.SSL_NEED_CLIENT_AUTH); 395 final boolean sslNeedClientAuth = 396 Boolean.valueOf(sslNeedClientAuthStr).booleanValue(); 397 398 // Read SSL config file name 399 final String sslConfigFileName = 400 props.getProperty(PropertyNames.SSL_CONFIG_FILE_NAME); 401 402 String loginConfigName = null; 403 String passwordFileName = null; 404 String accessFileName = null; 405 406 // Initialize settings when authentication is active 407 if (useAuthentication) { 408 409 // Get non-default login configuration 410 loginConfigName = 411 props.getProperty(PropertyNames.LOGIN_CONFIG_NAME); 412 413 if (loginConfigName == null) { 414 // Get password file 415 passwordFileName = 416 props.getProperty(PropertyNames.PASSWORD_FILE_NAME, 417 getDefaultFileName(DefaultValues.PASSWORD_FILE_NAME)); 418 checkPasswordFile(passwordFileName); 419 } 420 421 // Get access file 422 accessFileName = props.getProperty(PropertyNames.ACCESS_FILE_NAME, 423 getDefaultFileName(DefaultValues.ACCESS_FILE_NAME)); 424 checkAccessFile(accessFileName); 425 } 426 427 if (log.debugOn()) { 428 log.debug("startRemoteConnectorServer", 429 Agent.getText("jmxremote.ConnectorBootstrap.starting") + 430 "\n\t" + PropertyNames.PORT + "=" + port + 431 "\n\t" + PropertyNames.RMI_PORT + "=" + rmiPort + 432 "\n\t" + PropertyNames.USE_SSL + "=" + useSsl + 433 "\n\t" + PropertyNames.USE_REGISTRY_SSL + "=" + useRegistrySsl + 434 "\n\t" + PropertyNames.SSL_CONFIG_FILE_NAME + "=" + sslConfigFileName + 435 "\n\t" + PropertyNames.SSL_ENABLED_CIPHER_SUITES + "=" + 436 enabledCipherSuites + 437 "\n\t" + PropertyNames.SSL_ENABLED_PROTOCOLS + "=" + 438 enabledProtocols + 439 "\n\t" + PropertyNames.SSL_NEED_CLIENT_AUTH + "=" + 440 sslNeedClientAuth + 441 "\n\t" + PropertyNames.USE_AUTHENTICATION + "=" + 442 useAuthentication + 443 (useAuthentication ? (loginConfigName == null ? ("\n\t" + PropertyNames.PASSWORD_FILE_NAME + "=" + 444 passwordFileName) : ("\n\t" + PropertyNames.LOGIN_CONFIG_NAME + "=" + 445 loginConfigName)) : "\n\t" + 446 Agent.getText("jmxremote.ConnectorBootstrap.noAuthentication")) + 447 (useAuthentication ? ("\n\t" + PropertyNames.ACCESS_FILE_NAME + "=" + 448 accessFileName) : "") + 449 ""); 450 } 451 452 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 453 JMXConnectorServer cs = null; 454 JMXServiceURL url = null; 455 try { 456 final JMXConnectorServerData data = exportMBeanServer( 457 mbs, port, rmiPort, useSsl, useRegistrySsl, 458 sslConfigFileName, enabledCipherSuitesList, 459 enabledProtocolsList, sslNeedClientAuth, 460 useAuthentication, loginConfigName, 461 passwordFileName, accessFileName); 462 cs = data.jmxConnectorServer; 463 url = data.jmxRemoteURL; 464 log.config("startRemoteConnectorServer", 465 Agent.getText("jmxremote.ConnectorBootstrap.ready", 466 url.toString())); 467 } catch (Exception e) { 468 throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString()); 469 } 470 try { 471 // Export remote connector address and associated configuration 472 // properties to the instrumentation buffer. 473 Map<String, String> properties = new HashMap<>(); 474 properties.put("remoteAddress", url.toString()); 475 properties.put("authenticate", useAuthenticationStr); 476 properties.put("ssl", useSslStr); 477 properties.put("sslRegistry", useRegistrySslStr); 478 properties.put("sslNeedClientAuth", sslNeedClientAuthStr); 479 ConnectorAddressLink.exportRemote(properties); 480 } catch (Exception e) { 481 // Remote connector server started but unable to export remote 482 // connector address and associated configuration properties to 483 // the instrumentation buffer - non-fatal error. 484 log.debug("startRemoteConnectorServer", e); 485 } 486 return cs; 487 } 488 489 /* 490 * Creates and starts a RMI Connector Server for "local" monitoring 491 * and management. 492 */ 493 public static JMXConnectorServer startLocalConnectorServer() { 494 // Ensure cryptographically strong random number generater used 495 // to choose the object number - see java.rmi.server.ObjID 496 System.setProperty("java.rmi.server.randomIDs", "true"); 497 498 // This RMI server should not keep the VM alive 499 Map<String, Object> env = new HashMap<>(); 500 env.put(RMIExporter.EXPORTER_ATTRIBUTE, new PermanentExporter()); 501 502 // The local connector server need only be available via the 503 // loopback connection. 504 String localhost = "localhost"; 505 InetAddress lh = null; 506 try { 507 lh = InetAddress.getByName(localhost); 508 localhost = lh.getHostAddress(); 509 } catch (UnknownHostException x) { 510 } 511 512 // localhost unknown or (somehow) didn't resolve to 513 // a loopback address. 514 if (lh == null || !lh.isLoopbackAddress()) { 515 localhost = "127.0.0.1"; 516 } 517 518 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 519 try { 520 JMXServiceURL url = new JMXServiceURL("rmi", localhost, 0); 521 // Do we accept connections from local interfaces only? 522 Properties props = Agent.getManagementProperties(); 523 if (props == null) { 524 props = new Properties(); 525 } 526 String useLocalOnlyStr = props.getProperty( 527 PropertyNames.USE_LOCAL_ONLY, DefaultValues.USE_LOCAL_ONLY); 528 boolean useLocalOnly = Boolean.valueOf(useLocalOnlyStr).booleanValue(); 529 if (useLocalOnly) { 530 env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, 531 new LocalRMIServerSocketFactory()); 532 } 533 JMXConnectorServer server = 534 JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); 535 server.start(); 536 return server; 537 } catch (Exception e) { 538 throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString()); 539 } 540 } 541 542 private static void checkPasswordFile(String passwordFileName) { 543 if (passwordFileName == null || passwordFileName.length() == 0) { 544 throw new AgentConfigurationError(PASSWORD_FILE_NOT_SET); 545 } 546 File file = new File(passwordFileName); 547 if (!file.exists()) { 548 throw new AgentConfigurationError(PASSWORD_FILE_NOT_FOUND, passwordFileName); 549 } 550 551 if (!file.canRead()) { 552 throw new AgentConfigurationError(PASSWORD_FILE_NOT_READABLE, passwordFileName); 553 } 554 555 FileSystem fs = FileSystem.open(); 556 try { 557 if (fs.supportsFileSecurity(file)) { 558 if (!fs.isAccessUserOnly(file)) { 559 final String msg = Agent.getText("jmxremote.ConnectorBootstrap.password.readonly", 560 passwordFileName); 561 log.config("startRemoteConnectorServer", msg); 562 throw new AgentConfigurationError(PASSWORD_FILE_ACCESS_NOT_RESTRICTED, 563 passwordFileName); 564 } 565 } 566 } catch (IOException e) { 567 throw new AgentConfigurationError(PASSWORD_FILE_READ_FAILED, 568 e, passwordFileName); 569 } 570 } 571 572 private static void checkAccessFile(String accessFileName) { 573 if (accessFileName == null || accessFileName.length() == 0) { 574 throw new AgentConfigurationError(ACCESS_FILE_NOT_SET); 575 } 576 File file = new File(accessFileName); 577 if (!file.exists()) { 578 throw new AgentConfigurationError(ACCESS_FILE_NOT_FOUND, accessFileName); 579 } 580 581 if (!file.canRead()) { 582 throw new AgentConfigurationError(ACCESS_FILE_NOT_READABLE, accessFileName); 583 } 584 } 585 586 private static void checkRestrictedFile(String restrictedFileName) { 587 if (restrictedFileName == null || restrictedFileName.length() == 0) { 588 throw new AgentConfigurationError(FILE_NOT_SET); 589 } 590 File file = new File(restrictedFileName); 591 if (!file.exists()) { 592 throw new AgentConfigurationError(FILE_NOT_FOUND, restrictedFileName); 593 } 594 if (!file.canRead()) { 595 throw new AgentConfigurationError(FILE_NOT_READABLE, restrictedFileName); 596 } 597 FileSystem fs = FileSystem.open(); 598 try { 599 if (fs.supportsFileSecurity(file)) { 600 if (!fs.isAccessUserOnly(file)) { 601 final String msg = Agent.getText( 602 "jmxremote.ConnectorBootstrap.file.readonly", 603 restrictedFileName); 604 log.config("startRemoteConnectorServer", msg); 605 throw new AgentConfigurationError( 606 FILE_ACCESS_NOT_RESTRICTED, restrictedFileName); 607 } 608 } 609 } catch (IOException e) { 610 throw new AgentConfigurationError( 611 FILE_READ_FAILED, e, restrictedFileName); 612 } 613 } 614 615 /** 616 * Compute the full path name for a default file. 617 * @param basename basename (with extension) of the default file. 618 * @return ${JRE}/lib/management/${basename} 619 **/ 620 private static String getDefaultFileName(String basename) { 621 final String fileSeparator = File.separator; 622 return System.getProperty("java.home") + fileSeparator + "lib" + 623 fileSeparator + "management" + fileSeparator + 624 basename; 625 } 626 627 private static SslRMIServerSocketFactory createSslRMIServerSocketFactory( 628 String sslConfigFileName, 629 String[] enabledCipherSuites, 630 String[] enabledProtocols, 631 boolean sslNeedClientAuth) { 632 if (sslConfigFileName == null) { 633 return new SslRMIServerSocketFactory( 634 enabledCipherSuites, 635 enabledProtocols, 636 sslNeedClientAuth); 637 } else { 638 checkRestrictedFile(sslConfigFileName); 639 try { 640 // Load the SSL keystore properties from the config file 641 Properties p = new Properties(); 642 try (InputStream in = new FileInputStream(sslConfigFileName)) { 643 BufferedInputStream bin = new BufferedInputStream(in); 644 p.load(bin); 645 } 646 String keyStore = 647 p.getProperty("javax.net.ssl.keyStore"); 648 String keyStorePassword = 649 p.getProperty("javax.net.ssl.keyStorePassword", ""); 650 String trustStore = 651 p.getProperty("javax.net.ssl.trustStore"); 652 String trustStorePassword = 653 p.getProperty("javax.net.ssl.trustStorePassword", ""); 654 655 char[] keyStorePasswd = null; 656 if (keyStorePassword.length() != 0) { 657 keyStorePasswd = keyStorePassword.toCharArray(); 658 } 659 660 char[] trustStorePasswd = null; 661 if (trustStorePassword.length() != 0) { 662 trustStorePasswd = trustStorePassword.toCharArray(); 663 } 664 665 KeyStore ks = null; 666 if (keyStore != null) { 667 ks = KeyStore.getInstance(KeyStore.getDefaultType()); 668 try (FileInputStream ksfis = new FileInputStream(keyStore)) { 669 ks.load(ksfis, keyStorePasswd); 670 } 671 } 672 KeyManagerFactory kmf = KeyManagerFactory.getInstance( 673 KeyManagerFactory.getDefaultAlgorithm()); 674 kmf.init(ks, keyStorePasswd); 675 676 KeyStore ts = null; 677 if (trustStore != null) { 678 ts = KeyStore.getInstance(KeyStore.getDefaultType()); 679 try (FileInputStream tsfis = new FileInputStream(trustStore)) { 680 ts.load(tsfis, trustStorePasswd); 681 } 682 } 683 TrustManagerFactory tmf = TrustManagerFactory.getInstance( 684 TrustManagerFactory.getDefaultAlgorithm()); 685 tmf.init(ts); 686 687 SSLContext ctx = SSLContext.getInstance("SSL"); 688 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 689 690 return new SslRMIServerSocketFactory( 691 ctx, 692 enabledCipherSuites, 693 enabledProtocols, 694 sslNeedClientAuth); 695 } catch (Exception e) { 696 throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString()); 697 } 698 } 699 } 700 701 private static JMXConnectorServerData exportMBeanServer( 702 MBeanServer mbs, 703 int port, 704 int rmiPort, 705 boolean useSsl, 706 boolean useRegistrySsl, 707 String sslConfigFileName, 708 String[] enabledCipherSuites, 709 String[] enabledProtocols, 710 boolean sslNeedClientAuth, 711 boolean useAuthentication, 712 String loginConfigName, 713 String passwordFileName, 714 String accessFileName) 715 throws IOException, MalformedURLException { 716 717 /* Make sure we use non-guessable RMI object IDs. Otherwise 718 * attackers could hijack open connections by guessing their 719 * IDs. */ 720 System.setProperty("java.rmi.server.randomIDs", "true"); 721 722 JMXServiceURL url = new JMXServiceURL("rmi", null, rmiPort); 723 724 Map<String, Object> env = new HashMap<>(); 725 726 PermanentExporter exporter = new PermanentExporter(); 727 728 env.put(RMIExporter.EXPORTER_ATTRIBUTE, exporter); 729 730 if (useAuthentication) { 731 if (loginConfigName != null) { 732 env.put("jmx.remote.x.login.config", loginConfigName); 733 } 734 if (passwordFileName != null) { 735 env.put("jmx.remote.x.password.file", passwordFileName); 736 } 737 738 env.put("jmx.remote.x.access.file", accessFileName); 739 740 if (env.get("jmx.remote.x.password.file") != null || 741 env.get("jmx.remote.x.login.config") != null) { 742 env.put(JMXConnectorServer.AUTHENTICATOR, 743 new AccessFileCheckerAuthenticator(env)); 744 } 745 } 746 747 RMIClientSocketFactory csf = null; 748 RMIServerSocketFactory ssf = null; 749 750 if (useSsl || useRegistrySsl) { 751 csf = new SslRMIClientSocketFactory(); 752 ssf = createSslRMIServerSocketFactory( 753 sslConfigFileName, enabledCipherSuites, 754 enabledProtocols, sslNeedClientAuth); 755 } 756 757 if (useSsl) { 758 env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, 759 csf); 760 env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, 761 ssf); 762 } 763 764 JMXConnectorServer connServer = null; 765 try { 766 connServer = 767 JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); 768 connServer.start(); 769 } catch (IOException e) { 770 if (connServer == null || connServer.getAddress() == null) { 771 throw new AgentConfigurationError(CONNECTOR_SERVER_IO_ERROR, 772 e, url.toString()); 773 } else { 774 throw new AgentConfigurationError(CONNECTOR_SERVER_IO_ERROR, 775 e, connServer.getAddress().toString()); 776 } 777 } 778 779 if (useRegistrySsl) { 780 registry = 781 new SingleEntryRegistry(port, csf, ssf, 782 "jmxrmi", exporter.firstExported); 783 } else { 784 registry = 785 new SingleEntryRegistry(port, 786 "jmxrmi", exporter.firstExported); 787 } 788 789 790 int registryPort = 791 ((UnicastRef) ((RemoteObject) registry).getRef()).getLiveRef().getPort(); 792 String jmxUrlStr = String.format("service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", 793 url.getHost(), registryPort); 794 JMXServiceURL remoteURL = new JMXServiceURL(jmxUrlStr); 795 796 /* Our exporter remembers the first object it was asked to 797 export, which will be an RMIServerImpl appropriate for 798 publication in our special registry. We could 799 alternatively have constructed the RMIServerImpl explicitly 800 and then constructed an RMIConnectorServer passing it as a 801 parameter, but that's quite a bit more verbose and pulls in 802 lots of knowledge of the RMI connector. */ 803 804 return new JMXConnectorServerData(connServer, remoteURL); 805 } 806 807 /** 808 * This class cannot be instantiated. 809 **/ 810 private ConnectorBootstrap() { 811 } 812 813 private static final ClassLogger log = 814 new ClassLogger(ConnectorBootstrap.class.getPackage().getName(), 815 "ConnectorBootstrap"); 816 }