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