Mon, 12 Nov 2018 08:33:59 -0800
8160768: Add capability to custom resolve host/domain names within the default JNDI LDAP provider
Reviewed-by: alanb, dfuchs, chegar, mchung, vtewari
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/jndi/ldap/DefaultLdapDnsProvider.java Mon Nov 12 08:33:59 2018 -0800 1.3 @@ -0,0 +1,86 @@ 1.4 +/* 1.5 + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.jndi.ldap; 1.30 + 1.31 +import com.sun.jndi.ldap.spi.LdapDnsProvider; 1.32 +import com.sun.jndi.ldap.spi.LdapDnsProviderResult; 1.33 +import javax.naming.NamingException; 1.34 +import java.util.ArrayList; 1.35 +import java.util.List; 1.36 +import java.util.Map; 1.37 +import java.util.Optional; 1.38 + 1.39 +public class DefaultLdapDnsProvider { 1.40 + 1.41 + public Optional<LdapDnsProviderResult> lookupEndpoints(String url, 1.42 + Map<?,?> env) 1.43 + throws NamingException 1.44 + { 1.45 + if (url == null || env == null) { 1.46 + throw new NullPointerException(); 1.47 + } 1.48 + 1.49 + String domainName; 1.50 + List<String> endpoints = new ArrayList<>(); 1.51 + LdapURL ldapUrl = new LdapURL(url); 1.52 + String dn = ldapUrl.getDN(); 1.53 + String host = ldapUrl.getHost(); 1.54 + int port = ldapUrl.getPort(); 1.55 + String[] hostports; 1.56 + 1.57 + // handle a URL with no hostport (ldap:/// or ldaps:///) 1.58 + // locate the LDAP service using the URL's distinguished name 1.59 + if (host == null 1.60 + && port == -1 1.61 + && dn != null 1.62 + && (domainName = ServiceLocator.mapDnToDomainName(dn)) != null 1.63 + && (hostports = ServiceLocator.getLdapService(domainName, env)) != null) { 1.64 + // Generate new URLs that include the discovered hostports. 1.65 + // Reuse the original URL scheme. 1.66 + String scheme = ldapUrl.getScheme() + "://"; 1.67 + String query = ldapUrl.getQuery(); 1.68 + String urlSuffix = ldapUrl.getPath() + (query != null ? query : ""); 1.69 + for (String hostPort : hostports) { 1.70 + // the hostports come from the DNS SRV records 1.71 + // we assume the SRV record is scheme aware 1.72 + endpoints.add(scheme + hostPort + urlSuffix); 1.73 + } 1.74 + } else { 1.75 + // we don't have enough information to set the domain name 1.76 + // correctly 1.77 + domainName = ""; 1.78 + endpoints.add(url); 1.79 + } 1.80 + 1.81 + LdapDnsProviderResult res = new LdapDnsProviderResult(domainName, endpoints); 1.82 + if (res.getEndpoints().size() == 0 && res.getDomainName().isEmpty()) { 1.83 + return Optional.empty(); 1.84 + } else { 1.85 + return Optional.of(res); 1.86 + } 1.87 + } 1.88 + 1.89 +}
2.1 --- a/src/share/classes/com/sun/jndi/ldap/LdapCtxFactory.java Tue Aug 04 17:19:21 2020 -0300 2.2 +++ b/src/share/classes/com/sun/jndi/ldap/LdapCtxFactory.java Mon Nov 12 08:33:59 2018 -0800 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -35,6 +35,7 @@ 2.11 import javax.naming.spi.InitialContextFactory; 2.12 import javax.naming.ldap.Control; 2.13 2.14 +import com.sun.jndi.ldap.spi.LdapDnsProviderResult; 2.15 import com.sun.jndi.url.ldap.ldapURLContextFactory; 2.16 2.17 final public class LdapCtxFactory implements ObjectFactory, InitialContextFactory { 2.18 @@ -158,41 +159,73 @@ 2.19 } 2.20 2.21 private static DirContext getUsingURL(String url, Hashtable<?,?> env) 2.22 - throws NamingException { 2.23 - DirContext ctx = null; 2.24 - LdapURL ldapUrl = new LdapURL(url); 2.25 - String dn = ldapUrl.getDN(); 2.26 - String host = ldapUrl.getHost(); 2.27 - int port = ldapUrl.getPort(); 2.28 - String[] hostports; 2.29 - String domainName = null; 2.30 + throws NamingException 2.31 + { 2.32 + try { 2.33 + LdapDnsProviderResult r = 2.34 + LdapDnsProviderService.getInstance().lookupEndpoints(url, env); 2.35 + LdapCtx ctx; 2.36 + NamingException lastException = null; 2.37 2.38 - // handle a URL with no hostport (ldap:/// or ldaps:///) 2.39 - // locate the LDAP service using the URL's distinguished name 2.40 - if (host == null && 2.41 - port == -1 && 2.42 - dn != null && 2.43 - (domainName = ServiceLocator.mapDnToDomainName(dn)) != null && 2.44 - (hostports = ServiceLocator.getLdapService(domainName, env)) 2.45 - != null) { 2.46 - // Generate new URLs that include the discovered hostports. 2.47 - // Reuse the original URL scheme. 2.48 - String scheme = ldapUrl.getScheme() + "://"; 2.49 - String[] newUrls = new String[hostports.length]; 2.50 - String query = ldapUrl.getQuery(); 2.51 - String urlSuffix = ldapUrl.getPath() + (query != null ? query : ""); 2.52 - for (int i = 0; i < hostports.length; i++) { 2.53 - newUrls[i] = scheme + hostports[i] + urlSuffix; 2.54 + /* 2.55 + * Prior to this change we had been assuming that the url.getDN() 2.56 + * should be converted to a domain name via 2.57 + * ServiceLocator.mapDnToDomainName(url.getDN()) 2.58 + * 2.59 + * However this is incorrect as we can't assume that the supplied 2.60 + * url.getDN() is the same as the dns domain for the directory 2.61 + * server. 2.62 + * 2.63 + * This means that we depend on the dnsProvider to return both 2.64 + * the list of urls of individual hosts from which we attempt to 2.65 + * create an LdapCtx from *AND* the domain name that they serve 2.66 + * 2.67 + * In order to do this the dnsProvider must return an 2.68 + * {@link LdapDnsProviderResult}. 2.69 + * 2.70 + */ 2.71 + for (String u : r.getEndpoints()) { 2.72 + try { 2.73 + ctx = getLdapCtxFromUrl( 2.74 + r.getDomainName(), url, new LdapURL(u), env); 2.75 + return ctx; 2.76 + } catch (NamingException e) { 2.77 + // try the next element 2.78 + lastException = e; 2.79 + } 2.80 } 2.81 - ctx = getUsingURLs(newUrls, env); 2.82 - // Associate the derived domain name with the context 2.83 - ((LdapCtx)ctx).setDomainName(domainName); 2.84 2.85 - } else { 2.86 - ctx = new LdapCtx(dn, host, port, env, ldapUrl.useSsl()); 2.87 - // Record the URL that created the context 2.88 - ((LdapCtx)ctx).setProviderUrl(url); 2.89 + if (lastException != null) { 2.90 + throw lastException; 2.91 + } 2.92 + 2.93 + // lookupEndpoints returned an LdapDnsProviderResult with an empty 2.94 + // list of endpoints 2.95 + throw new NamingException("Could not resolve a valid ldap host"); 2.96 + } catch (NamingException e) { 2.97 + // lookupEndpoints(url, env) may throw a NamingException, which 2.98 + // there is no need to wrap. 2.99 + throw e; 2.100 + } catch (Exception e) { 2.101 + NamingException ex = new NamingException(); 2.102 + ex.setRootCause(e); 2.103 + throw ex; 2.104 } 2.105 + } 2.106 + 2.107 + private static LdapCtx getLdapCtxFromUrl(String domain, 2.108 + String url, 2.109 + LdapURL u, 2.110 + Hashtable<?,?> env) 2.111 + throws NamingException 2.112 + { 2.113 + String dn = u.getDN(); 2.114 + String host = u.getHost(); 2.115 + int port = u.getPort(); 2.116 + LdapCtx ctx = new LdapCtx(dn, host, port, env, u.useSsl()); 2.117 + ctx.setDomainName(domain); 2.118 + // Record the URL that created the context 2.119 + ctx.setProviderUrl(url); 2.120 return ctx; 2.121 } 2.122 2.123 @@ -202,19 +235,17 @@ 2.124 * Not pretty, but potentially more informative than returning null. 2.125 */ 2.126 private static DirContext getUsingURLs(String[] urls, Hashtable<?,?> env) 2.127 - throws NamingException { 2.128 - NamingException ne = null; 2.129 - DirContext ctx = null; 2.130 - for (int i = 0; i < urls.length; i++) { 2.131 + throws NamingException 2.132 + { 2.133 + NamingException ex = null; 2.134 + for (String u : urls) { 2.135 try { 2.136 - return getUsingURL(urls[i], env); 2.137 - } catch (AuthenticationException e) { 2.138 - throw e; 2.139 + return getUsingURL(u, env); 2.140 } catch (NamingException e) { 2.141 - ne = e; 2.142 + ex = e; 2.143 } 2.144 } 2.145 - throw ne; 2.146 + throw ex; 2.147 } 2.148 2.149 /**
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java Mon Nov 12 08:33:59 2018 -0800 3.3 @@ -0,0 +1,111 @@ 3.4 +/* 3.5 + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. Oracle designates this 3.11 + * particular file as subject to the "Classpath" exception as provided 3.12 + * by Oracle in the LICENSE file that accompanied this code. 3.13 + * 3.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.17 + * version 2 for more details (a copy is included in the LICENSE file that 3.18 + * accompanied this code). 3.19 + * 3.20 + * You should have received a copy of the GNU General Public License version 3.21 + * 2 along with this work; if not, write to the Free Software Foundation, 3.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.23 + * 3.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.25 + * or visit www.oracle.com if you need additional information or have any 3.26 + * questions. 3.27 + */ 3.28 + 3.29 +package com.sun.jndi.ldap; 3.30 + 3.31 +import java.security.AccessController; 3.32 +import java.security.PrivilegedAction; 3.33 +import java.util.*; 3.34 +import javax.naming.NamingException; 3.35 +import com.sun.jndi.ldap.spi.LdapDnsProvider; 3.36 +import com.sun.jndi.ldap.spi.LdapDnsProviderResult; 3.37 +import sun.security.util.SecurityConstants; 3.38 + 3.39 +/** 3.40 + * The {@code LdapDnsProviderService} is responsible for creating and providing 3.41 + * access to the registered {@code LdapDnsProvider}s. The {@link ServiceLoader} 3.42 + * is used to find and register any implementations of {@link LdapDnsProvider}. 3.43 + */ 3.44 +final class LdapDnsProviderService { 3.45 + 3.46 + private static volatile LdapDnsProviderService service; 3.47 + private static final Object LOCK = new int[0]; 3.48 + private final ServiceLoader<LdapDnsProvider> providers; 3.49 + 3.50 + /** 3.51 + * Creates a new instance of LdapDnsProviderService 3.52 + */ 3.53 + private LdapDnsProviderService() { 3.54 + SecurityManager sm = System.getSecurityManager(); 3.55 + if (sm == null) { 3.56 + providers = ServiceLoader.load( 3.57 + LdapDnsProvider.class, 3.58 + ClassLoader.getSystemClassLoader()); 3.59 + } else { 3.60 + final PrivilegedAction<ServiceLoader<LdapDnsProvider>> pa = 3.61 + () -> ServiceLoader.load( 3.62 + LdapDnsProvider.class, 3.63 + ClassLoader.getSystemClassLoader()); 3.64 + 3.65 + providers = AccessController.doPrivileged( 3.66 + pa, 3.67 + null, 3.68 + new RuntimePermission("ldapDnsProvider"), 3.69 + SecurityConstants.GET_CLASSLOADER_PERMISSION); 3.70 + } 3.71 + } 3.72 + 3.73 + /** 3.74 + * Retrieve the singleton static instance of LdapDnsProviderService. 3.75 + */ 3.76 + static LdapDnsProviderService getInstance() { 3.77 + if (service != null) return service; 3.78 + synchronized(LOCK) { 3.79 + if (service != null) return service; 3.80 + service = new LdapDnsProviderService(); 3.81 + } 3.82 + return service; 3.83 + } 3.84 + 3.85 + /** 3.86 + * Retrieve result from the first provider that successfully resolves 3.87 + * the endpoints. If no results are found when calling installed 3.88 + * subclasses of {@code LdapDnsProvider} then this method will fall back 3.89 + * to the {@code DefaultLdapDnsProvider}. 3.90 + * 3.91 + * @throws NamingException if the {@code url} in not valid or an error 3.92 + * occurred while performing the lookup. 3.93 + */ 3.94 + LdapDnsProviderResult lookupEndpoints(String url, Hashtable<?,?> env) 3.95 + throws NamingException 3.96 + { 3.97 + Iterator<LdapDnsProvider> iterator = providers.iterator(); 3.98 + Hashtable<?, ?> envCopy = new Hashtable<>(env); 3.99 + LdapDnsProviderResult result = null; 3.100 + 3.101 + while (result == null && iterator.hasNext()) { 3.102 + result = iterator.next().lookupEndpoints(url, envCopy) 3.103 + .filter(r -> r.getEndpoints().size() > 0) 3.104 + .orElse(null); 3.105 + } 3.106 + 3.107 + if (result == null) { 3.108 + return new DefaultLdapDnsProvider().lookupEndpoints(url, env) 3.109 + .orElse(new LdapDnsProviderResult("", Collections.emptyList())); 3.110 + } 3.111 + return result; 3.112 + } 3.113 + 3.114 +}
4.1 --- a/src/share/classes/com/sun/jndi/ldap/ServiceLocator.java Tue Aug 04 17:19:21 2020 -0300 4.2 +++ b/src/share/classes/com/sun/jndi/ldap/ServiceLocator.java Mon Nov 12 08:33:59 2018 -0800 4.3 @@ -1,5 +1,5 @@ 4.4 /* 4.5 - * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. 4.6 + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. 4.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.8 * 4.9 * This code is free software; you can redistribute it and/or modify it 4.10 @@ -25,11 +25,7 @@ 4.11 4.12 package com.sun.jndi.ldap; 4.13 4.14 -import java.util.Arrays; 4.15 -import java.util.Hashtable; 4.16 -import java.util.Random; 4.17 -import java.util.StringTokenizer; 4.18 -import java.util.List; 4.19 +import java.util.*; 4.20 4.21 import javax.naming.*; 4.22 import javax.naming.directory.*; 4.23 @@ -113,6 +109,23 @@ 4.24 * @return An ordered list of hostports for the LDAP service or null if 4.25 * the service has not been located. 4.26 */ 4.27 + static String[] getLdapService(String domainName, Map<?,?> environment) { 4.28 + if (environment instanceof Hashtable) { 4.29 + return getLdapService(domainName, (Hashtable)environment); 4.30 + } 4.31 + return getLdapService(domainName, new Hashtable<>(environment)); 4.32 + } 4.33 + 4.34 + /** 4.35 + * Locates the LDAP service for a given domain. 4.36 + * Queries DNS for a list of LDAP Service Location Records (SRV) for a 4.37 + * given domain name. 4.38 + * 4.39 + * @param domainName A string domain name. 4.40 + * @param environment The possibly null environment of the context. 4.41 + * @return An ordered list of hostports for the LDAP service or null if 4.42 + * the service has not been located. 4.43 + */ 4.44 static String[] getLdapService(String domainName, Hashtable<?,?> environment) { 4.45 4.46 if (domainName == null || domainName.length() == 0) {
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/share/classes/com/sun/jndi/ldap/spi/LdapDnsProvider.java Mon Nov 12 08:33:59 2018 -0800 5.3 @@ -0,0 +1,109 @@ 5.4 +/* 5.5 + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. Oracle designates this 5.11 + * particular file as subject to the "Classpath" exception as provided 5.12 + * by Oracle in the LICENSE file that accompanied this code. 5.13 + * 5.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.17 + * version 2 for more details (a copy is included in the LICENSE file that 5.18 + * accompanied this code). 5.19 + * 5.20 + * You should have received a copy of the GNU General Public License version 5.21 + * 2 along with this work; if not, write to the Free Software Foundation, 5.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.23 + * 5.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.25 + * or visit www.oracle.com if you need additional information or have any 5.26 + * questions. 5.27 + */ 5.28 + 5.29 +package com.sun.jndi.ldap.spi; 5.30 + 5.31 +import javax.naming.Context; 5.32 +import javax.naming.NamingException; 5.33 +import java.util.Map; 5.34 +import java.util.Optional; 5.35 + 5.36 +/** 5.37 + * Service-provider class for DNS lookups when performing LDAP operations. 5.38 + * 5.39 + * <p> An LDAP DNS provider is a concrete subclass of this class that 5.40 + * has a zero-argument constructor. LDAP DNS providers are located using the 5.41 + * ServiceLoader facility, as specified by 5.42 + * {@linkplain javax.naming.directory.InitialDirContext InitialDirectContext}. 5.43 + * 5.44 + * The 5.45 + * {@link java.util.ServiceLoader ServiceLoader} is used to create and register 5.46 + * implementations of {@code LdapDnsProvider}. 5.47 + * 5.48 + * <p> An LDAP DNS provider can be used in environments where the default 5.49 + * DNS resolution mechanism is not sufficient to accurately pinpoint the 5.50 + * correct LDAP servers needed to perform LDAP operations. For example, in an 5.51 + * environment containing a mix of {@code ldap} and {@code ldaps} servers 5.52 + * you may want the {@linkplain javax.naming.ldap.LdapContext LdapContext} 5.53 + * to query {@code ldaps} servers only. 5.54 + * 5.55 + * @since 12 5.56 + */ 5.57 +public abstract class LdapDnsProvider { 5.58 + 5.59 + // The {@code RuntimePermission("ldapDnsProvider")} is 5.60 + // necessary to subclass and instantiate the {@code LdapDnsProvider} class. 5.61 + private static final RuntimePermission DNSPROVIDER_PERMISSION = 5.62 + new RuntimePermission("ldapDnsProvider"); 5.63 + 5.64 + /** 5.65 + * Creates a new instance of {@code LdapDnsProvider}. 5.66 + * 5.67 + * @throws SecurityException if a security manager is present and its 5.68 + * {@code checkPermission} method doesn't allow 5.69 + * the {@code RuntimePermission("ldapDnsProvider")}. 5.70 + */ 5.71 + protected LdapDnsProvider() { 5.72 + this(checkPermission()); 5.73 + } 5.74 + 5.75 + private LdapDnsProvider(Void unused) { 5.76 + // nothing to do. 5.77 + } 5.78 + 5.79 + private static Void checkPermission() { 5.80 + final SecurityManager sm = System.getSecurityManager(); 5.81 + if (sm != null) { 5.82 + sm.checkPermission(DNSPROVIDER_PERMISSION); 5.83 + } 5.84 + return null; 5.85 + } 5.86 + 5.87 + /** 5.88 + * Lookup the endpoints and domain name for the given {@link Context} 5.89 + * {@link Context#PROVIDER_URL provider URL} and environment. The resolved 5.90 + * endpoints and domain name are returned as an 5.91 + * {@link LdapDnsProviderResult}. 5.92 + * 5.93 + * <p> An endpoint is a {@code String} representation of an LDAP URL which 5.94 + * points to an LDAP server to be used for LDAP operations. The syntax of 5.95 + * an LDAP URL is defined by <a href="http://www.ietf.org/rfc/rfc2255.txt"> 5.96 + * <i>RFC 2255: The LDAP URL Format</i></a>. 5.97 + * 5.98 + * @param url The {@link Context} {@link Context#PROVIDER_URL provider URL} 5.99 + * @param env The {@link Context} environment. 5.100 + * 5.101 + * @return an {@link LdapDnsProviderResult} or empty {@code Optional} 5.102 + * if the lookup fails. 5.103 + * 5.104 + * @throws NamingException if the {@code url} is not valid or an error 5.105 + * occurred while performing the lookup. 5.106 + * @throws NullPointerException if either {@code url} or {@code env} are 5.107 + * {@code null}. 5.108 + */ 5.109 + public abstract Optional<LdapDnsProviderResult> lookupEndpoints( 5.110 + String url, Map<?,?> env) throws NamingException; 5.111 + 5.112 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/share/classes/com/sun/jndi/ldap/spi/LdapDnsProviderResult.java Mon Nov 12 08:33:59 2018 -0800 6.3 @@ -0,0 +1,88 @@ 6.4 +/* 6.5 + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.7 + * 6.8 + * This code is free software; you can redistribute it and/or modify it 6.9 + * under the terms of the GNU General Public License version 2 only, as 6.10 + * published by the Free Software Foundation. Oracle designates this 6.11 + * particular file as subject to the "Classpath" exception as provided 6.12 + * by Oracle in the LICENSE file that accompanied this code. 6.13 + * 6.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.17 + * version 2 for more details (a copy is included in the LICENSE file that 6.18 + * accompanied this code). 6.19 + * 6.20 + * You should have received a copy of the GNU General Public License version 6.21 + * 2 along with this work; if not, write to the Free Software Foundation, 6.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.23 + * 6.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.25 + * or visit www.oracle.com if you need additional information or have any 6.26 + * questions. 6.27 + */ 6.28 + 6.29 +package com.sun.jndi.ldap.spi; 6.30 + 6.31 +import java.util.List; 6.32 +import java.util.ArrayList; 6.33 + 6.34 +/** 6.35 + * The result of a DNS lookup for an LDAP URL. 6.36 + * 6.37 + * <p> This class is used by an {@link LdapDnsProvider} to return the result 6.38 + * of a DNS lookup for a given LDAP URL. The result consists of a domain name 6.39 + * and its associated ldap server endpoints. 6.40 + * 6.41 + * <p> A {@code null} {@code domainName} is equivalent to and represented 6.42 + * by an empty string. 6.43 + * 6.44 + * @since 12 6.45 + */ 6.46 +public final class LdapDnsProviderResult { 6.47 + 6.48 + private final String domainName; 6.49 + private final List<String> endpoints; 6.50 + 6.51 + /** 6.52 + * Construct an LdapDnsProviderResult consisting of a resolved domain name 6.53 + * and the ldap server endpoints that serve the domain. 6.54 + * 6.55 + * @param domainName the resolved domain name; can be null. 6.56 + * @param endpoints the possibly empty list of resolved ldap server 6.57 + * endpoints 6.58 + * 6.59 + * @throws NullPointerException if {@code endpoints} contains {@code null} 6.60 + * elements. 6.61 + * @throws ClassCastException if {@code endpoints} contains non- 6.62 + * {@code String} elements. 6.63 + */ 6.64 + public LdapDnsProviderResult(String domainName, List<String> endpoints) { 6.65 + this.domainName = (domainName == null) ? "" : domainName; 6.66 + this.endpoints = new ArrayList<>(endpoints); 6.67 + } 6.68 + 6.69 + /** 6.70 + * Returns the domain name resolved from the ldap URL. This method returns 6.71 + * the empty string if the {@code LdapDnsProviderResult} is created with a 6.72 + * null domain name. 6.73 + * 6.74 + * @return the resolved domain name 6.75 + */ 6.76 + public String getDomainName() { 6.77 + return domainName; 6.78 + } 6.79 + 6.80 + /** 6.81 + * Returns the possibly empty list of individual server endpoints resolved 6.82 + * from the ldap URL. 6.83 + * 6.84 + * @return a possibly empty unmodifiable {@link List} containing the 6.85 + * resolved ldap server endpoints 6.86 + */ 6.87 + public List<String> getEndpoints() { 6.88 + return endpoints; 6.89 + } 6.90 + 6.91 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/test/com/sun/jndi/ldap/LdapDnsProviderTest.java Mon Nov 12 08:33:59 2018 -0800 7.3 @@ -0,0 +1,230 @@ 7.4 +/* 7.5 + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 7.7 + * 7.8 + * This code is free software; you can redistribute it and/or modify it 7.9 + * under the terms of the GNU General Public License version 2 only, as 7.10 + * published by the Free Software Foundation. Oracle designates this 7.11 + * particular file as subject to the "Classpath" exception as provided 7.12 + * by Oracle in the LICENSE file that accompanied this code. 7.13 + * 7.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 7.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 7.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 7.17 + * version 2 for more details (a copy is included in the LICENSE file that 7.18 + * accompanied this code). 7.19 + * 7.20 + * You should have received a copy of the GNU General Public License version 7.21 + * 2 along with this work; if not, write to the Free Software Foundation, 7.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 7.23 + * 7.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 7.25 + * or visit www.oracle.com if you need additional information or have any 7.26 + * questions. 7.27 + */ 7.28 + 7.29 +import java.io.File; 7.30 +import java.io.FileOutputStream; 7.31 +import java.io.IOException; 7.32 +import java.security.Permission; 7.33 +import java.util.Hashtable; 7.34 +import java.util.concurrent.Callable; 7.35 +import java.util.concurrent.FutureTask; 7.36 + 7.37 +import javax.naming.Context; 7.38 +import javax.naming.InitialContext; 7.39 +import javax.naming.NamingException; 7.40 +import javax.naming.directory.InitialDirContext; 7.41 +import javax.naming.directory.SearchControls; 7.42 + 7.43 +/** 7.44 + * @test 7.45 + * @bug 8160768 7.46 + * @summary ctx provider tests for ldap 7.47 + * @compile dnsprovider/TestDnsProvider.java 7.48 + * @run main/othervm LdapDnsProviderTest 7.49 + * @run main/othervm LdapDnsProviderTest nosm 7.50 + * @run main/othervm LdapDnsProviderTest smnodns 7.51 + * @run main/othervm LdapDnsProviderTest smdns 7.52 + * @run main/othervm LdapDnsProviderTest nosmbaddns 7.53 + */ 7.54 + 7.55 +class DNSSecurityManager extends SecurityManager { 7.56 + 7.57 + 7.58 + 7.59 + /* run main/othervm LdapDnsProviderTest 7.60 + 7.61 + * run main/othervm LdapDnsProviderTest nosm 7.62 + * run main/othervm LdapDnsProviderTest smnodns 7.63 + * run main/othervm LdapDnsProviderTest smdns 7.64 + * run main/othervm LdapDnsProviderTest nosmbaddns 7.65 + */ 7.66 + 7.67 + private boolean dnsProvider = false; 7.68 + 7.69 + public void setAllowDnsProvider(boolean allow) { 7.70 + dnsProvider = allow; 7.71 + } 7.72 + 7.73 + @Override 7.74 + public void checkPermission(Permission p) { 7.75 + if (p.getName().equals("ldapDnsProvider") && !dnsProvider) { 7.76 + throw new SecurityException(p.getName()); 7.77 + } 7.78 + } 7.79 +} 7.80 + 7.81 +class ProviderTest implements Callable<Boolean> { 7.82 + 7.83 + private final String url; 7.84 + private final String expected; 7.85 + private final Hashtable<String, String> env = new Hashtable<>(11); 7.86 + 7.87 + public ProviderTest(String url, String expected) { 7.88 + this.url = url; 7.89 + this.expected = expected; 7.90 + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 7.91 + } 7.92 + 7.93 + boolean shutItDown(InitialContext ctx) { 7.94 + try { 7.95 + if (ctx != null) ctx.close(); 7.96 + return true; 7.97 + } catch (NamingException ex) { 7.98 + return false; 7.99 + } 7.100 + } 7.101 + 7.102 + public Boolean call() { 7.103 + boolean passed; 7.104 + InitialContext ctx = null; 7.105 + 7.106 + if (url != null) { 7.107 + env.put(Context.PROVIDER_URL, url); 7.108 + } 7.109 + 7.110 + try { 7.111 + ctx = new InitialDirContext(env); 7.112 + SearchControls scl = new SearchControls(); 7.113 + scl.setSearchScope(SearchControls.SUBTREE_SCOPE); 7.114 + ((InitialDirContext)ctx).search( 7.115 + "ou=People,o=Test", "(objectClass=*)", scl); 7.116 + throw new RuntimeException("Search should not complete"); 7.117 + } catch (NamingException e) { 7.118 + e.printStackTrace(); 7.119 + passed = e.toString().contains(expected); 7.120 + } finally { 7.121 + shutItDown(ctx); 7.122 + } 7.123 + return passed; 7.124 + } 7.125 +} 7.126 + 7.127 +public class LdapDnsProviderTest { 7.128 + 7.129 + private static final String TEST_CLASSES = 7.130 + System.getProperty("test.classes", "."); 7.131 + 7.132 + public static void writeFile(String content, File dstFile) 7.133 + throws IOException 7.134 + { 7.135 + try (FileOutputStream dst = new FileOutputStream(dstFile)) { 7.136 + byte[] buf = content.getBytes(); 7.137 + dst.write(buf, 0, buf.length); 7.138 + } 7.139 + } 7.140 + 7.141 + public static void installServiceConfigurationFile(String content) { 7.142 + String filename = "com.sun.jndi.ldap.spi.LdapDnsProvider"; 7.143 + 7.144 + File dstDir = new File(TEST_CLASSES, "META-INF/services"); 7.145 + if (!dstDir.exists()) { 7.146 + if (!dstDir.mkdirs()) { 7.147 + throw new RuntimeException( 7.148 + "could not create META-INF/services directory " + dstDir); 7.149 + } 7.150 + } 7.151 + File dstFile = new File(dstDir, filename); 7.152 + 7.153 + try { 7.154 + writeFile(content, dstFile); 7.155 + } catch (IOException e) { 7.156 + throw new RuntimeException("could not install " + dstFile, e); 7.157 + } 7.158 + } 7.159 + 7.160 + public static void main(String[] args) throws Exception { 7.161 + if (args.length > 0 && args[0].equals("nosm")) { 7.162 + // no security manager, serviceloader 7.163 + installServiceConfigurationFile("dnsprovider.TestDnsProvider"); 7.164 + runTest("ldap:///dc=example,dc=com", "yupyupyup:389"); 7.165 + } else if (args.length > 0 && args[0].equals("smnodns")) { 7.166 + // security manager & serviceloader 7.167 + installServiceConfigurationFile("dnsprovider.TestDnsProvider"); 7.168 + // install security manager 7.169 + System.setSecurityManager(new DNSSecurityManager()); 7.170 + runTest("ldap:///dc=example,dc=com", "ServiceConfigurationError"); 7.171 + } else if (args.length > 0 && args[0].equals("smdns")) { 7.172 + // security manager & serviceloader 7.173 + DNSSecurityManager sm = new DNSSecurityManager(); 7.174 + installServiceConfigurationFile("dnsprovider.TestDnsProvider"); 7.175 + // install security manager 7.176 + System.setSecurityManager(sm); 7.177 + sm.setAllowDnsProvider(true); 7.178 + runTest("ldap:///dc=example,dc=com", "yupyupyup:389"); 7.179 + } else if (args.length > 0 && args[0].equals("nosmbaddns")) { 7.180 + // no security manager, no serviceloader 7.181 + // DefaultLdapDnsProvider 7.182 + installServiceConfigurationFile("dnsprovider.MissingDnsProvider"); 7.183 + // no SecurityManager 7.184 + runTest("ldap:///dc=example,dc=com", "not found"); 7.185 + } else { 7.186 + // no security manager, no serviceloader 7.187 + // DefaultLdapDnsProvider 7.188 + System.err.println("TEST_CLASSES:"); 7.189 + System.err.println(TEST_CLASSES); 7.190 + File f = new File( 7.191 + TEST_CLASSES, "META-INF/services/com.sun.jndi.ldap.spi.LdapDnsProvider"); 7.192 + if (f.exists()) { 7.193 + f.delete(); 7.194 + } 7.195 + 7.196 + // no SecurityManager 7.197 + runTest("ldap:///dc=example,dc=com", "localhost:389"); 7.198 + runTest("ldap://localhost/dc=example,dc=com", "localhost:389"); 7.199 + runTest("ldap://localhost:111/dc=example,dc=com", "localhost:111"); 7.200 + runTest("ldaps://localhost:111/dc=example,dc=com", "localhost:111"); 7.201 + runTest("ldaps://localhost/dc=example,dc=com", "localhost:636"); 7.202 + runTest(null, "localhost:389"); 7.203 + runTest("", "ConfigurationException"); 7.204 + } 7.205 + } 7.206 + 7.207 + private static void runTest(String url, String expected) { 7.208 + FutureTask<Boolean> future = 7.209 + new FutureTask<>( 7.210 + new ProviderTest(url, expected)); 7.211 + new Thread(future).start(); 7.212 + 7.213 + System.err.println("Testing: " + url + ", " + expected); 7.214 + while (!future.isDone()) { 7.215 + try { 7.216 + if (!future.get()) { 7.217 + System.err.println("Test failed"); 7.218 + throw new RuntimeException( 7.219 + "Test failed, ProviderTest returned false"); 7.220 + } 7.221 + } catch (Exception e) { 7.222 + if (!e.toString().contains(expected)) { 7.223 + System.err.println("Test failed"); 7.224 + throw new RuntimeException( 7.225 + "Test failed, unexpected result"); 7.226 + } 7.227 + } 7.228 + } 7.229 + System.err.println("Test passed"); 7.230 + } 7.231 + 7.232 +} 7.233 +
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/test/com/sun/jndi/ldap/dnsprovider/TestDnsProvider.java Mon Nov 12 08:33:59 2018 -0800 8.3 @@ -0,0 +1,20 @@ 8.4 +package dnsprovider; 8.5 + 8.6 +import java.util.ArrayList; 8.7 +import java.util.List; 8.8 +import java.util.Map; 8.9 +import java.util.Optional; 8.10 +import com.sun.jndi.ldap.spi.LdapDnsProvider; 8.11 +import com.sun.jndi.ldap.spi.LdapDnsProviderResult; 8.12 + 8.13 +public class TestDnsProvider extends LdapDnsProvider { 8.14 + @Override 8.15 + public Optional<LdapDnsProviderResult> lookupEndpoints(String url, 8.16 + Map<?, ?> env) 8.17 + { 8.18 + List<String> endpoints = new ArrayList<>(); 8.19 + endpoints.add("ldap://yupyupyup:389"); 8.20 + return Optional.of( 8.21 + new LdapDnsProviderResult("test.com", endpoints)); 8.22 + } 8.23 +}
9.1 --- a/test/com/sun/security/auth/module/LdapLoginModule/CheckConfigs.policy Tue Aug 04 17:19:21 2020 -0300 9.2 +++ b/test/com/sun/security/auth/module/LdapLoginModule/CheckConfigs.policy Mon Nov 12 08:33:59 2018 -0800 9.3 @@ -6,6 +6,7 @@ 9.4 //permission java.net.SocketPermission "*:389", "connect"; 9.5 //permission java.net.SocketPermission "*:636", "connect"; 9.6 //permission javax.security.auth.AuthPermission "modifyPrincipals"; 9.7 + permission java.lang.RuntimePermission "ldapDnsProvider"; 9.8 }; 9.9 9.10