001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020package org.apache.directory.server.ldap.handlers.extended;
021
022
023import java.security.Provider;
024import java.security.SecureRandom;
025import java.security.Security;
026import java.util.Collections;
027import java.util.HashSet;
028import java.util.List;
029import java.util.Set;
030
031import javax.net.ssl.SSLContext;
032import javax.net.ssl.TrustManager;
033
034import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsRequest;
035import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsResponse;
036import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsResponseImpl;
037import org.apache.directory.api.ldap.model.message.ExtendedRequest;
038import org.apache.directory.api.ldap.model.message.ExtendedResponse;
039import org.apache.directory.api.ldap.model.message.LdapResult;
040import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
041import org.apache.directory.ldap.client.api.NoVerificationTrustManager;
042import org.apache.directory.server.i18n.I18n;
043import org.apache.directory.server.ldap.ExtendedOperationHandler;
044import org.apache.directory.server.ldap.LdapServer;
045import org.apache.directory.server.ldap.LdapSession;
046import org.apache.directory.server.protocol.shared.transport.TcpTransport;
047import org.apache.directory.server.protocol.shared.transport.Transport;
048import org.apache.mina.core.filterchain.IoFilterChain;
049import org.apache.mina.filter.ssl.SslFilter;
050import org.slf4j.Logger;
051import org.slf4j.LoggerFactory;
052
053
054/**
055 * Handler for the StartTLS extended operation.
056 *
057 * @see <a href="http://www.ietf.org/rfc/rfc2830.txt">RFC 2830</a>
058 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
059 */
060public class StartTlsHandler implements ExtendedOperationHandler<ExtendedRequest, ExtendedResponse>
061{
062    public static final String EXTENSION_OID = StartTlsRequest.EXTENSION_OID;
063
064    private static final Set<String> EXTENSION_OIDS;
065    private static final Logger LOG = LoggerFactory.getLogger( StartTlsHandler.class );
066
067    /** The SSL Context instance */
068    private SSLContext sslContext;
069
070    /** The list of enabled ciphers */
071    private List<String> cipherSuite;
072
073    /** The list of enabled protocols */
074    private List<String> enabledProtocols;
075
076    /** The 'needClientAuth' SSL flag */
077    private boolean needClientAuth;
078
079    /** The 'wantClientAuth' SSL flag */
080    private boolean wantClientAuth;
081
082    static
083    {
084        Set<String> set = new HashSet<>( 3 );
085        set.add( EXTENSION_OID );
086        EXTENSION_OIDS = Collections.unmodifiableSet( set );
087    }
088
089
090    /**
091     * {@inheritDoc}
092     */
093    public void handleExtendedOperation( LdapSession session, ExtendedRequest req ) throws Exception
094    {
095        LOG.info( "Handling StartTLS request." );
096
097        IoFilterChain chain = session.getIoSession().getFilterChain();
098        SslFilter sslFilter = ( SslFilter ) chain.get( "sslFilter" );
099
100        if ( sslFilter == null )
101        {
102            sslFilter = new SslFilter( sslContext, false );
103
104            // Set the cipher suite
105            if ( ( cipherSuite != null ) && !cipherSuite.isEmpty() )
106            {
107                sslFilter.setEnabledCipherSuites( cipherSuite.toArray( new String[cipherSuite.size()] ) );
108            }
109
110            // Set the enabled protocols, default to no SSLV3
111            if ( ( enabledProtocols != null ) && !enabledProtocols.isEmpty() )
112            {
113                sslFilter.setEnabledProtocols( enabledProtocols.toArray( new String[enabledProtocols.size()] ) );
114            }
115            else
116            {
117                // default to TLS only
118                sslFilter.setEnabledProtocols( new String[]{ "TLSv1", "TLSv1.1", "TLSv1.2" } );
119            }
120
121            // Set the remaining SSL flags
122            sslFilter.setNeedClientAuth( needClientAuth );
123            sslFilter.setWantClientAuth( wantClientAuth );
124
125            chain.addFirst( "sslFilter", sslFilter );
126        }
127        else
128        {
129            // Be sure we disable SSLV3
130            sslFilter.setEnabledProtocols( new String[]
131                { "TLSv1", "TLSv1.1", "TLSv1.2" } );
132            sslFilter.startSsl( session.getIoSession() );
133        }
134
135        StartTlsResponse res = new StartTlsResponseImpl( req.getMessageId() );
136        LdapResult result = res.getLdapResult();
137        result.setResultCode( ResultCodeEnum.SUCCESS );
138        res.setResponseName( EXTENSION_OID );
139
140        // Send a response.
141        session.getIoSession().setAttribute( SslFilter.DISABLE_ENCRYPTION_ONCE );
142        session.getIoSession().write( res );
143    }
144
145
146    /**
147     * {@inheritDoc}
148     */
149    public final Set<String> getExtensionOids()
150    {
151        return EXTENSION_OIDS;
152    }
153
154
155    /**
156     * {@inheritDoc}
157     */
158    public final String getOid()
159    {
160        return EXTENSION_OID;
161    }
162
163
164    /**
165     * {@inheritDoc}
166     */
167    public void setLdapServer( LdapServer ldapServer )
168    {
169        LOG.debug( "Setting LDAP Service" );
170        Provider provider = Security.getProvider( "SUN" );
171        LOG.debug( "provider = {}", provider );
172
173        try
174        {
175            sslContext = SSLContext.getInstance( "TLS" );
176        }
177        catch ( Exception e )
178        {
179            throw new RuntimeException( I18n.err( I18n.ERR_681 ), e );
180        }
181
182        try
183        {
184            sslContext.init( ldapServer.getKeyManagerFactory().getKeyManagers(), new TrustManager[]
185                { new NoVerificationTrustManager() }, new SecureRandom() );
186        }
187        catch ( Exception e )
188        {
189            throw new RuntimeException( I18n.err( I18n.ERR_682 ), e );
190        }
191
192        // Get the transport
193        Transport[] transports = ldapServer.getTransports();
194
195        // Check for any SSL parameter
196        for ( Transport transport : transports )
197        {
198            if ( transport instanceof TcpTransport )
199            {
200                TcpTransport tcpTransport = ( TcpTransport ) transport;
201
202                cipherSuite = tcpTransport.getCipherSuite();
203                enabledProtocols = tcpTransport.getEnabledProtocols();
204                needClientAuth = tcpTransport.isNeedClientAuth();
205                wantClientAuth = tcpTransport.isWantClientAuth();
206
207                break;
208            }
209        }
210    }
211}