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;
032
033import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsRequest;
034import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsResponse;
035import org.apache.directory.api.ldap.extras.extended.startTls.StartTlsResponseImpl;
036import org.apache.directory.api.ldap.model.message.ExtendedRequest;
037import org.apache.directory.api.ldap.model.message.ExtendedResponse;
038import org.apache.directory.api.ldap.model.message.LdapResult;
039import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
040import org.apache.directory.server.i18n.I18n;
041import org.apache.directory.server.ldap.ExtendedOperationHandler;
042import org.apache.directory.server.ldap.LdapServer;
043import org.apache.directory.server.ldap.LdapSession;
044import org.apache.directory.server.protocol.shared.transport.TcpTransport;
045import org.apache.directory.server.protocol.shared.transport.Transport;
046import org.apache.mina.core.filterchain.IoFilterChain;
047import org.apache.mina.filter.ssl.SslFilter;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051
052/**
053 * Handler for the StartTLS extended operation.
054 *
055 * @see <a href="http://www.ietf.org/rfc/rfc2830.txt">RFC 2830</a>
056 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
057 */
058public class StartTlsHandler implements ExtendedOperationHandler<ExtendedRequest, ExtendedResponse>
059{
060    public static final String EXTENSION_OID = StartTlsRequest.EXTENSION_OID;
061
062    private static final Set<String> EXTENSION_OIDS;
063    private static final Logger LOG = LoggerFactory.getLogger( StartTlsHandler.class );
064
065    /** The SSL Context instance */
066    private SSLContext sslContext;
067
068    /** The list of enabled ciphers */
069    private List<String> cipherSuite;
070
071    /** The list of enabled protocols */
072    private List<String> enabledProtocols;
073
074    /** The 'needClientAuth' SSL flag */
075    private boolean needClientAuth;
076
077    /** The 'wantClientAuth' SSL flag */
078    private boolean wantClientAuth;
079
080    static
081    {
082        Set<String> set = new HashSet<>( 3 );
083        set.add( EXTENSION_OID );
084        EXTENSION_OIDS = Collections.unmodifiableSet( set );
085    }
086
087
088    /**
089     * {@inheritDoc}
090     */
091    public void handleExtendedOperation( LdapSession session, ExtendedRequest req ) throws Exception
092    {
093        LOG.info( "Handling StartTLS request." );
094
095        IoFilterChain chain = session.getIoSession().getFilterChain();
096        SslFilter sslFilter = ( SslFilter ) chain.get( "sslFilter" );
097
098        if ( sslFilter == null )
099        {
100            sslFilter = new SslFilter( sslContext );
101
102            // Set the cipher suite
103            if ( ( cipherSuite != null ) && !cipherSuite.isEmpty() )
104            {
105                sslFilter.setEnabledCipherSuites( cipherSuite.toArray( new String[cipherSuite.size()] ) );
106            }
107
108            // Set the enabled protocols, default to no SSLV3
109            if ( ( enabledProtocols != null ) && !enabledProtocols.isEmpty() )
110            {
111                sslFilter.setEnabledProtocols( enabledProtocols.toArray( new String[enabledProtocols.size()] ) );
112            }
113            else
114            {
115                // default to TLS only
116                sslFilter.setEnabledProtocols( new String[]{ "TLSv1", "TLSv1.1", "TLSv1.2" } );
117            }
118
119            // Set the remaining SSL flags
120            sslFilter.setNeedClientAuth( needClientAuth );
121            sslFilter.setWantClientAuth( wantClientAuth );
122
123            StartTlsFilter startTlsFilter = new StartTlsFilter();
124            chain.addFirst( "startTls", startTlsFilter );
125            chain.addFirst( "sslFilter", sslFilter );
126        }
127
128        StartTlsResponse res = new StartTlsResponseImpl( req.getMessageId() );
129        LdapResult result = res.getLdapResult();
130        result.setResultCode( ResultCodeEnum.SUCCESS );
131        res.setResponseName( EXTENSION_OID );
132
133        // Send a response.
134        session.getIoSession().write( res );
135    }
136
137
138    /**
139     * {@inheritDoc}
140     */
141    public final Set<String> getExtensionOids()
142    {
143        return EXTENSION_OIDS;
144    }
145
146
147    /**
148     * {@inheritDoc}
149     */
150    public final String getOid()
151    {
152        return EXTENSION_OID;
153    }
154
155
156    /**
157     * {@inheritDoc}
158     */
159    public void setLdapServer( LdapServer ldapServer )
160    {
161        LOG.debug( "Setting LDAP Service" );
162        Provider provider = Security.getProvider( "SUN" );
163        LOG.debug( "provider = {}", provider );
164
165        try
166        {
167            sslContext = SSLContext.getInstance( "TLS" );
168        }
169        catch ( Exception e )
170        {
171            throw new RuntimeException( I18n.err( I18n.ERR_681 ), e );
172        }
173
174        try
175        {
176            sslContext.init( ldapServer.getKeyManagerFactory().getKeyManagers(),
177                    ldapServer.getTrustManagers(), new SecureRandom() );
178        }
179        catch ( Exception e )
180        {
181            throw new RuntimeException( I18n.err( I18n.ERR_682 ), e );
182        }
183
184        // Get the transport
185        Transport[] transports = ldapServer.getTransports();
186
187        // Check for any SSL parameter
188        for ( Transport transport : transports )
189        {
190            if ( transport instanceof TcpTransport )
191            {
192                TcpTransport tcpTransport = ( TcpTransport ) transport;
193
194                cipherSuite = tcpTransport.getCipherSuite();
195                enabledProtocols = tcpTransport.getEnabledProtocols();
196                needClientAuth = tcpTransport.isNeedClientAuth();
197                wantClientAuth = tcpTransport.isWantClientAuth();
198
199                break;
200            }
201        }
202    }
203}