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     */
020    package org.apache.directory.server.ldap.handlers.extended;
021    
022    
023    import java.security.KeyStore;
024    import java.security.Provider;
025    import java.security.SecureRandom;
026    import java.security.Security;
027    import java.security.cert.CertificateException;
028    import java.security.cert.X509Certificate;
029    import java.util.Collections;
030    import java.util.HashSet;
031    import java.util.Set;
032    
033    import javax.net.ssl.KeyManagerFactory;
034    import javax.net.ssl.SSLContext;
035    import javax.net.ssl.TrustManager;
036    import javax.net.ssl.X509TrustManager;
037    
038    import org.apache.directory.server.core.security.CoreKeyStoreSpi;
039    import org.apache.directory.server.ldap.ExtendedOperationHandler;
040    import org.apache.directory.server.ldap.LdapServer;
041    import org.apache.directory.server.ldap.LdapSession;
042    import org.apache.directory.shared.ldap.message.InternalExtendedRequest;
043    import org.apache.directory.shared.ldap.message.InternalExtendedResponse;
044    import org.apache.directory.shared.ldap.message.ExtendedResponseImpl;
045    import org.apache.directory.shared.ldap.message.InternalLdapResult;
046    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
047    import org.apache.mina.core.filterchain.IoFilterChain;
048    import org.apache.mina.filter.ssl.SslFilter;
049    
050    import org.slf4j.Logger;
051    import org.slf4j.LoggerFactory;
052    
053    
054    /**
055     * Handler for the StartTLS extended operation.
056     *
057     * @org.apache.xbean.XBean
058     * @see <a href="http://www.ietf.org/rfc/rfc2830.txt">RFC 2830</a>
059     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
060     * @version $Rev$, $Date$
061     */
062    public class StartTlsHandler implements ExtendedOperationHandler
063    {
064        public static final String EXTENSION_OID = "1.3.6.1.4.1.1466.20037";
065    
066        private static final Set<String> EXTENSION_OIDS;
067        private static final Logger LOG = LoggerFactory.getLogger( StartTlsHandler.class );
068        
069        private SSLContext sslContext;
070    
071        
072        static
073        {
074            Set<String> set = new HashSet<String>( 3 );
075            set.add( EXTENSION_OID );
076            EXTENSION_OIDS = Collections.unmodifiableSet( set );
077        }
078        
079    
080        public void handleExtendedOperation( LdapSession session, InternalExtendedRequest req ) throws Exception
081        {
082            LOG.info( "Handling StartTLS request." );
083            
084            IoFilterChain chain = session.getIoSession().getFilterChain();
085            SslFilter sslFilter = ( SslFilter ) chain.get( "sslFilter" );
086            if( sslFilter == null )
087            {
088                sslFilter = new SslFilter( sslContext );
089                chain.addFirst( "sslFilter", sslFilter );
090            }
091            else
092            {
093                sslFilter.startSsl( session.getIoSession() );
094            }
095            
096            InternalExtendedResponse res = new ExtendedResponseImpl( req.getMessageId() );
097            InternalLdapResult result = res.getLdapResult();
098            result.setResultCode( ResultCodeEnum.SUCCESS );
099            res.setResponseName( EXTENSION_OID );
100            res.setResponse( new byte[ 0 ] );
101    
102            // Send a response.
103            session.getIoSession().setAttribute( SslFilter.DISABLE_ENCRYPTION_ONCE );
104            session.getIoSession().write( res );
105        }
106        
107        
108        class ServerX509TrustManager implements X509TrustManager
109        {
110            public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException
111            {
112                LOG.debug( "checkClientTrusted() called" );
113            }
114    
115            public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException
116            {
117                LOG.debug( "checkServerTrusted() called" );
118            }
119    
120            public X509Certificate[] getAcceptedIssuers()
121            {
122                LOG.debug( "getAcceptedIssuers() called" );
123                return new X509Certificate[0];
124            }
125        }
126    
127    
128        public final Set<String> getExtensionOids()
129        {
130            return EXTENSION_OIDS;
131        }
132    
133    
134        public final String getOid()
135        {
136            return EXTENSION_OID;
137        }
138    
139        
140        public void setLdapServer( LdapServer ldapServer )
141        {
142            LOG.debug( "Setting LDAP Service" );
143            Provider provider = Security.getProvider( "SUN" );
144            LOG.debug( "provider = {}", provider );
145            CoreKeyStoreSpi coreKeyStoreSpi = new CoreKeyStoreSpi( ldapServer.getDirectoryService() );
146            KeyStore keyStore = new KeyStore( coreKeyStoreSpi, provider, "JKS" ) {};
147    
148            try
149            {
150                keyStore.load( null, null );
151            }
152            catch ( Exception e1 )
153            {
154                throw new RuntimeException( "Failed on keystore load which should never really happen." );
155            }
156            
157            KeyManagerFactory keyManagerFactory = null;
158            try
159            {
160                keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
161            }
162            catch ( Exception e )
163            {
164                throw new RuntimeException( "Failed to create KeyManagerFactory", e );
165            }
166            
167            try
168            {
169                keyManagerFactory.init( keyStore, null );
170            }
171            catch ( Exception e )
172            {
173                throw new RuntimeException( "Failed to initialize KeyManagerFactory", e );
174            }
175            
176            try
177            {
178                sslContext = SSLContext.getInstance( "TLS" );
179            }
180            catch ( Exception e )
181            {
182                throw new RuntimeException( "Failed to create SSLContext", e );
183            }
184            
185            try
186            {
187                sslContext.init( keyManagerFactory.getKeyManagers(), 
188                    new TrustManager[] { new ServerX509TrustManager() }, 
189                    new SecureRandom() );
190            }
191            catch ( Exception e )
192            {
193                throw new RuntimeException( "Failed to initialize SSLContext", e );
194            }
195        }
196    }