8290368: Introduce LDAP and RMI protocol-specific object factory filters to JNDI implementation

Reviewed-by: dfuchs, rriggs, jpai
This commit is contained in:
Aleksei Efimov 2022-10-19 14:41:28 +00:00
parent 21aeb9e794
commit d37ce4cdd1
22 changed files with 1263 additions and 355 deletions

View File

@ -1376,17 +1376,18 @@ jdk.io.permissionsUseCanonicalPath=false
jdk.tls.alpnCharset=ISO_8859_1
#
# JNDI Object Factories Filter
# Global JNDI Object Factories Filter
#
# This filter is used by the JNDI runtime to control the set of object factory classes
# which will be allowed to instantiate objects from object references returned by
# naming/directory systems. The factory class named by the reference instance will be
# matched against this filter. The filter property supports pattern-based filter syntax
# with the same format as jdk.serialFilter.
# with the same format as jdk.serialFilter. Limit patterns specified in the filter property
# are unused.
#
# Each pattern is matched against the factory class name to allow or disallow it's
# instantiation. The access to a factory class is allowed unless the filter returns
# REJECTED.
# Each class name pattern is matched against the factory class name to allow or disallow its
# instantiation. The access to a factory class is allowed if the filter returns
# ALLOWED.
#
# Note: This property is currently used by the JDK Reference implementation.
# It is not guaranteed to be examined and used by other implementations.
@ -1398,6 +1399,58 @@ jdk.tls.alpnCharset=ISO_8859_1
# instance to recreate the referenced object.
#jdk.jndi.object.factoriesFilter=*
#
# Protocol Specific JNDI/LDAP Object Factories Filter
#
# This filter is used by the JNDI/LDAP provider implementation in the JDK to further control the
# set of object factory classes which will be allowed to instantiate objects from object
# references bound to LDAP contexts. The factory class named by the reference instance will
# be matched against this filter. The filter property supports pattern-based filter syntax
# with the same format as jdk.serialFilter. Limit patterns specified in the filter property
# are unused.
#
# Each class name pattern is matched against the factory class name to allow or disallow its
# instantiation. The access to a factory class is allowed only when it is not rejected by this filter
# or by the global filter defined by "jdk.jndi.object.factoriesFilter", and at least one of these
# two filters returns ALLOWED.
#
# Note: This property is currently used by the JDK Reference implementation.
# It is not guaranteed to be examined and used by other implementations.
#
# If the system property jdk.jndi.ldap.object.factoriesFilter is also specified, it supersedes
# the security property value defined here. The default value of the property is
# "java.naming/com.sun.jndi.ldap.**;!*".
#
# The default pattern value allows any object factory class defined in the java.naming module
# to be specified by the reference instance, but rejects any other.
#jdk.jndi.ldap.object.factoriesFilter=java.naming/com.sun.jndi.ldap.**;!*
#
# Protocol Specific JNDI/RMI Object Factories Filter
#
# This filter is used by the JNDI/RMI provider implementation in the JDK to further control the
# set of object factory classes which will be allowed to instantiate objects from object
# references bound to RMI names. The factory class named by the reference instance will
# be matched against this filter. The filter property supports pattern-based filter syntax
# with the same format as jdk.serialFilter. Limit patterns specified in the filter property
# are unused.
#
# Each class name pattern is matched against the factory class name to allow or disallow its
# instantiation. The access to a factory class is allowed only when it is not rejected by this filter
# or by the global filter defined by "jdk.jndi.object.factoriesFilter", and at least one of these
# two filters returns ALLOWED.
#
# Note: This property is currently used by the JDK Reference implementation.
# It is not guaranteed to be examined and used by other implementations.
#
# If the system property jdk.jndi.rmi.object.factoriesFilter is also specified, it supersedes
# the security property value defined here. The default value of the property is
# "jdk.naming.rmi/com.sun.jndi.rmi.**;!*".
#
# The default pattern value allows any object factory class defined in the jdk.naming.rmi module
# to be specified by the reference instance, but rejects any other.
#jdk.jndi.rmi.object.factoriesFilter=jdk.naming.rmi/com.sun.jndi.rmi.**;!*
#
# Policy for non-forwardable service ticket in a S4U2proxy request
#

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2022, 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
@ -33,9 +33,10 @@ import java.util.Vector;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.Control;
import javax.naming.spi.*;
import com.sun.jndi.toolkit.ctx.Continuation;
import com.sun.naming.internal.NamingManagerHelper;
import com.sun.naming.internal.ObjectFactoriesFilter;
final class LdapBindingEnumeration
extends AbstractLdapNamingEnumeration<Binding> {
@ -76,8 +77,8 @@ final class LdapBindingEnumeration
cn.add(atom);
try {
obj = DirectoryManager.getObjectInstance(obj, cn, homeCtx,
homeCtx.envprops, attrs);
obj = NamingManagerHelper.getDirObjectInstance(obj, cn, homeCtx,
homeCtx.envprops, attrs, ObjectFactoriesFilter::checkLdapFilter);
} catch (NamingException e) {
throw e;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2022, 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
@ -27,7 +27,6 @@ package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;
import javax.naming.event.*;
import javax.naming.ldap.*;
import javax.naming.ldap.LdapName;
@ -54,6 +53,8 @@ import com.sun.jndi.toolkit.ctx.*;
import com.sun.jndi.toolkit.dir.HierMemDirCtx;
import com.sun.jndi.toolkit.dir.SearchFilter;
import com.sun.jndi.ldap.ext.StartTlsResponseImpl;
import com.sun.naming.internal.NamingManagerHelper;
import com.sun.naming.internal.ObjectFactoriesFilter;
/**
* The LDAP context implementation.
@ -1111,8 +1112,8 @@ public final class LdapCtx extends ComponentDirContext
}
try {
return DirectoryManager.getObjectInstance(obj, name,
this, envprops, attrs);
return NamingManagerHelper.getDirObjectInstance(obj, name, this,
envprops, attrs, ObjectFactoriesFilter::checkLdapFilter);
} catch (NamingException e) {
throw cont.fillInException(e);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2022, 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
@ -27,12 +27,13 @@ package com.sun.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;
import javax.naming.ldap.*;
import java.util.Hashtable;
import java.util.StringTokenizer;
import com.sun.jndi.toolkit.dir.SearchFilter;
import com.sun.naming.internal.NamingManagerHelper;
import com.sun.naming.internal.ObjectFactoriesFilter;
/**
* A context for handling referrals.
@ -116,8 +117,8 @@ final class LdapReferralContext implements DirContext, LdapContext {
Object obj;
try {
obj = NamingManager.getObjectInstance(ref, null, null, env);
obj = NamingManagerHelper.getObjectInstance(ref, null, null,
env, ObjectFactoriesFilter::checkLdapFilter);
} catch (NamingException e) {
if (handleReferrals == LdapClient.LDAP_REF_THROW) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2022, 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,11 +32,12 @@ import java.security.PrivilegedExceptionAction;
import java.util.Vector;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;
import javax.naming.ldap.*;
import javax.naming.ldap.LdapName;
import com.sun.jndi.toolkit.ctx.Continuation;
import com.sun.naming.internal.NamingManagerHelper;
import com.sun.naming.internal.ObjectFactoriesFilter;
final class LdapSearchEnumeration
extends AbstractLdapNamingEnumeration<SearchResult> {
@ -134,9 +135,9 @@ final class LdapSearchEnumeration
// Call getObjectInstance before removing unrequested attributes
try {
// rcn is either relative to homeCtx or a fully qualified DN
obj = DirectoryManager.getObjectInstance(
obj = NamingManagerHelper.getDirObjectInstance(
obj, rcn, (relative ? homeCtx : null),
homeCtx.envprops, attrs);
homeCtx.envprops, attrs, ObjectFactoriesFilter::checkLdapFilter);
} catch (NamingException e) {
throw e;
} catch (Exception e) {

View File

@ -0,0 +1,422 @@
/*
* Copyright (c) 2022, 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 com.sun.naming.internal;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.naming.directory.Attributes;
import javax.naming.spi.DirObjectFactory;
import javax.naming.spi.ObjectFactory;
import javax.naming.spi.ObjectFactoryBuilder;
import java.net.MalformedURLException;
import java.util.Hashtable;
import java.util.function.Predicate;
public class NamingManagerHelper {
public static Object getObjectInstance(Object refInfo, Name name, Context nameCtx,
Hashtable<?,?> environment,
Predicate<Class<?>> factoryFilter) throws Exception {
ObjectFactory factory;
// Use builder if installed
ObjectFactoryBuilder builder = getObjectFactoryBuilder();
if (builder != null) {
// builder must return non-null factory
factory = builder.createObjectFactory(refInfo, environment);
return factory.getObjectInstance(refInfo, name, nameCtx,
environment);
}
// Use reference if possible
Reference ref = null;
if (refInfo instanceof Reference) {
ref = (Reference) refInfo;
} else if (refInfo instanceof Referenceable) {
ref = ((Referenceable)(refInfo)).getReference();
}
Object answer;
if (ref != null) {
String f = ref.getFactoryClassName();
if (f != null) {
// if reference identifies a factory, use exclusively
factory = getObjectFactoryFromReference(ref, f, factoryFilter);
if (factory != null) {
return factory.getObjectInstance(ref, name, nameCtx,
environment);
}
// No factory found, so return original refInfo.
// That could happen if:
// - a factory class is not in a class path and reference does
// not contain a URL for it
// - a factory class is available but object factory filters
// disallow its usage
return refInfo;
} else {
// if reference has no factory, check for addresses
// containing URLs
answer = processURLAddrs(ref, name, nameCtx, environment);
if (answer != null) {
return answer;
}
}
}
// try using any specified factories
answer =
createObjectFromFactories(refInfo, name, nameCtx, environment);
return (answer != null) ? answer : refInfo;
}
public static Object getDirObjectInstance(Object refInfo, Name name, Context nameCtx,
Hashtable<?,?> environment, Attributes attrs,
Predicate<Class<?>> factoryFilter) throws Exception {
ObjectFactory factory;
ObjectFactoryBuilder builder = getObjectFactoryBuilder();
if (builder != null) {
// builder must return non-null factory
factory = builder.createObjectFactory(refInfo, environment);
if (factory instanceof DirObjectFactory) {
return ((DirObjectFactory)factory).getObjectInstance(
refInfo, name, nameCtx, environment, attrs);
} else {
return factory.getObjectInstance(refInfo, name, nameCtx,
environment);
}
}
// use reference if possible
Reference ref = null;
if (refInfo instanceof Reference) {
ref = (Reference) refInfo;
} else if (refInfo instanceof Referenceable) {
ref = ((Referenceable)(refInfo)).getReference();
}
Object answer;
if (ref != null) {
String f = ref.getFactoryClassName();
if (f != null) {
// if reference identifies a factory, use exclusively
factory = getObjectFactoryFromReference(ref, f, factoryFilter);
if (factory instanceof DirObjectFactory) {
return ((DirObjectFactory)factory).getObjectInstance(
ref, name, nameCtx, environment, attrs);
} else if (factory != null) {
return factory.getObjectInstance(ref, name, nameCtx,
environment);
}
// No factory found, so return original refInfo.
// That could happen if:
// - a factory class is not in a class path and reference does
// not contain a URL for it
// - a factory class is available but object factory filters
// disallow its usage
return refInfo;
} else {
// if reference has no factory, check for addresses
// containing URLs
// ignore name & attrs params; not used in URL factory
// RMI references from '
answer = processURLAddrs(ref, name, nameCtx, environment);
if (answer != null) {
return answer;
}
}
}
// try using any specified factories
answer = createObjectFromFactories(refInfo, name, nameCtx,
environment, attrs);
return (answer != null) ? answer : refInfo;
}
/**
* Retrieves the ObjectFactory for the object identified by a reference,
* using the reference's factory class name and factory codebase
* to load in the factory's class.
* @param ref The non-null reference to use.
* @param factoryName The non-null class name of the factory.
* @return The object factory for the object identified by ref; null
* if unable to load the factory.
*/
static ObjectFactory getObjectFactoryFromReference(
Reference ref, String factoryName, Predicate<Class<?>> filter)
throws IllegalAccessException,
InstantiationException,
MalformedURLException {
Class<?> clas = null;
// Try to use current class loader
try {
clas = helper.loadClassWithoutInit(factoryName);
// Validate factory's class with the objects factory serial filter
if (!filter.test(clas)) {
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) {
}
}
@SuppressWarnings("deprecation") // Class.newInstance
ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null;
return result;
}
/**
* Creates an object using the factories specified in the
* {@code Context.OBJECT_FACTORIES} property of the environment
* or of the provider resource file associated with {@code nameCtx}.
*
* @return factory created; null if cannot create
*/
private static Object createObjectFromFactories(Object obj, Name name,
Context nameCtx, Hashtable<?,?> environment, Attributes attrs)
throws Exception {
FactoryEnumeration factories = ResourceManager.getFactories(
Context.OBJECT_FACTORIES, environment, nameCtx);
if (factories == null)
return null;
ObjectFactory factory;
Object answer = null;
// Try each factory until one succeeds
while (answer == null && factories.hasMore()) {
factory = (ObjectFactory)factories.next();
if (factory instanceof DirObjectFactory) {
answer = ((DirObjectFactory)factory).
getObjectInstance(obj, name, nameCtx, environment, attrs);
} else {
answer =
factory.getObjectInstance(obj, name, nameCtx, environment);
}
}
return answer;
}
/*
* Ref has no factory. For each address of type "URL", try its URL
* context factory. Returns null if unsuccessful in creating and
* invoking a factory.
*/
static Object processURLAddrs(Reference ref, Name name, Context nameCtx,
Hashtable<?,?> environment)
throws NamingException {
for (int i = 0; i < ref.size(); i++) {
RefAddr addr = ref.get(i);
if (addr instanceof StringRefAddr &&
addr.getType().equalsIgnoreCase("URL")) {
String url = (String)addr.getContent();
Object answer = processURL(url, name, nameCtx, environment);
if (answer != null) {
return answer;
}
}
}
return null;
}
private static Object processURL(Object refInfo, Name name,
Context nameCtx, Hashtable<?,?> environment)
throws NamingException {
Object answer;
// If refInfo is a URL string, try to use its URL context factory
// If no context found, continue to try object factories.
if (refInfo instanceof String) {
String url = (String)refInfo;
String scheme = getURLScheme(url);
if (scheme != null) {
answer = getURLObject(scheme, refInfo, name, nameCtx,
environment);
if (answer != null) {
return answer;
}
}
}
// If refInfo is an array of URL strings,
// try to find a context factory for any one of its URLs.
// If no context found, continue to try object factories.
if (refInfo instanceof String[]) {
String[] urls = (String[])refInfo;
for (int i = 0; i <urls.length; i++) {
String scheme = getURLScheme(urls[i]);
if (scheme != null) {
answer = getURLObject(scheme, refInfo, name, nameCtx,
environment);
if (answer != null)
return answer;
}
}
}
return null;
}
private static String getURLScheme(String str) {
int colon_posn = str.indexOf(':');
int slash_posn = str.indexOf('/');
if (colon_posn > 0 && (slash_posn == -1 || colon_posn < slash_posn))
return str.substring(0, colon_posn);
return null;
}
/**
* Creates an object for the given URL scheme id using
* the supplied urlInfo.
* <p>
* If urlInfo is null, the result is a context for resolving URLs
* with the scheme id 'scheme'.
* If urlInfo is a URL, the result is a context named by the URL.
* Names passed to this context is assumed to be relative to this
* context (i.e. not a URL). For example, if urlInfo is
* "ldap://ldap.wiz.com/o=Wiz,c=us", the resulting context will
* be that pointed to by "o=Wiz,c=us" on the server 'ldap.wiz.com'.
* Subsequent names that can be passed to this context will be
* LDAP names relative to this context (e.g. cn="Barbs Jensen").
* If urlInfo is an array of URLs, the URLs are assumed
* to be equivalent in terms of the context to which they refer.
* The resulting context is like that of the single URL case.
* If urlInfo is of any other type, that is handled by the
* context factory for the URL scheme.
* @param scheme the URL scheme id for the context
* @param urlInfo information used to create the context
* @param name name of this object relative to {@code nameCtx}
* @param nameCtx Context whose provider resource file will be searched
* for package prefix values (or null if none)
* @param environment Environment properties for creating the context
* @see javax.naming.InitialContext
*/
private static Object getURLObject(String scheme, Object urlInfo,
Name name, Context nameCtx,
Hashtable<?,?> environment)
throws NamingException {
// e.g. "ftpURLContextFactory"
ObjectFactory factory = (ObjectFactory)ResourceManager.getFactory(
Context.URL_PKG_PREFIXES, environment, nameCtx,
"." + scheme + "." + scheme + "URLContextFactory", DEFAULT_PKG_PREFIX);
if (factory == null)
return null;
// Found object factory
try {
return factory.getObjectInstance(urlInfo, name, nameCtx, environment);
} catch (NamingException e) {
throw e;
} catch (Exception e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
}
/**
* Creates an object using the factories specified in the
* {@code Context.OBJECT_FACTORIES} property of the environment
* or of the provider resource file associated with {@code nameCtx}.
*
* @return factory created; null if cannot create
*/
private static Object createObjectFromFactories(Object obj, Name name,
Context nameCtx, Hashtable<?,?> environment) throws Exception {
FactoryEnumeration factories = ResourceManager.getFactories(
Context.OBJECT_FACTORIES, environment, nameCtx);
if (factories == null)
return null;
// Try each factory until one succeeds
ObjectFactory factory;
Object answer = null;
while (answer == null && factories.hasMore()) {
factory = (ObjectFactory)factories.next();
answer = factory.getObjectInstance(obj, name, nameCtx, environment);
}
return answer;
}
public static synchronized void setObjectFactoryBuilder(
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;
}
public static synchronized ObjectFactoryBuilder getObjectFactoryBuilder() {
return object_factory_builder;
}
private static final String DEFAULT_PKG_PREFIX = "com.sun.jndi.url";
static final VersionHelper helper = VersionHelper.getVersionHelper();
private static ObjectFactoryBuilder object_factory_builder = null;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, 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
@ -50,19 +50,58 @@ public final class ObjectFactoriesFilter {
/**
* Checks if serial filter configured with {@code "jdk.jndi.object.factoriesFilter"}
* system property value allows instantiation of the specified objects factory class.
* If the filter result is not {@linkplain Status#REJECTED REJECTED}, the filter will
* If the filter result is {@linkplain Status#ALLOWED ALLOWED}, the filter will
* allow the instantiation of objects factory class.
*
* @param factoryClass objects factory class
* @param serialClass objects factory class
* @return true - if the factory is allowed to be instantiated; false - otherwise
*/
public static boolean canInstantiateObjectsFactory(Class<?> factoryClass) {
return checkInput(() -> factoryClass);
public static boolean checkGlobalFilter(Class<?> serialClass) {
return checkInput(GLOBAL_FILTER, () -> serialClass);
}
private static boolean checkInput(FactoryInfo factoryInfo) {
Status result = GLOBAL.checkInput(factoryInfo);
return result != Status.REJECTED;
/**
* Checks if the factory filters allow the given factory class for LDAP.
* This method combines the global and LDAP specific filter results to determine
* if the given factory class is allowed.
* The given factory class is rejected if any of these two filters reject
* it, or if none of them allow it.
*
* @param serialClass objects factory class
* @return true - if the factory is allowed to be instantiated; false - otherwise
*/
public static boolean checkLdapFilter(Class<?> serialClass) {
return checkInput(LDAP_FILTER, () -> serialClass);
}
/**
* Checks if the factory filters allow the given factory class for RMI.
* This method combines the global and RMI specific filter results to determine
* if the given factory class is allowed.
* The given factory class is rejected if any of these two filters reject
* it, or if none of them allow it.
*
* @param serialClass objects factory class
* @return true - if the factory is allowed to be instantiated; false - otherwise
*/
public static boolean checkRmiFilter(Class<?> serialClass) {
return checkInput(RMI_FILTER, () -> serialClass);
}
private static boolean checkInput(ConfiguredFilter filter, FactoryInfo serialClass) {
var globalFilter = GLOBAL_FILTER.filter();
var specificFilter = filter.filter();
Status globalResult = globalFilter.checkInput(serialClass);
// Check if a specific filter is the global one
if (filter == GLOBAL_FILTER) {
return globalResult == Status.ALLOWED;
}
return switch (globalResult) {
case ALLOWED -> specificFilter.checkInput(serialClass) != Status.REJECTED;
case REJECTED -> false;
case UNDECIDED -> specificFilter.checkInput(serialClass) == Status.ALLOWED;
};
}
// FilterInfo to check if objects factory class is allowed by the system-wide
@ -97,18 +136,91 @@ public final class ObjectFactoriesFilter {
}
// System property name that contains the patterns to filter object factory names
private static final String FACTORIES_FILTER_PROPNAME = "jdk.jndi.object.factoriesFilter";
private static final String GLOBAL_FACTORIES_FILTER_PROPNAME =
"jdk.jndi.object.factoriesFilter";
// Default system property value that allows the load of any object factory classes
private static final String DEFAULT_SP_VALUE = "*";
// System property name that contains the patterns to filter LDAP object factory
// names
private static final String LDAP_FACTORIES_FILTER_PROPNAME =
"jdk.jndi.ldap.object.factoriesFilter";
// System wide object factories filter constructed from the system property
private static final ObjectInputFilter GLOBAL =
ObjectInputFilter.Config.createFilter(getFilterPropertyValue());
// System property name that contains the patterns to filter RMI object factory
// names
private static final String RMI_FACTORIES_FILTER_PROPNAME =
"jdk.jndi.rmi.object.factoriesFilter";
// Default system property value that allows the load of any object factory
// classes
private static final String DEFAULT_GLOBAL_SP_VALUE = "*";
// Default system property value that allows the load of any object factory
// class provided by the JDK LDAP provider implementation
private static final String DEFAULT_LDAP_SP_VALUE =
"java.naming/com.sun.jndi.ldap.**;!*";
// Default system property value that allows the load of any object factory
// class provided by the JDK RMI provider implementation
private static final String DEFAULT_RMI_SP_VALUE =
"jdk.naming.rmi/com.sun.jndi.rmi.**;!*";
// A system-wide global object factories filter constructed from the system
// property
private static final ConfiguredFilter GLOBAL_FILTER =
initializeFilter(GLOBAL_FACTORIES_FILTER_PROPNAME, DEFAULT_GLOBAL_SP_VALUE);
// A system-wide LDAP specific object factories filter constructed from the system
// property
private static final ConfiguredFilter LDAP_FILTER =
initializeFilter(LDAP_FACTORIES_FILTER_PROPNAME, DEFAULT_LDAP_SP_VALUE);
// A system-wide RMI specific object factories filter constructed from the system
// property
private static final ConfiguredFilter RMI_FILTER =
initializeFilter(RMI_FACTORIES_FILTER_PROPNAME, DEFAULT_RMI_SP_VALUE);
// Record for storing a factory filter configuration
private interface ConfiguredFilter {
ObjectInputFilter filter();
}
// Record to store an object input filter constructed from a valid filter
// pattern string
private record ValidFilter(ObjectInputFilter filter)
implements ConfiguredFilter {
}
// Record to store parsing results for a filter with
// illegal or malformed pattern string
private record InvalidFilter(String filterPropertyName,
IllegalArgumentException error)
implements ConfiguredFilter {
@Override
public ObjectInputFilter filter() {
// Report a filter property name and an error message
throw new IllegalArgumentException(filterPropertyName +
": " + error.getMessage());
}
}
// Read filter pattern value from a system/security property
// and create a filter record from it (valid or invalid).
private static ConfiguredFilter initializeFilter(String filterPropertyName,
String filterDefaultValue) {
try {
var filter = ObjectInputFilter.Config.createFilter(
getFilterPropertyValue(filterPropertyName,
filterDefaultValue));
return new ValidFilter(filter);
} catch (IllegalArgumentException iae) {
return new InvalidFilter(filterPropertyName, iae);
}
}
// Get security or system property value
private static String getFilterPropertyValue() {
String propVal = SecurityProperties.privilegedGetOverridable(FACTORIES_FILTER_PROPNAME);
return propVal != null ? propVal : DEFAULT_SP_VALUE;
private static String getFilterPropertyValue(String propertyName,
String defaultValue) {
String propVal = SecurityProperties.privilegedGetOverridable(propertyName);
return propVal != null ? propVal : defaultValue;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2022, 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
@ -29,13 +29,13 @@ import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.NamingException;
import javax.naming.CannotProceedException;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attributes;
import com.sun.naming.internal.NamingManagerHelper;
import com.sun.naming.internal.ObjectFactoriesFilter;
import com.sun.naming.internal.ResourceManager;
import com.sun.naming.internal.FactoryEnumeration;
@ -154,92 +154,8 @@ public class DirectoryManager extends NamingManager {
getObjectInstance(Object refInfo, Name name, Context nameCtx,
Hashtable<?,?> environment, Attributes attrs)
throws Exception {
ObjectFactory factory;
ObjectFactoryBuilder builder = getObjectFactoryBuilder();
if (builder != null) {
// builder must return non-null factory
factory = builder.createObjectFactory(refInfo, environment);
if (factory instanceof DirObjectFactory) {
return ((DirObjectFactory)factory).getObjectInstance(
refInfo, name, nameCtx, environment, attrs);
} else {
return factory.getObjectInstance(refInfo, name, nameCtx,
environment);
}
}
// use reference if possible
Reference ref = null;
if (refInfo instanceof Reference) {
ref = (Reference) refInfo;
} else if (refInfo instanceof Referenceable) {
ref = ((Referenceable)(refInfo)).getReference();
}
Object answer;
if (ref != null) {
String f = ref.getFactoryClassName();
if (f != null) {
// if reference identifies a factory, use exclusively
factory = getObjectFactoryFromReference(ref, f);
if (factory instanceof DirObjectFactory) {
return ((DirObjectFactory)factory).getObjectInstance(
ref, name, nameCtx, environment, attrs);
} else if (factory != null) {
return factory.getObjectInstance(ref, name, nameCtx,
environment);
}
// No factory found, so return original refInfo.
// Will reach this point if factory class is not in
// class path and reference does not contain a URL for it
return refInfo;
} else {
// if reference has no factory, check for addresses
// containing URLs
// ignore name & attrs params; not used in URL factory
answer = processURLAddrs(ref, name, nameCtx, environment);
if (answer != null) {
return answer;
}
}
}
// try using any specified factories
answer = createObjectFromFactories(refInfo, name, nameCtx,
environment, attrs);
return (answer != null) ? answer : refInfo;
}
private static Object createObjectFromFactories(Object obj, Name name,
Context nameCtx, Hashtable<?,?> environment, Attributes attrs)
throws Exception {
FactoryEnumeration factories = ResourceManager.getFactories(
Context.OBJECT_FACTORIES, environment, nameCtx);
if (factories == null)
return null;
ObjectFactory factory;
Object answer = null;
// Try each factory until one succeeds
while (answer == null && factories.hasMore()) {
factory = (ObjectFactory)factories.next();
if (factory instanceof DirObjectFactory) {
answer = ((DirObjectFactory)factory).
getObjectInstance(obj, name, nameCtx, environment, attrs);
} else {
answer =
factory.getObjectInstance(obj, name, nameCtx, environment);
}
}
return answer;
return NamingManagerHelper.getDirObjectInstance(refInfo, name, nameCtx,
environment, attrs, ObjectFactoriesFilter::checkGlobalFilter);
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2022, 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,13 +25,13 @@
package javax.naming.spi;
import java.net.MalformedURLException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import javax.naming.*;
import com.sun.naming.internal.NamingManagerHelper;
import com.sun.naming.internal.ObjectFactoriesFilter;
import com.sun.naming.internal.VersionHelper;
import com.sun.naming.internal.ResourceManager;
@ -78,11 +78,6 @@ public class NamingManager {
// --------- object factory stuff
/**
* Package-private; used by DirectoryManager and NamingManager.
*/
private static ObjectFactoryBuilder object_factory_builder = null;
private static final ClassLoaderValue<InitialContextFactory> FACTORIES_CACHE =
new ClassLoaderValue<>();
@ -111,110 +106,16 @@ public class NamingManager {
* @see ObjectFactoryBuilder
* @see java.lang.SecurityManager#checkSetFactory
*/
public static synchronized void setObjectFactoryBuilder(
public static void setObjectFactoryBuilder(
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;
NamingManagerHelper.setObjectFactoryBuilder(builder);
}
/**
* Used for accessing object factory builder.
*/
static synchronized ObjectFactoryBuilder getObjectFactoryBuilder() {
return object_factory_builder;
}
/**
* Retrieves the ObjectFactory for the object identified by a reference,
* using the reference's factory class name and factory codebase
* to load in the factory's class.
* @param ref The non-null reference to use.
* @param factoryName The non-null class name of the factory.
* @return The object factory for the object identified by ref; null
* if unable to load the factory.
*/
static ObjectFactory getObjectFactoryFromReference(
Reference ref, String factoryName)
throws IllegalAccessException,
InstantiationException,
MalformedURLException {
Class<?> clas = null;
// Try to use current class loader
try {
clas = helper.loadClassWithoutInit(factoryName);
// Validate factory's class with the objects factory serial filter
if (!ObjectFactoriesFilter.canInstantiateObjectsFactory(clas)) {
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 ||
!ObjectFactoriesFilter.canInstantiateObjectsFactory(clas)) {
return null;
}
} catch (ClassNotFoundException e) {
}
}
@SuppressWarnings("deprecation") // Class.newInstance
ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null;
return result;
}
/**
* Creates an object using the factories specified in the
* {@code Context.OBJECT_FACTORIES} property of the environment
* or of the provider resource file associated with {@code nameCtx}.
*
* @return factory created; null if cannot create
*/
private static Object createObjectFromFactories(Object obj, Name name,
Context nameCtx, Hashtable<?,?> environment) throws Exception {
FactoryEnumeration factories = ResourceManager.getFactories(
Context.OBJECT_FACTORIES, environment, nameCtx);
if (factories == null)
return null;
// Try each factory until one succeeds
ObjectFactory factory;
Object answer = null;
while (answer == null && factories.hasMore()) {
factory = (ObjectFactory)factories.next();
answer = factory.getObjectInstance(obj, name, nameCtx, environment);
}
return answer;
}
private static String getURLScheme(String str) {
int colon_posn = str.indexOf(':');
int slash_posn = str.indexOf('/');
if (colon_posn > 0 && (slash_posn == -1 || colon_posn < slash_posn))
return str.substring(0, colon_posn);
return null;
static ObjectFactoryBuilder getObjectFactoryBuilder() {
return NamingManagerHelper.getObjectFactoryBuilder();
}
/**
@ -308,124 +209,11 @@ public class NamingManager {
public static Object
getObjectInstance(Object refInfo, Name name, Context nameCtx,
Hashtable<?,?> environment)
throws Exception
{
ObjectFactory factory;
// Use builder if installed
ObjectFactoryBuilder builder = getObjectFactoryBuilder();
if (builder != null) {
// builder must return non-null factory
factory = builder.createObjectFactory(refInfo, environment);
return factory.getObjectInstance(refInfo, name, nameCtx,
environment);
}
// Use reference if possible
Reference ref = null;
if (refInfo instanceof Reference) {
ref = (Reference) refInfo;
} else if (refInfo instanceof Referenceable) {
ref = ((Referenceable)(refInfo)).getReference();
}
Object answer;
if (ref != null) {
String f = ref.getFactoryClassName();
if (f != null) {
// if reference identifies a factory, use exclusively
factory = getObjectFactoryFromReference(ref, f);
if (factory != null) {
return factory.getObjectInstance(ref, name, nameCtx,
environment);
}
// No factory found, so return original refInfo.
// Will reach this point if factory class is not in
// class path and reference does not contain a URL for it
return refInfo;
} else {
// if reference has no factory, check for addresses
// containing URLs
answer = processURLAddrs(ref, name, nameCtx, environment);
if (answer != null) {
return answer;
}
}
}
// try using any specified factories
answer =
createObjectFromFactories(refInfo, name, nameCtx, environment);
return (answer != null) ? answer : refInfo;
throws Exception {
return NamingManagerHelper.getObjectInstance(refInfo, name, nameCtx,
environment, ObjectFactoriesFilter::checkGlobalFilter);
}
/*
* Ref has no factory. For each address of type "URL", try its URL
* context factory. Returns null if unsuccessful in creating and
* invoking a factory.
*/
static Object processURLAddrs(Reference ref, Name name, Context nameCtx,
Hashtable<?,?> environment)
throws NamingException {
for (int i = 0; i < ref.size(); i++) {
RefAddr addr = ref.get(i);
if (addr instanceof StringRefAddr &&
addr.getType().equalsIgnoreCase("URL")) {
String url = (String)addr.getContent();
Object answer = processURL(url, name, nameCtx, environment);
if (answer != null) {
return answer;
}
}
}
return null;
}
private static Object processURL(Object refInfo, Name name,
Context nameCtx, Hashtable<?,?> environment)
throws NamingException {
Object answer;
// If refInfo is a URL string, try to use its URL context factory
// If no context found, continue to try object factories.
if (refInfo instanceof String) {
String url = (String)refInfo;
String scheme = getURLScheme(url);
if (scheme != null) {
answer = getURLObject(scheme, refInfo, name, nameCtx,
environment);
if (answer != null) {
return answer;
}
}
}
// If refInfo is an array of URL strings,
// try to find a context factory for any one of its URLs.
// If no context found, continue to try object factories.
if (refInfo instanceof String[]) {
String[] urls = (String[])refInfo;
for (int i = 0; i <urls.length; i++) {
String scheme = getURLScheme(urls[i]);
if (scheme != null) {
answer = getURLObject(scheme, refInfo, name, nameCtx,
environment);
if (answer != null)
return answer;
}
}
}
return null;
}
/**
* Retrieves a context identified by {@code obj}, using the specified
* environment.

View File

@ -96,13 +96,31 @@
* be allowed to instantiate objects from object references returned by naming/directory systems.
* The factory class named by the reference instance will be matched against this filter.
* The filter property supports pattern-based filter syntax with the same format as
* {@link java.io.ObjectInputFilter.Config#createFilter(String) jdk.serialFilter}.
* {@link java.io.ObjectInputFilter.Config#createFilter(String) jdk.serialFilter}. Limit patterns
* specified in the filter property are unused.
* This property can also be specified as a {@linkplain java.security.Security security property}.
* This property is also supported by the <a href="{@docRoot}/jdk.naming.rmi/module-summary.html">default JNDI
* RMI Provider</a>.
* <br>The default value allows any object factory class specified by the reference
* instance to recreate the referenced object.
* </li>
* <li>{@systemProperty jdk.jndi.ldap.object.factoriesFilter}:
* <br>The value of this system property defines a filter used by
* the JDK LDAP provider implementation to further restrict the set of object factory classes which will
* be allowed to instantiate objects from object references returned by LDAP systems.
* The factory class named by the {@linkplain javax.naming.Reference reference instance} first will be
* matched against this specific filter and then against the global filter. The factory class is rejected
* if any of these two filters reject it, or if none of them allow it.
* The filter property supports pattern-based filter syntax with the same format as
* {@link java.io.ObjectInputFilter.Config#createFilter(String) jdk.serialFilter}. Limit patterns
* specified in the filter property are unused.
* <br>The default value allows any object factory class provided by the JDK LDAP provider
* implementation.
* <br>This system property will be used to filter LDAP specific object factories only if
* global {@link javax.naming.spi.ObjectFactoryBuilder} is {@linkplain
* javax.naming.spi.NamingManager#setObjectFactoryBuilder(javax.naming.spi.ObjectFactoryBuilder)
* not set}.
* </li>
* </ul>
* <p>Other providers may define additional properties in their module description:
* <ul>
@ -133,6 +151,8 @@ module java.naming {
exports com.sun.jndi.toolkit.url to
jdk.naming.dns,
jdk.naming.rmi;
exports com.sun.naming.internal to
jdk.naming.rmi;
uses javax.naming.ldap.StartTlsResponse;
uses javax.naming.spi.InitialContextFactory;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2022, 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
@ -26,6 +26,9 @@
package com.sun.jndi.rmi.registry;
import com.sun.naming.internal.NamingManagerHelper;
import com.sun.naming.internal.ObjectFactoriesFilter;
import java.util.Hashtable;
import java.util.Properties;
import java.rmi.*;
@ -498,8 +501,8 @@ public class RegistryContext implements Context, Referenceable {
"The object factory is untrusted. Set the system property" +
" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
}
return NamingManager.getObjectInstance(obj, name, this,
environment);
return NamingManagerHelper.getObjectInstance(obj, name, this,
environment, ObjectFactoriesFilter::checkRmiFilter);
} catch (NamingException e) {
throw e;
} catch (RemoteException e) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2022, 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
@ -36,13 +36,27 @@
* be allowed to instantiate objects from object references returned by naming/directory systems.
* The factory class named by the reference instance will be matched against this filter.
* The filter property supports pattern-based filter syntax with the same format as
* {@link java.io.ObjectInputFilter.Config#createFilter(String) jdk.serialFilter}.
* {@link java.io.ObjectInputFilter.Config#createFilter(String) jdk.serialFilter}. Limit patterns
* specified in the filter property are unused.
* This property can also be specified as a {@linkplain java.security.Security security property}.
* This property is also supported by the <a href="{@docRoot}/java.naming/module-summary.html">default
* LDAP Naming Service Provider</a>.
* <br>The default value allows any object factory class specified by the reference
* instance to recreate the referenced object.
* </li>
* <li>{@systemProperty jdk.jndi.rmi.object.factoriesFilter}:
* <br>The value of this system property defines a filter used by
* the JDK RMI provider implementation to further restrict the set of object factory classes which will
* be allowed to instantiate objects from object references returned by RMI systems.
* The factory class named by the {@linkplain javax.naming.Reference reference instance} first will be
* matched against this specific filter and then against the global filter. The factory class is rejected
* if any of these two filters reject it, or if none of them allow it.
* The filter property supports pattern-based filter syntax with the same format as
* {@link java.io.ObjectInputFilter.Config#createFilter(String) jdk.serialFilter}. Limit patterns
* specified in the filter property are unused.
* <br>The default value allows any object factory class provided by the JDK RMI provider
* implementation.
* </li>
* </ul>
* @provides javax.naming.spi.InitialContextFactory
* @moduleGraph

View File

@ -0,0 +1,207 @@
/*
* Copyright (c) 2022, 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 jdk.test.lib.net.URIBuilder;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.util.Hashtable;
/*
* @test
* @bug 8290368
* @summary Checks if LDAP specific objects factory filter system and security
* properties can be used to restrict usage of object factories during
* LDAP lookup operations.
* @modules java.naming/com.sun.jndi.ldap
* @library /test/lib ../../lib /javax/naming/module/src/test/test/
* @build LDAPServer LDAPTestUtils TestFactory
*
* @run main/othervm LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=*
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=com.**;!*
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=com.test.**;!*
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=com.test.*;!*
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=com.test.Test*;!*
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=!com.test.**
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=!com.test.TestFactory;com.**
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=!com.test.TestFactory;com.test.*
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=!com.test.Test*
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=com.*;!*
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=com.test.TestFactor;!*
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=com.test.TestFactoryy;!*
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djava.security.properties=${test.src}/disallowLdapFilter.props
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djava.security.properties=${test.src}/disallowLdapFilter.props
* -Djdk.jndi.ldap.object.factoriesFilter=com.test.TestFactory
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djava.security.properties=${test.src}/allowLdapFilter.props
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djava.security.properties=${test.src}/allowLdapFilter.props
* -Djdk.jndi.rmi.object.factoriesFilter=!com.test.TestFactory
* LdapFactoriesFilterTest true true
*
* @run main/othervm -Djava.security.properties=${test.src}/allowLdapFilter.props
* -Djdk.jndi.ldap.object.factoriesFilter=!com.test.TestFactory
* LdapFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=.*
* LdapFactoriesFilterTest false false
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=*
* -Djdk.jndi.object.factoriesFilter=.*
* LdapFactoriesFilterTest false false
*
* @run main/othervm -Djdk.jndi.ldap.object.factoriesFilter=*
* -Djdk.jndi.object.factoriesFilter=*
* -Djdk.jndi.rmi.object.factoriesFilter=.*
* LdapFactoriesFilterTest true true
*/
public class LdapFactoriesFilterTest {
public static void main(String[] args) throws Exception {
boolean testFactoryAllowed = Boolean.parseBoolean(args[0]);
boolean ldapAndGlobalFiltersValid =
Boolean.parseBoolean(args[1]);
// Create unbound server socket
ServerSocket serverSocket = new ServerSocket();
try (serverSocket) {
// Bind it to the loopback address
SocketAddress sockAddr = new InetSocketAddress(
InetAddress.getLoopbackAddress(), 0);
serverSocket.bind(sockAddr);
// Construct the provider URL for LDAPTestUtils
String providerURL = URIBuilder.newBuilder()
.scheme("ldap")
.loopback()
.port(serverSocket.getLocalPort())
.buildUnchecked().toString();
// Create and initialize test environment variables
Hashtable<Object, Object> env;
env = LDAPTestUtils.initEnv(serverSocket, providerURL,
LdapFactoriesFilterTest.class.getName(), args, false);
DirContext ctx = new InitialDirContext(env);
Exception observedException = null;
Object lookupRes = null;
// Lookup bound reference
try {
lookupRes = ctx.lookup("Example");
System.err.println("Lookup results: " + lookupRes.getClass().getCanonicalName());
} catch (Exception ex) {
observedException = ex;
}
// Check lookup operation results
if (testFactoryAllowed) {
// NamingException with RuntimeException cause is expected here
if (observedException instanceof NamingException namingException) {
System.err.println("Observed NamingException: " + observedException);
Throwable cause = namingException.getCause();
System.err.println("NamingException cause: " + cause);
// We expect RuntimeException from factory for cases when LDAP factory
// filter allows the test factory
if (cause instanceof RuntimeException rte) {
String rteMessage = rte.getMessage();
System.err.println("RuntimeException message: " + rteMessage);
if (!com.test.TestFactory.RUNTIME_EXCEPTION_MESSAGE.equals(rteMessage)) {
throw new AssertionError(
"Unexpected RuntimeException message observed");
}
} else {
throw new AssertionError(
"RuntimeException is expected to be thrown" +
" by the test object factory");
}
} else {
throw new AssertionError(
"NamingException was not thrown as expected");
}
} else if (!ldapAndGlobalFiltersValid) {
// If LDAP or GLOBAL factories filter are not properly formatted we're expecting to
// get NamingException with IllegalArgumentException set as a cause that contains
// formatting error message.
// If RMI filter is not properly formatted we're not expecting IAE here since
// this test only performing LDAP lookups
if (observedException instanceof NamingException ne) {
if (ne.getCause() instanceof IllegalArgumentException iae) {
// All tests with malformed filters contain wildcards with
// package name missing, therefore the message is expected
// to start with "package missing in:"
System.err.println("Found expected exception: " + iae);
} else {
throw new AssertionError("IllegalArgumentException" +
" is expected for malformed filter values");
}
}
} else {
// Object factory is not allowed by the factories filter
// we expect reference here
if (lookupRes instanceof Reference ref) {
System.err.println("Lookup result is a reference: " +
ref.getFactoryClassLocation() + " " + ref.getFactoryClassName());
} else {
new AssertionError("Reference was not returned as a lookup result");
}
}
}
}
}

View File

@ -0,0 +1,58 @@
#
# Copyright (c) 2022, 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.
#
################################################################################
# Capture file for LdapFactoriesFilterTest.java
#
# NOTE: This hexadecimal dump of LDAP protocol messages was generated by
# running the LdapFactoriesFilterTest application program against a real
# LDAP server and setting the JNDI/LDAP environment property:
# com.sun.jndi.ldap.trace.ber to activate LDAP message tracing.
#
################################################################################
# LDAP BindRequest
0000: 30 0C 02 01 01 60 07 02 01 03 04 00 80 00 0....`........
# LDAP BindResponse
0000: 30 0C 02 01 01 61 07 0A 01 00 04 00 04 00 0....a........
# LDAP SearchRequest
0000: 30 49 02 01 02 63 27 04 07 45 78 61 6D 70 6C 65 0I...c'..Example
0010: 0A 01 00 0A 01 03 02 01 00 02 01 00 01 01 00 87 ................
0020: 0B 6F 62 6A 65 63 74 43 6C 61 73 73 30 00 A0 1B .objectClass0...
0030: 30 19 04 17 32 2E 31 36 2E 38 34 30 2E 31 2E 31 0...2.16.840.1.1
0040: 31 33 37 33 30 2E 33 2E 34 2E 32 13730.3.4.2
# LDAP SearchResultEntry
0000: 30 75 02 01 02 64 70 04 07 45 78 61 6D 70 6C 65 0u...dp..Example
0010: 30 65 30 16 04 0D 6A 61 76 61 43 6C 61 73 73 4E 0e0...javaClassN
0020: 61 6D 65 31 05 04 03 66 6F 6F 30 24 04 0B 6F 62 ame1...foo0$..ob
0030: 6A 65 63 74 43 6C 61 73 73 31 15 04 13 6A 61 76 jectClass1...jav
0040: 61 4E 61 6D 69 6E 67 52 65 66 65 72 65 6E 63 65 aNamingReference
0050: 30 25 04 0B 6A 61 76 61 46 61 63 74 6F 72 79 31 0%..javaFactory1
0060: 16 04 14 63 6F 6D 2E 74 65 73 74 2E 54 65 73 74 ...com.test.Test
0070: 46 61 63 74 6F 72 79 Factory
# LDAP SearchResultDone
0000: 30 0C 02 01 02 65 07 0A 01 00 04 00 04 00 0....e........

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2022, 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.
*/
package com.test;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class TestFactory implements ObjectFactory {
public static final String RUNTIME_EXCEPTION_MESSAGE =
"Test object factory is called to instantiate factory";
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
System.err.println("obj:" + obj);
throw new RuntimeException(RUNTIME_EXCEPTION_MESSAGE);
}
}

View File

@ -0,0 +1 @@
jdk.jndi.ldap.object.factoriesFilter=com.test.TestFactory;!*

View File

@ -0,0 +1 @@
jdk.jndi.ldap.object.factoriesFilter=!com.test.TestFactory

View File

@ -0,0 +1,189 @@
/*
* Copyright (c) 2022, 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.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.util.Hashtable;
/*
* @test
* @bug 8290368
* @summary Checks if RMI specific objects factory filter system and security
* properties can be used to restrict usage of object factories during
* RMI lookup operations.
* @modules java.rmi/sun.rmi.registry
* java.rmi/sun.rmi.server
* java.rmi/sun.rmi.transport
* java.rmi/sun.rmi.transport.tcp
* @library ../../../../../../java/rmi/testlibrary
* @build TestLibrary
* @compile TestFactory.java
*
* @run main/othervm RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=*
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=com.**;!*
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=com.test.**;!*
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=com.test.*;!*
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=com.test.Test*;!*
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=!com.test.**
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=!com.test.TestFactory;com.**
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=!com.test.TestFactory;com.test.*
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=!com.test.Test*
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=com.*;!*
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=com.test.TestFactor;!*
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=com.test.TestFactoryy;!*
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djava.security.properties=${test.src}/disallowRmiFilter.props
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djava.security.properties=${test.src}/disallowRmiFilter.props
* -Djdk.jndi.rmi.object.factoriesFilter=com.test.TestFactory
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djava.security.properties=${test.src}/allowRmiFilter.props
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djava.security.properties=${test.src}/allowRmiFilter.props
* -Djdk.jndi.ldap.object.factoriesFilter=!com.test.TestFactory
* RmiFactoriesFilterTest true true
*
* @run main/othervm -Djava.security.properties=${test.src}/allowRmiFilter.props
* -Djdk.jndi.rmi.object.factoriesFilter=!com.test.TestFactory
* RmiFactoriesFilterTest false true
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=.*
* RmiFactoriesFilterTest false false
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=*
* -Djdk.jndi.object.factoriesFilter=.*
* RmiFactoriesFilterTest false false
*
* @run main/othervm -Djdk.jndi.rmi.object.factoriesFilter=*
* -Djdk.jndi.object.factoriesFilter=*
* -Djdk.jndi.ldap.object.factoriesFilter=.*
* RmiFactoriesFilterTest true true
*/
public class RmiFactoriesFilterTest {
public static void main(String[] args) throws Exception {
boolean classExpectedToLoad = Boolean.parseBoolean(args[0]);
boolean rmiAndGlobalFiltersValid =
Boolean.parseBoolean(args[1]);
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
Reference ref = new Reference("TestObject", "com.test.TestFactory",
null);
context.bind("objectTest", ref);
loadUsingFactoryFromTCCL(registryPort, classExpectedToLoad, rmiAndGlobalFiltersValid);
if (!rmiAndGlobalFiltersValid) {
// Check that IAE is set as NamingException cause for malformed RMI or GLOBAL
// filter values when lookup is called for the second time
loadUsingFactoryFromTCCL(registryPort, classExpectedToLoad, false);
}
}
private static Context getInitialContext(int port) throws NamingException {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://127.0.0.1:" + port);
return new InitialContext(env);
}
private static void loadUsingFactoryFromTCCL(int registryPort,
boolean classExpectedToLoad,
boolean rmiAndGlobalFiltersValid) {
try {
Context context = getInitialContext(registryPort);
Object object = context.lookup("objectTest");
System.out.println("Number of getObjectInstance calls:" +
com.test.TestFactory.getNumberOfGetInstanceCalls());
System.out.println("Loaded class type:" + object.getClass().getCanonicalName());
System.out.println("Loaded class: " + object);
if (classExpectedToLoad) {
if (!"TestObject".equals(object)) {
throw new AssertionError("Class was expected to get loaded by the factory");
}
} else {
if ("TestObject".equals(object)) {
throw new AssertionError("Class was unexpectedly loaded by the factory");
}
}
} catch (NamingException ne) {
// Only expecting NamingException for cases when RMI or GLOBAL filters are malformed
if (rmiAndGlobalFiltersValid) {
throw new AssertionError("Unexpected NamingException observed", ne);
}
if (ne.getCause() instanceof IllegalArgumentException iae) {
// All tests with malformed filters contain wildcards with
// package name missing, therefore the message is expected
// to start with "package missing in:"
System.err.println("Found expected exception: " + iae);
} else {
throw new AssertionError("IllegalArgument exception" +
" is expected for malformed filter values");
}
}
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2022, 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.
*/
package com.test;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class TestFactory implements ObjectFactory {
/**
* Get the number of {@code TestFactory.getObjectInstance} calls
*
* @return the number of calls
*/
public static int getNumberOfGetInstanceCalls() {
return timesGetInstanceCalled;
}
/**
* @param obj The possibly null object containing location or reference
* information that can be used in creating an object.
* @param name The name of this object relative to {@code nameCtx},
* or null if no name is specified.
* @param nameCtx The context relative to which the {@code name}
* parameter is specified, or null if {@code name} is
* relative to the default initial context.
* @param environment The possibly null environment that is used in
* creating the object.
* @return If specified object is a {@code Reference} returns a {@code String} with a class
* name specified in the reference, otherwise returns {@code "TestObj"}
* @throws Exception
*/
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
timesGetInstanceCalled++;
String loadedObject;
if (obj instanceof Reference) {
Reference r = (Reference) obj;
System.err.println("TestFactory: loading javax.naming.Reference:");
System.err.println("\tFactory location=" + r.getFactoryClassLocation());
System.err.println("\tFactory class name=" + r.getFactoryClassName());
System.err.println("\tClass name=" + r.getClassName());
loadedObject = r.getClassName();
} else {
System.err.println("TestFactory: loading " + obj.getClass().getName());
loadedObject = "TestObj";
}
return loadedObject;
}
private static volatile int timesGetInstanceCalled;
}

View File

@ -0,0 +1 @@
jdk.jndi.rmi.object.factoriesFilter=com.test.TestFactory;!*

View File

@ -0,0 +1 @@
jdk.jndi.rmi.object.factoriesFilter=!com.test.TestFactory;*

View File

@ -98,7 +98,8 @@ public class RunBasic {
runTest("person", "test.StorePerson",
"-Dcom.sun.jndi.ldap.object.trustSerialData=true");
runTest("fruit", "test.StoreFruit",
"-Dcom.sun.jndi.ldap.object.trustSerialData=true");
"-Dcom.sun.jndi.ldap.object.trustSerialData=true",
"-Djdk.jndi.ldap.object.factoriesFilter=org.example.fruit.FruitFactory");
runTest("hello", "test.StoreRemote",
"-Dcom.sun.jndi.ldap.object.trustSerialData=true");
runTest("foo", "test.ConnectWithFoo");