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
26package javax.naming.ldap;
27
28import java.util.Iterator;
29import java.security.AccessController;
30import java.security.PrivilegedAction;
31import javax.naming.ConfigurationException;
32import javax.naming.NamingException;
33import com.sun.naming.internal.VersionHelper;
34import java.util.ServiceLoader;
35import 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 */
79public 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            @SuppressWarnings("deprecation")
196            Object o = helper.loadClass(
197                "com.sun.jndi.ldap.ext.StartTlsResponseImpl").newInstance();
198            resp = (StartTlsResponse) o;
199
200        } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) {
201            throw wrapException(e);
202        }
203
204        return resp;
205    }
206
207    /*
208     * Wrap an exception, thrown while attempting to load the StartTlsResponse
209     * class, in a configuration exception.
210     */
211    private ConfigurationException wrapException(Exception e) {
212        ConfigurationException ce = new ConfigurationException(
213            "Cannot load implementation of javax.naming.ldap.StartTlsResponse");
214
215        ce.setRootCause(e);
216        return ce;
217    }
218
219    /*
220     * Acquire the class loader associated with this thread.
221     */
222    private final ClassLoader getContextClassLoader() {
223        return AccessController.doPrivileged(
224            new PrivilegedAction<ClassLoader>() {
225                public ClassLoader run() {
226                    return Thread.currentThread().getContextClassLoader();
227                }
228            }
229        );
230    }
231
232    private final static boolean privilegedHasNext(final Iterator<StartTlsResponse> iter) {
233        Boolean answer = AccessController.doPrivileged(
234            new PrivilegedAction<Boolean>() {
235            public Boolean run() {
236                return Boolean.valueOf(iter.hasNext());
237            }
238        });
239        return answer.booleanValue();
240    }
241
242    private static final long serialVersionUID = 4441679576360753397L;
243}
244