1/*
2 * Copyright (c) 2016, 2017, 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
24import java.io.IOException;
25import java.io.Serializable;
26
27import java.rmi.AlreadyBoundException;
28import java.rmi.MarshalledObject;
29import java.rmi.NotBoundException;
30import java.rmi.Remote;
31import java.rmi.RemoteException;
32import java.rmi.registry.LocateRegistry;
33import java.rmi.registry.Registry;
34import java.security.Security;
35import java.util.Objects;
36
37import org.testng.Assert;
38import org.testng.TestNG;
39import org.testng.annotations.BeforeSuite;
40import org.testng.annotations.DataProvider;
41import org.testng.annotations.Test;
42
43/*
44 * @test
45 * @library /java/rmi/testlibrary
46 * @modules java.rmi/sun.rmi.registry
47 *          java.rmi/sun.rmi.server
48 *          java.rmi/sun.rmi.transport
49 *          java.rmi/sun.rmi.transport.tcp
50 * @build TestLibrary
51 * @summary Test filters for the RMI Registry
52 * @run testng/othervm RegistryFilterTest
53 * @run testng/othervm
54 *        -Dsun.rmi.registry.registryFilter=!java.lang.Long;!RegistryFilterTest$RejectableClass;maxdepth=19
55 *        -Dtest.maxdepth=19
56 *        RegistryFilterTest
57 * @run testng/othervm/policy=security.policy
58 *        -Djava.security.properties=${test.src}/java.security-extra1
59 *        RegistryFilterTest
60 */
61public class RegistryFilterTest {
62    private static Registry impl;
63    private static int port;
64    private static Registry registry;
65
66    static final int REGISTRY_MAX_DEPTH = 20;
67
68    static final int REGISTRY_MAX_ARRAY = 10000;
69
70    static final String registryFilter =
71            System.getProperty("sun.rmi.registry.registryFilter",
72                    Security.getProperty("sun.rmi.registry.registryFilter"));
73
74    @DataProvider(name = "bindAllowed")
75    static Object[][] bindAllowedObjects() {
76        Object[][] objects = {
77        };
78        return objects;
79    }
80
81    /**
82     * Data RMI Regiry bind test.
83     * - name
84     * - Object
85     * - true/false if object is blacklisted by a filter (implicit or explicit)
86     * @return array of test data
87     */
88    @DataProvider(name = "bindData")
89    static Object[][] bindObjects() {
90        Object[][] data = {
91                { "byte[max]", new XX(new byte[REGISTRY_MAX_ARRAY]), false },
92                { "String", new XX("now is the time"), false},
93                { "String[]", new XX(new String[3]), false},
94                { "Long[4]", new XX(new Long[4]), registryFilter != null },
95                { "rej-byte[toobig]", new XX(new byte[REGISTRY_MAX_ARRAY + 1]), true },
96                { "rej-MarshalledObject", createMarshalledObject(), true },
97                { "rej-RejectableClass", new RejectableClass(), registryFilter != null},
98        };
99        return data;
100    }
101
102    static XX createMarshalledObject() {
103        try {
104            return new XX(new MarshalledObject<>(null));
105        } catch (IOException ioe) {
106            return new XX(ioe);
107        }
108    }
109
110    @BeforeSuite
111    static void setupRegistry() {
112        try {
113            impl = TestLibrary.createRegistryOnEphemeralPort();
114            port = TestLibrary.getRegistryPort(impl);
115            registry = LocateRegistry.getRegistry("localhost", port);
116        } catch (RemoteException ex) {
117            Assert.fail("initialization of registry", ex);
118        }
119
120        System.out.printf("RMI Registry filter: %s%n", registryFilter);
121    }
122
123
124    /*
125     * Test registry rejects an object with the max array size + 1.
126     */
127    @Test(dataProvider="bindData")
128    public void simpleBind(String name, Remote obj, boolean blacklisted) throws RemoteException, AlreadyBoundException, NotBoundException {
129        try {
130            registry.bind(name, obj);
131            Assert.assertFalse(blacklisted, "Registry filter did not reject (but should have) ");
132            registry.unbind(name);
133        } catch (Exception rex) {
134            Assert.assertTrue(blacklisted, "Registry filter should not have rejected");
135        }
136    }
137
138    /*
139     * Test registry rejects an object with a well known class
140     * if blacklisted in the security properties.
141     */
142    @Test
143    public void simpleRejectableClass() throws RemoteException, AlreadyBoundException, NotBoundException {
144        RejectableClass r1 = null;
145        try {
146            String name = "reject1";
147            r1 = new RejectableClass();
148            registry.bind(name, r1);
149            registry.unbind(name);
150            Assert.assertNull(registryFilter, "Registry filter should have rejected");
151        } catch (Exception rex) {
152            Assert.assertNotNull(registryFilter, "Registry filter should not have rejected");
153        }
154    }
155
156    /*
157     * Test registry does not reject an object with depth at the built-in limit.
158     */
159    @Test
160    public void simpleDepthBuiltinNonRejectable() throws RemoteException, AlreadyBoundException, NotBoundException {
161        int depthOverride = Integer.getInteger("test.maxdepth", REGISTRY_MAX_DEPTH);
162        depthOverride = Math.min(depthOverride, REGISTRY_MAX_DEPTH);
163        System.out.printf("overrideDepth: %d, filter: %s%n", depthOverride, registryFilter);
164        try {
165            String name = "reject2";
166            DepthRejectableClass r1 = DepthRejectableClass.create(depthOverride);
167            registry.bind(name, r1);
168            registry.unbind(name);
169        } catch (Exception rex) {
170            Assert.fail("Registry filter should not have rejected depth: "
171                            + depthOverride);
172        }
173    }
174
175    /*
176     * Test registry rejects an object with depth at the limit + 1.
177     */
178    @Test
179    public void simpleDepthRejectable() throws RemoteException, AlreadyBoundException, NotBoundException {
180        int depthOverride = Integer.getInteger("test.maxdepth", REGISTRY_MAX_DEPTH);
181        depthOverride = Math.min(depthOverride, REGISTRY_MAX_DEPTH);
182        System.out.printf("overrideDepth: %d, filter: %s%n", depthOverride, registryFilter);
183        try {
184            String name = "reject3";
185            DepthRejectableClass r1 = DepthRejectableClass.create(depthOverride + 1);
186            registry.bind(name, r1);
187            Assert.fail("Registry filter should have rejected depth: " + depthOverride + 1);
188        } catch (Exception rex) {
189            // Rejection expected
190        }
191    }
192
193    /**
194     * A simple Serializable Remote object that is passed by value.
195     * It and its contents are checked by the Registry serial filter.
196     */
197    static class XX implements Serializable, Remote {
198        private static final long serialVersionUID = 362498820763181265L;
199
200        final Object obj;
201
202        XX(Object obj) {
203            this.obj = obj;
204        }
205
206        public String toString() {
207            return super.toString() + "//" + Objects.toString(obj);
208        }
209    }
210
211    /**
212     * A simple Serializable Remote object that is passed by value.
213     * It and its contents are checked by the Registry serial filter.
214     */
215    static class RejectableClass implements Serializable, Remote {
216        private static final long serialVersionUID = 362498820763181264L;
217
218        RejectableClass() {}
219    }
220
221    /**
222     * A simple Serializable Remote object that is passed by value.
223     * It and its contents are checked by the Registry serial filter.
224     */
225    static class DepthRejectableClass implements Serializable, Remote {
226        private static final long serialVersionUID = 362498820763181264L;
227        private final DepthRejectableClass next;
228
229        private DepthRejectableClass(DepthRejectableClass next) {
230            this.next = next;
231        }
232
233        static DepthRejectableClass create(int depth) {
234            DepthRejectableClass next = new DepthRejectableClass(null);
235            for (int i = 1; i < depth; i++) {
236                next = new DepthRejectableClass(next);
237            }
238            return next;
239        }
240    }
241
242}
243