8174770: Check registry registration location
Reviewed-by: dfuchs, smarks, chegar
--- 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);
+ }
+ }
+}