8338536: Permanently disable remote code downloading in JNDI
Reviewed-by: dfuchs
This commit is contained in:
parent
7709d435d0
commit
cee74f9e67
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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
|
||||
@ -236,7 +236,7 @@ final class Obj {
|
||||
if (!VersionHelper.isSerialDataAllowed()) {
|
||||
throw new NamingException("Object deserialization is not allowed");
|
||||
}
|
||||
ClassLoader cl = helper.getURLClassLoader(codebases);
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
return deserializeObject((byte[])attr.get(), cl);
|
||||
} else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {
|
||||
// javaRemoteLocation attribute (RMI stub will be created)
|
||||
@ -246,7 +246,7 @@ final class Obj {
|
||||
// For backward compatibility only
|
||||
return decodeRmiObject(
|
||||
(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),
|
||||
(String)attr.get(), codebases);
|
||||
(String)attr.get());
|
||||
}
|
||||
|
||||
attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);
|
||||
@ -368,7 +368,7 @@ final class Obj {
|
||||
* @deprecated For backward compatibility only
|
||||
*/
|
||||
private static Object decodeRmiObject(String className,
|
||||
String rmiName, String[] codebases) throws NamingException {
|
||||
String rmiName) throws NamingException {
|
||||
return new Reference(className, new StringRefAddr("URL", rmiName));
|
||||
}
|
||||
|
||||
@ -410,7 +410,7 @@ final class Obj {
|
||||
int start, sep, posn;
|
||||
Base64.Decoder decoder = null;
|
||||
|
||||
ClassLoader cl = helper.getURLClassLoader(codebases);
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
/*
|
||||
* Temporary array for decoded RefAddr addresses - used to ensure
|
||||
|
@ -25,22 +25,10 @@
|
||||
|
||||
package com.sun.jndi.ldap;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
public final class VersionHelper {
|
||||
|
||||
private static final VersionHelper helper = new VersionHelper();
|
||||
|
||||
/**
|
||||
* Determines whether classes may be loaded from an arbitrary URL code base.
|
||||
*/
|
||||
private static final boolean trustURLCodebase;
|
||||
|
||||
/**
|
||||
* Determines whether objects may be deserialized or reconstructed from a content of
|
||||
* 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes.
|
||||
@ -48,29 +36,13 @@ public final class VersionHelper {
|
||||
private static final boolean trustSerialData;
|
||||
|
||||
static {
|
||||
// System property to control whether classes may be loaded from an
|
||||
// arbitrary URL code base
|
||||
String trust = getPrivilegedProperty(
|
||||
"com.sun.jndi.ldap.object.trustURLCodebase", "false");
|
||||
trustURLCodebase = "true".equalsIgnoreCase(trust);
|
||||
|
||||
// System property to control whether classes are allowed to be loaded from
|
||||
// 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes.
|
||||
String trustSerialDataSp = getPrivilegedProperty(
|
||||
String trustSerialDataSp = System.getProperty(
|
||||
"com.sun.jndi.ldap.object.trustSerialData", "false");
|
||||
trustSerialData = "true".equalsIgnoreCase(trustSerialDataSp);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static String getPrivilegedProperty(String propertyName, String defaultVal) {
|
||||
PrivilegedAction<String> action = () -> System.getProperty(propertyName, defaultVal);
|
||||
if (System.getSecurityManager() == null) {
|
||||
return action.run();
|
||||
} else {
|
||||
return AccessController.doPrivileged(action);
|
||||
}
|
||||
}
|
||||
|
||||
private VersionHelper() {
|
||||
}
|
||||
|
||||
@ -89,41 +61,12 @@ public final class VersionHelper {
|
||||
return trustSerialData;
|
||||
}
|
||||
|
||||
ClassLoader getURLClassLoader(String[] url) throws MalformedURLException {
|
||||
ClassLoader parent = getContextClassLoader();
|
||||
/*
|
||||
* Classes may only be loaded from an arbitrary URL code base when
|
||||
* the system property com.sun.jndi.ldap.object.trustURLCodebase
|
||||
* has been set to "true".
|
||||
*/
|
||||
if (url != null && trustURLCodebase) {
|
||||
return URLClassLoader.newInstance(getUrlArray(url), parent);
|
||||
} else {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> loadClass(String className) throws ClassNotFoundException {
|
||||
return Class.forName(className, true, getContextClassLoader());
|
||||
return Class.forName(className, true,
|
||||
Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
Thread createThread(Runnable r) {
|
||||
return new Thread(r);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private ClassLoader getContextClassLoader() {
|
||||
PrivilegedAction<ClassLoader> act =
|
||||
Thread.currentThread()::getContextClassLoader;
|
||||
return AccessController.doPrivileged(act);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static URL[] getUrlArray(String[] url) throws MalformedURLException {
|
||||
URL[] urlArray = new URL[url.length];
|
||||
for (int i = 0; i < urlArray.length; i++) {
|
||||
urlArray[i] = new URL(url[i]);
|
||||
}
|
||||
return urlArray;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2024, 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
|
||||
@ -181,9 +181,8 @@ public class NamingManagerHelper {
|
||||
static ObjectFactory getObjectFactoryFromReference(
|
||||
Reference ref, String factoryName, Predicate<Class<?>> filter)
|
||||
throws IllegalAccessException,
|
||||
InstantiationException,
|
||||
MalformedURLException {
|
||||
Class<?> clas = null;
|
||||
InstantiationException {
|
||||
Class<?> clas;
|
||||
|
||||
// Try to use current class loader
|
||||
try {
|
||||
@ -193,27 +192,11 @@ public class NamingManagerHelper {
|
||||
return null;
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore and continue
|
||||
// e.printStackTrace();
|
||||
}
|
||||
// All other exceptions are passed up.
|
||||
|
||||
// Not in class path; try to use codebase
|
||||
String codebase;
|
||||
if (clas == null &&
|
||||
(codebase = ref.getFactoryClassLocation()) != null) {
|
||||
try {
|
||||
clas = helper.loadClass(factoryName, codebase);
|
||||
// Validate factory's class with the objects factory serial filter
|
||||
if (clas == null || !filter.test(clas)) {
|
||||
return null;
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
}
|
||||
|
||||
assert clas != null;
|
||||
@SuppressWarnings("deprecation") // Class.newInstance
|
||||
ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null;
|
||||
ObjectFactory result = (ObjectFactory) clas.newInstance();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -401,12 +384,6 @@ public class NamingManagerHelper {
|
||||
ObjectFactoryBuilder builder) throws NamingException {
|
||||
if (object_factory_builder != null)
|
||||
throw new IllegalStateException("ObjectFactoryBuilder already set");
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security != null) {
|
||||
security.checkSetFactory();
|
||||
}
|
||||
object_factory_builder = builder;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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
|
||||
@ -28,15 +28,9 @@ package com.sun.naming.internal;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@ -53,21 +47,6 @@ import java.util.*;
|
||||
public final class VersionHelper {
|
||||
private static final VersionHelper helper = new VersionHelper();
|
||||
|
||||
/**
|
||||
* Determines whether classes may be loaded from an arbitrary URL code base.
|
||||
*/
|
||||
private static final boolean TRUST_URL_CODE_BASE;
|
||||
|
||||
static {
|
||||
// System property to control whether classes may be loaded from an
|
||||
// arbitrary URL code base
|
||||
PrivilegedAction<String> act
|
||||
= () -> System.getProperty("com.sun.jndi.ldap.object.trustURLCodebase", "false");
|
||||
@SuppressWarnings("removal")
|
||||
String trust = AccessController.doPrivileged(act);
|
||||
TRUST_URL_CODE_BASE = "true".equalsIgnoreCase(trust);
|
||||
}
|
||||
|
||||
static final String[] PROPS = new String[]{
|
||||
javax.naming.Context.INITIAL_CONTEXT_FACTORY,
|
||||
javax.naming.Context.OBJECT_FACTORIES,
|
||||
@ -101,22 +80,6 @@ public final class VersionHelper {
|
||||
return loadClass(className, false, getContextClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param className A non-null fully qualified class name.
|
||||
* @param codebase A non-null, space-separated list of URL strings.
|
||||
*/
|
||||
public Class<?> loadClass(String className, String codebase)
|
||||
throws ClassNotFoundException, MalformedURLException {
|
||||
if (TRUST_URL_CODE_BASE) {
|
||||
ClassLoader parent = getContextClassLoader();
|
||||
ClassLoader cl
|
||||
= URLClassLoader.newInstance(getUrlArray(codebase), parent);
|
||||
return loadClass(className, cl);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private.
|
||||
* <p>
|
||||
@ -136,37 +99,19 @@ public final class VersionHelper {
|
||||
|
||||
/*
|
||||
* Returns a JNDI property from the system properties. Returns
|
||||
* null if the property is not set, or if there is no permission
|
||||
* to read it.
|
||||
* null if the property is not set.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
String getJndiProperty(int i) {
|
||||
PrivilegedAction<String> act = () -> {
|
||||
try {
|
||||
return System.getProperty(PROPS[i]);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return AccessController.doPrivileged(act);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads each property in PROPS from the system properties, and
|
||||
* returns their values -- in order -- in an array. For each
|
||||
* unset property, the corresponding array element is set to null.
|
||||
* Returns null if there is no permission to call System.getProperties().
|
||||
*/
|
||||
String[] getJndiProperties() {
|
||||
PrivilegedAction<Properties> act = () -> {
|
||||
try {
|
||||
return System.getProperties();
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@SuppressWarnings("removal")
|
||||
Properties sysProps = AccessController.doPrivileged(act);
|
||||
Properties sysProps = System.getProperties();
|
||||
if (sysProps == null) {
|
||||
return null;
|
||||
}
|
||||
@ -199,16 +144,12 @@ public final class VersionHelper {
|
||||
* Returns the resource of a given name associated with a particular
|
||||
* class (never null), or null if none can be found.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
InputStream getResourceAsStream(Class<?> c, String name) {
|
||||
PrivilegedAction<InputStream> act = () -> {
|
||||
try {
|
||||
return c.getModule().getResourceAsStream(resolveName(c, name));
|
||||
} catch (IOException x) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return AccessController.doPrivileged(act);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -217,9 +158,7 @@ public final class VersionHelper {
|
||||
*
|
||||
* @param filename The file name, sans directory.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
InputStream getJavaHomeConfStream(String filename) {
|
||||
PrivilegedAction<InputStream> act = () -> {
|
||||
try {
|
||||
String javahome = System.getProperty("java.home");
|
||||
if (javahome == null) {
|
||||
@ -229,8 +168,6 @@ public final class VersionHelper {
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return AccessController.doPrivileged(act);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -239,19 +176,12 @@ public final class VersionHelper {
|
||||
* loader. Null represents the bootstrap class loader in some
|
||||
* Java implementations.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
NamingEnumeration<InputStream> getResources(ClassLoader cl,
|
||||
String name) throws IOException {
|
||||
Enumeration<URL> urls;
|
||||
PrivilegedExceptionAction<Enumeration<URL>> act = () ->
|
||||
(cl == null)
|
||||
urls = (cl == null)
|
||||
? ClassLoader.getSystemResources(name)
|
||||
: cl.getResources(name);
|
||||
try {
|
||||
urls = AccessController.doPrivileged(act);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw (IOException) e.getException();
|
||||
}
|
||||
return new InputStreamEnumeration(urls);
|
||||
}
|
||||
|
||||
@ -265,39 +195,18 @@ public final class VersionHelper {
|
||||
* Please don't expose this method as public.
|
||||
* @throws SecurityException if the class loader is not accessible
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
ClassLoader getContextClassLoader() {
|
||||
|
||||
PrivilegedAction<ClassLoader> act = () -> {
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
if (loader == null) {
|
||||
// Don't use bootstrap class loader directly!
|
||||
loader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
return loader;
|
||||
};
|
||||
return AccessController.doPrivileged(act);
|
||||
}
|
||||
|
||||
private static URL[] getUrlArray(String codebase)
|
||||
throws MalformedURLException {
|
||||
// Parse codebase into separate URLs
|
||||
StringTokenizer parser = new StringTokenizer(codebase);
|
||||
List<URL> list = new ArrayList<>();
|
||||
while (parser.hasMoreTokens()) {
|
||||
@SuppressWarnings("deprecation")
|
||||
var u = new URL(parser.nextToken());
|
||||
list.add(u);
|
||||
}
|
||||
return list.toArray(new URL[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an enumeration of URLs, an instance of this class represents
|
||||
* an enumeration of their InputStreams. Each operation on the URL
|
||||
* enumeration is performed within a doPrivileged block.
|
||||
* This is used to enumerate the resources under a foreign codebase.
|
||||
* This class is not MT-safe.
|
||||
* an enumeration of their InputStreams.
|
||||
*/
|
||||
private class InputStreamEnumeration implements
|
||||
NamingEnumeration<InputStream> {
|
||||
@ -314,9 +223,7 @@ public final class VersionHelper {
|
||||
* Returns the next InputStream, or null if there are no more.
|
||||
* An InputStream that cannot be opened is skipped.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
private InputStream getNextElement() {
|
||||
PrivilegedAction<InputStream> act = () -> {
|
||||
while (urls.hasMoreElements()) {
|
||||
try {
|
||||
return urls.nextElement().openStream();
|
||||
@ -325,8 +232,6 @@ public final class VersionHelper {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return AccessController.doPrivileged(act);
|
||||
}
|
||||
|
||||
public boolean hasMore() {
|
||||
|
@ -123,9 +123,12 @@ public class NamingManager {
|
||||
* or {@code Referenceable} containing a factory class name,
|
||||
* use the named factory to create the object.
|
||||
* Return {@code refInfo} if the factory cannot be created.
|
||||
* Under JDK 1.1, if the factory class must be loaded from a location
|
||||
* specified in the reference, a {@code SecurityManager} must have
|
||||
* been installed or the factory creation will fail.
|
||||
* Downloading a factory class from a location specified in the reference
|
||||
* can be supported by a custom implementation of {@link ObjectFactoryBuilder}.
|
||||
* The {@linkplain Reference#getFactoryClassLocation() factory class
|
||||
* location}, if present, is ignored. A custom {@link ObjectFactoryBuilder}
|
||||
* {@linkplain #setObjectFactoryBuilder(ObjectFactoryBuilder) may be used}
|
||||
* if a different policy is desired.
|
||||
* If an exception is encountered while creating the factory,
|
||||
* it is passed up to the caller.
|
||||
* <li>If {@code refInfo} is a {@code Reference} or
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2024, 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
|
||||
@ -35,8 +35,6 @@ import java.rmi.*;
|
||||
import java.rmi.server.*;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.spi.NamingManager;
|
||||
@ -57,19 +55,6 @@ public class RegistryContext implements Context, Referenceable {
|
||||
private int port;
|
||||
private static final NameParser nameParser = new AtomicNameParser();
|
||||
private static final String SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket";
|
||||
/**
|
||||
* Determines whether classes may be loaded from an arbitrary URL code base.
|
||||
*/
|
||||
static final boolean trustURLCodebase;
|
||||
static {
|
||||
// System property to control whether classes may be loaded from an
|
||||
// arbitrary URL codebase
|
||||
PrivilegedAction<String> act = () -> System.getProperty(
|
||||
"com.sun.jndi.rmi.object.trustURLCodebase", "false");
|
||||
@SuppressWarnings("removal")
|
||||
String trust = AccessController.doPrivileged(act);
|
||||
trustURLCodebase = "true".equalsIgnoreCase(trust);
|
||||
}
|
||||
|
||||
Reference reference = null; // ref used to create this context, if any
|
||||
|
||||
@ -481,12 +466,6 @@ public class RegistryContext implements Context, Referenceable {
|
||||
? ((RemoteReference)r).getReference()
|
||||
: (Object)r;
|
||||
|
||||
/*
|
||||
* Classes may only be loaded from an arbitrary URL codebase when
|
||||
* the system property com.sun.jndi.rmi.object.trustURLCodebase
|
||||
* has been set to "true".
|
||||
*/
|
||||
|
||||
// Use reference if possible
|
||||
Reference ref = null;
|
||||
if (obj instanceof Reference) {
|
||||
@ -495,11 +474,14 @@ public class RegistryContext implements Context, Referenceable {
|
||||
ref = ((Referenceable)(obj)).getReference();
|
||||
}
|
||||
|
||||
if (ref != null && ref.getFactoryClassLocation() != null &&
|
||||
!trustURLCodebase) {
|
||||
/*
|
||||
* Downloading a factory class from a location specified in the reference
|
||||
* can be supported by a custom implementation of "ObjectFactoryBuilder".
|
||||
*/
|
||||
if (NamingManagerHelper.getObjectFactoryBuilder() == null
|
||||
&& ref != null && ref.getFactoryClassLocation() != null) {
|
||||
throw new ConfigurationException(
|
||||
"The object factory is untrusted. Set the system property" +
|
||||
" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
|
||||
"Remote object factories are not supported");
|
||||
}
|
||||
return NamingManagerHelper.getObjectInstance(obj, name, this,
|
||||
environment, ObjectFactoriesFilter::checkRmiFilter);
|
||||
|
@ -58,6 +58,11 @@
|
||||
* implementation.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p> Downloading a factory class from a {@linkplain javax.naming.Reference#getFactoryClassLocation()
|
||||
* location} specified in the reference can be supported by a custom implementation of {@link
|
||||
* javax.naming.spi.ObjectFactoryBuilder}. If a location is specified, then
|
||||
* unless an {@link javax.naming.spi.ObjectFactoryBuilder} is installed a
|
||||
* {@link javax.naming.ConfigurationException} is thrown.
|
||||
* @provides javax.naming.spi.InitialContextFactory
|
||||
* @moduleGraph
|
||||
* @since 9
|
||||
|
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 com.sun.net.httpserver.HttpServer;
|
||||
import com.sun.net.httpserver.SimpleFileServer;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.spi.NamingManager;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.RMISocketFactory;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8338536
|
||||
* @summary Check if an object factory builder can be used to reconstruct
|
||||
* object factories from a code base specified in a
|
||||
* @modules java.rmi/sun.rmi.registry
|
||||
* java.rmi/sun.rmi.server
|
||||
* java.rmi/sun.rmi.transport
|
||||
* java.rmi/sun.rmi.transport.tcp
|
||||
* @library /test/lib ../../../../../../java/rmi/testlibrary
|
||||
* @build TestLibrary
|
||||
* @compile TestFactory.java TestObjectFactoryBuilder.java
|
||||
*
|
||||
* @run main/othervm ObjectFactoryBuilderCodebaseTest setObjectFactoryBuilder
|
||||
* @run main/othervm ObjectFactoryBuilderCodebaseTest default
|
||||
*/
|
||||
public class ObjectFactoryBuilderCodebaseTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
setupRmiHostNameAndRmiSocketFactory();
|
||||
boolean useCustomObjectFactoryBuilder =
|
||||
"setObjectFactoryBuilder".equals(args[0]);
|
||||
|
||||
if (args.length > 0 && useCustomObjectFactoryBuilder) {
|
||||
NamingManager.setObjectFactoryBuilder(new TestObjectFactoryBuilder());
|
||||
}
|
||||
FileServer fileServer = configureAndLaunchFileServer();
|
||||
int registryPort;
|
||||
try {
|
||||
Registry registry = TestLibrary.createRegistryOnEphemeralPort();
|
||||
registryPort = TestLibrary.getRegistryPort(registry);
|
||||
System.out.println("Registry port: " + registryPort);
|
||||
} catch (RemoteException re) {
|
||||
throw new RuntimeException("Failed to create registry", re);
|
||||
}
|
||||
|
||||
Context context = getInitialContext(registryPort);
|
||||
// Bind the Reference object
|
||||
String factoryURL = fileServer.factoryLocation();
|
||||
System.err.println("Setting Reference factory location: " + factoryURL);
|
||||
Reference ref = new Reference("TestObject", "com.test.TestFactory",
|
||||
factoryURL);
|
||||
context.bind("objectTest", ref);
|
||||
|
||||
// Try to load bound reference
|
||||
try {
|
||||
Object object = context.lookup("objectTest");
|
||||
if (!useCustomObjectFactoryBuilder) {
|
||||
throw new RuntimeException("Lookup not expected to complete");
|
||||
}
|
||||
System.err.println("Loaded object: " + object);
|
||||
} catch (NamingException ne) {
|
||||
if (useCustomObjectFactoryBuilder) {
|
||||
throw new RuntimeException("Lookup expected to complete successfully", ne);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Context getInitialContext(int port) throws NamingException {
|
||||
Hashtable<String, String> env = new Hashtable<>();
|
||||
|
||||
// Prepare registry URL
|
||||
String providerURL = URIBuilder.newBuilder()
|
||||
.loopback()
|
||||
.port(port)
|
||||
.scheme("rmi")
|
||||
.buildUnchecked().toString();
|
||||
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY,
|
||||
"com.sun.jndi.rmi.registry.RegistryContextFactory");
|
||||
env.put(Context.PROVIDER_URL, providerURL);
|
||||
return new InitialContext(env);
|
||||
}
|
||||
|
||||
private record FileServer(Path rootPath, InetSocketAddress address, HttpServer httpServer) {
|
||||
|
||||
public static FileServer newInstance(Path rootPath, InetSocketAddress address) {
|
||||
Objects.requireNonNull(address);
|
||||
Objects.requireNonNull(rootPath);
|
||||
var httpServer = SimpleFileServer.createFileServer(address, rootPath,
|
||||
SimpleFileServer.OutputLevel.VERBOSE);
|
||||
return new FileServer(rootPath, address, httpServer);
|
||||
}
|
||||
|
||||
String factoryLocation() {
|
||||
return URIBuilder.newBuilder()
|
||||
.loopback()
|
||||
.port(port())
|
||||
.scheme("http")
|
||||
.path("/")
|
||||
.buildUnchecked()
|
||||
.toString();
|
||||
}
|
||||
|
||||
int port() {
|
||||
return httpServer.getAddress().getPort();
|
||||
}
|
||||
|
||||
void start() {
|
||||
httpServer.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Prepares and launches the file server capable of serving TestFactory.class
|
||||
private static FileServer configureAndLaunchFileServer() throws IOException {
|
||||
|
||||
// Location of compiled classes with compiled MyFactory
|
||||
Path factoryClassPath = Path.of(System.getProperty("test.classes", "."))
|
||||
.resolve(OBJ_FACTORY_PACKAGE_PATH)
|
||||
.resolve(OBJ_FACTORY_CLASS_NAME);
|
||||
|
||||
// File server content root directory
|
||||
Path serverRoot = Paths.get("serverRoot").toAbsolutePath();
|
||||
Path packageDirInServerRoot = serverRoot.resolve(OBJ_FACTORY_PACKAGE_PATH);
|
||||
Path factoryClassFileInServerRoot = packageDirInServerRoot.resolve(OBJ_FACTORY_CLASS_NAME);
|
||||
|
||||
// Remove files from previous run
|
||||
Files.deleteIfExists(factoryClassFileInServerRoot);
|
||||
Files.deleteIfExists(packageDirInServerRoot);
|
||||
Files.deleteIfExists(packageDirInServerRoot.getParent());
|
||||
Files.deleteIfExists(serverRoot);
|
||||
|
||||
// Create server root and copy compiled object factory inside
|
||||
Files.createDirectories(packageDirInServerRoot);
|
||||
Files.copy(factoryClassPath, factoryClassFileInServerRoot);
|
||||
|
||||
// Bind file server to loopback address
|
||||
InetSocketAddress serverAddress =
|
||||
new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
FileServer fileServer = FileServer.newInstance(serverRoot, serverAddress);
|
||||
|
||||
// Start the file server
|
||||
fileServer.start();
|
||||
System.err.println("File server content root: " + serverRoot);
|
||||
System.err.printf("File server running on %s:%d%n",
|
||||
serverAddress.getAddress(), fileServer.port());
|
||||
return fileServer;
|
||||
}
|
||||
|
||||
|
||||
// Configure RMI to launch registry on a loopback address
|
||||
private static void setupRmiHostNameAndRmiSocketFactory() throws IOException {
|
||||
String rmiServerHostAddressString = InetAddress.getLoopbackAddress().getHostAddress();
|
||||
System.out.println("Setting 'java.rmi.server.hostname' to: " + rmiServerHostAddressString);
|
||||
System.setProperty("java.rmi.server.hostname", rmiServerHostAddressString);
|
||||
RMISocketFactory.setSocketFactory(new TestRmiSocketFactory());
|
||||
}
|
||||
|
||||
private static class TestRmiSocketFactory extends RMISocketFactory {
|
||||
public ServerSocket createServerSocket(int port) throws IOException {
|
||||
var loopbackAddress = InetAddress.getLoopbackAddress();
|
||||
System.out.printf("Creating RMI server socket on %s:%d%n", loopbackAddress, port);
|
||||
ServerSocket rmiServerSocket = new ServerSocket();
|
||||
rmiServerSocket.setOption(java.net.StandardSocketOptions.SO_REUSEADDR, false);
|
||||
SocketAddress serverAddress = new InetSocketAddress(loopbackAddress, port);
|
||||
rmiServerSocket.bind(serverAddress, BACKLOG_OF_5);
|
||||
return rmiServerSocket;
|
||||
}
|
||||
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
System.out.printf("Creating RMI client socket connected to %s:%d%n", host, port);
|
||||
// just call the default client socket factory
|
||||
return RMISocketFactory.getDefaultSocketFactory()
|
||||
.createSocket(host, port);
|
||||
}
|
||||
}
|
||||
|
||||
// File server backlog value
|
||||
private static final int BACKLOG_OF_5 = 5;
|
||||
// Test objects factory class filename
|
||||
private static final String OBJ_FACTORY_CLASS_NAME = "TestFactory.class";
|
||||
// Package directory of the test's objects factory class
|
||||
private static final Path OBJ_FACTORY_PACKAGE_PATH = Paths.get("com").resolve("test");
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 javax.naming.ConfigurationException;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.Reference;
|
||||
import javax.naming.spi.ObjectFactory;
|
||||
import javax.naming.spi.ObjectFactoryBuilder;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Test library class that implements {@code javax.naming.spi.ObjectFactoryBuilder} interface.
|
||||
* Its implementation allows object factory class loading from any remote location.
|
||||
*/
|
||||
public class TestObjectFactoryBuilder implements ObjectFactoryBuilder {
|
||||
|
||||
@Override
|
||||
public ObjectFactory createObjectFactory(Object obj, Hashtable<?, ?> environment) throws NamingException {
|
||||
System.err.println("TestObjectFactoryBuilder: Creating new object factory");
|
||||
System.err.println("Builder for object: " + obj);
|
||||
System.err.println("And for environment: " + environment);
|
||||
// Only objects of the Reference type are supported, others are rejected
|
||||
if (obj instanceof Reference ref) {
|
||||
String objectFactoryLocation = ref.getFactoryClassLocation();
|
||||
try {
|
||||
URL factoryURL = new URL(objectFactoryLocation);
|
||||
var cl = new URLClassLoader(new URL[]{factoryURL});
|
||||
Class<?> factoryClass = cl.loadClass(ref.getFactoryClassName());
|
||||
System.err.println("Loaded object factory: " + factoryClass);
|
||||
if (ObjectFactory.class.isAssignableFrom(factoryClass)) {
|
||||
return (ObjectFactory) factoryClass
|
||||
.getDeclaredConstructor().newInstance();
|
||||
} else {
|
||||
throw new ConfigurationException("Test configuration error -" +
|
||||
" loaded object factory of wrong type");
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
throw new ConfigurationException("Error constructing test object factory");
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
|
||||
IllegalAccessException | InvocationTargetException e) {
|
||||
throw new ConfigurationException("Test configuration error: " +
|
||||
"factory class cannot be loaded from the provided " +
|
||||
"object factory location");
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("Test factory builder " +
|
||||
"supports only Reference types");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user