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