8174770: Check registry registration location
authorrriggs
Wed, 03 May 2017 12:56:02 -0400
changeset 45989 e4f526fd8e09
parent 45988 6b7aa871e786
child 45990 ba8bfbb9d633
8174770: Check registry registration location Reviewed-by: dfuchs, smarks, chegar
jdk/make/rmic/Rmic-java.rmi.gmk
jdk/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl.java
jdk/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl_Skel.java
jdk/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl_Stub.java
jdk/src/java.rmi/share/classes/sun/rmi/server/Activation.java
jdk/src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef.java
jdk/src/jdk.management.agent/share/classes/sun/management/jmxremote/SingleEntryRegistry.java
jdk/test/java/rmi/activation/nonLocalActivation/NonLocalActivationTest.java
jdk/test/java/rmi/registry/nonLocalRegistry/NonLocalRegistryTest.java
jdk/test/javax/management/remote/nonLocalAccess/NonLocalJMXRemoteTest.java
--- a/jdk/make/rmic/Rmic-java.rmi.gmk	Wed Apr 26 10:34:32 2017 -0700
+++ b/jdk/make/rmic/Rmic-java.rmi.gmk	Wed May 03 12:56:02 2017 -0400
@@ -40,15 +40,9 @@
     RUN_V12 := true))
 GENCLASSES += $(RMI_12)
 
-$(eval $(call SetupRMICompilation,RMI_11, \
-    CLASSES := sun.rmi.registry.RegistryImpl, \
-    CLASSES_DIR := $(CLASSES_DIR)/java.rmi, \
-    STUB_CLASSES_DIR := $(STUB_CLASSES_DIR)/java.rmi, \
-    RUN_V11 := true))
-GENCLASSES += $(RMI_11)
 
 ################################################################################
 
-all: $(RMI_11) $(RMI_12)
+all: $(RMI_12)
 
 .PHONY: all
--- a/jdk/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl.java	Wed Apr 26 10:34:32 2017 -0700
+++ b/jdk/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl.java	Wed May 03 12:56:02 2017 -0400
@@ -75,6 +75,10 @@
  * registry.
  *
  * The LocateRegistry class is used to obtain registry for different hosts.
+ * <p>
+ * The default RegistryImpl exported restricts access to clients on the local host
+ * for the methods {@link #bind}, {@link #rebind}, {@link #unbind} by checking
+ * the client host in the skeleton.
  *
  * @see java.rmi.registry.LocateRegistry
  */
@@ -144,13 +148,27 @@
                         RMIServerSocketFactory ssf)
         throws RemoteException
     {
+        this(port, csf, ssf, RegistryImpl::registryFilter);
+    }
+
+
+    /**
+     * Construct a new RegistryImpl on the specified port with the
+     * given custom socket factory pair and ObjectInputFilter.
+     */
+    public RegistryImpl(int port,
+                        RMIClientSocketFactory csf,
+                        RMIServerSocketFactory ssf,
+                        ObjectInputFilter serialFilter)
+        throws RemoteException
+    {
         if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
             // grant permission for default port only.
             try {
                 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
                     public Void run() throws RemoteException {
                         LiveRef lref = new LiveRef(id, port, csf, ssf);
-                        setup(new UnicastServerRef2(lref, RegistryImpl::registryFilter));
+                        setup(new UnicastServerRef2(lref, serialFilter));
                         return null;
                     }
                 }, null, new SocketPermission("localhost:"+port, "listen,accept"));
