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