jdk/test/javax/management/eventService/EventDelegateSecurityTest.java
changeset 4159 9e3aae7675f1
parent 4158 0b4d21bc8b5c
parent 4156 acaa49a2768a
child 4160 bda0a85afcb7
equal deleted inserted replaced
4158:0b4d21bc8b5c 4159:9e3aae7675f1
     1 /*
       
     2  * Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    20  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    21  * have any questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 5108776
       
    27  * @summary Test that the EventClientDelegate MBean does not require extra
       
    28  * permissions compared with plain addNotificationListener.
       
    29  * @author Eamonn McManus
       
    30  * @run main/othervm -Dxjava.security.debug=policy,access,failure EventDelegateSecurityTest
       
    31  */
       
    32 
       
    33 import java.io.File;
       
    34 import java.io.IOException;
       
    35 import java.io.PrintWriter;
       
    36 import java.lang.management.ManagementFactory;
       
    37 import java.lang.reflect.InvocationHandler;
       
    38 import java.lang.reflect.InvocationTargetException;
       
    39 import java.lang.reflect.Method;
       
    40 import java.lang.reflect.Proxy;
       
    41 import java.security.AccessControlContext;
       
    42 import java.security.AccessController;
       
    43 import java.security.AllPermission;
       
    44 import java.security.PrivilegedExceptionAction;
       
    45 import java.util.Arrays;
       
    46 import java.util.HashMap;
       
    47 import java.util.HashSet;
       
    48 import java.util.Map;
       
    49 import java.util.Set;
       
    50 import java.util.concurrent.BlockingQueue;
       
    51 import java.util.concurrent.SynchronousQueue;
       
    52 import java.util.concurrent.TimeUnit;
       
    53 import javax.management.MBeanPermission;
       
    54 import javax.management.MBeanServer;
       
    55 import javax.management.MBeanServerConnection;
       
    56 import javax.management.Notification;
       
    57 import javax.management.NotificationBroadcasterSupport;
       
    58 import javax.management.NotificationListener;
       
    59 import javax.management.ObjectName;
       
    60 import javax.management.event.EventClient;
       
    61 import javax.management.remote.JMXAuthenticator;
       
    62 import javax.management.remote.JMXConnector;
       
    63 import javax.management.remote.JMXConnectorFactory;
       
    64 import javax.management.remote.JMXConnectorServer;
       
    65 import javax.management.remote.JMXConnectorServerFactory;
       
    66 import javax.management.remote.JMXPrincipal;
       
    67 import javax.management.remote.JMXServiceURL;
       
    68 import javax.management.remote.MBeanServerForwarder;
       
    69 import javax.security.auth.Subject;
       
    70 
       
    71 public class EventDelegateSecurityTest {
       
    72     private static final BlockingQueue<Notification> notifQ =
       
    73             new SynchronousQueue<Notification>();
       
    74 
       
    75     private static volatile long seqNo;
       
    76     private static volatile long expectSeqNo;
       
    77 
       
    78     private static class QueueListener implements NotificationListener {
       
    79         public void handleNotification(Notification notification,
       
    80                                        Object handback) {
       
    81             try {
       
    82                 notifQ.put(notification);
       
    83             } catch (InterruptedException e) {
       
    84                 throw new AssertionError(e);
       
    85             }
       
    86         }
       
    87     }
       
    88     private static final NotificationListener queueListener = new QueueListener();
       
    89 
       
    90     public static interface SenderMBean {
       
    91         public void send();
       
    92     }
       
    93 
       
    94     public static class Sender
       
    95             extends NotificationBroadcasterSupport implements SenderMBean {
       
    96         public void send() {
       
    97             Notification n = new Notification("x", this, seqNo++);
       
    98             sendNotification(n);
       
    99         }
       
   100     }
       
   101 
       
   102     private static class LimitInvocationHandler implements InvocationHandler {
       
   103         private MBeanServer nextMBS;
       
   104         private final Set<String> allowedMethods = new HashSet<String>();
       
   105 
       
   106         void allow(String... names) {
       
   107             synchronized (allowedMethods) {
       
   108                 allowedMethods.addAll(Arrays.asList(names));
       
   109             }
       
   110         }
       
   111 
       
   112         public Object invoke(Object proxy, Method m, Object[] args)
       
   113                 throws Throwable {
       
   114             System.out.println(
       
   115                     "filter: " + m.getName() +
       
   116                     ((args == null) ? "[]" : Arrays.deepToString(args)));
       
   117             String name = m.getName();
       
   118 
       
   119             if (name.equals("getMBeanServer"))
       
   120                 return nextMBS;
       
   121 
       
   122             if (name.equals("setMBeanServer")) {
       
   123                 nextMBS = (MBeanServer) args[0];
       
   124                 return null;
       
   125             }
       
   126 
       
   127             if (m.getDeclaringClass() == Object.class ||
       
   128                     allowedMethods.contains(name)) {
       
   129                 try {
       
   130                     return m.invoke(nextMBS, args);
       
   131                 } catch (InvocationTargetException e) {
       
   132                     throw e.getCause();
       
   133                 }
       
   134             } else {
       
   135                 System.out.println("...refused");
       
   136                 throw new SecurityException(
       
   137                         "Method refused: " + m.getDeclaringClass().getName() +
       
   138                         "." + m.getName() +
       
   139                         ((args == null) ? "[]" : Arrays.deepToString(args)));
       
   140             }
       
   141         }
       
   142 
       
   143     }
       
   144 
       
   145     private static interface MakeConnectorServer {
       
   146         public JMXConnectorServer make(JMXServiceURL url) throws IOException;
       
   147     }
       
   148 
       
   149 
       
   150     public static void main(String[] args) throws Exception {
       
   151         JMXPrincipal rootPrincipal = new JMXPrincipal("root");
       
   152         Subject rootSubject = new Subject();
       
   153         rootSubject.getPrincipals().add(rootPrincipal);
       
   154         Subject.doAsPrivileged(rootSubject, new PrivilegedExceptionAction<Void>() {
       
   155             public Void run() throws Exception {
       
   156                 mainAsRoot();
       
   157                 return null;
       
   158             }
       
   159         }, null);
       
   160     }
       
   161 
       
   162     private static void mainAsRoot() throws Exception {
       
   163         AccessControlContext acc = AccessController.getContext();
       
   164         Subject subject = Subject.getSubject(acc);
       
   165         System.out.println("Subject: " + subject);
       
   166         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
       
   167         ObjectName name = new ObjectName("a:b=c");
       
   168         mbs.registerMBean(new Sender(), name);
       
   169 
       
   170         System.out.println("Test with no installed security");
       
   171         test(mbs, name, new MakeConnectorServer() {
       
   172             public JMXConnectorServer make(JMXServiceURL url) throws IOException {
       
   173                 return
       
   174                     JMXConnectorServerFactory.newJMXConnectorServer(url, null, null);
       
   175             }
       
   176         });
       
   177 
       
   178         System.out.println("Test with filtering MBeanServerForwarder");
       
   179         LimitInvocationHandler limitIH = new LimitInvocationHandler();
       
   180         // We allow getClassLoaderRepository because the ConnectorServer
       
   181         // calls it so any real checking MBeanServerForwarder must accept it.
       
   182         limitIH.allow(
       
   183                 "addNotificationListener", "removeNotificationListener",
       
   184                 "getClassLoaderRepository"
       
   185                 );
       
   186         final MBeanServerForwarder limitMBSF = (MBeanServerForwarder)
       
   187             Proxy.newProxyInstance(
       
   188                 MBeanServerForwarder.class.getClassLoader(),
       
   189                 new Class<?>[] {MBeanServerForwarder.class},
       
   190                 limitIH);
       
   191         // We go to considerable lengths to ensure that the ConnectorServer has
       
   192         // no MBeanServer when the EventClientDelegate forwarder is activated,
       
   193         // so that the calls it makes when it is later linked to an MBeanServer
       
   194         // go through the limitMBSF.
       
   195         test(mbs, name, new MakeConnectorServer() {
       
   196             public JMXConnectorServer make(JMXServiceURL url) throws IOException {
       
   197                 JMXConnectorServer cs =
       
   198                     JMXConnectorServerFactory.newJMXConnectorServer(url, null, null);
       
   199                 limitMBSF.setMBeanServer(mbs);
       
   200                 cs.setMBeanServerForwarder(limitMBSF);
       
   201                 return cs;
       
   202             }
       
   203         });
       
   204 
       
   205         final File policyFile =
       
   206                 File.createTempFile("EventDelegateSecurityTest", ".policy");
       
   207         PrintWriter pw = new PrintWriter(policyFile);
       
   208         String JMXPrincipal = JMXPrincipal.class.getName();
       
   209         String AllPermission = AllPermission.class.getName();
       
   210         String MBeanPermission = MBeanPermission.class.getName();
       
   211         pw.println("grant principal " + JMXPrincipal + " \"root\" {");
       
   212         pw.println("    permission " + AllPermission + ";");
       
   213         pw.println("};");
       
   214         pw.println("grant principal " + JMXPrincipal + " \"user\" {");
       
   215         pw.println("    permission " + MBeanPermission + " \"*\", " +
       
   216                 " \"addNotificationListener\";");
       
   217         pw.println("    permission " + MBeanPermission + " \"*\", " +
       
   218                 " \"removeNotificationListener\";");
       
   219         pw.println("};");
       
   220         pw.close();
       
   221         Runtime.getRuntime().addShutdownHook(new Thread() {
       
   222             @Override
       
   223             public void run() {
       
   224                 policyFile.delete();
       
   225             }
       
   226         });
       
   227         System.setProperty("java.security.policy", policyFile.getAbsolutePath());
       
   228         System.setSecurityManager(new SecurityManager());
       
   229         test(mbs, name, new MakeConnectorServer() {
       
   230             public JMXConnectorServer make(JMXServiceURL url) throws IOException {
       
   231                 Map<String, Object> env = new HashMap<String, Object>();
       
   232                 env.put(JMXConnectorServer.AUTHENTICATOR, new JMXAuthenticator() {
       
   233                     public Subject authenticate(Object credentials) {
       
   234                         Subject s = new Subject();
       
   235                         s.getPrincipals().add(new JMXPrincipal("user"));
       
   236                         return s;
       
   237                     }
       
   238                 });
       
   239                 return
       
   240                     JMXConnectorServerFactory.newJMXConnectorServer(url, env, null);
       
   241             }
       
   242         });
       
   243     }
       
   244 
       
   245     private static void test(MBeanServer mbs, ObjectName name) throws Exception {
       
   246         test(mbs, name, null);
       
   247     }
       
   248 
       
   249     private static void test(
       
   250             MBeanServer mbs, ObjectName name, MakeConnectorServer make)
       
   251             throws Exception {
       
   252         JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
       
   253         JMXConnectorServer cs = make.make(url);
       
   254         ObjectName csName = new ObjectName("a:type=ConnectorServer");
       
   255         mbs.registerMBean(cs, csName);
       
   256         cs.start();
       
   257         try {
       
   258             JMXServiceURL addr = cs.getAddress();
       
   259             JMXConnector cc = JMXConnectorFactory.connect(addr);
       
   260             MBeanServerConnection mbsc = cc.getMBeanServerConnection();
       
   261             test(mbs, mbsc, name);
       
   262             cc.close();
       
   263             mbs.unregisterMBean(csName);
       
   264         } finally {
       
   265             cs.stop();
       
   266         }
       
   267     }
       
   268 
       
   269     private static void test(
       
   270             MBeanServer mbs, MBeanServerConnection mbsc, ObjectName name)
       
   271             throws Exception {
       
   272         EventClient ec = new EventClient(mbsc);
       
   273         ec.addNotificationListener(name, queueListener, null, null);
       
   274         mbs.invoke(name, "send", null, null);
       
   275 
       
   276         Notification n = notifQ.poll(5, TimeUnit.SECONDS);
       
   277         if (n == null)
       
   278             throw new Exception("FAILED: notif not delivered");
       
   279         if (n.getSequenceNumber() != expectSeqNo) {
       
   280             throw new Exception(
       
   281                     "FAILED: notif seqno " + n.getSequenceNumber() +
       
   282                     " should be " + expectSeqNo);
       
   283         }
       
   284         expectSeqNo++;
       
   285 
       
   286         ec.removeNotificationListener(name, queueListener);
       
   287         ec.close();
       
   288     }
       
   289 }