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