diff --git a/make/GenerateClasses.gmk b/make/GenerateClasses.gmk
index 4738d830afdc280cead615b3dee06811f3181f7d..7bbb3a2a12cfe1d45cad8e671a475708c4b4b2ec 100644
--- a/make/GenerateClasses.gmk
+++ b/make/GenerateClasses.gmk
@@ -78,13 +78,6 @@ $(eval $(call SetupRMICompilation,RMI_12, \
RUN_V12 := true))
GENCLASSES += $(RMI_12)
-$(eval $(call SetupRMICompilation,RMI_11, \
- CLASSES := sun.rmi.registry.RegistryImpl, \
- CLASSES_DIR := $(CLASSES_DIR), \
- STUB_CLASSES_DIR := $(STUB_CLASSES_DIR), \
- RUN_V11 := true))
-GENCLASSES += $(RMI_11)
-
# For RMI/IIOP call rmic a second time with -standardPackage option
# so that *_tie classes are generated in package without the prefix
# org.omg.stub (6375696)
@@ -111,7 +104,7 @@ GENCLASSES += $(filter %.java, $(RMI_SRC))
##########################################################################################
-$(RMI_12) $(RMI_11) $(RMI_IIOP) $(RMI_SRC): $(BUILD_BOOTSTRAP_RMIC)
+$(RMI_12) $(RMI_IIOP) $(RMI_SRC): $(BUILD_BOOTSTRAP_RMIC)
$(RMIC_GENSRC_DIR)/_the.classes.removed: $(GENCLASSES)
$(FIND) $(RMIC_GENSRC_DIR) -name "*.class" $(FIND_DELETE)
diff --git a/src/share/classes/sun/management/jmxremote/SingleEntryRegistry.java b/src/share/classes/sun/management/jmxremote/SingleEntryRegistry.java
index 998b00ab14e654c75ade9f3e3340284b4dd9eb91..b3b5281358d9a4a77d1f4e3bb061252b77799fd4 100644
--- a/src/share/classes/sun/management/jmxremote/SingleEntryRegistry.java
+++ b/src/share/classes/sun/management/jmxremote/SingleEntryRegistry.java
@@ -32,6 +32,7 @@
package sun.management.jmxremote;
+import sun.misc.ObjectInputFilter;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
@@ -56,7 +57,7 @@ public class SingleEntryRegistry extends RegistryImpl {
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 @@ public class SingleEntryRegistry extends RegistryImpl {
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;
diff --git a/src/share/classes/sun/rmi/registry/RegistryImpl.java b/src/share/classes/sun/rmi/registry/RegistryImpl.java
index e23379db3b96961e2970f62d86882ace22e0dc35..2f36e6d808eb1b3783cc30cf6b58fa13fe9c3fd3 100644
--- a/src/share/classes/sun/rmi/registry/RegistryImpl.java
+++ b/src/share/classes/sun/rmi/registry/RegistryImpl.java
@@ -69,6 +69,10 @@ import sun.rmi.transport.LiveRef;
* registry.
*
* The LocateRegistry class is used to obtain registry for different hosts.
+ *
+ * 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
*/
@@ -136,6 +140,20 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
RMIClientSocketFactory csf,
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.
@@ -143,7 +161,7 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
AccessController.doPrivileged(new PrivilegedExceptionAction() {
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"));
@@ -219,7 +237,8 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
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)
@@ -236,7 +255,8 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
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)
@@ -252,7 +272,8 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
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);
}
@@ -279,7 +300,6 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
* The client must be on same the same host as this server.
*/
public static void checkAccess(String op) throws AccessException {
-
try {
/*
* Get client host that this registry operation was made from.
@@ -305,7 +325,7 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
if (clientHost.isAnyLocalAddress()) {
throw new AccessException(
- "Registry." + op + " disallowed; origin unknown");
+ op + " disallowed; origin unknown");
}
try {
@@ -328,7 +348,7 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
// must have been an IOException
throw new AccessException(
- "Registry." + op + " disallowed; origin " +
+ op + " disallowed; origin " +
clientHost + " is non-local host");
}
}
@@ -337,8 +357,7 @@ public class RegistryImpl extends java.rmi.server.RemoteServer
* 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");
}
}
diff --git a/src/share/classes/sun/rmi/registry/RegistryImpl_Skel.java b/src/share/classes/sun/rmi/registry/RegistryImpl_Skel.java
new file mode 100644
index 0000000000000000000000000000000000000000..842d47719591b7e74c5894b513245a40d8b7608c
--- /dev/null
+++ b/src/share/classes/sun/rmi/registry/RegistryImpl_Skel.java
@@ -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");
+ }
+ }
+}
diff --git a/src/share/classes/sun/rmi/registry/RegistryImpl_Stub.java b/src/share/classes/sun/rmi/registry/RegistryImpl_Stub.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8574869147855dc78e2a70bdaa3e7603aba9d94
--- /dev/null
+++ b/src/share/classes/sun/rmi/registry/RegistryImpl_Stub.java
@@ -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);
+ }
+ }
+}
diff --git a/src/share/classes/sun/rmi/server/Activation.java b/src/share/classes/sun/rmi/server/Activation.java
index 928b1d549ec7b8f9b50585b6041f1e979d1e4dbe..cb36f2da7fc85ead55074b8826433e73560bb6ee 100644
--- a/src/share/classes/sun/rmi/server/Activation.java
+++ b/src/share/classes/sun/rmi/server/Activation.java
@@ -30,6 +30,7 @@ import java.io.File;
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.LogHandler;
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.action.GetBooleanAction;
import sun.security.action.GetIntegerAction;
@@ -345,6 +345,7 @@ public class Activation implements Serializable {
throw new AccessException(
"binding ActivationSystem is disallowed");
} else {
+ RegistryImpl.checkAccess("ActivationSystem.bind");
super.bind(name, obj);
}
}
@@ -356,6 +357,7 @@ public class Activation implements Serializable {
throw new AccessException(
"unbinding ActivationSystem is disallowed");
} else {
+ RegistryImpl.checkAccess("ActivationSystem.unbind");
super.unbind(name);
}
}
@@ -368,6 +370,7 @@ public class Activation implements Serializable {
throw new AccessException(
"binding ActivationSystem is disallowed");
} else {
+ RegistryImpl.checkAccess("ActivationSystem.rebind");
super.rebind(name, obj);
}
}
@@ -458,6 +461,33 @@ public class Activation implements Serializable {
}
+ /**
+ * 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
@@ -475,7 +505,8 @@ public class Activation implements Serializable {
* '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);
}
@@ -484,8 +515,8 @@ public class Activation implements Serializable {
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);
@@ -496,15 +527,18 @@ public class Activation implements Serializable {
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);
}
public ActivationGroupID registerGroup(ActivationGroupDesc desc)
throws ActivationException, RemoteException
{
+ Thread.dumpStack();
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);
@@ -521,7 +555,8 @@ public class Activation implements Serializable {
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;
@@ -531,7 +566,8 @@ public class Activation implements Serializable {
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
@@ -543,7 +579,8 @@ public class Activation implements Serializable {
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(
@@ -557,8 +594,8 @@ public class Activation implements Serializable {
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);
@@ -568,7 +605,8 @@ public class Activation implements Serializable {
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);
}
@@ -577,8 +615,8 @@ public class Activation implements Serializable {
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;
}
@@ -588,7 +626,8 @@ public class Activation implements Serializable {
* 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) {
diff --git a/src/share/classes/sun/rmi/server/UnicastServerRef.java b/src/share/classes/sun/rmi/server/UnicastServerRef.java
index cd2e20a5240d824138ae028269048dff881e94eb..285dba5e0716c21c24e33741bfc458b16e263eba 100644
--- a/src/share/classes/sun/rmi/server/UnicastServerRef.java
+++ b/src/share/classes/sun/rmi/server/UnicastServerRef.java
@@ -31,6 +31,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutput;
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;
@@ -289,20 +290,25 @@ public class UnicastServerRef extends UnicastRef
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);
}
/*
@@ -335,6 +341,11 @@ public class UnicastServerRef extends UnicastRef
params[i] = unmarshalValue(types[i], in);
}
+ } 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();
@@ -371,6 +382,7 @@ public class UnicastServerRef extends UnicastRef
*/
}
} catch (Throwable e) {
+ Throwable origEx = e;
logCallException(e);
ObjectOutput out = call.getResultStream(false);
@@ -386,6 +398,12 @@ public class UnicastServerRef extends UnicastRef
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();
@@ -417,62 +435,41 @@ public class UnicastServerRef extends UnicastRef
* 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);
-
- 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();
+ try {
+ hash = in.readLong();
+ } catch (Exception ioe) {
+ throw new UnmarshalException("error unmarshalling call header", ioe);
}
+
+ // 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);
}
/**
diff --git a/test/java/rmi/activation/nonLocalActivation/NonLocalActivationTest.java b/test/java/rmi/activation/nonLocalActivation/NonLocalActivationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..594148805be1f2a74985294df1c7817f40e33118
--- /dev/null
+++ b/test/java/rmi/activation/nonLocalActivation/NonLocalActivationTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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;
+import java.util.HashSet;
+
+/*
+ * @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=");
+ }
+
+ // Check if running the test on a local system; it only applies to remote
+ String myHostName = InetAddress.getLocalHost().getHostName();
+ Set myAddrs = new HashSet<>();
+ InetAddress[] myAddrsArr = InetAddress.getAllByName(myHostName);
+ for (InetAddress a : myAddrsArr) {
+ myAddrs.add(a);
+ }
+ Set hostAddrs = new HashSet<>();
+ InetAddress[] hostAddrsArr = InetAddress.getAllByName(host);
+ for (InetAddress a : hostAddrsArr) {
+ hostAddrs.add(a);
+ }
+ 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);
+ }
+ }
+}
diff --git a/test/java/rmi/registry/nonLocalRegistry/NonLocalRegistryTest.java b/test/java/rmi/registry/nonLocalRegistry/NonLocalRegistryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..771a34a7dce7cfb5213f57e6621e325727bd16b4
--- /dev/null
+++ b/test/java/rmi/registry/nonLocalRegistry/NonLocalRegistryTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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;
+import java.util.HashSet;
+
+/* @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=");
+ }
+
+ // Check if running the test on a local system; it only applies to remote
+ String myHostName = InetAddress.getLocalHost().getHostName();
+ Set myAddrs = new HashSet<>();
+ InetAddress[] myAddrsArr = InetAddress.getAllByName(myHostName);
+ for (InetAddress a : myAddrsArr) {
+ myAddrs.add(a);
+ }
+ Set hostAddrs = new HashSet<>();
+ InetAddress[] hostAddrsArr = InetAddress.getAllByName(host);
+ for (InetAddress a : hostAddrsArr) {
+ hostAddrs.add(a);
+ }
+ 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);
+ }
+ }
+}
diff --git a/test/javax/management/remote/nonLocalAccess/NonLocalJMXRemoteTest.java b/test/javax/management/remote/nonLocalAccess/NonLocalJMXRemoteTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c20e67a571bc3de0788ae42abb6e36ea70a6dd57
--- /dev/null
+++ b/test/javax/management/remote/nonLocalAccess/NonLocalJMXRemoteTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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;
+import java.util.HashSet;
+
+/* @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=");
+ }
+ int port = Integer.getInteger("jmx-registry.port", -1);
+ if (port <= 0) {
+ throw new RuntimeException("Specify port with system property: -Djmx-registry.port=");
+ }
+
+ // Check if running the test on a local system; it only applies to remote
+ String myHostName = InetAddress.getLocalHost().getHostName();
+ Set myAddrs = new HashSet<>();
+ InetAddress[] myAddrsArr = InetAddress.getAllByName(myHostName);
+ for (InetAddress a : myAddrsArr) {
+ myAddrs.add(a);
+ }
+ Set hostAddrs = new HashSet<>();
+ InetAddress[] hostAddrsArr = InetAddress.getAllByName(host);
+ for (InetAddress a : hostAddrsArr) {
+ hostAddrs.add(a);
+ }
+ 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);
+ }
+ }
+}