1 /*
   2  * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.jndi.ldap;
  27 
  28 import java.util.Hashtable;
  29 import java.util.Vector;
  30 import java.util.Enumeration;
  31 
  32 import javax.naming.*;
  33 import javax.naming.directory.*;
  34 import javax.naming.spi.ObjectFactory;
  35 import javax.naming.spi.InitialContextFactory;
  36 import javax.naming.ldap.Control;
  37 
  38 import com.sun.jndi.url.ldap.ldapURLContextFactory;
  39 
  40 final public class LdapCtxFactory implements ObjectFactory, InitialContextFactory {
  41     /**
  42      * The type of each address in an LDAP reference.
  43      */
  44     public final static String ADDRESS_TYPE = "URL";
  45 
  46     // ----------------- ObjectFactory interface --------------------
  47 
  48     public Object getObjectInstance(Object ref, Name name, Context nameCtx,
  49         Hashtable<?,?> env) throws Exception {
  50 
  51         if (!isLdapRef(ref)) {
  52             return null;
  53         }
  54         ObjectFactory factory = new ldapURLContextFactory();
  55         String[] urls = getURLs((Reference)ref);
  56         return factory.getObjectInstance(urls, name, nameCtx, env);
  57     }
  58 
  59     // ----------------- InitialContext interface  --------------------
  60 
  61     public Context getInitialContext(Hashtable<?,?> envprops)
  62         throws NamingException {
  63 
  64         try {
  65             String providerUrl = (envprops != null) ?
  66                 (String)envprops.get(Context.PROVIDER_URL) : null;
  67 
  68             // If URL not in environment, use defaults
  69             if (providerUrl == null) {
  70                 return new LdapCtx("", LdapCtx.DEFAULT_HOST,
  71                     LdapCtx.DEFAULT_PORT, envprops, false);
  72             }
  73 
  74             // Extract URL(s)
  75             String[] urls = LdapURL.fromList(providerUrl);
  76 
  77             if (urls.length == 0) {
  78                 throw new ConfigurationException(Context.PROVIDER_URL +
  79                     " property does not contain a URL");
  80             }
  81 
  82             // Generate an LDAP context
  83             return getLdapCtxInstance(urls, envprops);
  84 
  85         } catch (LdapReferralException e) {
  86 
  87             if (envprops != null &&
  88                 "throw".equals(envprops.get(Context.REFERRAL))) {
  89                 throw e;
  90             }
  91 
  92             Control[] bindCtls = (envprops != null)?
  93                 (Control[])envprops.get(LdapCtx.BIND_CONTROLS) : null;
  94 
  95             return (LdapCtx)e.getReferralContext(envprops, bindCtls);
  96         }
  97     }
  98 
  99     /**
 100      * Returns true if argument is an LDAP reference.
 101      */
 102     private static boolean isLdapRef(Object obj) {
 103 
 104         if (!(obj instanceof Reference)) {
 105             return false;
 106         }
 107         String thisClassName = LdapCtxFactory.class.getName();
 108         Reference ref = (Reference)obj;
 109 
 110         return thisClassName.equals(ref.getFactoryClassName());
 111     }
 112 
 113     /**
 114      * Returns the URLs contained within an LDAP reference.
 115      */
 116     private static String[] getURLs(Reference ref) throws NamingException {
 117 
 118         int size = 0;   // number of URLs
 119         String[] urls = new String[ref.size()];
 120 
 121         Enumeration<RefAddr> addrs = ref.getAll();
 122         while (addrs.hasMoreElements()) {
 123             RefAddr addr = addrs.nextElement();
 124 
 125             if ((addr instanceof StringRefAddr) &&
 126                 addr.getType().equals(ADDRESS_TYPE)) {
 127 
 128                 urls[size++] = (String)addr.getContent();
 129             }
 130         }
 131         if (size == 0) {
 132             throw (new ConfigurationException(
 133                     "Reference contains no valid addresses"));
 134         }
 135 
 136         // Trim URL array down to size.
 137         if (size == ref.size()) {
 138             return urls;
 139         }
 140         String[] urls2 = new String[size];
 141         System.arraycopy(urls, 0, urls2, 0, size);
 142         return urls2;
 143     }
 144 
 145     // ------------ Utilities used by other classes ----------------
 146 
 147     public static DirContext getLdapCtxInstance(Object urlInfo, Hashtable<?,?> env)
 148             throws NamingException {
 149 
 150         if (urlInfo instanceof String) {
 151             return getUsingURL((String)urlInfo, env);
 152         } else if (urlInfo instanceof String[]) {
 153             return getUsingURLs((String[])urlInfo, env);
 154         } else {
 155             throw new IllegalArgumentException(
 156                 "argument must be an LDAP URL String or array of them");
 157         }
 158     }
 159 
 160     private static DirContext getUsingURL(String url, Hashtable<?,?> env)
 161             throws NamingException {
 162         DirContext ctx = null;
 163         LdapURL ldapUrl = new LdapURL(url);
 164         String dn = ldapUrl.getDN();
 165         String host = ldapUrl.getHost();
 166         int port = ldapUrl.getPort();
 167         String[] hostports;
 168         String domainName = null;
 169 
 170         // handle a URL with no hostport (ldap:/// or ldaps:///)
 171         // locate the LDAP service using the URL's distinguished name
 172         if (host == null &&
 173             port == -1 &&
 174             dn != null &&
 175             (domainName = ServiceLocator.mapDnToDomainName(dn)) != null &&
 176             (hostports = ServiceLocator.getLdapService(domainName, env))
 177                 != null) {
 178             // Generate new URLs that include the discovered hostports.
 179             // Reuse the original URL scheme.
 180             String scheme = ldapUrl.getScheme() + "://";
 181             String[] newUrls = new String[hostports.length];
 182             String query = ldapUrl.getQuery();
 183             String urlSuffix = ldapUrl.getPath() + (query != null ? query : "");
 184             for (int i = 0; i < hostports.length; i++) {
 185                 newUrls[i] = scheme + hostports[i] + urlSuffix;
 186             }
 187             ctx = getUsingURLs(newUrls, env);
 188             // Associate the derived domain name with the context
 189             ((LdapCtx)ctx).setDomainName(domainName);
 190 
 191         } else {
 192             ctx = new LdapCtx(dn, host, port, env, ldapUrl.useSsl());
 193             // Record the URL that created the context
 194             ((LdapCtx)ctx).setProviderUrl(url);
 195         }
 196         return ctx;
 197     }
 198 
 199     /*
 200      * Try each URL until one of them succeeds.
 201      * If all URLs fail, throw one of the exceptions arbitrarily.
 202      * Not pretty, but potentially more informative than returning null.
 203      */
 204     private static DirContext getUsingURLs(String[] urls, Hashtable<?,?> env)
 205             throws NamingException {
 206         NamingException ne = null;
 207         DirContext ctx = null;
 208         for (int i = 0; i < urls.length; i++) {
 209             try {
 210                 return getUsingURL(urls[i], env);
 211             } catch (AuthenticationException e) {
 212                 throw e;
 213             } catch (NamingException e) {
 214                 ne = e;
 215             }
 216         }
 217         throw ne;
 218     }
 219 
 220     /**
 221      * Used by Obj and obj/RemoteToAttrs too so must be public
 222      */
 223     public static Attribute createTypeNameAttr(Class<?> cl) {
 224         Vector<String> v = new Vector<>(10);
 225         String[] types = getTypeNames(cl, v);
 226         if (types.length > 0) {
 227             BasicAttribute tAttr =
 228                 new BasicAttribute(Obj.JAVA_ATTRIBUTES[Obj.TYPENAME]);
 229             for (int i = 0; i < types.length; i++) {
 230                 tAttr.add(types[i]);
 231             }
 232             return tAttr;
 233         }
 234         return null;
 235     }
 236 
 237     private static String[] getTypeNames(Class<?> currentClass, Vector<String> v) {
 238 
 239         getClassesAux(currentClass, v);
 240         Class<?>[] members = currentClass.getInterfaces();
 241         for (int i = 0; i < members.length; i++) {
 242             getClassesAux(members[i], v);
 243         }
 244         String[] ret = new String[v.size()];
 245         int i = 0;
 246 
 247         for (String name : v) {
 248             ret[i++] = name;
 249         }
 250         return ret;
 251     }
 252 
 253     private static void getClassesAux(Class<?> currentClass, Vector<String> v) {
 254         if (!v.contains(currentClass.getName())) {
 255             v.addElement(currentClass.getName());
 256         }
 257         currentClass = currentClass.getSuperclass();
 258 
 259         while (currentClass != null) {
 260             getTypeNames(currentClass, v);
 261             currentClass = currentClass.getSuperclass();
 262         }
 263     }
 264 }