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.bind.gssapi;
021    
022    
023    import java.security.PrivilegedExceptionAction;
024    import java.util.HashMap;
025    import java.util.Map;
026    
027    import javax.security.auth.Subject;
028    import javax.security.auth.callback.CallbackHandler;
029    import javax.security.auth.kerberos.KerberosKey;
030    import javax.security.auth.kerberos.KerberosPrincipal;
031    import javax.security.sasl.Sasl;
032    import javax.security.sasl.SaslServer;
033    
034    import org.apache.directory.server.core.CoreSession;
035    import org.apache.directory.server.i18n.I18n;
036    import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
037    import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
038    import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
039    import org.apache.directory.server.kerberos.shared.store.operations.GetPrincipal;
040    import org.apache.directory.server.ldap.LdapServer;
041    import org.apache.directory.server.ldap.LdapSession;
042    import org.apache.directory.server.ldap.handlers.bind.AbstractMechanismHandler;
043    import org.apache.directory.server.ldap.handlers.bind.SaslConstants;
044    import org.apache.directory.server.protocol.shared.ServiceConfigurationException;
045    import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
046    import org.apache.directory.shared.ldap.message.internal.InternalBindRequest;
047    import org.apache.directory.shared.ldap.name.DN;
048    
049    
050    /**
051     * The GSSAPI Sasl mechanism handler.
052     *
053     * @org.apache.xbean.XBean
054     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
055     * @version $Rev$, $Date$
056     */
057    public class GssapiMechanismHandler extends AbstractMechanismHandler
058    {
059        public SaslServer handleMechanism( LdapSession ldapSession, InternalBindRequest bindRequest ) throws Exception
060        {
061            SaslServer ss = ( SaslServer ) ldapSession.getSaslProperty( SaslConstants.SASL_SERVER );
062    
063            if ( ss == null )
064            {
065                //Subject subject = ( Subject ) ldapSession.getIoSession().getAttribute( "saslSubject" );
066    
067                Subject subject = getSubject( ldapSession.getLdapServer() );
068                final String saslHost = ( String ) ldapSession.getSaslProperty( SaslConstants.SASL_HOST );
069                final Map<String, String> saslProps = ( Map<String, String> ) ldapSession
070                    .getSaslProperty( SaslConstants.SASL_PROPS );
071    
072                CoreSession adminSession = ldapSession.getLdapServer().getDirectoryService().getAdminSession();
073    
074                final CallbackHandler callbackHandler = new GssapiCallbackHandler( ldapSession, adminSession, bindRequest );
075    
076                ss = ( SaslServer ) Subject.doAs( subject, new PrivilegedExceptionAction<SaslServer>()
077                {
078                    public SaslServer run() throws Exception
079                    {
080                        return Sasl.createSaslServer( SupportedSaslMechanisms.GSSAPI, SaslConstants.LDAP_PROTOCOL,
081                            saslHost, saslProps, callbackHandler );
082                    }
083                } );
084    
085                ldapSession.putSaslProperty( SaslConstants.SASL_SERVER, ss );
086            }
087    
088            return ss;
089        }
090    
091    
092        /**
093         * {@inheritDoc}
094         */
095        public void init( LdapSession ldapSession )
096        {
097            // Store the host in the ldap session
098            String saslHost = ldapSession.getLdapServer().getSaslHost();
099            ldapSession.putSaslProperty( SaslConstants.SASL_HOST, saslHost );
100    
101            Map<String, String> saslProps = new HashMap<String, String>();
102            saslProps.put( Sasl.QOP, ldapSession.getLdapServer().getSaslQopString() );
103            //saslProps.put( "com.sun.security.sasl.digest.realm", getActiveRealms( ldapSession.getLdapServer() ) );
104            ldapSession.putSaslProperty( SaslConstants.SASL_PROPS, saslProps );
105        }
106    
107    
108        /**
109         * Remove the Host, UserBaseDn, props and Mechanism property.
110         * 
111         * @param ldapSession the Ldapsession instance
112         */
113        public void cleanup( LdapSession ldapSession )
114        {
115            // Inject the Sasl Filter
116            insertSaslFilter( ldapSession );
117    
118            // and remove the useless informations
119            ldapSession.removeSaslProperty( SaslConstants.SASL_HOST );
120            ldapSession.removeSaslProperty( SaslConstants.SASL_USER_BASE_DN );
121            ldapSession.removeSaslProperty( SaslConstants.SASL_MECH );
122            ldapSession.removeSaslProperty( SaslConstants.SASL_PROPS );
123            ldapSession.removeSaslProperty( SaslConstants.SASL_AUTHENT_USER );
124        }
125    
126    
127        private Subject getSubject( LdapServer ldapServer ) throws Exception
128        {
129            String servicePrincipalName = ldapServer.getSaslPrincipal();
130            KerberosPrincipal servicePrincipal = new KerberosPrincipal( servicePrincipalName );
131            GetPrincipal getPrincipal = new GetPrincipal( servicePrincipal );
132    
133            PrincipalStoreEntry entry = null;
134    
135            try
136            {
137                entry = findPrincipal( ldapServer, getPrincipal );
138            }
139            catch ( ServiceConfigurationException sce )
140            {
141                String message = I18n.err( I18n.ERR_659, servicePrincipalName, ldapServer.getSearchBaseDn() );
142                throw new ServiceConfigurationException( message, sce );
143            }
144    
145            if ( entry == null )
146            {
147                String message = I18n.err( I18n.ERR_659, servicePrincipalName, ldapServer.getSearchBaseDn() );
148                throw new ServiceConfigurationException( message );
149            }
150    
151            Subject subject = new Subject();
152    
153            for ( EncryptionType encryptionType : entry.getKeyMap().keySet() )
154            {
155                EncryptionKey key = entry.getKeyMap().get( encryptionType );
156    
157                byte[] keyBytes = key.getKeyValue();
158                int type = key.getKeyType().getOrdinal();
159                int kvno = key.getKeyVersion();
160    
161                KerberosKey serviceKey = new KerberosKey( servicePrincipal, keyBytes, type, kvno );
162    
163                subject.getPrivateCredentials().add( serviceKey );
164            }
165    
166            return subject;
167        }
168    
169    
170        private PrincipalStoreEntry findPrincipal( LdapServer ldapServer, GetPrincipal getPrincipal ) throws Exception
171        {
172            CoreSession adminSession = ldapServer.getDirectoryService().getAdminSession();
173            return ( PrincipalStoreEntry ) getPrincipal.execute( adminSession, new DN( ldapServer.getSearchBaseDn() ) );
174        }
175    }