1 /*
2  * Copyright (c) 2005, 2008, 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 5106721
27  * @summary Check the emission of notifications when a Security Manager is
28  * installed. Test the property "jmx.remote.x.check.notification.emission".
29  * @author Luis-Miguel Alventosa
30  * @run clean NotificationEmissionTest
31  * @run build NotificationEmissionTest
32  * @run main NotificationEmissionTest 1
33  * @run main NotificationEmissionTest 2
34  * @run main NotificationEmissionTest 3
35  * @run main NotificationEmissionTest 4
36  * @run main NotificationEmissionTest 5
37  */
38 
39 import java.io.File;
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import javax.management.MBeanServer;
46 import javax.management.MBeanServerConnection;
47 import javax.management.MBeanServerFactory;
48 import javax.management.Notification;
49 import javax.management.NotificationBroadcasterSupport;
50 import javax.management.NotificationListener;
51 import javax.management.ObjectName;
52 import javax.management.remote.JMXAuthenticator;
53 import javax.management.remote.JMXConnector;
54 import javax.management.remote.JMXConnectorFactory;
55 import javax.management.remote.JMXConnectorServer;
56 import javax.management.remote.JMXConnectorServerFactory;
57 import javax.management.remote.JMXPrincipal;
58 import javax.management.remote.JMXServiceURL;
59 import javax.security.auth.Subject;
60 
61 public class NotificationEmissionTest {
62 
63     public class CustomJMXAuthenticator implements JMXAuthenticator {
authenticate(Object credentials)64         public Subject authenticate(Object credentials) {
65             String role = ((String[]) credentials)[0];
66             echo("Create principal with name = " + role);
67             return new Subject(true,
68                                Collections.singleton(new JMXPrincipal(role)),
69                                Collections.EMPTY_SET,
70                                Collections.EMPTY_SET);
71         }
72     }
73 
74     public interface NBMBean {
emitNotification(int seqnum, ObjectName name)75         public void emitNotification(int seqnum, ObjectName name);
76     }
77 
78     public static class NB
79         extends NotificationBroadcasterSupport
80         implements NBMBean {
emitNotification(int seqnum, ObjectName name)81         public void emitNotification(int seqnum, ObjectName name) {
82             if (name == null) {
83                 sendNotification(new Notification("nb", this, seqnum));
84             } else {
85                 sendNotification(new Notification("nb", name, seqnum));
86             }
87         }
88     }
89 
90     public class Listener implements NotificationListener {
91         public List<Notification> notifs = new ArrayList<Notification>();
handleNotification(Notification n, Object h)92         public void handleNotification(Notification n, Object h) {
93             echo("handleNotification:");
94             echo("\tNotification = " + n);
95             echo("\tNotification.SeqNum = " + n.getSequenceNumber());
96             echo("\tHandback = " + h);
97             notifs.add(n);
98         }
99     }
100 
checkNotifs(int size, List<Notification> received, List<ObjectName> expected)101     public int checkNotifs(int size,
102                            List<Notification> received,
103                            List<ObjectName> expected) {
104         if (received.size() != size) {
105             echo("Error: expecting " + size + " notifications, got " +
106                     received.size());
107             return 1;
108         } else {
109             for (Notification n : received) {
110                 echo("Received notification: " + n);
111                 if (!n.getType().equals("nb")) {
112                     echo("Notification type must be \"nb\"");
113                     return 1;
114                 }
115                 ObjectName o = (ObjectName) n.getSource();
116                 int index = (int) n.getSequenceNumber();
117                 ObjectName nb = expected.get(index);
118                 if (!o.equals(nb)) {
119                     echo("Notification source must be " + nb);
120                     return 1;
121                 }
122             }
123         }
124         return 0;
125     }
126 
runTest(int testcase)127     public int runTest(int testcase) throws Exception {
128         echo("\n=-=-= Running testcase " + testcase + " =-=-=");
129         switch (testcase) {
130             case 1:
131                 return testNotificationEmissionProperty();
132             case 2:
133                 return testNotificationEmissionPositive(false);
134             case 3:
135                 return testNotificationEmissionNegative(false);
136             case 4:
137                 return testNotificationEmissionPositive(true);
138             case 5:
139                 return testNotificationEmissionNegative(true);
140             default:
141                 echo("Invalid testcase");
142                 return 1;
143         }
144     }
145 
testNotificationEmissionProperty(boolean exception, Object propValue)146     public int testNotificationEmissionProperty(boolean exception,
147                                                 Object propValue)
148         throws Exception {
149         try {
150             testNotificationEmission(propValue);
151             if (exception) {
152                 echo("Did not get expected exception for value: " + propValue);
153                 return 1;
154             } else {
155                 echo("Property has been correctly set to value: " + propValue);
156             }
157         } catch (Exception e) {
158             if (exception) {
159                 echo("Got expected exception for value: " + propValue);
160                 echo("Exception: " + e);
161             } else {
162                 echo("Got unexpected exception for value: " + propValue);
163                 echo("Exception: " + e);
164                 return 1;
165             }
166         }
167         return 0;
168     }
169 
testNotificationEmissionProperty()170     public int testNotificationEmissionProperty() throws Exception {
171         int error = 0;
172         error += testNotificationEmissionProperty(true, new Boolean(false));
173         error += testNotificationEmissionProperty(true, new Boolean(true));
174         error += testNotificationEmissionProperty(true, "dummy");
175         error += testNotificationEmissionProperty(false, "false");
176         error += testNotificationEmissionProperty(false, "true");
177         error += testNotificationEmissionProperty(false, "FALSE");
178         error += testNotificationEmissionProperty(false, "TRUE");
179         return error;
180     }
181 
testNotificationEmissionPositive(boolean prop)182     public int testNotificationEmissionPositive(boolean prop) throws Exception {
183         return testNotificationEmission(prop, "true", true, true);
184     }
185 
testNotificationEmissionNegative(boolean prop)186     public int testNotificationEmissionNegative(boolean prop) throws Exception {
187         return testNotificationEmission(prop, "true", true, false);
188     }
189 
testNotificationEmission(Object propValue)190     public int testNotificationEmission(Object propValue) throws Exception {
191         return testNotificationEmission(true, propValue, false, true);
192     }
193 
testNotificationEmission(boolean prop, Object propValue, boolean sm, boolean policyPositive)194     public int testNotificationEmission(boolean prop,
195                                         Object propValue,
196                                         boolean sm,
197                                         boolean policyPositive)
198         throws Exception {
199 
200         JMXConnectorServer server = null;
201         JMXConnector client = null;
202 
203         // Set policy file
204         //
205         String policyFile =
206             System.getProperty("test.src") + File.separator +
207             (policyPositive ? "policy.positive" : "policy.negative");
208         echo("\nSetting policy file " + policyFile);
209         System.setProperty("java.security.policy", policyFile);
210 
211         // Create a new MBeanServer
212         //
213         final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
214 
215         try {
216             // Create server environment map
217             //
218             final Map<String,Object> env = new HashMap<String,Object>();
219             env.put("jmx.remote.authenticator", new CustomJMXAuthenticator());
220             if (prop)
221                 env.put("jmx.remote.x.check.notification.emission", propValue);
222 
223             // Create the JMXServiceURL
224             //
225             final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
226 
227             // Create a JMXConnectorServer
228             //
229             server = JMXConnectorServerFactory.newJMXConnectorServer(url,
230                                                                      env,
231                                                                      mbs);
232 
233             // Start the JMXConnectorServer
234             //
235             server.start();
236 
237             // Create server environment map
238             //
239             final Map<String,Object> cenv = new HashMap<String,Object>();
240             String[] credentials = new String[] { "role" , "password" };
241             cenv.put("jmx.remote.credentials", credentials);
242 
243             // Create JMXConnector and connect to JMXConnectorServer
244             //
245             client = JMXConnectorFactory.connect(server.getAddress(), cenv);
246 
247             // Get non-secure MBeanServerConnection
248             //
249             final MBeanServerConnection mbsc =
250                 client.getMBeanServerConnection();
251 
252             // Create NB MBean
253             //
254             ObjectName nb1 = ObjectName.getInstance("domain:type=NB,name=1");
255             ObjectName nb2 = ObjectName.getInstance("domain:type=NB,name=2");
256             ObjectName nb3 = ObjectName.getInstance("domain:type=NB,name=3");
257             mbsc.createMBean(NB.class.getName(), nb1);
258             mbsc.createMBean(NB.class.getName(), nb2);
259             mbsc.createMBean(NB.class.getName(), nb3);
260 
261             // Add notification listener
262             //
263             Listener li = new Listener();
264             mbsc.addNotificationListener(nb1, li, null, null);
265             mbsc.addNotificationListener(nb2, li, null, null);
266 
267             // Set security manager
268             //
269             if (sm) {
270                 echo("Setting SM");
271                 System.setSecurityManager(new SecurityManager());
272             }
273 
274             // Invoke the "sendNotification" method
275             //
276             mbsc.invoke(nb1, "emitNotification",
277                 new Object[] {0, null},
278                 new String[] {"int", "javax.management.ObjectName"});
279             mbsc.invoke(nb2, "emitNotification",
280                 new Object[] {1, null},
281                 new String[] {"int", "javax.management.ObjectName"});
282             mbsc.invoke(nb2, "emitNotification",
283                 new Object[] {2, nb3},
284                 new String[] {"int", "javax.management.ObjectName"});
285 
286             // If the check is effective and we're using policy.negative,
287             // then we should see the two notifs sent by nb2 (of which one
288             // has a getSource() that is nb3), but not the notif sent by nb1.
289             // Otherwise we should see all three notifs.  The check is only
290             // effective if the property jmx.remote.x.check.notification.emission
291             // is explicitly true and there is a security manager.
292             int expectedNotifs =
293                     (prop && sm && !policyPositive) ? 2 : 3;
294 
295             // Wait for notifications to be emitted
296             //
297             long deadline = System.currentTimeMillis() + 2000;
298             while (li.notifs.size() < expectedNotifs &&
299                     System.currentTimeMillis() < deadline)
300                 Thread.sleep(1);
301 
302             // Remove notification listener
303             //
304             mbsc.removeNotificationListener(nb1, li);
305             mbsc.removeNotificationListener(nb2, li);
306 
307             int result = 0;
308             List<ObjectName> sources = new ArrayList<ObjectName>();
309             sources.add(nb1);
310             sources.add(nb2);
311             sources.add(nb3);
312 
313             result = checkNotifs(expectedNotifs, li.notifs, sources);
314             if (result > 0) {
315                 echo("...SecurityManager=" + sm + "; policy=" + policyPositive);
316                 return result;
317             }
318         } finally {
319             // Close the connection
320             //
321             if (client != null)
322                 client.close();
323 
324             // Stop the connector server
325             //
326             if (server != null)
327                 server.stop();
328 
329             // Release the MBeanServer
330             //
331             if (mbs != null)
332                 MBeanServerFactory.releaseMBeanServer(mbs);
333         }
334 
335         return 0;
336     }
337 
echo(String message)338     private static void echo(String message) {
339         System.out.println(message);
340     }
341 
main(String[] args)342     public static void main(String[] args) throws Exception {
343 
344         echo("\n--- Check the emission of notifications " +
345              "when a Security Manager is installed");
346 
347         NotificationEmissionTest net = new NotificationEmissionTest();
348 
349         int error = 0;
350 
351         error += net.runTest(Integer.parseInt(args[0]));
352 
353         if (error > 0) {
354             final String msg = "\nTest FAILED! Got " + error + " error(s)";
355             echo(msg);
356             throw new IllegalArgumentException(msg);
357         } else {
358             echo("\nTest PASSED!");
359         }
360     }
361 }
362