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