1 /* 2 * Copyright (c) 2003, 2015, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8058865 27 * @summary Checks various authentication behavior from remote jmx client 28 * @author Olivier Lagneau 29 * @modules java.management.rmi 30 * @library /lib/testlibrary 31 * @library /test/lib 32 * @compile Simple.java 33 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials 34 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException 35 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 36 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials 37 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedGetException 38 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 39 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 40 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials 41 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException 42 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 43 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username4 -Dpassword=password4 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedGetException -expectedSetException 44 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 45 */ 46 47 import java.io.File; 48 import java.util.Map ; 49 import java.util.HashMap ; 50 import java.util.List; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 54 import java.lang.management.ManagementFactory; 55 56 import javax.management.MBeanServer; 57 import javax.management.MBeanServerFactory ; 58 import javax.management.MBeanServerConnection; 59 import javax.management.remote.JMXConnector; 60 import javax.management.remote.JMXConnectorFactory; 61 import javax.management.remote.JMXConnectorServer; 62 import javax.management.remote.JMXConnectorServerFactory; 63 import javax.management.remote.JMXServiceURL; 64 65 import javax.management.Attribute ; 66 import javax.management.ObjectName ; 67 68 import jdk.test.lib.process.ProcessTools; 69 import jdk.testlibrary.JDKToolFinder; 70 71 public class AuthorizationTest { 72 73 static final String SERVER_CLASS_NAME = "AuthorizationTest"; 74 static final String CLIENT_CLASS_NAME = "AuthorizationTest$ClientSide"; 75 static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME; 76 77 static final String USERNAME_PROPERTY = "username"; 78 static final String PASSWORD_PROPERTY = "password"; 79 80 private JMXConnectorServer cs; 81 82 /* 83 * First Debug properties and arguments are collect in expected 84 * map (argName, value) format, then calls original test's run method. 85 */ 86 public static void main(String args[]) throws Exception { 87 88 System.out.println("================================================="); 89 90 // Parses parameters 91 Utils.parseDebugProperties(); 92 93 // Supported parameters list format is : 94 // "MainClass [-server <param-spec> ...] [-client <param-spec> ...] 95 // with <param-spec> either "-parami valuei" or "-parami" 96 HashMap<String, Object> serverMap = new HashMap<>() ; 97 int clientArgsIndex = 98 Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap); 99 100 // Extract and records client params 101 String[] clientParams = null; 102 if (clientArgsIndex < args.length) { 103 int clientParamsSize = args.length - clientArgsIndex; 104 clientParams = new String[clientParamsSize]; 105 System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize); 106 } else { 107 clientParams = new String[0]; 108 } 109 110 // Run test 111 AuthorizationTest test = new AuthorizationTest(); 112 test.run(serverMap, clientParams); 113 114 } 115 116 /* 117 * Create the MBeansServer side of the test and returns its address 118 */ 119 private JMXServiceURL createServerSide(Map<String, Object> serverMap) 120 throws Exception { 121 final int NINETY_SECONDS = 90; 122 123 System.out.println("AuthorizationTest::createServerSide: Start") ; 124 125 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 126 JMXServiceURL url = new JMXServiceURL("rmi", null, 0); 127 128 // Creates connection environment from server side params 129 HashMap<String, Object> env = new HashMap<>(); 130 String value = null; 131 132 if ((value = (String)serverMap.get("-mapType")) != null) { 133 if (value.contains("x.access.file")) { 134 String accessFileStr = System.getProperty("test.src") + 135 File.separator + "access.properties"; 136 env.put("jmx.remote.x.access.file", accessFileStr); 137 System.out.println("Added " + accessFileStr + " file as jmx.remote.x.access.file"); 138 } 139 if (value.contains("x.password.file")) { 140 String passwordFileStr = System.getProperty("test.src") + 141 File.separator + "password.properties"; 142 env.put("jmx.remote.x.password.file", passwordFileStr); 143 System.out.println("Added " + passwordFileStr + " file as jmx.remote.x.password.file"); 144 } 145 } 146 147 if (serverMap.containsKey("-populate")) { 148 String populateClassName = "Simple"; 149 ObjectName on = 150 new ObjectName("defaultDomain:class=Simple"); 151 152 Utils.debug(Utils.DEBUG_STANDARD, "create and register Simple MBean") ; 153 mbs.createMBean(populateClassName, on); 154 } 155 156 cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); 157 cs.start(); 158 159 Utils.waitReady(cs, NINETY_SECONDS); 160 161 JMXServiceURL addr = cs.getAddress(); 162 163 System.out.println("AuthorizationTest::createServerSide: Done.") ; 164 165 return addr; 166 } 167 168 /* 169 * Creating command-line for running subprocess JVM: 170 * 171 * JVM command line is like: 172 * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main 173 * 174 * {defaultopts} are the default java options set by the framework. 175 * 176 */ 177 private List<String> buildCommandLine(String args[]) { 178 List<String> opts = new ArrayList<>(); 179 opts.add(JDKToolFinder.getJDKTool("java")); 180 opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); 181 182 String usernameValue = System.getProperty(USERNAME_PROPERTY); 183 if (usernameValue != null) { 184 opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue); 185 } 186 String passwordValue = System.getProperty(PASSWORD_PROPERTY); 187 if (passwordValue != null) { 188 opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue); 189 } 190 191 opts.add("-cp"); 192 opts.add(System.getProperty("test.class.path", "test.class.path")); 193 opts.add(CLIENT_CLASS_MAIN); 194 opts.addAll(Arrays.asList(args)); 195 return opts; 196 } 197 198 /** 199 * Runs AuthorizationTest$ClientSide with the passed options and redirects 200 * subprocess standard I/O to the current (parent) process. This provides a 201 * trace of what happens in the subprocess while it is runnning (and before 202 * it terminates). 203 * 204 * @param serviceUrlStr string representing the JMX service Url to connect to. 205 */ 206 private int runClientSide(String args[], String serviceUrlStr) throws Exception { 207 208 // Building command-line 209 List<String> opts = buildCommandLine(args); 210 opts.add("-serviceUrl"); 211 opts.add(serviceUrlStr); 212 213 // Launch separate JVM subprocess 214 int exitCode = 0; 215 String[] optsArray = opts.toArray(new String[0]); 216 ProcessBuilder pb = new ProcessBuilder(optsArray); 217 Process p = ProcessTools.startProcess("AuthorizationTest$ClientSide", pb); 218 219 // Handling end of subprocess 220 try { 221 exitCode = p.waitFor(); 222 if (exitCode != 0) { 223 System.out.println( 224 "Subprocess unexpected exit value of [" + exitCode + 225 "]. Expected 0.\n"); 226 } 227 } catch (InterruptedException e) { 228 System.out.println("Parent process interrupted with exception : \n " + e + " :" ); 229 230 // Parent thread unknown state, killing subprocess. 231 p.destroyForcibly(); 232 233 throw new RuntimeException( 234 "Parent process interrupted with exception : \n " + e + " :" ); 235 236 } finally { 237 if (p.isAlive()) { 238 p.destroyForcibly(); 239 } 240 return exitCode; 241 } 242 243 } 244 245 public void run(Map<String, Object> serverArgs, String clientArgs[]) { 246 247 System.out.println("AuthorizationTest::run: Start") ; 248 int errorCount = 0; 249 250 try { 251 // Initialise the server side 252 JMXServiceURL urlToUse = createServerSide(serverArgs); 253 254 // Run client side 255 errorCount = runClientSide(clientArgs, urlToUse.toString()); 256 257 if ( errorCount == 0 ) { 258 System.out.println("AuthorizationTest::run: Done without any error") ; 259 } else { 260 System.out.println("AuthorizationTest::run: Done with " 261 + errorCount 262 + " error(s)") ; 263 throw new RuntimeException("errorCount = " + errorCount); 264 } 265 266 cs.stop(); 267 268 } catch(Exception e) { 269 throw new RuntimeException(e); 270 } 271 272 } 273 274 private static class ClientSide { 275 276 private JMXConnector cc = null; 277 private MBeanServerConnection mbsc = null; 278 279 public static void main(String args[]) throws Exception { 280 281 // Parses parameters 282 Utils.parseDebugProperties(); 283 284 // Supported parameters list format is : "MainClass [-client <param-spec> ...] 285 // with <param-spec> either "-parami valuei" or "-parami" 286 HashMap<String, Object> clientMap = new HashMap<>() ; 287 Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap); 288 289 // Run test 290 ClientSide test = new ClientSide(); 291 test.run(clientMap); 292 293 } 294 295 public void run(Map<String, Object> args) { 296 297 int errorCount = 0 ; 298 299 try { 300 boolean expectedCreateException = 301 (args.containsKey("-expectedCreateException")) ? true : false ; 302 boolean expectedGetException = 303 (args.containsKey("-expectedGetException")) ? true : false ; 304 boolean expectedSetException = 305 (args.containsKey("-expectedSetException")) ? true : false ; 306 boolean expectedInvokeException = 307 (args.containsKey("-expectedInvokeException")) ? true : false ; 308 // JSR262 (see bug 6440374) 309 // There is no special JSR262 protocol operation for connect. 310 // The first request sent initiate the connection. 311 // In the JSR262 current implementation, getDefaultDomain is sent to 312 // the server in order to get the server part of the connection ID. 313 // => the connection may fail if no access permission on get requests. 314 boolean expectedConnectException = 315 (args.containsKey("-expectedConnectException")) ? true : false ; 316 // Before connection, 317 // remove the element of the Map with null values (not supported by RMI) 318 // See bug 4982668 319 args.remove("-expectedCreateException"); 320 args.remove("-expectedGetException"); 321 args.remove("-expectedSetException"); 322 args.remove("-expectedInvokeException"); 323 args.remove("-expectedConnectException"); 324 325 326 // Here do connect to the JMX Server 327 String username = System.getProperty("username"); 328 Utils.debug(Utils.DEBUG_STANDARD, 329 "ClientSide::run: CONNECT on behalf of \"" + username + "\""); 330 doConnect(args, expectedConnectException); 331 332 // If the connection did not fail, perform some requests. 333 // At this stage the mbeanserver connection is up and running 334 if (mbsc != null) { 335 ObjectName on = new ObjectName("defaultDomain:class=Simple"); 336 337 // Create request 338 Utils.debug(Utils.DEBUG_STANDARD, 339 "ClientSide::run: CREATE on behalf of \"" + 340 username + "\""); 341 errorCount += doCreateRequest(mbsc, 342 new ObjectName("defaultDomain:class=Simple,user=" + username), 343 expectedCreateException); 344 345 // Get request 346 Utils.debug(Utils.DEBUG_STANDARD, 347 "ClientSide::run: GET on behalf of \"" + 348 username + "\""); 349 errorCount += doGetRequest(mbsc, on, expectedGetException); 350 351 // Set request 352 Utils.debug(Utils.DEBUG_STANDARD, 353 "ClientSide::run: SET on behalf of \"" + 354 username + "\""); 355 errorCount += doSetRequest(mbsc, on, expectedSetException); 356 357 // Invoke request 358 Utils.debug(Utils.DEBUG_STANDARD, 359 "ClientSide::run: INVOKE on behalf of \"" + 360 username + "\""); 361 errorCount += doInvokeRequest(mbsc, on, expectedInvokeException); 362 } 363 364 } catch(Exception e) { 365 Utils.printThrowable(e, true) ; 366 errorCount++; 367 } finally { 368 // Terminate the JMX Client 369 try { 370 cc.close(); 371 } catch (Exception e) { 372 Utils.printThrowable(e, true) ; 373 errorCount++; 374 } 375 } 376 377 System.out.println("ClientSide::run: Done") ; 378 379 // Handle result 380 if (errorCount == 0) { 381 System.out.println("ClientSide::run: (OK) authorization test succeeded."); 382 } else { 383 String message = "AuthorizationTest$ClientSide::run: (ERROR) " + 384 " authorization test failed with " + 385 errorCount + " error(s)"; 386 System.out.println(message); 387 throw new RuntimeException(message); 388 } 389 } 390 391 protected void doConnect(Map<String, Object> args, 392 boolean expectedException) { 393 394 String msgTag = "ClientSide::doConnect"; 395 boolean throwRuntimeException = false; 396 String message = ""; 397 398 try { 399 Utils.debug(Utils.DEBUG_STANDARD, 400 "ClientSide::doConnect: Connect the client"); 401 402 // Collect connection environment 403 HashMap<String, Object> env = new HashMap<>(); 404 405 Object value = args.get("-mapType"); 406 if (value != null) { 407 String username = System.getProperty("username"); 408 String password = System.getProperty("password"); 409 Utils.debug(Utils.DEBUG_STANDARD, 410 msgTag + "add \"jmx.remote.credentials\" = \"" + 411 username + "\", \"" + password + "\""); 412 env.put("jmx.remote.credentials", 413 new String[] { username , password }); 414 } 415 416 // Get a connection to remote mbean server 417 JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl")); 418 cc = JMXConnectorFactory.connect(addr,env); 419 mbsc = cc.getMBeanServerConnection(); 420 421 if (expectedException) { 422 message = "ClientSide::doConnect: (ERROR) " + 423 "Connect did not fail with expected SecurityException"; 424 System.out.println(message); 425 throwRuntimeException = true; 426 } else { 427 System.out.println("ClientSide::doConnect: (OK) Connect succeed"); 428 } 429 } catch(Exception e) { 430 Utils.printThrowable(e, true); 431 if (expectedException) { 432 if (e instanceof java.lang.SecurityException) { 433 System.out.println("ClientSide::doConnect: (OK) " + 434 "Connect failed with expected SecurityException"); 435 } else { 436 message = "ClientSide::doConnect: (ERROR) " + 437 "Create failed with " + e.getClass() + 438 " instead of expected SecurityException"; 439 System.out.println(message); 440 throwRuntimeException = true; 441 } 442 } else { 443 message = "ClientSide::doConnect: (ERROR) " + 444 "Connect failed"; 445 System.out.println(message); 446 throwRuntimeException = true; 447 } 448 } 449 450 // If the connection failed, or if the connection succeeded but should not, 451 // no need to go further => throw RuntimeException and exit the test 452 if (throwRuntimeException) { 453 throw new RuntimeException(message); 454 } 455 } 456 457 protected int doCreateRequest(MBeanServerConnection mbsc, 458 ObjectName on, 459 boolean expectedException) { 460 int errorCount = 0; 461 462 try { 463 Utils.debug(Utils.DEBUG_STANDARD, 464 "ClientSide::doCreateRequest: Create and register the MBean") ; 465 466 mbsc.createMBean("Simple", on) ; 467 468 if (expectedException) { 469 System.out.println("ClientSide::doCreateRequest: " + 470 "(ERROR) Create did not fail with expected SecurityException"); 471 errorCount++; 472 } else { 473 System.out.println("ClientSide::doCreateRequest: (OK) Create succeed") ; 474 } 475 } catch(Exception e) { 476 Utils.printThrowable(e, true) ; 477 if (expectedException) { 478 if (e instanceof java.lang.SecurityException) { 479 System.out.println("ClientSide::doCreateRequest: " + 480 "(OK) Create failed with expected SecurityException") ; 481 } else { 482 System.out.println("ClientSide::doCreateRequest: " + 483 "(ERROR) Create failed with " + 484 e.getClass() + " instead of expected SecurityException"); 485 errorCount++; 486 } 487 } else { 488 System.out.println("ClientSide::doCreateRequest: " + 489 "(ERROR) Create failed"); 490 errorCount++; 491 } 492 } 493 return errorCount; 494 } 495 496 protected int doGetRequest(MBeanServerConnection mbsc, 497 ObjectName on, 498 boolean expectedException) { 499 int errorCount = 0; 500 501 try { 502 Utils.debug(Utils.DEBUG_STANDARD, 503 "ClientSide::doGetRequest: Get attributes of the MBean") ; 504 505 mbsc.getAttribute(on, "Attribute"); 506 507 if (expectedException) { 508 System.out.println("ClientSide::doGetRequest: " + 509 "(ERROR) Get did not fail with expected SecurityException"); 510 errorCount++; 511 } else { 512 System.out.println("ClientSide::doGetRequest: (OK) Get succeed") ; 513 } 514 } catch(Exception e) { 515 Utils.printThrowable(e, true) ; 516 if (expectedException) { 517 if (e instanceof java.lang.SecurityException) { 518 System.out.println("ClientSide::doGetRequest: " + 519 "(OK) Get failed with expected SecurityException") ; 520 } else { 521 System.out.println("ClientSide::doGetRequest: " + 522 "(ERROR) Get failed with " + 523 e.getClass() + " instead of expected SecurityException"); 524 errorCount++; 525 } 526 } else { 527 System.out.println("ClientSide::doGetRequest: (ERROR) Get failed"); 528 errorCount++; 529 } 530 } 531 532 return errorCount; 533 } 534 535 protected int doSetRequest(MBeanServerConnection mbsc, 536 ObjectName on, 537 boolean expectedException) { 538 int errorCount = 0; 539 540 try { 541 Utils.debug(Utils.DEBUG_STANDARD, 542 "ClientSide::doSetRequest: Set attributes of the MBean") ; 543 544 Attribute attribute = new Attribute("Attribute", "My value") ; 545 mbsc.setAttribute(on, attribute) ; 546 547 if (expectedException) { 548 System.out.println("ClientSide::doSetRequest: " + 549 "(ERROR) Set did not fail with expected SecurityException"); 550 errorCount++; 551 } else { 552 System.out.println("ClientSide::doSetRequest: (OK) Set succeed") ; 553 } 554 } catch(Exception e) { 555 Utils.printThrowable(e, true) ; 556 if (expectedException) { 557 if (e instanceof java.lang.SecurityException) { 558 System.out.println("ClientSide::doSetRequest: " + 559 "(OK) Set failed with expected SecurityException") ; 560 } else { 561 System.out.println("ClientSide::doSetRequest: " + 562 "(ERROR) Set failed with " + 563 e.getClass() + " instead of expected SecurityException"); 564 errorCount++; 565 } 566 } else { 567 System.out.println("ClientSide::doSetRequest: (ERROR) Set failed"); 568 errorCount++; 569 } 570 } 571 return errorCount; 572 } 573 574 protected int doInvokeRequest(MBeanServerConnection mbsc, 575 ObjectName on, 576 boolean expectedException) { 577 int errorCount = 0; 578 579 try { 580 Utils.debug(Utils.DEBUG_STANDARD, 581 "ClientSide::doInvokeRequest: Invoke operations on the MBean") ; 582 583 mbsc.invoke(on, "operation", null, null) ; 584 585 if (expectedException) { 586 System.out.println("ClientSide::doInvokeRequest: " + 587 "(ERROR) Invoke did not fail with expected SecurityException"); 588 errorCount++; 589 } else { 590 System.out.println("ClientSide::doInvokeRequest: (OK) Invoke succeed") ; 591 } 592 } catch(Exception e) { 593 Utils.printThrowable(e, true) ; 594 if (expectedException) { 595 if (e instanceof java.lang.SecurityException) { 596 System.out.println("ClientSide::doInvokeRequest: " + 597 "(OK) Invoke failed with expected SecurityException") ; 598 } else { 599 System.out.println("ClientSide::doInvokeRequest: " + 600 " (ERROR) Invoke failed with " + 601 e.getClass() + " instead of expected SecurityException"); 602 errorCount++; 603 } 604 } else { 605 System.out.println("ClientSide::doInvokeRequest: " + 606 "(ERROR) Invoke failed"); 607 errorCount++; 608 } 609 } 610 return errorCount; 611 } 612 613 } 614 }