001    /*
002     * Copyright 2011-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2011-2015 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.listener;
022    
023    
024    
025    import java.io.OutputStream;
026    import java.util.List;
027    import javax.net.ssl.SSLSocketFactory;
028    
029    import com.unboundid.asn1.ASN1Buffer;
030    import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
031    import com.unboundid.ldap.protocol.AddRequestProtocolOp;
032    import com.unboundid.ldap.protocol.BindRequestProtocolOp;
033    import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
034    import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
035    import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
036    import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
037    import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
038    import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
039    import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
040    import com.unboundid.ldap.protocol.UnbindRequestProtocolOp;
041    import com.unboundid.ldap.protocol.LDAPMessage;
042    import com.unboundid.ldap.sdk.Control;
043    import com.unboundid.ldap.sdk.ExtendedRequest;
044    import com.unboundid.ldap.sdk.LDAPException;
045    import com.unboundid.ldap.sdk.ResultCode;
046    import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
047    import com.unboundid.util.Debug;
048    import com.unboundid.util.StaticUtils;
049    import com.unboundid.util.ThreadSafety;
050    import com.unboundid.util.ThreadSafetyLevel;
051    
052    import 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)
063    public 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    }