001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    
029    
030    
031    import org.opends.messages.MessageBuilder;
032    import org.opends.server.admin.std.server.StartTLSExtendedOperationHandlerCfg;
033    import org.opends.server.api.ClientConnection;
034    import org.opends.server.api.ExtendedOperationHandler;
035    import org.opends.server.config.ConfigException;
036    import org.opends.server.core.DirectoryServer;
037    import org.opends.server.core.ExtendedOperation;
038    import org.opends.server.loggers.debug.DebugTracer;
039    import org.opends.server.types.DebugLogLevel;
040    import org.opends.server.types.DirectoryException;
041    import org.opends.server.types.DisconnectReason;
042    import org.opends.server.types.InitializationException;
043    import org.opends.server.types.ResultCode;
044    
045    import static org.opends.server.loggers.ErrorLogger.*;
046    import static org.opends.server.loggers.debug.DebugLogger.*;
047    import static org.opends.messages.ExtensionMessages.*;
048    import static org.opends.server.util.ServerConstants.*;
049    import static org.opends.server.util.StaticUtils.*;
050    
051    
052    
053    /**
054     * This class provides an implementation of the StartTLS extended operation as
055     * defined in RFC 2830.  It can enable the TLS connection security provider on
056     * an established connection upon receiving an appropriate request from a
057     * client.
058     */
059    public class StartTLSExtendedOperation
060           extends ExtendedOperationHandler<StartTLSExtendedOperationHandlerCfg>
061    {
062      /**
063       * The tracer object for the debug logger.
064       */
065      private static final DebugTracer TRACER = getTracer();
066    
067    
068    
069      /**
070       * Create an instance of this StartTLS extended operation handler.  All
071       * initialization should be performed in the
072       * <CODE>initializeExtendedOperationHandler</CODE> method.
073       */
074      public StartTLSExtendedOperation()
075      {
076        super();
077      }
078    
079    
080      /**
081       * Initializes this extended operation handler based on the information in the
082       * provided configuration entry.  It should also register itself with the
083       * Directory Server for the particular kinds of extended operations that it
084       * will process.
085       *
086       * @param  config       The configuration that contains the information
087       *                      to use to initialize this extended operation handler.
088       *
089       * @throws  ConfigException  If an unrecoverable problem arises in the
090       *                           process of performing the initialization.
091       *
092       * @throws  InitializationException  If a problem occurs during initialization
093       *                                   that is not related to the server
094       *                                   configuration.
095       */
096      public void initializeExtendedOperationHandler(
097                       StartTLSExtendedOperationHandlerCfg config)
098             throws ConfigException, InitializationException
099      {
100        // FIXME -- Are there any configurable options that we should support?
101        DirectoryServer.registerSupportedExtension(OID_START_TLS_REQUEST, this);
102    
103        registerControlsAndFeatures();
104      }
105    
106    
107    
108      /**
109       * Performs any finalization that may be necessary for this extended
110       * operation handler.  By default, no finalization is performed.
111       */
112      public void finalizeExtendedOperationHandler()
113      {
114        DirectoryServer.deregisterSupportedExtension(OID_START_TLS_REQUEST);
115    
116        deregisterControlsAndFeatures();
117      }
118    
119    
120    
121      /**
122       * Processes the provided extended operation.
123       *
124       * @param  operation  The extended operation to be processed.
125       */
126      public void processExtendedOperation(ExtendedOperation operation)
127      {
128        // We should always include the StartTLS OID in the response (the same OID
129        // is used for both the request and the response), so make sure that it will
130        // happen.
131        operation.setResponseOID(OID_START_TLS_REQUEST);
132    
133    
134        // Get the reference to the client connection.  If there is none, then fail.
135        ClientConnection clientConnection = operation.getClientConnection();
136        if (clientConnection == null)
137        {
138          operation.setResultCode(ResultCode.UNAVAILABLE);
139    
140    
141          operation.appendErrorMessage(ERR_STARTTLS_NO_CLIENT_CONNECTION.get());
142          return;
143        }
144    
145    
146        // Make sure that the client connection is capable of enabling TLS.  If not,
147        // then fail.
148        TLSCapableConnection tlsCapableConnection;
149        if (clientConnection instanceof TLSCapableConnection)
150        {
151          tlsCapableConnection = (TLSCapableConnection) clientConnection;
152        }
153        else
154        {
155          operation.setResultCode(ResultCode.UNAVAILABLE);
156    
157    
158          operation.appendErrorMessage(ERR_STARTTLS_NOT_TLS_CAPABLE.get());
159          return;
160        }
161    
162        MessageBuilder unavailableReason = new MessageBuilder();
163        if (! tlsCapableConnection.tlsProtectionAvailable(unavailableReason))
164        {
165          operation.setResultCode(ResultCode.UNAVAILABLE);
166          operation.setErrorMessage(unavailableReason);
167          return;
168        }
169    
170    
171        // Actually enable TLS protection on the client connection.  This may fail,
172        // but if it does then the connection will be closed so we'll just need to
173        // log it.
174        try
175        {
176          tlsCapableConnection.enableTLSConnectionSecurityProvider();
177        }
178        catch (DirectoryException de)
179        {
180          if (debugEnabled())
181          {
182            TRACER.debugCaught(DebugLogLevel.ERROR, de);
183          }
184    
185          logError(ERR_STARTTLS_ERROR_ON_ENABLE.get(getExceptionMessage(de)));
186        }
187    
188    
189        // TLS was successfully enabled on the client connection, but we need to
190        // send the response in the clear.
191        operation.setResultCode(ResultCode.SUCCESS);
192    
193        try
194        {
195          tlsCapableConnection.sendClearResponse(operation);
196          operation.setResponseSent();
197        }
198        catch (Exception e)
199        {
200          if (debugEnabled())
201          {
202            TRACER.debugCaught(DebugLogLevel.ERROR, e);
203          }
204    
205          logError(ERR_STARTTLS_ERROR_SENDING_CLEAR_RESPONSE.get(
206              getExceptionMessage(e)));
207    
208          clientConnection.disconnect(DisconnectReason.SECURITY_PROBLEM, false,
209                                      ERR_STARTTLS_ERROR_SENDING_CLEAR_RESPONSE.get(
210                                      getExceptionMessage(e)));
211        }
212      }
213    }
214