001/*
002 * Copyright 2011-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2011-2020 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2011-2020 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.listener;
037
038
039
040import java.io.OutputStream;
041import java.util.List;
042import javax.net.ssl.SSLSocketFactory;
043
044import com.unboundid.asn1.ASN1Buffer;
045import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
046import com.unboundid.ldap.protocol.AddRequestProtocolOp;
047import com.unboundid.ldap.protocol.BindRequestProtocolOp;
048import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
049import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
050import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
051import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
052import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
053import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
054import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
055import com.unboundid.ldap.protocol.UnbindRequestProtocolOp;
056import com.unboundid.ldap.protocol.LDAPMessage;
057import com.unboundid.ldap.sdk.Control;
058import com.unboundid.ldap.sdk.ExtendedRequest;
059import com.unboundid.ldap.sdk.LDAPException;
060import com.unboundid.ldap.sdk.ResultCode;
061import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
062import com.unboundid.util.Debug;
063import com.unboundid.util.NotNull;
064import com.unboundid.util.Nullable;
065import com.unboundid.util.StaticUtils;
066import com.unboundid.util.ThreadSafety;
067import com.unboundid.util.ThreadSafetyLevel;
068
069import static com.unboundid.ldap.listener.ListenerMessages.*;
070
071
072
073/**
074 * This class provides a request handler implementation that can be used to
075 * convert an existing connection to use TLS encryption.  It will handle
076 * StartTLS extended operations directly, but will pass all other requests and
077 * responses through to another request handler.
078 */
079@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
080public final class StartTLSRequestHandler
081       extends LDAPListenerRequestHandler
082{
083  // The client connection with which this request handler is associated.
084  @Nullable private final LDAPListenerClientConnection connection;
085
086  // The request handler that will be used to process all operations except the
087  // StartTLS extended operation.
088  @NotNull private final LDAPListenerRequestHandler requestHandler;
089
090  // The SSL socket factory that will be used to SSL-enable the existing socket.
091  @NotNull private final SSLSocketFactory sslSocketFactory;
092
093
094
095  /**
096   * Creates a new StartTLS request handler with the provided information.
097   *
098   * @param  sslSocketFactory  The SSL socket factory that will be used to
099   *                           convert the existing socket to use SSL
100   *                           encryption.
101   * @param  requestHandler    The request handler that will be used to process
102   *                           all operations except StartTLS extended
103   *                           operations.
104   */
105  public StartTLSRequestHandler(
106              @NotNull final SSLSocketFactory sslSocketFactory,
107              @NotNull final LDAPListenerRequestHandler requestHandler)
108  {
109    this.sslSocketFactory = sslSocketFactory;
110    this.requestHandler   = requestHandler;
111
112    connection = null;
113  }
114
115
116
117  /**
118   * Creates a new StartTLS request handler with the provided information.
119   *
120   * @param  sslSocketFactory  The SSL socket factory that will be used to
121   *                           convert the existing socket to use SSL
122   *                           encryption.
123   * @param  requestHandler    The request handler that will be used to process
124   *                           all operations except StartTLS extended
125   *                           operations.
126   * @param  connection        The connection to the associated client.
127   */
128  private StartTLSRequestHandler(
129               @NotNull final SSLSocketFactory sslSocketFactory,
130               @NotNull final LDAPListenerRequestHandler requestHandler,
131               @NotNull final LDAPListenerClientConnection connection)
132  {
133    this.sslSocketFactory = sslSocketFactory;
134    this.requestHandler   = requestHandler;
135    this.connection       = connection;
136  }
137
138
139
140  /**
141   * {@inheritDoc}
142   */
143  @Override()
144  @NotNull()
145  public StartTLSRequestHandler newInstance(
146              @NotNull final LDAPListenerClientConnection connection)
147         throws LDAPException
148  {
149    return new StartTLSRequestHandler(sslSocketFactory,
150         requestHandler.newInstance(connection), connection);
151  }
152
153
154
155  /**
156   * {@inheritDoc}
157   */
158  @Override()
159  public void closeInstance()
160  {
161    requestHandler.closeInstance();
162  }
163
164
165
166  /**
167   * {@inheritDoc}
168   */
169  @Override()
170  public void processAbandonRequest(final int messageID,
171                   @NotNull final AbandonRequestProtocolOp request,
172                   @NotNull final List<Control> controls)
173  {
174    requestHandler.processAbandonRequest(messageID, request, controls);
175  }
176
177
178
179  /**
180   * {@inheritDoc}
181   */
182  @Override()
183  @NotNull()
184  public LDAPMessage processAddRequest(final int messageID,
185                          @NotNull final AddRequestProtocolOp request,
186                          @NotNull final List<Control> controls)
187  {
188    return requestHandler.processAddRequest(messageID, request, controls);
189  }
190
191
192
193  /**
194   * {@inheritDoc}
195   */
196  @Override()
197  @NotNull()
198  public LDAPMessage processBindRequest(final int messageID,
199                          @NotNull final BindRequestProtocolOp request,
200                          @NotNull final List<Control> controls)
201  {
202    return requestHandler.processBindRequest(messageID, request, controls);
203  }
204
205
206
207  /**
208   * {@inheritDoc}
209   */
210  @Override()
211  @NotNull()
212  public LDAPMessage processCompareRequest(final int messageID,
213                          @NotNull final CompareRequestProtocolOp request,
214                          @NotNull final List<Control> controls)
215  {
216    return requestHandler.processCompareRequest(messageID, request, controls);
217  }
218
219
220
221  /**
222   * {@inheritDoc}
223   */
224  @Override()
225  @NotNull()
226  public LDAPMessage processDeleteRequest(final int messageID,
227                          @NotNull final DeleteRequestProtocolOp request,
228                          @NotNull final List<Control> controls)
229  {
230    return requestHandler.processDeleteRequest(messageID, request, controls);
231  }
232
233
234
235  /**
236   * {@inheritDoc}
237   */
238  @Override()
239  @NotNull()
240  public LDAPMessage processExtendedRequest(final int messageID,
241                          @NotNull final ExtendedRequestProtocolOp request,
242                          @NotNull final List<Control> controls)
243  {
244    if (request.getOID().equals(StartTLSExtendedRequest.STARTTLS_REQUEST_OID))
245    {
246      try
247      {
248        // Make sure we can decode the request as a valid StartTLS request.
249        final StartTLSExtendedRequest startTLSRequest =
250             new StartTLSExtendedRequest(new ExtendedRequest(request.getOID(),
251                  request.getValue()));
252
253        final OutputStream clearOutputStream =
254             connection.convertToTLS(sslSocketFactory);
255
256        final LDAPMessage responseMessage = new LDAPMessage(messageID,
257             new ExtendedResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE, null,
258                  null, null, null, null));
259        final ASN1Buffer buffer = new ASN1Buffer();
260        responseMessage.writeTo(buffer);
261
262        try
263        {
264          buffer.writeTo(clearOutputStream);
265          clearOutputStream.flush();
266        }
267        catch (final Exception e)
268        {
269          Debug.debugException(e);
270          final LDAPException le = new LDAPException(ResultCode.LOCAL_ERROR,
271               ERR_START_TLS_REQUEST_HANDLER_WRITE_RESPONSE_FAILURE.get(
272                    StaticUtils.getExceptionMessage(e)),
273               e);
274          connection.close(le);
275          throw le;
276        }
277
278        return responseMessage;
279      }
280      catch (final LDAPException le)
281      {
282        Debug.debugException(le);
283
284        return new LDAPMessage(messageID,
285             new ExtendedResponseProtocolOp(le.getResultCode().intValue(),
286                  le.getMatchedDN(), le.getDiagnosticMessage(),
287                  StaticUtils.toList(le.getReferralURLs()), null, null),
288             le.getResponseControls());
289      }
290    }
291    else
292    {
293      return requestHandler.processExtendedRequest(messageID, request,
294           controls);
295    }
296  }
297
298
299
300  /**
301   * {@inheritDoc}
302   */
303  @Override()
304  @NotNull()
305  public LDAPMessage processModifyRequest(final int messageID,
306                          @NotNull final ModifyRequestProtocolOp request,
307                          @NotNull final List<Control> controls)
308  {
309    return requestHandler.processModifyRequest(messageID, request, controls);
310  }
311
312
313
314  /**
315   * {@inheritDoc}
316   */
317  @Override()
318  @NotNull()
319  public LDAPMessage processModifyDNRequest(final int messageID,
320                          @NotNull final ModifyDNRequestProtocolOp request,
321                          @NotNull final List<Control> controls)
322  {
323    return requestHandler.processModifyDNRequest(messageID, request, controls);
324  }
325
326
327
328  /**
329   * {@inheritDoc}
330   */
331  @Override()
332  @NotNull()
333  public LDAPMessage processSearchRequest(final int messageID,
334                          @NotNull final SearchRequestProtocolOp request,
335                          @NotNull final List<Control> controls)
336  {
337    return requestHandler.processSearchRequest(messageID, request, controls);
338  }
339
340
341
342  /**
343   * {@inheritDoc}
344   */
345  @Override()
346  public void processUnbindRequest(final int messageID,
347                   @NotNull final UnbindRequestProtocolOp request,
348                   @NotNull final List<Control> controls)
349  {
350    requestHandler.processUnbindRequest(messageID, request, controls);
351  }
352}