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