001 /*
002 * Copyright 2007-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2013 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk.extensions;
022
023
024
025 import javax.net.ssl.SSLContext;
026
027 import com.unboundid.ldap.sdk.Control;
028 import com.unboundid.ldap.sdk.ExtendedRequest;
029 import com.unboundid.ldap.sdk.ExtendedResult;
030 import com.unboundid.ldap.sdk.InternalSDKHelper;
031 import com.unboundid.ldap.sdk.LDAPConnection;
032 import com.unboundid.ldap.sdk.LDAPException;
033 import com.unboundid.ldap.sdk.ResultCode;
034 import com.unboundid.util.NotMutable;
035 import com.unboundid.util.ThreadSafety;
036 import com.unboundid.util.ThreadSafetyLevel;
037 import com.unboundid.util.ssl.SSLUtil;
038
039 import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
040 import static com.unboundid.util.Debug.*;
041
042
043
044 /**
045 * This class provides an implementation of the LDAP StartTLS extended request
046 * as defined in <A HREF="http://www.ietf.org/rfc/rfc4511.txt">RFC 4511</A>
047 * section 4.14. It may be used to establish a secure communication channel
048 * over an otherwise unencrypted connection.
049 * <BR><BR>
050 * Note that when using the StartTLS extended operation, you should establish
051 * a connection to the server's unencrypted LDAP port rather than its secure
052 * port. Then, you can use the StartTLS extended request in order to secure
053 * that connection.
054 * <BR><BR>
055 * <H2>Example</H2>
056 * The following example attempts to use the StartTLS extended request in order
057 * to secure communication on a previously insecure connection. In this case,
058 * it will use the {@link com.unboundid.util.ssl.SSLUtil} class in conjunction
059 * with the {@link com.unboundid.util.ssl.TrustAllTrustManager} class to
060 * simplify the process of performing the SSL negotiation by blindly trusting
061 * whatever certificate the server might happen to present. In real-world
062 * applications, if stronger verification is required then it is recommended
063 * that you use an {@link SSLContext} that is configured to perform an
064 * appropriate level of validation.
065 * <PRE>
066 * SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
067 * SSLContext sslContext = sslUtil.createSSLContext();
068 * ExtendedResult extendedResult = connection.processExtendedOperation(
069 * new StartTLSExtendedRequest(sslContext));
070 *
071 * // NOTE: The processExtendedOperation method will only throw an exception
072 * // if a problem occurs while trying to send the request or read the
073 * // response. It will not throw an exception because of a non-success
074 * // response.
075 *
076 * if (extendedResult.getResultCode() == ResultCode.SUCCESS)
077 * {
078 * System.out.println("Communication with the server is now secure.");
079 * }
080 * else
081 * {
082 * System.err.println("An error occurred while attempting to perform " +
083 * "StartTLS negotiation. The connection can no longer be used.");
084 * connection.close();
085 * }
086 * </PRE>
087 */
088 @NotMutable()
089 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
090 public final class StartTLSExtendedRequest
091 extends ExtendedRequest
092 {
093 /**
094 * The OID (1.3.6.1.4.1.1466.20037) for the StartTLS extended request.
095 */
096 public static final String STARTTLS_REQUEST_OID = "1.3.6.1.4.1.1466.20037";
097
098
099
100 /**
101 * The serial version UID for this serializable class.
102 */
103 private static final long serialVersionUID = -3234194603452821233L;
104
105
106
107 // The SSL context to use to perform the negotiation.
108 private final SSLContext sslContext;
109
110
111
112 /**
113 * Creates a new StartTLS extended request using a default SSL context.
114 *
115 * @throws LDAPException If a problem occurs while trying to initialize a
116 * default SSL context.
117 */
118 public StartTLSExtendedRequest()
119 throws LDAPException
120 {
121 this(null, null);
122 }
123
124
125
126 /**
127 * Creates a new StartTLS extended request using a default SSL context.
128 *
129 * @param controls The set of controls to include in the request.
130 *
131 * @throws LDAPException If a problem occurs while trying to initialize a
132 * default SSL context.
133 */
134 public StartTLSExtendedRequest(final Control[] controls)
135 throws LDAPException
136 {
137 this(null, controls);
138 }
139
140
141
142 /**
143 * Creates a new StartTLS extended request using the provided SSL context.
144 *
145 * @param sslContext The SSL context to use to perform the negotiation. It
146 * may be {@code null} to indicate that a default SSL
147 * context should be used. If an SSL context is provided,
148 * then it must already be initialized.
149 *
150 * @throws LDAPException If a problem occurs while trying to initialize a
151 * default SSL context.
152 */
153 public StartTLSExtendedRequest(final SSLContext sslContext)
154 throws LDAPException
155 {
156 this(sslContext, null);
157 }
158
159
160
161 /**
162 * Creates a new StartTLS extended request.
163 *
164 * @param sslContext The SSL context to use to perform the negotiation. It
165 * may be {@code null} to indicate that a default SSL
166 * context should be used. If an SSL context is provided,
167 * then it must already be initialized.
168 * @param controls The set of controls to include in the request.
169 *
170 * @throws LDAPException If a problem occurs while trying to initialize a
171 * default SSL context.
172 */
173 public StartTLSExtendedRequest(final SSLContext sslContext,
174 final Control[] controls)
175 throws LDAPException
176 {
177 super(STARTTLS_REQUEST_OID, controls);
178
179 if (sslContext == null)
180 {
181 try
182 {
183 this.sslContext =
184 SSLContext.getInstance(SSLUtil.getDefaultSSLProtocol());
185 this.sslContext.init(null, null, null);
186 }
187 catch (Exception e)
188 {
189 debugException(e);
190 throw new LDAPException(ResultCode.LOCAL_ERROR,
191 ERR_STARTTLS_REQUEST_CANNOT_CREATE_DEFAULT_CONTEXT.get(e), e);
192 }
193 }
194 else
195 {
196 this.sslContext = sslContext;
197 }
198 }
199
200
201
202 /**
203 * Creates a new StartTLS extended request from the provided generic extended
204 * request.
205 *
206 * @param extendedRequest The generic extended request to use to create this
207 * StartTLS extended request.
208 *
209 * @throws LDAPException If a problem occurs while decoding the request.
210 */
211 public StartTLSExtendedRequest(final ExtendedRequest extendedRequest)
212 throws LDAPException
213 {
214 this(extendedRequest.getControls());
215
216 if (extendedRequest.hasValue())
217 {
218 throw new LDAPException(ResultCode.DECODING_ERROR,
219 ERR_STARTTLS_REQUEST_HAS_VALUE.get());
220 }
221 }
222
223
224
225 /**
226 * {@inheritDoc}
227 */
228 @Override()
229 public ExtendedResult process(final LDAPConnection connection,
230 final int depth)
231 throws LDAPException
232 {
233 // Set an SO_TIMEOUT on the connection if it's not operating in synchronous
234 // mode to make it more responsive during the negotiation phase.
235 InternalSDKHelper.setSoTimeout(connection, 50);
236
237 final ExtendedResult result = super.process(connection, depth);
238 if (result.getResultCode() == ResultCode.SUCCESS)
239 {
240 InternalSDKHelper.convertToTLS(connection, sslContext);
241 }
242
243 return result;
244 }
245
246
247
248 /**
249 * {@inheritDoc}
250 */
251 @Override()
252 public StartTLSExtendedRequest duplicate()
253 {
254 return duplicate(getControls());
255 }
256
257
258
259 /**
260 * {@inheritDoc}
261 */
262 @Override()
263 public StartTLSExtendedRequest duplicate(final Control[] controls)
264 {
265 try
266 {
267 final StartTLSExtendedRequest r =
268 new StartTLSExtendedRequest(sslContext, controls);
269 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
270 return r;
271 }
272 catch (Exception e)
273 {
274 // This should never happen, since an exception should only be thrown if
275 // there is no SSL context, but this instance already has a context.
276 debugException(e);
277 return null;
278 }
279 }
280
281
282
283 /**
284 * {@inheritDoc}
285 */
286 @Override()
287 public String getExtendedRequestName()
288 {
289 return INFO_EXTENDED_REQUEST_NAME_START_TLS.get();
290 }
291
292
293
294 /**
295 * {@inheritDoc}
296 */
297 @Override()
298 public void toString(final StringBuilder buffer)
299 {
300 buffer.append("StartTLSExtendedRequest(");
301
302 final Control[] controls = getControls();
303 if (controls.length > 0)
304 {
305 buffer.append("controls={");
306 for (int i=0; i < controls.length; i++)
307 {
308 if (i > 0)
309 {
310 buffer.append(", ");
311 }
312
313 buffer.append(controls[i]);
314 }
315 buffer.append('}');
316 }
317
318 buffer.append(')');
319 }
320 }