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