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