@@ -226,7 +244,8 @@
     public void bind(String name, Remote obj)
         throws RemoteException, AlreadyBoundException, AccessException
     {
-        checkAccess("Registry.bind");
+        // The access check preventing remote access is done in the skeleton
+        // and is not applicable to local access.
         synchronized (bindings) {
             Remote curr = bindings.get(name);
             if (curr != null)
@@ -243,7 +262,8 @@
     public void unbind(String name)
         throws RemoteException, NotBoundException, AccessException
     {
-        checkAccess("Registry.unbind");
+        // The access check preventing remote access is done in the skeleton
+        // and is not applicable to local access.
         synchronized (bindings) {
             Remote obj = bindings.get(name);
             if (obj == null)
@@ -259,7 +279,8 @@
     public void rebind(String name, Remote obj)
         throws RemoteException, AccessException
     {
-        checkAccess("Registry.rebind");
+        // The access check preventing remote access is done in the skeleton
+        // and is not applicable to local access.
         bindings.put(name, obj);
     }
 
@@ -312,7 +333,7 @@
 
                 if (clientHost.isAnyLocalAddress()) {
                     throw new AccessException(
-                        "Registry." + op + " disallowed; origin unknown");
+                        op + " disallowed; origin unknown");
                 }
 
                 try {
@@ -335,7 +356,7 @@
                     // must have been an IOException
 
                     throw new AccessException(
-                        "Registry." + op + " disallowed; origin " +
+                        op + " disallowed; origin " +
                         clientHost + " is non-local host");
                 }
             }
@@ -344,8 +365,7 @@
              * Local call from this VM: allow access.
              */
         } catch (java.net.UnknownHostException ex) {
-            throw new AccessException("Registry." + op +
-                                      " disallowed; origin is unknown host");
+            throw new AccessException(op + " disallowed; origin is unknown host");
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl_Skel.java	Wed May 03 12:56:02 2017 -0400
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package sun.rmi.registry;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.rmi.AccessException;
+import java.rmi.server.RemoteCall;
+
+import sun.rmi.transport.Connection;
+import sun.rmi.transport.StreamRemoteCall;
+import sun.rmi.transport.tcp.TCPConnection;
+
+/**
+ * Skeleton to dispatch RegistryImpl methods.
+ * Originally generated by RMIC but frozen to match the stubs.
+ */
+@SuppressWarnings({"deprecation", "serial"})
+public final class RegistryImpl_Skel
+        implements java.rmi.server.Skeleton {
+    private static final java.rmi.server.Operation[] operations = {
+            new java.rmi.server.Operation("void bind(java.lang.String, java.rmi.Remote)"),
+            new java.rmi.server.Operation("java.lang.String list()[]"),
+            new java.rmi.server.Operation("java.rmi.Remote lookup(java.lang.String)"),
+            new java.rmi.server.Operation("void rebind(java.lang.String, java.rmi.Remote)"),
+            new java.rmi.server.Operation("void unbind(java.lang.String)")
+    };
+
+    private static final long interfaceHash = 4905912898345647071L;
+
+    public java.rmi.server.Operation[] getOperations() {
+        return operations.clone();
+    }
+
+    public void dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall call, int opnum, long hash)
+            throws java.lang.Exception {
+        if (hash != interfaceHash)
+            throw new java.rmi.server.SkeletonMismatchException("interface hash mismatch");
+
+        sun.rmi.registry.RegistryImpl server = (sun.rmi.registry.RegistryImpl) obj;
+        switch (opnum) {
+            case 0: // bind(String, Remote)
+            {
+                // Check access before reading the arguments
+                RegistryImpl.checkAccess("Registry.bind");
+
+                java.lang.String $param_String_1;
+                java.rmi.Remote $param_Remote_2;
+                try {
+                    java.io.ObjectInput in = call.getInputStream();
+                    $param_String_1 = (java.lang.String) in.readObject();
+                    $param_Remote_2 = (java.rmi.Remote) in.readObject();
+                } catch (java.io.IOException | java.lang.ClassNotFoundException e) {
+                    throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
+                } finally {
+                    call.releaseInputStream();
+                }
+                server.bind($param_String_1, $param_Remote_2);
+                try {
+                    call.getResultStream(true);
+                } catch (java.io.IOException e) {
+                    throw new java.rmi.MarshalException("error marshalling return", e);
+                }
+                break;
+            }
+
+            case 1: // list()
+            {
+                call.releaseInputStream();
+                java.lang.String[] $result = server.list();
+                try {
+                    java.io.ObjectOutput out = call.getResultStream(true);
+                    out.writeObject($result);
+                } catch (java.io.IOException e) {
+                    throw new java.rmi.MarshalException("error marshalling return", e);
+                }
+                break;
+            }
+
+            case 2: // lookup(String)
+            {
+                java.lang.String $param_String_1;
+                try {
+                    java.io.ObjectInput in = call.getInputStream();
+                    $param_String_1 = (java.lang.String) in.readObject();
+                } catch (java.io.IOException | java.lang.ClassNotFoundException e) {
+                    throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
+                } finally {
+                    call.releaseInputStream();
+                }
+                java.rmi.Remote $result = server.lookup($param_String_1);
+                try {
+                    java.io.ObjectOutput out = call.getResultStream(true);
+                    out.writeObject($result);
+                } catch (java.io.IOException e) {
+                    throw new java.rmi.MarshalException("error marshalling return", e);
+                }
+                break;
+            }
+
+            case 3: // rebind(String, Remote)
+            {
+                // Check access before reading the arguments
+                RegistryImpl.checkAccess("Registry.rebind");
+
+                java.lang.String $param_String_1;
+                java.rmi.Remote $param_Remote_2;
+                try {
+                    java.io.ObjectInput in = call.getInputStream();
+                    $param_String_1 = (java.lang.String) in.readObject();
+                    $param_Remote_2 = (java.rmi.Remote) in.readObject();
+                } catch (java.io.IOException | java.lang.ClassNotFoundException e) {
+                    throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
+                } finally {
+                    call.releaseInputStream();
+                }
+                server.rebind($param_String_1, $param_Remote_2);
+                try {
+                    call.getResultStream(true);
+                } catch (java.io.IOException e) {
+                    throw new java.rmi.MarshalException("error marshalling return", e);
+                }
+                break;
+            }
+
+            case 4: // unbind(String)
+            {
+                // Check access before reading the arguments
+                RegistryImpl.checkAccess("Registry.unbind");
+
+                java.lang.String $param_String_1;
+                try {
+                    java.io.ObjectInput in = call.getInputStream();
+                    $param_String_1 = (java.lang.String) in.readObject();
+                } catch (java.io.IOException | java.lang.ClassNotFoundException e) {
+                    throw new java.rmi.UnmarshalException("error unmarshalling arguments", e);
+                } finally {
+                    call.releaseInputStream();
+                }
+                server.unbind($param_String_1);
+                try {
+                    call.getResultStream(true);
+                } catch (java.io.IOException e) {
+                    throw new java.rmi.MarshalException("error marshalling return", e);
+                }
+                break;
+            }
+
+            default:
+                throw new java.rmi.UnmarshalException("invalid method number");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl_Stub.java	Wed May 03 12:56:02 2017 -0400
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.rmi.registry;
+/**
+ * Stubs to invoke RegistryImpl remote methods.
+ * Originally generated from RMIC but frozen to match RegistryImpl_Skel.
+ */
+@SuppressWarnings({"deprecation", "serial"})
+public final class RegistryImpl_Stub
+        extends java.rmi.server.RemoteStub
+        implements java.rmi.registry.Registry, java.rmi.Remote {
+    private static final java.rmi.server.Operation[] operations = {
+            new java.rmi.server.Operation("void bind(java.lang.String, java.rmi.Remote)"),
+            new java.rmi.server.Operation("java.lang.String list()[]"),
+            new java.rmi.server.Operation("java.rmi.Remote lookup(java.lang.String)"),
+            new java.rmi.server.Operation("void rebind(java.lang.String, java.rmi.Remote)"),
+            new java.rmi.server.Operation("void unbind(java.lang.String)")
+    };
+
+    private static final long interfaceHash = 4905912898345647071L;
+
+    // constructors
+    public RegistryImpl_Stub() {
+        super();
+    }
+
+    public RegistryImpl_Stub(java.rmi.server.RemoteRef ref) {
+        super(ref);
+    }
+
+    // methods from remote interfaces
+
+    // implementation of bind(String, Remote)
+    public void bind(java.lang.String $param_String_1, java.rmi.Remote $param_Remote_2)
+            throws java.rmi.AccessException, java.rmi.AlreadyBoundException, java.rmi.RemoteException {
+        try {
+            java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 0, interfaceHash);
+            try {
+                java.io.ObjectOutput out = call.getOutputStream();
+                out.writeObject($param_String_1);
+                out.writeObject($param_Remote_2);
+            } catch (java.io.IOException e) {
+                throw new java.rmi.MarshalException("error marshalling arguments", e);
+            }
+            ref.invoke(call);
+            ref.done(call);
+        } catch (java.lang.RuntimeException e) {
+            throw e;
+        } catch (java.rmi.RemoteException e) {
+            throw e;
+        } catch (java.rmi.AlreadyBoundException e) {
+            throw e;
+        } catch (java.lang.Exception e) {
+            throw new java.rmi.UnexpectedException("undeclared checked exception", e);
+        }
+    }
+
+    // implementation of list()
+    public java.lang.String[] list()
+            throws java.rmi.AccessException, java.rmi.RemoteException {
+        try {
+            java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 1, interfaceHash);
+            ref.invoke(call);
+            java.lang.String[] $result;
+            try {
+                java.io.ObjectInput in = call.getInputStream();
+                $result = (java.lang.String[]) in.readObject();
+            } catch (java.io.IOException e) {
+                throw new java.rmi.UnmarshalException("error unmarshalling return", e);
+            } catch (java.lang.ClassNotFoundException e) {
+                throw new java.rmi.UnmarshalException("error unmarshalling return", e);
+            } finally {
+                ref.done(call);
+            }
+            return $result;
+        } catch (java.lang.RuntimeException e) {
+            throw e;
+        } catch (java.rmi.RemoteException e) {
+            throw e;
+        } catch (java.lang.Exception e) {
+            throw new java.rmi.UnexpectedException("undeclared checked exception", e);
+        }
+    }
+
+    // implementation of lookup(String)
+    public java.rmi.Remote lookup(java.lang.String $param_String_1)
+            throws java.rmi.AccessException, java.rmi.NotBoundException, java.rmi.RemoteException {
+        try {
+            java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 2, interfaceHash);
+            try {
+                java.io.ObjectOutput out = call.getOutputStream();
+                out.writeObject($param_String_1);
+            } catch (java.io.IOException e) {
+                throw new java.rmi.MarshalException("error marshalling arguments", e);
+            }
+            ref.invoke(call);
+            java.rmi.Remote $result;
+            try {
+                java.io.ObjectInput in = call.getInputStream();
+                $result = (java.rmi.Remote) in.readObject();
+            } catch (java.io.IOException e) {
+                throw new java.rmi.UnmarshalException("error unmarshalling return", e);
+            } catch (java.lang.ClassNotFoundException e) {
+                throw new java.rmi.UnmarshalException("error unmarshalling return", e);
+            } finally {
+                ref.done(call);
+            }
+            return $result;
+        } catch (java.lang.RuntimeException e) {
+            throw e;
+        } catch (java.rmi.RemoteException e) {
+            throw e;
+        } catch (java.rmi.NotBoundException e) {
+            throw e;
+        } catch (java.lang.Exception e) {
+            throw new java.rmi.UnexpectedException("undeclared checked exception", e);
+        }
+    }
+
+    // implementation of rebind(String, Remote)
+    public void rebind(java.lang.String $param_String_1, java.rmi.Remote $param_Remote_2)
+            throws java.rmi.AccessException, java.rmi.RemoteException {
+        try {
+            java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 3, interfaceHash);
+            try {
+                java.io.ObjectOutput out = call.getOutputStream();
+                out.writeObject($param_String_1);
+                out.writeObject($param_Remote_2);
+            } catch (java.io.IOException e) {
+                throw new java.rmi.MarshalException("error marshalling arguments", e);
+            }
+            ref.invoke(call);
+            ref.done(call);
+        } catch (java.lang.RuntimeException e) {
+            throw e;
+        } catch (java.rmi.RemoteException e) {
+            throw e;
+        } catch (java.lang.Exception e) {
+            throw new java.rmi.UnexpectedException("undeclared checked exception", e);
+        }
+    }
+
+    // implementation of unbind(String)
+    public void unbind(java.lang.String $param_String_1)
+            throws java.rmi.AccessException, java.rmi.NotBoundException, java.rmi.RemoteException {
+        try {
+            java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 4, interfaceHash);
+            try {
+                java.io.ObjectOutput out = call.getOutputStream();
+                out.writeObject($param_String_1);
+            } catch (java.io.IOException e) {
+                throw new java.rmi.MarshalException("error marshalling arguments", e);
+            }
+            ref.invoke(call);
+            ref.done(call);
+        } catch (java.lang.RuntimeException e) {
+            throw e;
+        } catch (java.rmi.RemoteException e) {
+            throw e;
+        } catch (java.rmi.NotBoundException e) {
+            throw e;
+        } catch (java.lang.Exception e) {
+            throw new java.rmi.UnexpectedException("undeclared checked exception", e);
+        }
+    }
+}
--- a/jdk/src/java.rmi/share/classes/sun/rmi/server/Activation.java	Wed Apr 26 10:34:32 2017 -0700
+++ b/jdk/src/java.rmi/share/classes/sun/rmi/server/Activation.java	Wed May 03 12:56:02 2017 -0400
@@ -30,6 +30,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.ObjectInput;
 import java.io.ObjectInputStream;
 import java.io.OutputStream;
 import java.io.PrintStream;
