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 
  24 import java.io.IOException;
  25 import java.io.Serializable;
  26 
  27 import java.rmi.AlreadyBoundException;
  28 import java.rmi.MarshalledObject;
  29 import java.rmi.NotBoundException;
  30 import java.rmi.Remote;
  31 import java.rmi.RemoteException;
  32 import java.rmi.registry.LocateRegistry;
  33 import java.rmi.registry.Registry;
  34 import java.security.Security;
  35 import java.util.Objects;
  36 
  37 import org.testng.Assert;
  38 import org.testng.TestNG;
  39 import org.testng.annotations.BeforeSuite;
  40 import org.testng.annotations.DataProvider;
  41 import 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  */
  61 public 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 }