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