@@ -105,7 +106,6 @@
 import sun.rmi.log.ReliableLog;
 import sun.rmi.registry.RegistryImpl;
 import sun.rmi.runtime.NewThreadAction;
-import sun.rmi.server.UnicastServerRef;
 import sun.rmi.transport.LiveRef;
 import sun.security.provider.PolicyFile;
 import com.sun.rmi.rmid.ExecPermission;
@@ -375,6 +375,7 @@
                 throw new AccessException(
                     "binding ActivationSystem is disallowed");
             } else {
+                RegistryImpl.checkAccess("ActivationSystem.bind");
                 super.bind(name, obj);
             }
         }
@@ -386,6 +387,7 @@
                 throw new AccessException(
                     "unbinding ActivationSystem is disallowed");
             } else {
+                RegistryImpl.checkAccess("ActivationSystem.unbind");
                 super.unbind(name);
             }
         }
@@ -398,6 +400,7 @@
                 throw new AccessException(
                     "binding ActivationSystem is disallowed");
             } else {
+                RegistryImpl.checkAccess("ActivationSystem.rebind");
                 super.rebind(name, obj);
             }
         }
@@ -488,6 +491,33 @@
     }
 
 
+    /**
+     * SameHostOnlyServerRef checks that access is from a local client
+     * before the parameters are deserialized.  The unmarshalCustomCallData
+     * hook is used to check the network address of the caller
+     * with RegistryImpl.checkAccess().
+     * The kind of access is retained for an exception if one is thrown.
+     */
+    static class SameHostOnlyServerRef extends UnicastServerRef {
+        private static final long serialVersionUID = 1234L;
+        private String accessKind;      // an exception message
+
+        /**
+         * Construct a new SameHostOnlyServerRef from a LiveRef.
+         * @param lref a LiveRef
+         */
+        SameHostOnlyServerRef(LiveRef lref, String accessKind) {
+            super(lref);
+            this.accessKind = accessKind;
+        }
+
+        @Override
+        protected void unmarshalCustomCallData(ObjectInput in) throws IOException, ClassNotFoundException {
+            RegistryImpl.checkAccess(accessKind);
+            super.unmarshalCustomCallData(in);
+        }
+    }
+
     class ActivationSystemImpl
         extends RemoteServer
         implements ActivationSystem
