1 /* 2 * Copyright (c) 2003, 2006, 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 com.sun.jmx.remote.security; 27 28 import com.sun.jmx.mbeanserver.GetPropertyAction; 29 import java.io.ObjectInputStream; 30 import java.security.AccessController; 31 import java.util.Set; 32 import javax.management.Attribute; 33 import javax.management.AttributeList; 34 import javax.management.AttributeNotFoundException; 35 import javax.management.InstanceNotFoundException; 36 import javax.management.InstanceAlreadyExistsException; 37 import javax.management.IntrospectionException; 38 import javax.management.InvalidAttributeValueException; 39 import javax.management.ListenerNotFoundException; 40 import javax.management.MBeanException; 41 import javax.management.MBeanInfo; 42 import javax.management.MBeanRegistrationException; 43 import javax.management.MBeanServer; 44 import javax.management.NotCompliantMBeanException; 45 import javax.management.NotificationFilter; 46 import javax.management.NotificationListener; 47 import javax.management.ObjectInstance; 48 import javax.management.ObjectName; 49 import javax.management.OperationsException; 50 import javax.management.QueryExp; 51 import javax.management.ReflectionException; 52 import javax.management.loading.ClassLoaderRepository; 53 import javax.management.remote.MBeanServerForwarder; 54 55 /** 56 * <p>An object of this class implements the MBeanServer interface 57 * and, for each of its methods, calls an appropriate checking method 58 * and then forwards the request to a wrapped MBeanServer object. The 59 * checking method may throw a RuntimeException if the operation is 60 * not allowed; in this case the request is not forwarded to the 61 * wrapped object.</p> 62 * 63 * <p>A typical use of this class is to insert it between a connector server 64 * such as the RMI connector and the MBeanServer with which the connector 65 * is associated. Requests from the connector client can then be filtered 66 * and those operations that are not allowed, or not allowed in a particular 67 * context, can be rejected by throwing a <code>SecurityException</code> 68 * in the corresponding <code>check*</code> method.</p> 69 * 70 * <p>This is an abstract class, because in its implementation none of 71 * the checking methods does anything. To be useful, it must be 72 * subclassed and at least one of the checking methods overridden to 73 * do some checking. Some or all of the MBeanServer methods may also 74 * be overridden, for instance if the default checking behavior is 75 * inappropriate.</p> 76 * 77 * <p>If there is no SecurityManager, then the access controller will refuse 78 * to create an MBean that is a ClassLoader, which includes MLets, or to 79 * execute the method addURL on an MBean that is an MLet. This prevents 80 * people from opening security holes unintentionally. Otherwise, it 81 * would not be obvious that granting write access grants the ability to 82 * download and execute arbitrary code in the target MBean server. Advanced 83 * users who do want the ability to use MLets are presumably advanced enough 84 * to handle policy files and security managers.</p> 85 */ 86 public abstract class MBeanServerAccessController 87 implements MBeanServerForwarder { 88 getMBeanServer()89 public MBeanServer getMBeanServer() { 90 return mbs; 91 } 92 setMBeanServer(MBeanServer mbs)93 public void setMBeanServer(MBeanServer mbs) { 94 if (mbs == null) 95 throw new IllegalArgumentException("Null MBeanServer"); 96 if (this.mbs != null) 97 throw new IllegalArgumentException("MBeanServer object already " + 98 "initialized"); 99 this.mbs = mbs; 100 } 101 102 /** 103 * Check if the caller can do read operations. This method does 104 * nothing if so, otherwise throws SecurityException. 105 */ checkRead()106 protected abstract void checkRead(); 107 108 /** 109 * Check if the caller can do write operations. This method does 110 * nothing if so, otherwise throws SecurityException. 111 */ checkWrite()112 protected abstract void checkWrite(); 113 114 /** 115 * Check if the caller can create the named class. The default 116 * implementation of this method calls {@link #checkWrite()}. 117 */ checkCreate(String className)118 protected void checkCreate(String className) { 119 checkWrite(); 120 } 121 122 /** 123 * Check if the caller can unregister the named MBean. The default 124 * implementation of this method calls {@link #checkWrite()}. 125 */ checkUnregister(ObjectName name)126 protected void checkUnregister(ObjectName name) { 127 checkWrite(); 128 } 129 130 //-------------------------------------------- 131 //-------------------------------------------- 132 // 133 // Implementation of the MBeanServer interface 134 // 135 //-------------------------------------------- 136 //-------------------------------------------- 137 138 /** 139 * Call <code>checkRead()</code>, then forward this method to the 140 * wrapped object. 141 */ addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback)142 public void addNotificationListener(ObjectName name, 143 NotificationListener listener, 144 NotificationFilter filter, 145 Object handback) 146 throws InstanceNotFoundException { 147 checkRead(); 148 getMBeanServer().addNotificationListener(name, listener, 149 filter, handback); 150 } 151 152 /** 153 * Call <code>checkRead()</code>, then forward this method to the 154 * wrapped object. 155 */ addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback)156 public void addNotificationListener(ObjectName name, 157 ObjectName listener, 158 NotificationFilter filter, 159 Object handback) 160 throws InstanceNotFoundException { 161 checkRead(); 162 getMBeanServer().addNotificationListener(name, listener, 163 filter, handback); 164 } 165 166 /** 167 * Call <code>checkCreate(className)</code>, then forward this method to the 168 * wrapped object. 169 */ createMBean(String className, ObjectName name)170 public ObjectInstance createMBean(String className, ObjectName name) 171 throws 172 ReflectionException, 173 InstanceAlreadyExistsException, 174 MBeanRegistrationException, 175 MBeanException, 176 NotCompliantMBeanException { 177 checkCreate(className); 178 SecurityManager sm = System.getSecurityManager(); 179 if (sm == null) { 180 Object object = getMBeanServer().instantiate(className); 181 checkClassLoader(object); 182 return getMBeanServer().registerMBean(object, name); 183 } else { 184 return getMBeanServer().createMBean(className, name); 185 } 186 } 187 188 /** 189 * Call <code>checkCreate(className)</code>, then forward this method to the 190 * wrapped object. 191 */ createMBean(String className, ObjectName name, Object params[], String signature[])192 public ObjectInstance createMBean(String className, ObjectName name, 193 Object params[], String signature[]) 194 throws 195 ReflectionException, 196 InstanceAlreadyExistsException, 197 MBeanRegistrationException, 198 MBeanException, 199 NotCompliantMBeanException { 200 checkCreate(className); 201 SecurityManager sm = System.getSecurityManager(); 202 if (sm == null) { 203 Object object = getMBeanServer().instantiate(className, 204 params, 205 signature); 206 checkClassLoader(object); 207 return getMBeanServer().registerMBean(object, name); 208 } else { 209 return getMBeanServer().createMBean(className, name, 210 params, signature); 211 } 212 } 213 214 /** 215 * Call <code>checkCreate(className)</code>, then forward this method to the 216 * wrapped object. 217 */ createMBean(String className, ObjectName name, ObjectName loaderName)218 public ObjectInstance createMBean(String className, 219 ObjectName name, 220 ObjectName loaderName) 221 throws 222 ReflectionException, 223 InstanceAlreadyExistsException, 224 MBeanRegistrationException, 225 MBeanException, 226 NotCompliantMBeanException, 227 InstanceNotFoundException { 228 checkCreate(className); 229 SecurityManager sm = System.getSecurityManager(); 230 if (sm == null) { 231 Object object = getMBeanServer().instantiate(className, 232 loaderName); 233 checkClassLoader(object); 234 return getMBeanServer().registerMBean(object, name); 235 } else { 236 return getMBeanServer().createMBean(className, name, loaderName); 237 } 238 } 239 240 /** 241 * Call <code>checkCreate(className)</code>, then forward this method to the 242 * wrapped object. 243 */ createMBean(String className, ObjectName name, ObjectName loaderName, Object params[], String signature[])244 public ObjectInstance createMBean(String className, 245 ObjectName name, 246 ObjectName loaderName, 247 Object params[], 248 String signature[]) 249 throws 250 ReflectionException, 251 InstanceAlreadyExistsException, 252 MBeanRegistrationException, 253 MBeanException, 254 NotCompliantMBeanException, 255 InstanceNotFoundException { 256 checkCreate(className); 257 SecurityManager sm = System.getSecurityManager(); 258 if (sm == null) { 259 Object object = getMBeanServer().instantiate(className, 260 loaderName, 261 params, 262 signature); 263 checkClassLoader(object); 264 return getMBeanServer().registerMBean(object, name); 265 } else { 266 return getMBeanServer().createMBean(className, name, loaderName, 267 params, signature); 268 } 269 } 270 271 /** 272 * Call <code>checkRead()</code>, then forward this method to the 273 * wrapped object. 274 */ 275 @Deprecated deserialize(ObjectName name, byte[] data)276 public ObjectInputStream deserialize(ObjectName name, byte[] data) 277 throws InstanceNotFoundException, OperationsException { 278 checkRead(); 279 return getMBeanServer().deserialize(name, data); 280 } 281 282 /** 283 * Call <code>checkRead()</code>, then forward this method to the 284 * wrapped object. 285 */ 286 @Deprecated deserialize(String className, byte[] data)287 public ObjectInputStream deserialize(String className, byte[] data) 288 throws OperationsException, ReflectionException { 289 checkRead(); 290 return getMBeanServer().deserialize(className, data); 291 } 292 293 /** 294 * Call <code>checkRead()</code>, then forward this method to the 295 * wrapped object. 296 */ 297 @Deprecated deserialize(String className, ObjectName loaderName, byte[] data)298 public ObjectInputStream deserialize(String className, 299 ObjectName loaderName, 300 byte[] data) 301 throws 302 InstanceNotFoundException, 303 OperationsException, 304 ReflectionException { 305 checkRead(); 306 return getMBeanServer().deserialize(className, loaderName, data); 307 } 308 309 /** 310 * Call <code>checkRead()</code>, then forward this method to the 311 * wrapped object. 312 */ getAttribute(ObjectName name, String attribute)313 public Object getAttribute(ObjectName name, String attribute) 314 throws 315 MBeanException, 316 AttributeNotFoundException, 317 InstanceNotFoundException, 318 ReflectionException { 319 checkRead(); 320 return getMBeanServer().getAttribute(name, attribute); 321 } 322 323 /** 324 * Call <code>checkRead()</code>, then forward this method to the 325 * wrapped object. 326 */ getAttributes(ObjectName name, String[] attributes)327 public AttributeList getAttributes(ObjectName name, String[] attributes) 328 throws InstanceNotFoundException, ReflectionException { 329 checkRead(); 330 return getMBeanServer().getAttributes(name, attributes); 331 } 332 333 /** 334 * Call <code>checkRead()</code>, then forward this method to the 335 * wrapped object. 336 */ getClassLoader(ObjectName loaderName)337 public ClassLoader getClassLoader(ObjectName loaderName) 338 throws InstanceNotFoundException { 339 checkRead(); 340 return getMBeanServer().getClassLoader(loaderName); 341 } 342 343 /** 344 * Call <code>checkRead()</code>, then forward this method to the 345 * wrapped object. 346 */ getClassLoaderFor(ObjectName mbeanName)347 public ClassLoader getClassLoaderFor(ObjectName mbeanName) 348 throws InstanceNotFoundException { 349 checkRead(); 350 return getMBeanServer().getClassLoaderFor(mbeanName); 351 } 352 353 /** 354 * Call <code>checkRead()</code>, then forward this method to the 355 * wrapped object. 356 */ getClassLoaderRepository()357 public ClassLoaderRepository getClassLoaderRepository() { 358 checkRead(); 359 return getMBeanServer().getClassLoaderRepository(); 360 } 361 362 /** 363 * Call <code>checkRead()</code>, then forward this method to the 364 * wrapped object. 365 */ getDefaultDomain()366 public String getDefaultDomain() { 367 checkRead(); 368 return getMBeanServer().getDefaultDomain(); 369 } 370 371 /** 372 * Call <code>checkRead()</code>, then forward this method to the 373 * wrapped object. 374 */ getDomains()375 public String[] getDomains() { 376 checkRead(); 377 return getMBeanServer().getDomains(); 378 } 379 380 /** 381 * Call <code>checkRead()</code>, then forward this method to the 382 * wrapped object. 383 */ getMBeanCount()384 public Integer getMBeanCount() { 385 checkRead(); 386 return getMBeanServer().getMBeanCount(); 387 } 388 389 /** 390 * Call <code>checkRead()</code>, then forward this method to the 391 * wrapped object. 392 */ getMBeanInfo(ObjectName name)393 public MBeanInfo getMBeanInfo(ObjectName name) 394 throws 395 InstanceNotFoundException, 396 IntrospectionException, 397 ReflectionException { 398 checkRead(); 399 return getMBeanServer().getMBeanInfo(name); 400 } 401 402 /** 403 * Call <code>checkRead()</code>, then forward this method to the 404 * wrapped object. 405 */ getObjectInstance(ObjectName name)406 public ObjectInstance getObjectInstance(ObjectName name) 407 throws InstanceNotFoundException { 408 checkRead(); 409 return getMBeanServer().getObjectInstance(name); 410 } 411 412 /** 413 * Call <code>checkCreate(className)</code>, then forward this method to the 414 * wrapped object. 415 */ instantiate(String className)416 public Object instantiate(String className) 417 throws ReflectionException, MBeanException { 418 checkCreate(className); 419 return getMBeanServer().instantiate(className); 420 } 421 422 /** 423 * Call <code>checkCreate(className)</code>, then forward this method to the 424 * wrapped object. 425 */ instantiate(String className, Object params[], String signature[])426 public Object instantiate(String className, 427 Object params[], 428 String signature[]) 429 throws ReflectionException, MBeanException { 430 checkCreate(className); 431 return getMBeanServer().instantiate(className, params, signature); 432 } 433 434 /** 435 * Call <code>checkCreate(className)</code>, then forward this method to the 436 * wrapped object. 437 */ instantiate(String className, ObjectName loaderName)438 public Object instantiate(String className, ObjectName loaderName) 439 throws ReflectionException, MBeanException, InstanceNotFoundException { 440 checkCreate(className); 441 return getMBeanServer().instantiate(className, loaderName); 442 } 443 444 /** 445 * Call <code>checkCreate(className)</code>, then forward this method to the 446 * wrapped object. 447 */ instantiate(String className, ObjectName loaderName, Object params[], String signature[])448 public Object instantiate(String className, ObjectName loaderName, 449 Object params[], String signature[]) 450 throws ReflectionException, MBeanException, InstanceNotFoundException { 451 checkCreate(className); 452 return getMBeanServer().instantiate(className, loaderName, 453 params, signature); 454 } 455 456 /** 457 * Call <code>checkWrite()</code>, then forward this method to the 458 * wrapped object. 459 */ invoke(ObjectName name, String operationName, Object params[], String signature[])460 public Object invoke(ObjectName name, String operationName, 461 Object params[], String signature[]) 462 throws 463 InstanceNotFoundException, 464 MBeanException, 465 ReflectionException { 466 checkWrite(); 467 checkMLetMethods(name, operationName); 468 return getMBeanServer().invoke(name, operationName, params, signature); 469 } 470 471 /** 472 * Call <code>checkRead()</code>, then forward this method to the 473 * wrapped object. 474 */ isInstanceOf(ObjectName name, String className)475 public boolean isInstanceOf(ObjectName name, String className) 476 throws InstanceNotFoundException { 477 checkRead(); 478 return getMBeanServer().isInstanceOf(name, className); 479 } 480 481 /** 482 * Call <code>checkRead()</code>, then forward this method to the 483 * wrapped object. 484 */ isRegistered(ObjectName name)485 public boolean isRegistered(ObjectName name) { 486 checkRead(); 487 return getMBeanServer().isRegistered(name); 488 } 489 490 /** 491 * Call <code>checkRead()</code>, then forward this method to the 492 * wrapped object. 493 */ queryMBeans(ObjectName name, QueryExp query)494 public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) { 495 checkRead(); 496 return getMBeanServer().queryMBeans(name, query); 497 } 498 499 /** 500 * Call <code>checkRead()</code>, then forward this method to the 501 * wrapped object. 502 */ queryNames(ObjectName name, QueryExp query)503 public Set<ObjectName> queryNames(ObjectName name, QueryExp query) { 504 checkRead(); 505 return getMBeanServer().queryNames(name, query); 506 } 507 508 /** 509 * Call <code>checkWrite()</code>, then forward this method to the 510 * wrapped object. 511 */ registerMBean(Object object, ObjectName name)512 public ObjectInstance registerMBean(Object object, ObjectName name) 513 throws 514 InstanceAlreadyExistsException, 515 MBeanRegistrationException, 516 NotCompliantMBeanException { 517 checkWrite(); 518 return getMBeanServer().registerMBean(object, name); 519 } 520 521 /** 522 * Call <code>checkRead()</code>, then forward this method to the 523 * wrapped object. 524 */ removeNotificationListener(ObjectName name, NotificationListener listener)525 public void removeNotificationListener(ObjectName name, 526 NotificationListener listener) 527 throws InstanceNotFoundException, ListenerNotFoundException { 528 checkRead(); 529 getMBeanServer().removeNotificationListener(name, listener); 530 } 531 532 /** 533 * Call <code>checkRead()</code>, then forward this method to the 534 * wrapped object. 535 */ removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback)536 public void removeNotificationListener(ObjectName name, 537 NotificationListener listener, 538 NotificationFilter filter, 539 Object handback) 540 throws InstanceNotFoundException, ListenerNotFoundException { 541 checkRead(); 542 getMBeanServer().removeNotificationListener(name, listener, 543 filter, handback); 544 } 545 546 /** 547 * Call <code>checkRead()</code>, then forward this method to the 548 * wrapped object. 549 */ removeNotificationListener(ObjectName name, ObjectName listener)550 public void removeNotificationListener(ObjectName name, 551 ObjectName listener) 552 throws InstanceNotFoundException, ListenerNotFoundException { 553 checkRead(); 554 getMBeanServer().removeNotificationListener(name, listener); 555 } 556 557 /** 558 * Call <code>checkRead()</code>, then forward this method to the 559 * wrapped object. 560 */ removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback)561 public void removeNotificationListener(ObjectName name, 562 ObjectName listener, 563 NotificationFilter filter, 564 Object handback) 565 throws InstanceNotFoundException, ListenerNotFoundException { 566 checkRead(); 567 getMBeanServer().removeNotificationListener(name, listener, 568 filter, handback); 569 } 570 571 /** 572 * Call <code>checkWrite()</code>, then forward this method to the 573 * wrapped object. 574 */ setAttribute(ObjectName name, Attribute attribute)575 public void setAttribute(ObjectName name, Attribute attribute) 576 throws 577 InstanceNotFoundException, 578 AttributeNotFoundException, 579 InvalidAttributeValueException, 580 MBeanException, 581 ReflectionException { 582 checkWrite(); 583 getMBeanServer().setAttribute(name, attribute); 584 } 585 586 /** 587 * Call <code>checkWrite()</code>, then forward this method to the 588 * wrapped object. 589 */ setAttributes(ObjectName name, AttributeList attributes)590 public AttributeList setAttributes(ObjectName name, 591 AttributeList attributes) 592 throws InstanceNotFoundException, ReflectionException { 593 checkWrite(); 594 return getMBeanServer().setAttributes(name, attributes); 595 } 596 597 /** 598 * Call <code>checkUnregister()</code>, then forward this method to the 599 * wrapped object. 600 */ unregisterMBean(ObjectName name)601 public void unregisterMBean(ObjectName name) 602 throws InstanceNotFoundException, MBeanRegistrationException { 603 checkUnregister(name); 604 getMBeanServer().unregisterMBean(name); 605 } 606 607 //---------------- 608 // PRIVATE METHODS 609 //---------------- 610 checkClassLoader(Object object)611 private void checkClassLoader(Object object) { 612 if (object instanceof ClassLoader) 613 throw new SecurityException("Access denied! Creating an " + 614 "MBean that is a ClassLoader " + 615 "is forbidden unless a security " + 616 "manager is installed."); 617 } 618 checkMLetMethods(ObjectName name, String operation)619 private void checkMLetMethods(ObjectName name, String operation) 620 throws InstanceNotFoundException { 621 // Check if security manager installed 622 SecurityManager sm = System.getSecurityManager(); 623 if (sm != null) { 624 return; 625 } 626 // Check for addURL and getMBeansFromURL methods 627 if (!operation.equals("addURL") && 628 !operation.equals("getMBeansFromURL")) { 629 return; 630 } 631 // Check if MBean is instance of MLet 632 if (!getMBeanServer().isInstanceOf(name, 633 "javax.management.loading.MLet")) { 634 return; 635 } 636 // Throw security exception 637 if (operation.equals("addURL")) { // addURL 638 throw new SecurityException("Access denied! MLet method addURL " + 639 "cannot be invoked unless a security manager is installed."); 640 } else { // getMBeansFromURL 641 // Whether or not calling getMBeansFromURL is allowed is controlled 642 // by the value of the "jmx.remote.x.mlet.allow.getMBeansFromURL" 643 // system property. If the value of this property is true, calling 644 // the MLet's getMBeansFromURL method is allowed. The default value 645 // for this property is false. 646 final String propName = "jmx.remote.x.mlet.allow.getMBeansFromURL"; 647 GetPropertyAction propAction = new GetPropertyAction(propName); 648 String propValue = AccessController.doPrivileged(propAction); 649 boolean allowGetMBeansFromURL = "true".equalsIgnoreCase(propValue); 650 if (!allowGetMBeansFromURL) { 651 throw new SecurityException("Access denied! MLet method " + 652 "getMBeansFromURL cannot be invoked unless a " + 653 "security manager is installed or the system property " + 654 "-Djmx.remote.x.mlet.allow.getMBeansFromURL=true " + 655 "is specified."); 656 } 657 } 658 } 659 660 //------------------ 661 // PRIVATE VARIABLES 662 //------------------ 663 664 private MBeanServer mbs; 665 } 666