1 /* 2 * Copyright (c) 2000, 2011, 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 javax.naming.ldap; 27 28 import java.util.Iterator; 29 import java.security.AccessController; 30 import java.security.PrivilegedAction; 31 import javax.naming.ConfigurationException; 32 import javax.naming.NamingException; 33 import com.sun.naming.internal.VersionHelper; 34 import java.util.ServiceLoader; 35 import java.util.ServiceConfigurationError; 36 37 /** 38 * This class implements the LDAPv3 Extended Request for StartTLS as 39 * defined in 40 * <a href="http://www.ietf.org/rfc/rfc2830.txt">Lightweight Directory 41 * Access Protocol (v3): Extension for Transport Layer Security</a> 42 * 43 * The object identifier for StartTLS is 1.3.6.1.4.1.1466.20037 44 * and no extended request value is defined. 45 *<p> 46 * {@code StartTlsRequest}/{@code StartTlsResponse} are used to establish 47 * a TLS connection over the existing LDAP connection associated with 48 * the JNDI context on which {@code extendedOperation()} is invoked. 49 * Typically, a JNDI program uses these classes as follows. 50 * <blockquote><pre> 51 * import javax.naming.ldap.*; 52 * 53 * // Open an LDAP association 54 * LdapContext ctx = new InitialLdapContext(); 55 * 56 * // Perform a StartTLS extended operation 57 * StartTlsResponse tls = 58 * (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest()); 59 * 60 * // Open a TLS connection (over the existing LDAP association) and get details 61 * // of the negotiated TLS session: cipher suite, peer certificate, etc. 62 * SSLSession session = tls.negotiate(); 63 * 64 * // ... use ctx to perform protected LDAP operations 65 * 66 * // Close the TLS connection (revert back to the underlying LDAP association) 67 * tls.close(); 68 * 69 * // ... use ctx to perform unprotected LDAP operations 70 * 71 * // Close the LDAP association 72 * ctx.close; 73 * </pre></blockquote> 74 * 75 * @since 1.4 76 * @see StartTlsResponse 77 * @author Vincent Ryan 78 */ 79 public class StartTlsRequest implements ExtendedRequest { 80 81 // Constant 82 83 /** 84 * The StartTLS extended request's assigned object identifier 85 * is 1.3.6.1.4.1.1466.20037. 86 */ 87 public static final String OID = "1.3.6.1.4.1.1466.20037"; 88 89 90 // Constructors 91 92 /** 93 * Constructs a StartTLS extended request. 94 */ 95 public StartTlsRequest() { 96 } 97 98 99 // ExtendedRequest methods 100 101 /** 102 * Retrieves the StartTLS request's object identifier string. 103 * 104 * @return The object identifier string, "1.3.6.1.4.1.1466.20037". 105 */ 106 public String getID() { 107 return OID; 108 } 109 110 /** 111 * Retrieves the StartTLS request's ASN.1 BER encoded value. 112 * Since the request has no defined value, null is always 113 * returned. 114 * 115 * @return The null value. 116 */ 117 public byte[] getEncodedValue() { 118 return null; 119 } 120 121 /** 122 * Creates an extended response object that corresponds to the 123 * LDAP StartTLS extended request. 124 * <p> 125 * The result must be a concrete subclass of StartTlsResponse 126 * and must have a public zero-argument constructor. 127 * <p> 128 * This method locates the implementation class by locating 129 * configuration files that have the name: 130 * <blockquote>{@code 131 * META-INF/services/javax.naming.ldap.StartTlsResponse 132 * }</blockquote> 133 * The configuration files and their corresponding implementation classes must 134 * be accessible to the calling thread's context class loader. 135 * <p> 136 * Each configuration file should contain a list of fully-qualified class 137 * names, one per line. Space and tab characters surrounding each name, as 138 * well as blank lines, are ignored. The comment character is {@code '#'} 139 * ({@code 0x23}); on each line all characters following the first comment 140 * character are ignored. The file must be encoded in UTF-8. 141 * <p> 142 * This method will return an instance of the first implementation 143 * class that it is able to load and instantiate successfully from 144 * the list of class names collected from the configuration files. 145 * This method uses the calling thread's context classloader to find the 146 * configuration files and to load the implementation class. 147 * <p> 148 * If no class can be found in this way, this method will use 149 * an implementation-specific way to locate an implementation. 150 * If none is found, a NamingException is thrown. 151 * 152 * @param id The object identifier of the extended response. 153 * Its value must be "1.3.6.1.4.1.1466.20037" or null. 154 * Both values are equivalent. 155 * @param berValue The possibly null ASN.1 BER encoded value of the 156 * extended response. This is the raw BER bytes 157 * including the tag and length of the response value. 158 * It does not include the response OID. 159 * Its value is ignored because a Start TLS response 160 * is not expected to contain any response value. 161 * @param offset The starting position in berValue of the bytes to use. 162 * Its value is ignored because a Start TLS response 163 * is not expected to contain any response value. 164 * @param length The number of bytes in berValue to use. 165 * Its value is ignored because a Start TLS response 166 * is not expected to contain any response value. 167 * @return The StartTLS extended response object. 168 * @exception NamingException If a naming exception was encountered 169 * while creating the StartTLS extended response object. 170 */ 171 public ExtendedResponse createExtendedResponse(String id, byte[] berValue, 172 int offset, int length) throws NamingException { 173 174 // Confirm that the object identifier is correct 175 if ((id != null) && (!id.equals(OID))) { 176 throw new ConfigurationException( 177 "Start TLS received the following response instead of " + 178 OID + ": " + id); 179 } 180 181 StartTlsResponse resp = null; 182 183 ServiceLoader<StartTlsResponse> sl = ServiceLoader.load( 184 StartTlsResponse.class, getContextClassLoader()); 185 Iterator<StartTlsResponse> iter = sl.iterator(); 186 187 while (resp == null && privilegedHasNext(iter)) { 188 resp = iter.next(); 189 } 190 if (resp != null) { 191 return resp; 192 } 193 try { 194 VersionHelper helper = VersionHelper.getVersionHelper(); 195 Class<?> clas = helper.loadClass( 196 "com.sun.jndi.ldap.ext.StartTlsResponseImpl"); 197 198 resp = (StartTlsResponse) clas.newInstance(); 199 200 } catch (IllegalAccessException e) { 201 throw wrapException(e); 202 203 } catch (InstantiationException e) { 204 throw wrapException(e); 205 206 } catch (ClassNotFoundException e) { 207 throw wrapException(e); 208 } 209 210 return resp; 211 } 212 213 /* 214 * Wrap an exception, thrown while attempting to load the StartTlsResponse 215 * class, in a configuration exception. 216 */ 217 private ConfigurationException wrapException(Exception e) { 218 ConfigurationException ce = new ConfigurationException( 219 "Cannot load implementation of javax.naming.ldap.StartTlsResponse"); 220 221 ce.setRootCause(e); 222 return ce; 223 } 224 225 /* 226 * Acquire the class loader associated with this thread. 227 */ 228 private final ClassLoader getContextClassLoader() { 229 return AccessController.doPrivileged( 230 new PrivilegedAction<ClassLoader>() { 231 public ClassLoader run() { 232 return Thread.currentThread().getContextClassLoader(); 233 } 234 } 235 ); 236 } 237 238 private final static boolean privilegedHasNext(final Iterator<StartTlsResponse> iter) { 239 Boolean answer = AccessController.doPrivileged( 240 new PrivilegedAction<Boolean>() { 241 public Boolean run() { 242 return Boolean.valueOf(iter.hasNext()); 243 } 244 }); 245 return answer.booleanValue(); 246 } 247 248 private static final long serialVersionUID = 4441679576360753397L; 249 }