@@ -505,7 +535,8 @@
              * 'this' can be exported.
              */
             LiveRef lref = new LiveRef(new ObjID(4), port, null, ssf);
-            UnicastServerRef uref = new UnicastServerRef(lref);
+            UnicastServerRef uref = new SameHostOnlyServerRef(lref,
+                    "ActivationSystem.nonLocalAccess");
             ref = uref;
             uref.exportObject(this, null);
         }
@@ -514,8 +545,8 @@
             throws ActivationException, UnknownGroupException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess("ActivationSystem.registerObject");
-
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
             ActivationGroupID groupID = desc.getGroupID();
             ActivationID id = new ActivationID(activatorStub);
             getGroupEntry(groupID).registerObject(id, desc, true);
@@ -526,7 +557,8 @@
             throws ActivationException, UnknownObjectException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess("ActivationSystem.unregisterObject");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
             getGroupEntry(id).unregisterObject(id, true);
         }
 
@@ -534,7 +566,8 @@
             throws ActivationException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess("ActivationSystem.registerGroup");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
             checkArgs(desc, null);
 
             ActivationGroupID id = new ActivationGroupID(systemStub);
@@ -551,7 +584,8 @@
             throws ActivationException, UnknownGroupException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess("ActivationSystem.activeGroup");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
 
             getGroupEntry(id).activeGroup(group, incarnation);
             return monitor;
@@ -561,7 +595,8 @@
             throws ActivationException, UnknownGroupException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess("ActivationSystem.unregisterGroup");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
 
             // remove entry before unregister so state is updated before
             // logged
