001/*
002 * Copyright 2011-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2011-2014 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 */
021package com.unboundid.ldap.listener;
022
023
024
025import java.io.OutputStream;
026import java.util.List;
027import javax.net.ssl.SSLSocketFactory;
028
029import com.unboundid.asn1.ASN1Buffer;
030import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
031import com.unboundid.ldap.protocol.AddRequestProtocolOp;
032import com.unboundid.ldap.protocol.BindRequestProtocolOp;
033import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
034import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
035import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
036import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
037import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
038import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
039import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
040import com.unboundid.ldap.protocol.UnbindRequestProtocolOp;
041import com.unboundid.ldap.protocol.LDAPMessage;
042import com.unboundid.ldap.sdk.Control;
043import com.unboundid.ldap.sdk.ExtendedRequest;
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
047import com.unboundid.util.Debug;
048import com.unboundid.util.StaticUtils;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052import static com.unboundid.ldap.listener.ListenerMessages.*;
053
054
055
056/**
057 * This class provides a request handler implementation that can be used to
058 * convert an existing connection to use TLS encryption.  It will handle
059 * StartTLS extended operations directly, but will pass all other requests and
060 * responses through to another request handler.
061 */
062@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
063public final class StartTLSRequestHandler
064       extends LDAPListenerRequestHandler
065{
066  // The client connection with which this request handler is associated.
067  private final LDAPListenerClientConnection connection;
068
069  // The request handler that will be used to process all operations except the
070  // StartTLS extended operation.
071  private final LDAPListenerRequestHandler requestHandler;
072
073  // The SSL socket factory that will be used to SSL-enable the existing socket.
074  private final SSLSocketFactory sslSocketFactory;
075
076
077
078  /**
079   * Creates a new StartTLS request handler with the provided information.
080   *
081   * @param  sslSocketFactory  The SSL socket factory that will be used to
082   *                           convert the existing socket to use SSL
083   *                           encryption.
084   * @param  requestHandler    The request handler that will be used to process
085   *                           all operations except StartTLS extended
086   *                           operations.
087   */
088  public StartTLSRequestHandler(final SSLSocketFactory sslSocketFactory,
089                                final LDAPListenerRequestHandler requestHandler)
090  {
091    this.sslSocketFactory = sslSocketFactory;
092    this.requestHandler   = requestHandler;
093
094    connection = null;
095  }
096
097
098
099  /**
100   * Creates a new StartTLS request handler with the provided information.
101   *
102   * @param  sslSocketFactory  The SSL socket factory that will be used to
103   *                           convert the existing socket to use SSL
104   *                           encryption.
105   * @param  requestHandler    The request handler that will be used to process
106   *                           all operations except StartTLS extended
107   *                           operations.
108   * @param  connection        The connection to the associated client.
109   */
110  private StartTLSRequestHandler(final SSLSocketFactory sslSocketFactory,
111               final LDAPListenerRequestHandler requestHandler,
112               final LDAPListenerClientConnection connection)
113  {
114    this.sslSocketFactory = sslSocketFactory;
115    this.requestHandler   = requestHandler;
116    this.connection       = connection;
117  }
118
119
120
121  /**
122   * {@inheritDoc}
123   */
124  @Override()
125  public StartTLSRequestHandler
126              newInstance(final LDAPListenerClientConnection connection)
127         throws LDAPException
128  {
129    return new StartTLSRequestHandler(sslSocketFactory,
130         requestHandler.newInstance(connection), connection);
131  }
132
133
134
135  /**
136   * {@inheritDoc}
137   */
138  @Override()
139  public void closeInstance()
140  {
141    requestHandler.closeInstance();
142  }
143
144
145
146  /**
147   * {@inheritDoc}
148   */
149  @Override()
150  public void processAbandonRequest(final int messageID,
151                                    final AbandonRequestProtocolOp request,
152                                    final List<Control> controls)
153  {
154    requestHandler.processAbandonRequest(messageID, request, controls);
155  }
156
157
158
159  /**
160   * {@inheritDoc}
161   */
162  @Override()
163  public LDAPMessage processAddRequest(final int messageID,
164                                       final AddRequestProtocolOp request,
165                                       final List<Control> controls)
166  {
167    return requestHandler.processAddRequest(messageID, request, controls);
168  }
169
170
171
172  /**
173   * {@inheritDoc}
174   */
175  @Override()
176  public LDAPMessage processBindRequest(final int messageID,
177                                        final BindRequestProtocolOp request,
178                                        final List<Control> controls)
179  {
180    return requestHandler.processBindRequest(messageID, request, controls);
181  }
182
183
184
185  /**
186   * {@inheritDoc}
187   */
188  @Override()
189  public LDAPMessage processCompareRequest(final int messageID,
190                          final CompareRequestProtocolOp request,
191                          final List<Control> controls)
192  {
193    return requestHandler.processCompareRequest(messageID, request, controls);
194  }
195
196
197
198  /**
199   * {@inheritDoc}
200   */
201  @Override()
202  public LDAPMessage processDeleteRequest(final int messageID,
203                                          final DeleteRequestProtocolOp request,
204                                          final List<Control> controls)
205  {
206    return requestHandler.processDeleteRequest(messageID, request, controls);
207  }
208
209
210
211  /**
212   * {@inheritDoc}
213   */
214  @Override()
215  public LDAPMessage processExtendedRequest(final int messageID,
216                          final ExtendedRequestProtocolOp request,
217                          final List<Control> controls)
218  {
219    if (request.getOID().equals(StartTLSExtendedRequest.STARTTLS_REQUEST_OID))
220    {
221      try
222      {
223        // Make sure we can decode the request as a valid StartTLS request.
224        final StartTLSExtendedRequest startTLSRequest =
225             new StartTLSExtendedRequest(new ExtendedRequest(request.getOID(),
226                  request.getValue()));
227
228        final OutputStream clearOutputStream =
229             connection.convertToTLS(sslSocketFactory);
230
231        final LDAPMessage responseMessage = new LDAPMessage(messageID,
232             new ExtendedResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null,
233                  null, null, null, null));
234        final ASN1Buffer buffer = new ASN1Buffer();
235        responseMessage.writeTo(buffer);
236
237        try
238        {
239          buffer.writeTo(clearOutputStream);
240          clearOutputStream.flush();
241        }
242        catch (final Exception e)
243        {
244          Debug.debugException(e);
245          final LDAPException le = new LDAPException(ResultCode.LOCAL_ERROR,
246               ERR_START_TLS_REQUEST_HANDLER_WRITE_RESPONSE_FAILURE.get(
247                    StaticUtils.getExceptionMessage(e)),
248               e);
249          connection.close(le);
250          throw le;
251        }
252
253        return responseMessage;
254      }
255      catch (final LDAPException le)
256      {
257        Debug.debugException(le);
258
259        return new LDAPMessage(messageID,
260             new ExtendedResponseProtocolOp(le.getResultCode().intValue(),
261                  le.getMatchedDN(), le.getDiagnosticMessage(),
262                  StaticUtils.toList(le.getReferralURLs()), null, null),
263             le.getResponseControls());
264      }
265    }
266    else
267    {
268      return requestHandler.processExtendedRequest(messageID, request,
269           controls);
270    }
271  }
272
273
274
275  /**
276   * {@inheritDoc}
277   */
278  @Override()
279  public LDAPMessage processModifyRequest(final int messageID,
280                                          final ModifyRequestProtocolOp request,
281                                          final List<Control> controls)
282  {
283    return requestHandler.processModifyRequest(messageID, request, controls);
284  }
285
286
287
288  /**
289   * {@inheritDoc}
290   */
291  @Override()
292  public LDAPMessage processModifyDNRequest(final int messageID,
293                          final ModifyDNRequestProtocolOp request,
294                          final List<Control> controls)
295  {
296    return requestHandler.processModifyDNRequest(messageID, request, controls);
297  }
298
299
300
301  /**
302   * {@inheritDoc}
303   */
304  @Override()
305  public LDAPMessage processSearchRequest(final int messageID,
306                                          final SearchRequestProtocolOp request,
307                                          final List<Control> controls)
308  {
309    return requestHandler.processSearchRequest(messageID, request, controls);
310  }
311
312
313
314  /**
315   * {@inheritDoc}
316   */
317  @Override()
318  public void processUnbindRequest(final int messageID,
319                                   final UnbindRequestProtocolOp request,
320                                   final List<Control> controls)
321  {
322    requestHandler.processUnbindRequest(messageID, request, controls);
323  }
324}