8187556: Backout of a fix reintroduced a dependency that had since been removed
Reviewed-by: duke
This commit is contained in:
parent
8d9060c1a6
commit
65db4f42d0
@ -44,7 +44,6 @@ import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static java.io.ObjectStreamClass.processQueue;
|
||||
|
||||
import jdk.internal.misc.ObjectStreamClassValidator;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
@ -1767,9 +1766,6 @@ public class ObjectInputStream
|
||||
throw new StreamCorruptedException(
|
||||
String.format("invalid type code: %02X", tc));
|
||||
}
|
||||
if (descriptor != null) {
|
||||
validateDescriptor(descriptor);
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@ -4013,20 +4009,4 @@ public class ObjectInputStream
|
||||
SharedSecrets.setJavaObjectInputStreamAccess(ObjectInputStream::checkArray);
|
||||
}
|
||||
|
||||
private void validateDescriptor(ObjectStreamClass descriptor) {
|
||||
ObjectStreamClassValidator validating = validator;
|
||||
if (validating != null) {
|
||||
validating.validateDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// controlled access to ObjectStreamClassValidator
|
||||
private volatile ObjectStreamClassValidator validator;
|
||||
|
||||
private static void setValidator(ObjectInputStream ois, ObjectStreamClassValidator validator) {
|
||||
ois.validator = validator;
|
||||
}
|
||||
static {
|
||||
SharedSecrets.setJavaObjectInputStreamAccess(ObjectInputStream::setValidator);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2016, 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
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
package com.sun.jmx.remote.internal.rmi;
|
||||
|
||||
import java.io.ObjectInputFilter;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
@ -51,7 +52,8 @@ public interface RMIExporter {
|
||||
public Remote exportObject(Remote obj,
|
||||
int port,
|
||||
RMIClientSocketFactory csf,
|
||||
RMIServerSocketFactory ssf)
|
||||
RMIServerSocketFactory ssf,
|
||||
ObjectInputFilter filter)
|
||||
throws RemoteException;
|
||||
|
||||
public boolean unexportObject(Remote obj, boolean force)
|
||||
|
@ -32,6 +32,7 @@ import com.sun.jmx.remote.util.EnvHelp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputFilter;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
@ -101,19 +102,59 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
"jmx.remote.rmi.server.socket.factory";
|
||||
|
||||
/**
|
||||
* Name of the attribute that specifies a list of class names acceptable
|
||||
* as parameters to the {@link RMIServer#newClient(java.lang.Object) RMIServer.newClient()}
|
||||
* Name of the attribute that specifies an
|
||||
* {@link ObjectInputFilter} pattern string to filter classes acceptable
|
||||
* for {@link RMIServer#newClient(java.lang.Object) RMIServer.newClient()}
|
||||
* remote method call.
|
||||
* <p>
|
||||
* This list of classes should correspond to the transitive closure of the
|
||||
* credentials class (or classes) used by the installed {@linkplain JMXAuthenticator}
|
||||
* associated with the {@linkplain RMIServer} implementation.
|
||||
* The filter pattern must be in same format as used in
|
||||
* {@link java.io.ObjectInputFilter.Config#createFilter}
|
||||
* <p>
|
||||
* If the attribute is not set, or is null, then any class is
|
||||
* deemed acceptable.
|
||||
* This list of classes allowed by filter should correspond to the
|
||||
* transitive closure of the credentials class (or classes) used by the
|
||||
* installed {@linkplain JMXAuthenticator} associated with the
|
||||
* {@linkplain RMIServer} implementation.
|
||||
* If the attribute is not set then any class is deemed acceptable.
|
||||
* @see ObjectInputFilter
|
||||
*/
|
||||
public static final String CREDENTIAL_TYPES =
|
||||
"jmx.remote.rmi.server.credential.types";
|
||||
public static final String CREDENTIALS_FILTER_PATTERN =
|
||||
"jmx.remote.rmi.server.credentials.filter.pattern";
|
||||
|
||||
/**
|
||||
* This attribute defines a pattern from which to create a
|
||||
* {@link java.io.ObjectInputFilter} that will be used when deserializing
|
||||
* objects sent to the {@code JMXConnectorServer} by any client.
|
||||
* <p>
|
||||
* The filter will be called for any class found in the serialized
|
||||
* stream sent to server by client, including all JMX defined classes
|
||||
* (such as {@link javax.management.ObjectName}), all method parameters,
|
||||
* and, if present in the stream, all classes transitively referred by
|
||||
* the serial form of any deserialized object.
|
||||
* The pattern must be in same format as used in
|
||||
* {@link java.io.ObjectInputFilter.Config#createFilter}.
|
||||
* It may define a white list of permitted classes, a black list of
|
||||
* rejected classes, a maximum depth for the deserialized objects,
|
||||
* etc.
|
||||
* <p>
|
||||
* To be functional, the filter should allow at least all the
|
||||
* concrete types in the transitive closure of all objects that
|
||||
* might get serialized when serializing all JMX classes referred
|
||||
* as parameters in the {@link
|
||||
* javax.management.remote.rmi.RMIConnection} interface,
|
||||
* plus all classes that a {@link javax.management.remote.rmi.RMIConnector client}
|
||||
* might need to transmit wrapped in {@linkplain java.rmi.MarshalledObject
|
||||
* marshalled objects} in order to interoperate with the MBeans registered
|
||||
* in the {@code MBeanServer}. That would potentially include all the
|
||||
* concrete {@linkplain javax.management.openmbean JMX OpenTypes} and the
|
||||
* classes they use in their serial form.
|
||||
* <p>
|
||||
* Care must be taken when defining such a filter, as defining
|
||||
* a white list too restrictive or a too wide a black list may
|
||||
* prevent legitimate clients from interoperating with the
|
||||
* {@code JMXConnectorServer}.
|
||||
*/
|
||||
public static final String SERIAL_FILTER_PATTERN =
|
||||
"jmx.remote.rmi.server.serial.filter.pattern";
|
||||
|
||||
/**
|
||||
* <p>Makes an <code>RMIConnectorServer</code>.
|
||||
|
@ -26,6 +26,7 @@
|
||||
package javax.management.remote.rmi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputFilter;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
@ -39,15 +40,13 @@ import javax.security.auth.Subject;
|
||||
|
||||
import com.sun.jmx.remote.internal.rmi.RMIExporter;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
import sun.rmi.server.DeserializationChecker;
|
||||
import sun.rmi.server.UnicastServerRef;
|
||||
import sun.rmi.server.UnicastServerRef2;
|
||||
import sun.rmi.transport.LiveRef;
|
||||
|
||||
/**
|
||||
* <p>An {@link RMIServer} object that is exported through JRMP and that
|
||||
@ -60,8 +59,6 @@ import sun.rmi.server.UnicastServerRef2;
|
||||
*/
|
||||
public class RMIJRMPServerImpl extends RMIServerImpl {
|
||||
|
||||
private final ExportedWrapper exportedWrapper;
|
||||
|
||||
/**
|
||||
* <p>Creates a new {@link RMIServer} object that will be exported
|
||||
* on the given port using the given socket factories.</p>
|
||||
@ -100,33 +97,48 @@ public class RMIJRMPServerImpl extends RMIServerImpl {
|
||||
this.ssf = ssf;
|
||||
this.env = (env == null) ? Collections.<String, Object>emptyMap() : env;
|
||||
|
||||
// This attribute was represented by RMIConnectorServer.CREDENTIALS_TYPES.
|
||||
// This attribute is superceded by
|
||||
// RMIConnectorServer.CREDENTIALS_FILTER_PATTERN.
|
||||
// Retaining this for backward compatibility.
|
||||
String[] credentialsTypes
|
||||
= (String[]) this.env.get(RMIConnectorServer.CREDENTIAL_TYPES);
|
||||
List<String> types = null;
|
||||
if (credentialsTypes != null) {
|
||||
types = new ArrayList<>();
|
||||
for (String type : credentialsTypes) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("A credential type is null.");
|
||||
}
|
||||
ReflectUtil.checkPackageAccess(type);
|
||||
types.add(type);
|
||||
}
|
||||
= (String[]) this.env.get("jmx.remote.rmi.server.credential.types");
|
||||
|
||||
String credentialsFilter
|
||||
= (String) this.env.get(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN);
|
||||
|
||||
// It is impossible for both attributes to be specified
|
||||
if(credentialsTypes != null && credentialsFilter != null)
|
||||
throw new IllegalArgumentException("Cannot specify both \""
|
||||
+ "jmx.remote.rmi.server.credential.types" + "\" and \""
|
||||
+ RMIConnectorServer.CREDENTIALS_FILTER_PATTERN + "\"");
|
||||
else if(credentialsFilter != null){
|
||||
cFilter = ObjectInputFilter.Config.createFilter(credentialsFilter);
|
||||
allowedTypes = null;
|
||||
}
|
||||
exportedWrapper = types != null ?
|
||||
new ExportedWrapper(this, types) :
|
||||
null;
|
||||
else if (credentialsTypes != null) {
|
||||
allowedTypes = Arrays.stream(credentialsTypes).filter(
|
||||
s -> s!= null).collect(Collectors.toSet());
|
||||
allowedTypes.stream().forEach(ReflectUtil::checkPackageAccess);
|
||||
cFilter = this::newClientCheckInput;
|
||||
} else {
|
||||
allowedTypes = null;
|
||||
cFilter = null;
|
||||
}
|
||||
|
||||
String userJmxFilter =
|
||||
(String) this.env.get(RMIConnectorServer.SERIAL_FILTER_PATTERN);
|
||||
if(userJmxFilter != null && !userJmxFilter.isEmpty())
|
||||
jmxRmiFilter = ObjectInputFilter.Config.createFilter(userJmxFilter);
|
||||
else
|
||||
jmxRmiFilter = null;
|
||||
}
|
||||
|
||||
protected void export() throws IOException {
|
||||
if (exportedWrapper != null) {
|
||||
export(exportedWrapper);
|
||||
} else {
|
||||
export(this);
|
||||
}
|
||||
export(this, cFilter);
|
||||
}
|
||||
|
||||
private void export(Remote obj) throws RemoteException {
|
||||
private void export(Remote obj, ObjectInputFilter typeFilter) throws RemoteException {
|
||||
final RMIExporter exporter =
|
||||
(RMIExporter) env.get(RMIExporter.EXPORTER_ATTRIBUTE);
|
||||
final boolean daemon = EnvHelp.isServerDaemon(env);
|
||||
@ -137,16 +149,14 @@ public class RMIJRMPServerImpl extends RMIServerImpl {
|
||||
" cannot be used to specify an exporter!");
|
||||
}
|
||||
|
||||
if (daemon) {
|
||||
if (csf == null && ssf == null) {
|
||||
new UnicastServerRef(port).exportObject(obj, null, true);
|
||||
} else {
|
||||
new UnicastServerRef2(port, csf, ssf).exportObject(obj, null, true);
|
||||
}
|
||||
} else if (exporter != null) {
|
||||
exporter.exportObject(obj, port, csf, ssf);
|
||||
if (exporter != null) {
|
||||
exporter.exportObject(obj, port, csf, ssf, typeFilter);
|
||||
} else {
|
||||
UnicastRemoteObject.exportObject(obj, port, csf, ssf);
|
||||
if (csf == null && ssf == null) {
|
||||
new UnicastServerRef(new LiveRef(port), typeFilter).exportObject(obj, null, daemon);
|
||||
} else {
|
||||
new UnicastServerRef2(port, csf, ssf, typeFilter).exportObject(obj, null, daemon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,11 +183,7 @@ public class RMIJRMPServerImpl extends RMIServerImpl {
|
||||
* RMIJRMPServerImpl has not been exported yet.
|
||||
*/
|
||||
public Remote toStub() throws IOException {
|
||||
if (exportedWrapper != null) {
|
||||
return RemoteObject.toStub(exportedWrapper);
|
||||
} else {
|
||||
return RemoteObject.toStub(this);
|
||||
}
|
||||
return RemoteObject.toStub(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,7 +213,7 @@ public class RMIJRMPServerImpl extends RMIServerImpl {
|
||||
RMIConnection client =
|
||||
new RMIConnectionImpl(this, connectionId, getDefaultClassLoader(),
|
||||
subject, env);
|
||||
export(client);
|
||||
export(client, jmxRmiFilter);
|
||||
return client;
|
||||
}
|
||||
|
||||
@ -224,56 +230,39 @@ public class RMIJRMPServerImpl extends RMIServerImpl {
|
||||
* server failed.
|
||||
*/
|
||||
protected void closeServer() throws IOException {
|
||||
if (exportedWrapper != null) {
|
||||
unexport(exportedWrapper, true);
|
||||
} else {
|
||||
unexport(this, true);
|
||||
unexport(this, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a type in the remote invocation of {@link RMIServerImpl#newClient}
|
||||
* is one of the {@code allowedTypes}.
|
||||
*
|
||||
* @param clazz the class; may be null
|
||||
* @param size the size for arrays, otherwise is 0
|
||||
* @param nObjectRefs the current number of object references
|
||||
* @param depth the current depth
|
||||
* @param streamBytes the current number of bytes consumed
|
||||
* @return {@code ObjectInputFilter.Status.ALLOWED} if the class is allowed,
|
||||
* otherwise {@code ObjectInputFilter.Status.REJECTED}
|
||||
*/
|
||||
ObjectInputFilter.Status newClientCheckInput(ObjectInputFilter.FilterInfo filterInfo) {
|
||||
ObjectInputFilter.Status status = ObjectInputFilter.Status.UNDECIDED;
|
||||
if (allowedTypes != null && filterInfo.serialClass() != null) {
|
||||
// If enabled, check type
|
||||
String type = filterInfo.serialClass().getName();
|
||||
if (allowedTypes.contains(type))
|
||||
status = ObjectInputFilter.Status.ALLOWED;
|
||||
else
|
||||
status = ObjectInputFilter.Status.REJECTED;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private final int port;
|
||||
private final RMIClientSocketFactory csf;
|
||||
private final RMIServerSocketFactory ssf;
|
||||
private final Map<String, ?> env;
|
||||
|
||||
private static class ExportedWrapper implements RMIServer, DeserializationChecker {
|
||||
private final RMIServer impl;
|
||||
private final List<String> allowedTypes;
|
||||
|
||||
private ExportedWrapper(RMIServer impl, List<String> credentialsTypes) {
|
||||
this.impl = impl;
|
||||
allowedTypes = credentialsTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() throws RemoteException {
|
||||
return impl.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RMIConnection newClient(Object credentials) throws IOException {
|
||||
return impl.newClient(credentials);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(Method method, ObjectStreamClass descriptor,
|
||||
int paramIndex, int callID) {
|
||||
String type = descriptor.getName();
|
||||
if (!allowedTypes.contains(type)) {
|
||||
throw new ClassCastException("Unsupported type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkProxyClass(Method method, String[] ifaces,
|
||||
int paramIndex, int callID) {
|
||||
if (ifaces != null && ifaces.length > 0) {
|
||||
for (String iface : ifaces) {
|
||||
if (!allowedTypes.contains(iface)) {
|
||||
throw new ClassCastException("Unsupported type: " + iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private final Set<String> allowedTypes;
|
||||
private final ObjectInputFilter jmxRmiFilter;
|
||||
private final ObjectInputFilter cFilter;
|
||||
}
|
||||
|
@ -35,8 +35,6 @@ import java.security.AccessControlException;
|
||||
import java.security.Permission;
|
||||
import java.rmi.server.RMIClassLoader;
|
||||
import java.security.PrivilegedAction;
|
||||
import jdk.internal.misc.ObjectStreamClassValidator;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
|
||||
/**
|
||||
* MarshalInputStream is an extension of ObjectInputStream. When resolving
|
||||
@ -54,11 +52,6 @@ import jdk.internal.misc.SharedSecrets;
|
||||
* @author Peter Jones
|
||||
*/
|
||||
public class MarshalInputStream extends ObjectInputStream {
|
||||
interface StreamChecker extends ObjectStreamClassValidator {
|
||||
void checkProxyInterfaceNames(String[] ifaces);
|
||||
}
|
||||
|
||||
private volatile StreamChecker streamChecker = null;
|
||||
|
||||
/**
|
||||
* Value of "java.rmi.server.useCodebaseOnly" property,
|
||||
@ -245,11 +238,6 @@ public class MarshalInputStream extends ObjectInputStream {
|
||||
protected Class<?> resolveProxyClass(String[] interfaces)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
StreamChecker checker = streamChecker;
|
||||
if (checker != null) {
|
||||
checker.checkProxyInterfaceNames(interfaces);
|
||||
}
|
||||
|
||||
/*
|
||||
* Always read annotation written by MarshalOutputStream.
|
||||
*/
|
||||
@ -330,28 +318,4 @@ public class MarshalInputStream extends ObjectInputStream {
|
||||
void useCodebaseOnly() {
|
||||
useCodebaseOnly = true;
|
||||
}
|
||||
|
||||
synchronized void setStreamChecker(StreamChecker checker) {
|
||||
streamChecker = checker;
|
||||
SharedSecrets.getJavaObjectInputStreamAccess().setValidator(this, checker);
|
||||
}
|
||||
@Override
|
||||
protected ObjectStreamClass readClassDescriptor() throws IOException,
|
||||
ClassNotFoundException {
|
||||
ObjectStreamClass descriptor = super.readClassDescriptor();
|
||||
|
||||
validateDesc(descriptor);
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private void validateDesc(ObjectStreamClass descriptor) {
|
||||
StreamChecker checker;
|
||||
synchronized (this) {
|
||||
checker = streamChecker;
|
||||
}
|
||||
if (checker != null) {
|
||||
checker.validateDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import java.io.ObjectInput;
|
||||
import java.io.ObjectInputFilter;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.AccessException;
|
||||
@ -330,11 +329,16 @@ public class UnicastServerRef extends UnicastRef
|
||||
logCall(obj, method);
|
||||
|
||||
// unmarshal parameters
|
||||
Object[] params = null;
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
Object[] params = new Object[types.length];
|
||||
|
||||
try {
|
||||
unmarshalCustomCallData(in);
|
||||
params = unmarshalParameters(obj, method, marshalStream);
|
||||
// Unmarshal the parameters
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
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
|
||||
@ -600,84 +604,4 @@ public class UnicastServerRef extends UnicastRef
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshal parameters for the given method of the given instance over
|
||||
* the given marshalinputstream. Perform any necessary checks.
|
||||
*/
|
||||
private Object[] unmarshalParameters(Object obj, Method method, MarshalInputStream in)
|
||||
throws IOException, ClassNotFoundException {
|
||||
return (obj instanceof DeserializationChecker) ?
|
||||
unmarshalParametersChecked((DeserializationChecker)obj, method, in) :
|
||||
unmarshalParametersUnchecked(method, in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshal parameters for the given method of the given instance over
|
||||
* the given marshalinputstream. Do not perform any additional checks.
|
||||
*/
|
||||
private Object[] unmarshalParametersUnchecked(Method method, ObjectInput in)
|
||||
throws IOException, ClassNotFoundException {
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
Object[] params = new Object[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
params[i] = unmarshalValue(types[i], in);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshal parameters for the given method of the given instance over
|
||||
* the given marshalinputstream. Do perform all additional checks.
|
||||
*/
|
||||
private Object[] unmarshalParametersChecked(
|
||||
DeserializationChecker checker,
|
||||
Method method, MarshalInputStream in)
|
||||
throws IOException, ClassNotFoundException {
|
||||
int callID = methodCallIDCount.getAndIncrement();
|
||||
MyChecker myChecker = new MyChecker(checker, method, callID);
|
||||
in.setStreamChecker(myChecker);
|
||||
try {
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
Object[] values = new Object[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
myChecker.setIndex(i);
|
||||
values[i] = unmarshalValue(types[i], in);
|
||||
}
|
||||
myChecker.end(callID);
|
||||
return values;
|
||||
} finally {
|
||||
in.setStreamChecker(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyChecker implements MarshalInputStream.StreamChecker {
|
||||
private final DeserializationChecker descriptorCheck;
|
||||
private final Method method;
|
||||
private final int callID;
|
||||
private int parameterIndex;
|
||||
|
||||
MyChecker(DeserializationChecker descriptorCheck, Method method, int callID) {
|
||||
this.descriptorCheck = descriptorCheck;
|
||||
this.method = method;
|
||||
this.callID = callID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateDescriptor(ObjectStreamClass descriptor) {
|
||||
descriptorCheck.check(method, descriptor, parameterIndex, callID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkProxyInterfaceNames(String[] ifaces) {
|
||||
descriptorCheck.checkProxyClass(method, ifaces, parameterIndex, callID);
|
||||
}
|
||||
|
||||
void setIndex(int parameterIndex) {
|
||||
this.parameterIndex = parameterIndex;
|
||||
}
|
||||
|
||||
void end(int callId) {
|
||||
descriptorCheck.end(callId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2016, 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
|
||||
@ -32,7 +32,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.ObjectInputFilter;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MalformedURLException;
|
||||
@ -45,14 +45,12 @@ import java.rmi.RemoteException;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
import java.rmi.server.RMIServerSocketFactory;
|
||||
import java.rmi.server.RMISocketFactory;
|
||||
import java.rmi.server.RemoteObject;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
import java.security.KeyStore;
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
@ -84,6 +82,7 @@ import jdk.internal.agent.FileSystem;
|
||||
import sun.rmi.server.UnicastRef;
|
||||
import sun.rmi.server.UnicastServerRef;
|
||||
import sun.rmi.server.UnicastServerRef2;
|
||||
import sun.rmi.transport.LiveRef;
|
||||
|
||||
/**
|
||||
* This class initializes and starts the RMIConnectorServer for JSR 163
|
||||
@ -142,6 +141,8 @@ public final class ConnectorBootstrap {
|
||||
"com.sun.management.jmxremote.ssl.need.client.auth";
|
||||
public static final String SSL_CONFIG_FILE_NAME =
|
||||
"com.sun.management.jmxremote.ssl.config.file";
|
||||
public static final String SERIAL_FILTER_PATTERN =
|
||||
"com.sun.management.jmxremote.serial.filter.pattern";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,7 +183,8 @@ public final class ConnectorBootstrap {
|
||||
public Remote exportObject(Remote obj,
|
||||
int port,
|
||||
RMIClientSocketFactory csf,
|
||||
RMIServerSocketFactory ssf)
|
||||
RMIServerSocketFactory ssf,
|
||||
ObjectInputFilter filter)
|
||||
throws RemoteException {
|
||||
|
||||
synchronized (this) {
|
||||
@ -193,9 +195,9 @@ public final class ConnectorBootstrap {
|
||||
|
||||
final UnicastServerRef ref;
|
||||
if (csf == null && ssf == null) {
|
||||
ref = new UnicastServerRef(port);
|
||||
ref = new UnicastServerRef(new LiveRef(port), filter);
|
||||
} else {
|
||||
ref = new UnicastServerRef2(port, csf, ssf);
|
||||
ref = new UnicastServerRef2(port, csf, ssf, filter);
|
||||
}
|
||||
return ref.exportObject(obj, null, true);
|
||||
}
|
||||
@ -435,6 +437,7 @@ public final class ConnectorBootstrap {
|
||||
|
||||
final String bindAddress =
|
||||
props.getProperty(PropertyNames.HOST);
|
||||
final String jmxRmiFilter = props.getProperty(PropertyNames.SERIAL_FILTER_PATTERN);
|
||||
|
||||
if (logger.isLoggable(Level.DEBUG)) {
|
||||
logger.log(Level.DEBUG, "startRemoteConnectorServer",
|
||||
@ -471,7 +474,7 @@ public final class ConnectorBootstrap {
|
||||
sslConfigFileName, enabledCipherSuitesList,
|
||||
enabledProtocolsList, sslNeedClientAuth,
|
||||
useAuthentication, loginConfigName,
|
||||
passwordFileName, accessFileName, bindAddress);
|
||||
passwordFileName, accessFileName, bindAddress, jmxRmiFilter);
|
||||
cs = data.jmxConnectorServer;
|
||||
url = data.jmxRemoteURL;
|
||||
config("startRemoteConnectorServer",
|
||||
@ -511,9 +514,7 @@ public final class ConnectorBootstrap {
|
||||
// This RMI server should not keep the VM alive
|
||||
Map<String, Object> env = new HashMap<>();
|
||||
env.put(RMIExporter.EXPORTER_ATTRIBUTE, new PermanentExporter());
|
||||
env.put(RMIConnectorServer.CREDENTIAL_TYPES, new String[]{
|
||||
String[].class.getName(), String.class.getName()
|
||||
});
|
||||
env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, String.class.getName() + ";!*");
|
||||
|
||||
// The local connector server need only be available via the
|
||||
// loopback connection.
|
||||
@ -729,7 +730,8 @@ public final class ConnectorBootstrap {
|
||||
String loginConfigName,
|
||||
String passwordFileName,
|
||||
String accessFileName,
|
||||
String bindAddress)
|
||||
String bindAddress,
|
||||
String jmxRmiFilter)
|
||||
throws IOException, MalformedURLException {
|
||||
|
||||
/* Make sure we use non-guessable RMI object IDs. Otherwise
|
||||
@ -744,9 +746,11 @@ public final class ConnectorBootstrap {
|
||||
PermanentExporter exporter = new PermanentExporter();
|
||||
|
||||
env.put(RMIExporter.EXPORTER_ATTRIBUTE, exporter);
|
||||
env.put(RMIConnectorServer.CREDENTIAL_TYPES, new String[]{
|
||||
String[].class.getName(), String.class.getName()
|
||||
});
|
||||
env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN, String.class.getName() + ";!*");
|
||||
|
||||
if(jmxRmiFilter != null && !jmxRmiFilter.isEmpty()) {
|
||||
env.put(RMIConnectorServer.SERIAL_FILTER_PATTERN, jmxRmiFilter);
|
||||
}
|
||||
|
||||
boolean useSocketFactory = bindAddress != null && !useSsl;
|
||||
|
||||
|
@ -329,3 +329,42 @@
|
||||
# The format of the value for that property is any string accepted
|
||||
# by java.net.InetAddress.getByName(String).
|
||||
#
|
||||
|
||||
# ################ Filter for ObjectInputStream #############################
|
||||
# com.sun.management.jmxremote.serial.filter.pattern=<filter-string>
|
||||
# A filter, if configured, is used by java.io.ObjectInputStream during
|
||||
# deserialization of parameters sent to the JMX default agent to validate the
|
||||
# contents of the stream.
|
||||
# A filter is configured as a sequence of patterns, each pattern is either
|
||||
# matched against the name of a class in the stream or defines a limit.
|
||||
# Patterns are separated by ";" (semicolon).
|
||||
# Whitespace is significant and is considered part of the pattern.
|
||||
#
|
||||
# If a pattern includes a "=", it sets a limit.
|
||||
# If a limit appears more than once the last value is used.
|
||||
# Limits are checked before classes regardless of the order in the sequence of patterns.
|
||||
# If any of the limits are exceeded, the filter status is REJECTED.
|
||||
#
|
||||
# maxdepth=value - the maximum depth of a graph
|
||||
# maxrefs=value - the maximum number of internal references
|
||||
# maxbytes=value - the maximum number of bytes in the input stream
|
||||
# maxarray=value - the maximum array length allowed
|
||||
#
|
||||
# Other patterns, from left to right, match the class or package name as
|
||||
# returned from Class.getName.
|
||||
# If the class is an array type, the class or package to be matched is the element type.
|
||||
# Arrays of any number of dimensions are treated the same as the element type.
|
||||
# For example, a pattern of "!example.Foo", rejects creation of any instance or
|
||||
# array of example.Foo.
|
||||
#
|
||||
# If the pattern starts with "!", the status is REJECTED if the remaining pattern
|
||||
# is matched; otherwise the status is ALLOWED if the pattern matches.
|
||||
# If the pattern contains "/", the non-empty prefix up to the "/" is the module name;
|
||||
# if the module name matches the module name of the class then
|
||||
# the remaining pattern is matched with the class name.
|
||||
# If there is no "/", the module name is not compared.
|
||||
# If the pattern ends with ".**" it matches any class in the package and all subpackages.
|
||||
# If the pattern ends with ".*" it matches any class in the package.
|
||||
# If the pattern ends with "*", it matches any class with the pattern as a prefix.
|
||||
# If the pattern is equal to the class name, it matches.
|
||||
# Otherwise, the status is UNDECIDED.
|
||||
|
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8159377
|
||||
* @library /lib/testlibrary
|
||||
* @summary Tests ObjectFilter on default agent
|
||||
* @author Harsha Wardhana B
|
||||
* @modules java.management
|
||||
* @build jdk.testlibrary.* DefaultAgentFilterTest
|
||||
* @run main/othervm/timeout=600 -XX:+UsePerfData DefaultAgentFilterTest
|
||||
*/
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidClassException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.BindException;
|
||||
import java.rmi.UnmarshalException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
import jdk.testlibrary.ProcessTools;
|
||||
import jdk.testlibrary.Utils;
|
||||
|
||||
public class DefaultAgentFilterTest {
|
||||
|
||||
public static class MyTestObject implements Serializable {
|
||||
|
||||
String a;
|
||||
int id;
|
||||
}
|
||||
|
||||
public interface TestMBean {
|
||||
|
||||
public void op1(HashSet<Object> params);
|
||||
|
||||
public void op2(String s, HashSet<String> params);
|
||||
|
||||
public void op3(MyTestObject obj, String s, HashMap<String, String> param);
|
||||
}
|
||||
|
||||
public static class Test implements TestMBean {
|
||||
|
||||
@Override
|
||||
public void op1(HashSet<Object> params) {
|
||||
System.out.println("Invoked op1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void op2(String s, HashSet<String> params) {
|
||||
System.out.println("Invoked op2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void op3(MyTestObject obj, String s, HashMap<String, String> param) {
|
||||
System.out.println("Invoked op3");
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestAppRun implements AutoCloseable {
|
||||
|
||||
private Process p;
|
||||
private final ProcessBuilder pb;
|
||||
private final String name;
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
private volatile long pid = -1;
|
||||
|
||||
public TestAppRun(ProcessBuilder pb, String name) {
|
||||
this.pb = pb;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public synchronized void start() throws Exception {
|
||||
if (started.compareAndSet(false, true)) {
|
||||
try {
|
||||
AtomicBoolean error = new AtomicBoolean(false);
|
||||
AtomicBoolean bindError = new AtomicBoolean(false);
|
||||
p = ProcessTools.startProcess(
|
||||
TEST_APP_NAME + "{" + name + "}",
|
||||
pb,
|
||||
(line) -> {
|
||||
if (line.toLowerCase().contains("exception")
|
||||
|| line.toLowerCase().contains("error")) {
|
||||
error.set(true);
|
||||
}
|
||||
bindError.set(line.toLowerCase().contains("bindexception"));
|
||||
return true;
|
||||
});
|
||||
if (bindError.get()) {
|
||||
throw new BindException("Process could not be started");
|
||||
} else if (error.get()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
pid = p.pid();
|
||||
} catch (Exception ex) {
|
||||
if (p != null) {
|
||||
p.destroy();
|
||||
p.waitFor();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public synchronized void stop()
|
||||
throws IOException, InterruptedException {
|
||||
if (started.compareAndSet(true, false)) {
|
||||
p.getOutputStream().write(0);
|
||||
p.getOutputStream().flush();
|
||||
int ec = p.waitFor();
|
||||
if (ec != 0) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append("Test application '").append(name);
|
||||
msg.append("' failed with exit code: ");
|
||||
msg.append(ec);
|
||||
System.err.println(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TEST_APP_NAME = "TestApp";
|
||||
|
||||
private static void testDefaultAgent(String propertyFile) throws Exception {
|
||||
int port = Utils.getFreePort();
|
||||
String propFile = System.getProperty("test.src") + File.separator + propertyFile;
|
||||
List<String> pbArgs = new ArrayList<>(Arrays.asList(
|
||||
"-cp",
|
||||
System.getProperty("test.class.path"),
|
||||
"-XX:+UsePerfData"
|
||||
));
|
||||
String[] args = new String[]{
|
||||
"-Dcom.sun.management.jmxremote.port=" + port,
|
||||
"-Dcom.sun.management.jmxremote.authenticate=false",
|
||||
"-Dcom.sun.management.jmxremote.ssl=false",
|
||||
"-Dcom.sun.management.config.file=" + propFile
|
||||
};
|
||||
pbArgs.addAll(Arrays.asList(args));
|
||||
pbArgs.add(TEST_APP_NAME);
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
|
||||
pbArgs.toArray(new String[pbArgs.size()])
|
||||
);
|
||||
|
||||
try (TestAppRun s = new TestAppRun(pb, DefaultAgentFilterTest.class.getSimpleName())) {
|
||||
s.start();
|
||||
JMXServiceURL url = testConnect(port);
|
||||
testMBeanOperations(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static JMXServiceURL testConnect(int port) throws Exception {
|
||||
EOFException lastException = null;
|
||||
JMXServiceURL url = null;
|
||||
// factor adjusted timeout (5 seconds) for the RMI to become available
|
||||
long timeout = System.currentTimeMillis() + Utils.adjustTimeout(5000);
|
||||
do {
|
||||
lastException = null;
|
||||
try {
|
||||
Registry registry = LocateRegistry.getRegistry(port);
|
||||
String[] relist = registry.list();
|
||||
for (int i = 0; i < relist.length; ++i) {
|
||||
System.out.println("Got registry: " + relist[i]);
|
||||
}
|
||||
String jmxUrlStr = String.format(
|
||||
"service:jmx:rmi:///jndi/rmi://localhost:%d/jmxrmi",
|
||||
port);
|
||||
url = new JMXServiceURL(jmxUrlStr);
|
||||
|
||||
try (JMXConnector c = JMXConnectorFactory.connect(url, null)) {
|
||||
MBeanServerConnection conn = c.getMBeanServerConnection();
|
||||
ObjectName name = new ObjectName("jtreg:type=Test");
|
||||
conn.createMBean(Test.class.getName(), name);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (ex instanceof EOFException) {
|
||||
lastException = (EOFException) ex;
|
||||
System.out.println("Error establishing RMI connection. Retrying in 500ms.");
|
||||
Thread.sleep(500);
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} while (lastException != null && System.currentTimeMillis() < timeout);
|
||||
if (lastException != null) {
|
||||
throw lastException;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("---" + DefaultAgentFilterTest.class.getName() + "-main: starting ...");
|
||||
|
||||
boolean retry = false;
|
||||
do {
|
||||
try {
|
||||
// blacklist String
|
||||
testDefaultAgent("mgmt1.properties");
|
||||
System.out.println("----\tTest FAILED !!");
|
||||
throw new RuntimeException("---" + DefaultAgentFilterTest.class.getName() + " - No exception reported");
|
||||
} catch (Exception ex) {
|
||||
if (ex instanceof InvocationTargetException) {
|
||||
if (ex.getCause() instanceof BindException
|
||||
|| ex.getCause() instanceof java.rmi.ConnectException) {
|
||||
System.out.println("Failed to allocate ports. Retrying ...");
|
||||
retry = true;
|
||||
}
|
||||
} else if (ex instanceof InvalidClassException) {
|
||||
System.out.println("----\tTest PASSED !!");
|
||||
} else if (ex instanceof UnmarshalException
|
||||
&& ((UnmarshalException) ex).getCause() instanceof InvalidClassException) {
|
||||
System.out.println("----\tTest PASSED !!");
|
||||
} else {
|
||||
System.out.println(ex);
|
||||
System.out.println("----\tTest FAILED !!");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} while (retry);
|
||||
retry = false;
|
||||
do {
|
||||
try {
|
||||
// blacklist non-existent class
|
||||
testDefaultAgent("mgmt2.properties");
|
||||
System.out.println("----\tTest PASSED !!");
|
||||
} catch (Exception ex) {
|
||||
if (ex instanceof InvocationTargetException) {
|
||||
if (ex.getCause() instanceof BindException
|
||||
|| ex.getCause() instanceof java.rmi.ConnectException) {
|
||||
System.out.println("Failed to allocate ports. Retrying ...");
|
||||
retry = true;
|
||||
}
|
||||
} else {
|
||||
System.out.println(ex);
|
||||
System.out.println("----\tTest FAILED !!");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} while (retry);
|
||||
|
||||
System.out.println("---" + DefaultAgentFilterTest.class.getName() + "-main: finished ...");
|
||||
}
|
||||
|
||||
private static void testMBeanOperations(JMXServiceURL serverUrl) throws Exception {
|
||||
Map<String, Object> clientEnv = new HashMap<>(1);
|
||||
ObjectName name = new ObjectName("jtreg:type=Test");
|
||||
try (JMXConnector client = JMXConnectorFactory.connect(serverUrl, clientEnv)) {
|
||||
MBeanServerConnection conn = client.getMBeanServerConnection();
|
||||
|
||||
HashSet<String> set = new HashSet<>();
|
||||
set.add("test1");
|
||||
set.add("test2");
|
||||
|
||||
String a = "A";
|
||||
|
||||
Object[] params1 = {set};
|
||||
String[] sig1 = {HashSet.class.getName()};
|
||||
conn.invoke(name, "op1", params1, sig1);
|
||||
|
||||
Object[] params2 = {a, set};
|
||||
String[] sig2 = {String.class.getName(), HashSet.class.getName()};
|
||||
conn.invoke(name, "op2", params2, sig2);
|
||||
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("a", "A");
|
||||
map.put("b", "B");
|
||||
|
||||
Object[] params3 = {new MyTestObject(), a, map};
|
||||
String[] sig3 = {MyTestObject.class.getName(), String.class.getName(),
|
||||
HashMap.class.getName()};
|
||||
conn.invoke(name, "op3", params3, sig3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestApp {
|
||||
|
||||
private static void doSomething() throws IOException {
|
||||
int r = System.in.read();
|
||||
System.out.println("read: " + r);
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
System.out.println("main enter");
|
||||
System.out.flush();
|
||||
doSomething();
|
||||
System.out.println("main exit");
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8159377
|
||||
* @summary Tests ObjectInputFilter on RMIServer.newClient
|
||||
* @author Harsha Wardhana B
|
||||
* @modules java.management
|
||||
* @run clean NewRMIClientFilterTest
|
||||
* @run build NewRMIClientFilterTest
|
||||
* @run main NewRMIClientFilterTest
|
||||
*/
|
||||
import java.io.InvalidClassException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class NewRMIClientFilterTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("---NewRMIClientFilterTest-main: starting ...");
|
||||
String filter1 = java.lang.String.class.getName() + ";!*";
|
||||
String filter2 = java.lang.String.class.getName() + ";" + MyCredentials.class.getName() + ";!*";
|
||||
|
||||
JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
|
||||
JMXServiceURL serverUrl = null;
|
||||
Map<String, Object> env = new HashMap<>(1);
|
||||
JMXConnectorServer server = null;
|
||||
|
||||
System.out.println("\n---NewRMIClientFilterTest-main: testing types = null");
|
||||
server = newServer(url, null);
|
||||
serverUrl = server.getAddress();
|
||||
doTest(serverUrl, null);
|
||||
doTest(serverUrl, new String[]{"toto", "titi"});
|
||||
doTest(serverUrl, new Object[]{new MyCredentials(), "toto"});
|
||||
server.stop();
|
||||
|
||||
System.out.println("\n---NewRMIClientFilterTest-main: testing types = String[]");
|
||||
env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN,
|
||||
filter1);
|
||||
server = newServer(url, env);
|
||||
serverUrl = server.getAddress();
|
||||
doTest(serverUrl, null);
|
||||
doTest(serverUrl, new String[]{"toto", "titi"});
|
||||
try {
|
||||
doTest(serverUrl, new MyCredentials());
|
||||
throw new Error("Bad client is not refused!");
|
||||
} catch (Exception e) {
|
||||
isInvalidClassEx(e);
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
System.out.println("\n---NewRMIClientFilterTest-main: testing user specific types = String, MyCredentials");
|
||||
env.put(RMIConnectorServer.CREDENTIALS_FILTER_PATTERN,
|
||||
filter2);
|
||||
server = newServer(url, env);
|
||||
serverUrl = server.getAddress();
|
||||
doTest(serverUrl, null);
|
||||
doTest(serverUrl, new String[]{"toto", "titi"});
|
||||
doTest(serverUrl, new MyCredentials[]{new MyCredentials(), (MyCredentials) null});
|
||||
try {
|
||||
doTest(serverUrl, new Object[]{"toto", new byte[3]});
|
||||
throw new Error("Bad client is not refused!");
|
||||
} catch (Exception e) {
|
||||
isInvalidClassEx(e);
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
System.out.println("---NewRMIClientFilterTest-main PASSED!!!");
|
||||
}
|
||||
|
||||
private static void doTest(JMXServiceURL serverAddr, Object credentials) throws Exception {
|
||||
System.out.println("---NewRMIClientFilterTest-test:\n\tserver address: "
|
||||
+ serverAddr + "\n\tcredentials: " + credentials);
|
||||
|
||||
Map<String, Object> env = new HashMap<>(1);
|
||||
env.put("jmx.remote.credentials", credentials);
|
||||
JMXConnector client = null;
|
||||
try {
|
||||
client = JMXConnectorFactory.connect(serverAddr, env);
|
||||
client.getMBeanServerConnection().getDefaultDomain();
|
||||
} finally {
|
||||
try {
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
System.out.println("---NewRMIClientFilterTest-test: PASSED!");
|
||||
}
|
||||
|
||||
private static JMXConnectorServer newServer(JMXServiceURL url, Map<String, Object> env)
|
||||
throws Exception {
|
||||
JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(
|
||||
url,
|
||||
env,
|
||||
ManagementFactory.getPlatformMBeanServer());
|
||||
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
private static class MyCredentials implements Serializable {
|
||||
}
|
||||
|
||||
private static void isInvalidClassEx(Exception e) {
|
||||
Throwable cause = e;
|
||||
while (cause != null) {
|
||||
if (cause instanceof InvalidClassException) {
|
||||
System.out.println("---NewRMIClientFilterTest-InvalidClassException expected: " + cause);
|
||||
return;
|
||||
}
|
||||
cause = cause.getCause();
|
||||
}
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Did not get expected InvalidClassException!");
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
# ################ Filter for ObjectInputStream #############################
|
||||
com.sun.management.jmxremote.serial.filter.pattern=!DefaultAgentFilterTest$MyTestObject
|
||||
# A filter, if configured, is used by java.io.ObjectInputStream during
|
||||
# deserialization of parameters sent to the JMX default agent to validate the
|
||||
# contents of the stream.
|
||||
# A filter is configured as a sequence of patterns, each pattern is either
|
||||
# matched against the name of a class in the stream or defines a limit.
|
||||
# Patterns are separated by ";" (semicolon).
|
||||
# Whitespace is significant and is considered part of the pattern.
|
||||
#
|
||||
# If a pattern includes a "=", it sets a limit.
|
||||
# If a limit appears more than once the last value is used.
|
||||
# Limits are checked before classes regardless of the order in the sequence of patterns.
|
||||
# If any of the limits are exceeded, the filter status is REJECTED.
|
||||
#
|
||||
# maxdepth=value - the maximum depth of a graph
|
||||
# maxrefs=value - the maximum number of internal references
|
||||
# maxbytes=value - the maximum number of bytes in the input stream
|
||||
# maxarray=value - the maximum array length allowed
|
||||
#
|
||||
# Other patterns, from left to right, match the class or package name as
|
||||
# returned from Class.getName.
|
||||
# If the class is an array type, the class or package to be matched is the element type.
|
||||
# Arrays of any number of dimensions are treated the same as the element type.
|
||||
# For example, a pattern of "!example.Foo", rejects creation of any instance or
|
||||
# array of example.Foo.
|
||||
#
|
||||
# If the pattern starts with "!", the status is REJECTED if the remaining pattern
|
||||
# is matched; otherwise the status is ALLOWED if the pattern matches.
|
||||
# If the pattern contains "/", the non-empty prefix up to the "/" is the module name;
|
||||
# if the module name matches the module name of the class then
|
||||
# the remaining pattern is matched with the class name.
|
||||
# If there is no "/", the module name is not compared.
|
||||
# If the pattern ends with ".**" it matches any class in the package and all subpackages.
|
||||
# If the pattern ends with ".*" it matches any class in the package.
|
||||
# If the pattern ends with "*", it matches any class with the pattern as a prefix.
|
||||
# If the pattern is equal to the class name, it matches.
|
||||
# Otherwise, the status is UNDECIDED.
|
@ -0,0 +1,38 @@
|
||||
# ################ Filter for ObjectInputStream #############################
|
||||
com.sun.management.jmxremote.serial.filter.pattern=!DefaultAgentFilterTest$ThisTypeIsNotUsed
|
||||
# A filter, if configured, is used by java.io.ObjectInputStream during
|
||||
# deserialization of parameters sent to the JMX default agent to validate the
|
||||
# contents of the stream.
|
||||
# A filter is configured as a sequence of patterns, each pattern is either
|
||||
# matched against the name of a class in the stream or defines a limit.
|
||||
# Patterns are separated by ";" (semicolon).
|
||||
# Whitespace is significant and is considered part of the pattern.
|
||||
#
|
||||
# If a pattern includes a "=", it sets a limit.
|
||||
# If a limit appears more than once the last value is used.
|
||||
# Limits are checked before classes regardless of the order in the sequence of patterns.
|
||||
# If any of the limits are exceeded, the filter status is REJECTED.
|
||||
#
|
||||
# maxdepth=value - the maximum depth of a graph
|
||||
# maxrefs=value - the maximum number of internal references
|
||||
# maxbytes=value - the maximum number of bytes in the input stream
|
||||
# maxarray=value - the maximum array length allowed
|
||||
#
|
||||
# Other patterns, from left to right, match the class or package name as
|
||||
# returned from Class.getName.
|
||||
# If the class is an array type, the class or package to be matched is the element type.
|
||||
# Arrays of any number of dimensions are treated the same as the element type.
|
||||
# For example, a pattern of "!example.Foo", rejects creation of any instance or
|
||||
# array of example.Foo.
|
||||
#
|
||||
# If the pattern starts with "!", the status is REJECTED if the remaining pattern
|
||||
# is matched; otherwise the status is ALLOWED if the pattern matches.
|
||||
# If the pattern contains "/", the non-empty prefix up to the "/" is the module name;
|
||||
# if the module name matches the module name of the class then
|
||||
# the remaining pattern is matched with the class name.
|
||||
# If there is no "/", the module name is not compared.
|
||||
# If the pattern ends with ".**" it matches any class in the package and all subpackages.
|
||||
# If the pattern ends with ".*" it matches any class in the package.
|
||||
# If the pattern ends with "*", it matches any class with the pattern as a prefix.
|
||||
# If the pattern is equal to the class name, it matches.
|
||||
# Otherwise, the status is UNDECIDED.
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 2016, 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
|
||||
@ -47,6 +47,7 @@ import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import com.sun.jmx.remote.internal.rmi.RMIExporter;
|
||||
import java.io.ObjectInputFilter;
|
||||
|
||||
public class RMIExporterTest {
|
||||
|
||||
@ -60,7 +61,8 @@ public class RMIExporterTest {
|
||||
public Remote exportObject(Remote obj,
|
||||
int port,
|
||||
RMIClientSocketFactory csf,
|
||||
RMIServerSocketFactory ssf)
|
||||
RMIServerSocketFactory ssf,
|
||||
ObjectInputFilter unused)
|
||||
throws RemoteException {
|
||||
System.out.println("CustomRMIExporter::exportObject():: " +
|
||||
"Remote = " + obj);
|
||||
|
Loading…
Reference in New Issue
Block a user