@@ -573,7 +608,8 @@
             throws ActivationException, UnknownObjectException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess("ActivationSystem.setActivationDesc");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
 
             if (!getGroupID(id).equals(desc.getGroupID())) {
                 throw new ActivationException(
@@ -587,8 +623,8 @@
             throws ActivationException, UnknownGroupException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess(
-                "ActivationSystem.setActivationGroupDesc");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
 
             checkArgs(desc, null);
             return getGroupEntry(id).setActivationGroupDesc(id, desc, true);
@@ -598,7 +634,8 @@
             throws ActivationException, UnknownObjectException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess("ActivationSystem.getActivationDesc");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
 
             return getGroupEntry(id).getActivationDesc(id);
         }
@@ -607,8 +644,8 @@
             throws ActivationException, UnknownGroupException, RemoteException
         {
             checkShutdown();
-            RegistryImpl.checkAccess
-                ("ActivationSystem.getActivationGroupDesc");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
 
             return getGroupEntry(id).desc;
         }
@@ -618,7 +655,8 @@
          * the activation daemon and exits the activation daemon.
          */
         public void shutdown() throws AccessException {
-            RegistryImpl.checkAccess("ActivationSystem.shutdown");
+            // RegistryImpl.checkAccess() is done in the SameHostOnlyServerRef
+            // during unmarshallCustomData and is not applicable to local access.
 
             Object lock = startupLock;
             if (lock != null) {
--- a/jdk/src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef.java	Wed Apr 26 10:34:32 2017 -0700
+++ b/jdk/src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef.java	Wed May 03 12:56:02 2017 -0400
@@ -33,6 +33,7 @@
 import java.io.ObjectStreamClass;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.rmi.AccessException;
 import java.rmi.MarshalException;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
@@ -288,20 +289,25 @@
             try {
                 in = call.getInputStream();
                 num = in.readInt();
-                if (num >= 0) {
-                    if (skel != null) {
-                        oldDispatch(obj, call, num);
-                        return;
-                    } else {
-                        throw new UnmarshalException(
-                            "skeleton class not found but required " +
-                            "for client version");
-                    }
+            } catch (Exception readEx) {
+                throw new UnmarshalException("error unmarshalling call header",
+                                             readEx);
+            }
+            if (num >= 0) {
+                if (skel != null) {
+                    oldDispatch(obj, call, num);
+                    return;
+                } else {
+                    throw new UnmarshalException(
+                        "skeleton class not found but required " +
+                        "for client version");
                 }
+            }
+            try {
                 op = in.readLong();
             } catch (Exception readEx) {
                 throw new UnmarshalException("error unmarshalling call header",
-                                             readEx);
+                        readEx);
             }
 
             /*
@@ -329,6 +335,11 @@
             try {
                 unmarshalCustomCallData(in);
                 params = unmarshalParameters(obj, method, marshalStream);
+            } catch (AccessException aex) {
+                // For compatibility, AccessException is not wrapped in UnmarshalException
+                // disable saving any refs in the inputStream for GC
+                ((StreamRemoteCall) call).discardPendingRefs();
+                throw aex;
             } catch (java.io.IOException | ClassNotFoundException e) {
                 // disable saving any refs in the inputStream for GC
                 ((StreamRemoteCall) call).discardPendingRefs();
@@ -365,6 +376,7 @@
                  */
             }
         } catch (Throwable e) {
+            Throwable origEx = e;
             logCallException(e);
 
             ObjectOutput out = call.getResultStream(false);
@@ -380,6 +392,12 @@
                 clearStackTraces(e);
             }
             out.writeObject(e);
+
+            // AccessExceptions should cause Transport.serviceCall
+            // to flag the connection as unusable.
+            if (origEx instanceof AccessException) {
+                throw new IOException("Connection is not reusable", origEx);
+            }
         } finally {
             call.releaseInputStream(); // in case skeleton doesn't
             call.releaseOutputStream();
@@ -408,62 +426,41 @@
      * Handle server-side dispatch using the RMI 1.1 stub/skeleton
      * protocol, given a non-negative operation number that has
      * already been read from the call stream.
+     * Exceptions are handled by the caller to be sent to the remote client.
      *
      * @param obj the target remote object for the call
      * @param call the "remote call" from which operation and
      * method arguments can be obtained.
      * @param op the operation number
-     * @exception IOException if unable to marshal return result or
+     * @throws Exception if unable to marshal return result or
      * release input or output streams
      */
-    public void oldDispatch(Remote obj, RemoteCall call, int op)
-        throws IOException
+    private void oldDispatch(Remote obj, RemoteCall call, int op)
+        throws Exception
     {
         long hash;              // hash for matching stub with skeleton
 
+        // read remote call header
+        ObjectInput in;
+        in = call.getInputStream();
         try {
-            // read remote call header
-            ObjectInput in;
-            try {
-                in = call.getInputStream();
-                try {
-                    Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel");
-                    if (clazz.isAssignableFrom(skel.getClass())) {
-                        ((MarshalInputStream)in).useCodebaseOnly();
-                    }
-                } catch (ClassNotFoundException ignore) { }
-                hash = in.readLong();
-            } catch (Exception readEx) {
-                throw new UnmarshalException("error unmarshalling call header",
-                                             readEx);
+            Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel");
+            if (clazz.isAssignableFrom(skel.getClass())) {
+                ((MarshalInputStream)in).useCodebaseOnly();
             }
+        } catch (ClassNotFoundException ignore) { }
 
-            // if calls are being logged, write out object id and operation
-            logCall(obj, skel.getOperations()[op]);
-            unmarshalCustomCallData(in);
-            // dispatch to skeleton for remote object
-            skel.dispatch(obj, call, op, hash);
-
-        } catch (Throwable e) {
-            logCallException(e);
+        try {
+            hash = in.readLong();
+        } catch (Exception ioe) {
+            throw new UnmarshalException("error unmarshalling call header", ioe);
+        }
 
-            ObjectOutput out = call.getResultStream(false);
-            if (e instanceof Error) {
-                e = new ServerError(
-                    "Error occurred in server thread", (Error) e);
-            } else if (e instanceof RemoteException) {
-                e = new ServerException(
-                    "RemoteException occurred in server thread",
-                    (Exception) e);
-            }
-            if (suppressStackTraces) {
-                clearStackTraces(e);
-            }
-            out.writeObject(e);
-        } finally {
-            call.releaseInputStream(); // in case skeleton doesn't
-            call.releaseOutputStream();
-        }
+        // if calls are being logged, write out object id and operation
+        logCall(obj, skel.getOperations()[op]);
+        unmarshalCustomCallData(in);
+        // dispatch to skeleton for remote object
+        skel.dispatch(obj, call, op, hash);
     }
 
     /**
--- a/jdk/src/jdk.management.agent/share/classes/sun/management/jmxremote/SingleEntryRegistry.java	Wed Apr 26 10:34:32 2017 -0700
+++ b/jdk/src/jdk.management.agent/share/classes/sun/management/jmxremote/SingleEntryRegistry.java	Wed May 03 12:56:02 2017 -0400
@@ -32,6 +32,7 @@
 
 package sun.management.jmxremote;
 
+import java.io.ObjectInputFilter;
 import java.rmi.AccessException;
 import java.rmi.NotBoundException;
 import java.rmi.Remote;
@@ -56,7 +57,7 @@
                         String name,
                         Remote object)
             throws RemoteException {
-        super(port, csf, ssf);
+        super(port, csf, ssf, SingleEntryRegistry::singleRegistryFilter);
         this.name = name;
         this.object = object;
     }
@@ -84,6 +85,23 @@
         throw new AccessException("Cannot modify this registry");
     }
 
+    /**
+     * ObjectInputFilter to check parameters to SingleEntryRegistry.
+     * Since it is a read-only Registry, no classes are accepted.
+     * String arguments are accepted without passing them to the serialFilter.
+     *
+     * @param info a reference to the serialization filter information
+     * @return Status.REJECTED if parameters are out of range
+     */
+    private static ObjectInputFilter.Status singleRegistryFilter(ObjectInputFilter.FilterInfo info) {
+        return (info.serialClass() != null ||
+                info.depth() > 2 ||
+                info.references() > 4 ||
+                info.arrayLength() >= 0)
+        ? ObjectInputFilter.Status.REJECTED
+        : ObjectInputFilter.Status.ALLOWED;
+    }
+
     private final String name;
     private final Remote object;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/rmi/activation/nonLocalActivation/NonLocalActivationTest.java	Wed May 03 12:56:02 2017 -0400
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.InetAddress;
+import java.rmi.AccessException;
+import java.rmi.activation.ActivationSystem;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.util.Set;
+
+/*
+ * @test
+ * @bug 8174770
+ * @summary Verify that ActivationSystem rejects non-local access.
+ *    The test is manual because the (non-local) host running rmid must be supplied as a property.
+ * @run main/manual/othervm -Dactivation.host=rmid-host NonLocalActivationTest
+ */
+
+/**
+ * Lookup the ActivationSystem on a different host and invoke its remote interface methods.
+ * They should all throw an exception, non-local access is prohibited.
+ *
+ * This test is a manual test and uses rmid running on a *different* host.
+ * The default port (1098) for the Activation System is ok and expected.
+ * Login or ssh to the different host and invoke {@code $JDK_HOME/bin/rmid}.
+ * It will not show any output.
+ *
+ * On the first host modify the @run command above to replace "rmid-host"
+ * with the hostname or IP address of the different host and run the test with jtreg.
+ */
+public class NonLocalActivationTest
+{
+    public static void main(String[] args) throws Exception {
+
+        String host = System.getProperty("activation.host");
+        if (host == null || host.isEmpty()) {
+            throw new RuntimeException("Specify host with system property: -Dactivation.host=<host>");
+        }
+
+        // Check if running the test on a local system; it only applies to remote
+        String myHostName = InetAddress.getLocalHost().getHostName();
+        Set<InetAddress> myAddrs = Set.of(InetAddress.getAllByName(myHostName));
+        Set<InetAddress> hostAddrs = Set.of(InetAddress.getAllByName(host));
+        if (hostAddrs.stream().anyMatch(i -> myAddrs.contains(i))
+                || hostAddrs.stream().anyMatch(h -> h.isLoopbackAddress())) {
+            throw new RuntimeException("Error: property 'activation.host' must not be the local host%n");
+        }
+
+        // Locate the registry operated by the ActivationSystem
+        // Test SystemRegistryImpl
+        Registry registry = LocateRegistry.getRegistry(host, ActivationSystem.SYSTEM_PORT);
+        try {
+            // Verify it is an ActivationSystem registry
+            registry.lookup("java.rmi.activation.ActivationSystem");
+        } catch (Exception nf) {
+            throw new RuntimeException("Not a ActivationSystem registry, does not contain java.rmi.activation.ActivationSystem", nf);
+        }
+
+        try {
+            registry.bind("foo", null);
+            throw new RuntimeException("Remote access should not succeed for method: bind");
+        } catch (Exception e) {
+            assertIsAccessException(e, "Registry.bind");
+        }
+
+        try {
+            registry.rebind("foo", null);
+            throw new RuntimeException("Remote access should not succeed for method: rebind");
+        } catch (Exception e) {
+            assertIsAccessException(e, "Registry.rebind");
+        }
+
+        try {
+            registry.unbind("foo");
+            throw new RuntimeException("Remote access should not succeed for method: unbind");
+        } catch (Exception e) {
+            assertIsAccessException(e, "Registry.unbind");
+        }
+
+
+        // Locate the ActivationSystem on the specified host and default port.
+        // Test each of the ActivationSystem methods
+        ActivationSystem as = (ActivationSystem) registry.lookup("java.rmi.activation.ActivationSystem");
+
+        // Argument is not material, access check is before arg processing
+
+        try {
+            as.registerGroup(null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+
+        try {
+            as.getActivationDesc(null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+
+        try {
+            as.getActivationGroupDesc(null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+
+        try {
+            as.registerObject(null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+
+        try {
+            as.unregisterGroup(null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+
+        try {
+            as.unregisterObject(null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+
+        try {
+            as.setActivationDesc(null, null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+
+        try {
+            as.setActivationGroupDesc(null, null);
+        } catch (Exception aex) {
+            assertIsAccessException(aex, "ActivationSystem.nonLocalAccess");
+        }
+    }
+
+    /**
+     * Check the exception chain for the expected AccessException and message.
+     * @param ex the exception from the remote invocation.
+     */
+    private static void assertIsAccessException(Exception ex, String msg1) {
+        Throwable t = ex;
+        System.out.println();
+        while (!(t instanceof AccessException) && t.getCause() != null) {
+            t = t.getCause();
+        }
+        if (t instanceof AccessException) {
+            String msg = t.getMessage();
+            int asIndex = msg.indexOf(msg1);
+            int disallowIndex = msg.indexOf("disallowed");
+            int nonLocalHostIndex = msg.indexOf("non-local host");
+            if (asIndex < 0 ||
+                    disallowIndex < 0 ||
+                    nonLocalHostIndex < 0 ) {
+                throw new RuntimeException("exception message is malformed", t);
+            }
+            System.out.printf("Found expected AccessException: %s%n", t);
+        } else {
+            throw new RuntimeException("AccessException did not occur", ex);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/rmi/registry/nonLocalRegistry/NonLocalRegistryTest.java	Wed May 03 12:56:02 2017 -0400
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.InetAddress;
+import java.rmi.AccessException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.util.Set;
+
+/* @test
+ * @bug 8174770
+ * @summary Verify that Registry rejects non-local access for bind, unbind, rebind.
+ *    The test is manual because the (non-local) host running rmiregistry must be supplied as a property.
+ * @run main/othervm/manual -Dregistry.host=rmi-registry-host NonLocalRegistryTest
+ */
+
+/**
+ * Verify that access checks for Registry.bind(), .rebind(), and .unbind()
+ * are prevented on remote access to the registry.
+ *
+ * This test is a manual test and uses a standard rmiregistry running
+ * on a *different* host.
+ * The test verifies that the access check is performed *before* the object to be
+ * bound or rebound is deserialized.
+ *
+ * Login or ssh to the different host and invoke {@code $JDK_HOME/bin/rmiregistry}.
+ * It will not show any output.
+ *
+ * On the first host modify the @run command above to replace "rmi-registry-host"
+ * with the hostname or IP address of the different host and run the test with jtreg.
+ */
+public class NonLocalRegistryTest {
+
+    public static void main(String[] args) throws Exception {
+
+        String host = System.getProperty("registry.host");
+        if (host == null || host.isEmpty()) {
+            throw new RuntimeException("Specify host with system property: -Dregistry.host=<host>");
+        }
+
+        // Check if running the test on a local system; it only applies to remote
+        String myHostName = InetAddress.getLocalHost().getHostName();
+        Set<InetAddress> myAddrs = Set.of(InetAddress.getAllByName(myHostName));
+        Set<InetAddress> hostAddrs = Set.of(InetAddress.getAllByName(host));
+        if (hostAddrs.stream().anyMatch(i -> myAddrs.contains(i))
+                || hostAddrs.stream().anyMatch(h -> h.isLoopbackAddress())) {
+            throw new RuntimeException("Error: property 'registry.host' must not be the local host%n");
+        }
+
+        Registry registry = LocateRegistry.getRegistry(host, Registry.REGISTRY_PORT);
+
+        try {
+            registry.bind("foo", null);
+            throw new RuntimeException("Remote access should not succeed for method: bind");
+        } catch (Exception e) {
+            assertIsAccessException(e);
+        }
+
+        try {
+            registry.rebind("foo", null);
+            throw new RuntimeException("Remote access should not succeed for method: rebind");
+        } catch (Exception e) {
+            assertIsAccessException(e);
+        }
+
+        try {
+            registry.unbind("foo");
+            throw new RuntimeException("Remote access should not succeed for method: unbind");
+        } catch (Exception e) {
+            assertIsAccessException(e);
+        }
+    }
+
+    /**
+     * Check the exception chain for the expected AccessException and message.
+     * @param ex the exception from the remote invocation.
+     */
+    private static void assertIsAccessException(Throwable ex) {
+        Throwable t = ex;
+        while (!(t instanceof AccessException) && t.getCause() != null) {
+            t = t.getCause();
+        }
+        if (t instanceof AccessException) {
+            String msg = t.getMessage();
+            int asIndex = msg.indexOf("Registry");
+            int rrIndex = msg.indexOf("Registry.Registry");     // Obsolete error text
+            int disallowIndex = msg.indexOf("disallowed");
+            int nonLocalHostIndex = msg.indexOf("non-local host");
+            if (asIndex < 0 ||
+                    rrIndex != -1 ||
+                    disallowIndex < 0 ||
+                    nonLocalHostIndex < 0 ) {
+                throw new RuntimeException("exception message is malformed", t);
+            }
+            System.out.printf("Found expected AccessException: %s%n%n", t);
+        } else {
+            throw new RuntimeException("AccessException did not occur when expected", ex);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/remote/nonLocalAccess/NonLocalJMXRemoteTest.java	Wed May 03 12:56:02 2017 -0400
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.InetAddress;
+import java.rmi.AccessException;
+import java.rmi.NotBoundException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.util.Set;
+
+/* @test
+ * @bug 8174770
+ * @summary Verify that JMX Registry rejects non-local access for bind, unbind, rebind.
+ *    The test is manual because the (non-local) host and port running JMX must be supplied as properties.
+ * @run main/othervm/manual -Djmx-registry.host=jmx-registry-host -Djmx-registry.port=jmx-registry-port NonLocalJMXRemoteTest
+ */
+
+/**
+ * Verify that access checks for the Registry exported by JMX Registry.bind(),
+ * .rebind(), and .unbind() are prevented on remote access to the registry.
+ * The test verifies that the access check is performed *before* the object to be
+ * bound or rebound is deserialized.
+ * This tests the SingleEntryRegistry implemented by JMX.
+ * This test is a manual test and uses JMX running on a *different* host.
+ * JMX can be enabled in any Java runtime; for example:
+ * login or ssh to the different host and invoke rmiregistry with arguments below.
+ * It will not show any output.
+ * {@code $JDK_HOME/bin/rmiregistry \
+ *         -J-Dcom.sun.management.jmxremote.port=8888 \
+ *         -J-Dcom.sun.management.jmxremote.local.only=false \
+ *         -J-Dcom.sun.management.jmxremote.ssl=false \
+ *         -J-Dcom.sun.management.jmxremote.authenticate=false
+ * }
+ * On the first host modify the @run command above to replace "jmx-registry-host"
+ * with the hostname or IP address of the different host and run the test with jtreg.
+ */
+public class NonLocalJMXRemoteTest {
+
+    public static void main(String[] args) throws Exception {
+
+        String host = System.getProperty("jmx-registry.host");
+        if (host == null || host.isEmpty()) {
+            throw new RuntimeException("Specify host with system property: -Djmx-registry.host=<host>");
+        }
+        int port = Integer.getInteger("jmx-registry.port", -1);
+        if (port <= 0) {
+            throw new RuntimeException("Specify port with system property: -Djmx-registry.port=<port>");
+        }
+
+        // Check if running the test on a local system; it only applies to remote
+        String myHostName = InetAddress.getLocalHost().getHostName();
+        Set<InetAddress> myAddrs = Set.of(InetAddress.getAllByName(myHostName));
+        Set<InetAddress> hostAddrs = Set.of(InetAddress.getAllByName(host));
+        if (hostAddrs.stream().anyMatch(i -> myAddrs.contains(i))
+                || hostAddrs.stream().anyMatch(h -> h.isLoopbackAddress())) {
+            throw new RuntimeException("Error: property 'jmx-registry.host' must not be the local host%n");
+        }
+
+        Registry registry = LocateRegistry.getRegistry(host, port);
+        try {
+            // Verify it is a JMX Registry
+            registry.lookup("jmxrmi");
+        } catch (NotBoundException nf) {
+            throw new RuntimeException("Not a JMX registry, jmxrmi is not bound", nf);
+        }
+
+        try {
+            registry.bind("foo", null);
+            throw new RuntimeException("Remote access should not succeed for method: bind");
+        } catch (Exception e) {
+            assertIsAccessException(e);
+        }
+
+        try {
+            registry.rebind("foo", null);
+            throw new RuntimeException("Remote access should not succeed for method: rebind");
+        } catch (Exception e) {
+            assertIsAccessException(e);
+        }
+
+        try {
+            registry.unbind("foo");
+            throw new RuntimeException("Remote access should not succeed for method: unbind");
+        } catch (Exception e) {
+            assertIsAccessException(e);
+        }
+    }
+
+    /**
+     * Check the exception chain for the expected AccessException and message.
+     * @param ex the exception from the remote invocation.
+     */
+    private static void assertIsAccessException(Throwable ex) {
+        Throwable t = ex;
+        while (!(t instanceof AccessException) && t.getCause() != null) {
+            t = t.getCause();
+        }
+        if (t instanceof AccessException) {
+            String msg = t.getMessage();
+            int asIndex = msg.indexOf("Registry");
+            int disallowIndex = msg.indexOf("disallowed");
+            int nonLocalHostIndex = msg.indexOf("non-local host");
+            if (asIndex < 0 ||
+                    disallowIndex < 0 ||
+                    nonLocalHostIndex < 0 ) {
+                throw new RuntimeException("exception message is malformed", t);
+            }
+            System.out.printf("Found expected AccessException: %s%n%n", t);
+        } else {
+            throw new RuntimeException("AccessException did not occur when expected", ex);
+        }
+    }
+}