8160768: Add capability to custom resolve host/domain names within the default JNDI LDAP provider

Mon, 12 Nov 2018 08:33:59 -0800

author
robm
date
Mon, 12 Nov 2018 08:33:59 -0800
changeset 14183
7aaaf8998988
parent 14182
5f94741b3fc7
child 14184
7747b41df314

8160768: Add capability to custom resolve host/domain names within the default JNDI LDAP provider
Reviewed-by: alanb, dfuchs, chegar, mchung, vtewari

src/share/classes/com/sun/jndi/ldap/DefaultLdapDnsProvider.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/jndi/ldap/LdapCtxFactory.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/jndi/ldap/ServiceLocator.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/jndi/ldap/spi/LdapDnsProvider.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/jndi/ldap/spi/LdapDnsProviderResult.java file | annotate | diff | comparison | revisions
test/com/sun/jndi/ldap/LdapDnsProviderTest.java file | annotate | diff | comparison | revisions
test/com/sun/jndi/ldap/dnsprovider/TestDnsProvider.java file | annotate | diff | comparison | revisions
test/com/sun/security/auth/module/LdapLoginModule/CheckConfigs.policy file | annotate | diff | comparison | revisions
     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&nbsp;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  

mercurial