Package org.apache.directory.server.kerberos.kdc.ticketgrant

Source Code of org.apache.directory.server.kerberos.kdc.ticketgrant.TicketGrantingService

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*    http://www.apache.org/licenses/LICENSE-2.0
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/
package org.apache.directory.server.kerberos.kdc.ticketgrant;


import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.security.auth.kerberos.KerberosPrincipal;

import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.kerberos.kdc.KdcContext;
import org.apache.directory.server.kerberos.kdc.KdcServer;
import org.apache.directory.server.kerberos.shared.KerberosConstants;
import org.apache.directory.server.kerberos.shared.KerberosUtils;
import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumHandler;
import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
import org.apache.directory.server.kerberos.shared.io.decoder.ApplicationRequestDecoder;
import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
import org.apache.directory.server.kerberos.shared.messages.KdcReply;
import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
import org.apache.directory.server.kerberos.shared.messages.value.PaData;
import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev: 583938 $, $Date: 2007-10-11 21:57:20 +0200 (Thu, 11 Oct 2007) $
*/
public class TicketGrantingService
{
   
    /** the log for this class */
    private static final Logger LOG = LoggerFactory.getLogger( TicketGrantingService.class );
   
    private static final InMemoryReplayCache replayCache = new InMemoryReplayCache();
    private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();

    private static final String SERVICE_NAME = "Ticket-Granting Service (TGS)";

    private static final ChecksumHandler checksumHandler = new ChecksumHandler();

    public static void execute( TicketGrantingContext tgsContext ) throws Exception
    {
        if ( LOG.isDebugEnabled() )
        {
            monitorRequest( tgsContext );
        }

        configureTicketGranting( tgsContext);
        selectEncryptionType( tgsContext );
        getAuthHeader( tgsContext );
        verifyTgt( tgsContext );
        getTicketPrincipalEntry( tgsContext );
        verifyTgtAuthHeader( tgsContext );
        verifyBodyChecksum( tgsContext );
        getRequestPrincipalEntry( tgsContext );
        generateTicket( tgsContext );
        buildReply( tgsContext );

        if ( LOG.isDebugEnabled() )
        {
            monitorContext( tgsContext );
            monitorReply( tgsContext );
        }

        sealReply( tgsContext );
    }
   
   
    private static void configureTicketGranting( TicketGrantingContext tgsContext ) throws KerberosException
    {
        KdcServer config = tgsContext.getConfig();
        long clockSkew = config.getAllowableClockSkew();
        replayCache.setClockSkew( clockSkew );
        tgsContext.setReplayCache( replayCache );

        tgsContext.setCipherTextHandler( cipherTextHandler );

        if ( tgsContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
        {
            throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
        }
    }
   

    private static void monitorRequest( KdcContext kdcContext ) throws Exception
    {
        KdcRequest request = kdcContext.getRequest();

        try
        {
            String clientAddress = kdcContext.getClientAddress().getHostAddress();

            StringBuffer sb = new StringBuffer();

            sb.append( "Received " + SERVICE_NAME + " request:" );
            sb.append( "\n\t" + "messageType:           " + request.getMessageType() );
            sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
            sb.append( "\n\t" + "clientAddress:         " + clientAddress );
            sb.append( "\n\t" + "nonce:                 " + request.getNonce() );
            sb.append( "\n\t" + "kdcOptions:            " + request.getKdcOptions() );
            sb.append( "\n\t" + "clientPrincipal:       " + request.getClientPrincipal() );
            sb.append( "\n\t" + "serverPrincipal:       " + request.getServerPrincipal() );
            sb.append( "\n\t" + "encryptionType:        " + KerberosUtils.getEncryptionTypesString( request.getEType() ) );
            sb.append( "\n\t" + "realm:                 " + request.getRealm() );
            sb.append( "\n\t" + "from time:             " + request.getFrom() );
            sb.append( "\n\t" + "till time:             " + request.getTill() );
            sb.append( "\n\t" + "renew-till time:       " + request.getRtime() );
            sb.append( "\n\t" + "hostAddresses:         " + request.getAddresses() );

            LOG.debug( sb.toString() );
        }
        catch ( Exception e )
        {
            // This is a monitor.  No exceptions should bubble up.
            LOG.error( I18n.err( I18n.ERR_153 ), e );
        }
    }
   
   
    private static void selectEncryptionType( TicketGrantingContext tgsContext ) throws Exception
    {
        KdcContext kdcContext = (KdcContext)tgsContext;
        KdcServer config = kdcContext.getConfig();

        Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType();

        EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );

        LOG.debug( "Session will use encryption type {}.", bestType );

        if ( bestType == null )
        {
            throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
        }

        kdcContext.setEncryptionType( bestType );
    }
   
   
    private static void getAuthHeader( TicketGrantingContext tgsContext ) throws Exception
    {
        KdcRequest request = tgsContext.getRequest();

        PaData[] preAuthData = request.getPreAuthData();

        if ( preAuthData == null || preAuthData.length < 1 )
        {
            throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
        }

        byte[] undecodedAuthHeader = null;

        for ( int ii = 0; ii < preAuthData.length; ii++ )
        {
            if ( preAuthData[ii].getPaDataType() == PaDataType.PA_TGS_REQ )
            {
                undecodedAuthHeader = preAuthData[ii].getPaDataValue();
            }
        }

        if ( undecodedAuthHeader == null )
        {
            throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
        }

        ApplicationRequestDecoder decoder = new ApplicationRequestDecoder();
        ApplicationRequest authHeader = decoder.decode( undecodedAuthHeader );
       
        Ticket tgt = authHeader.getTicket();

        tgsContext.setAuthHeader( authHeader );
        tgsContext.setTgt( tgt );
    }
   
   
    public static void verifyTgt( TicketGrantingContext tgsContext ) throws KerberosException
    {
        KdcServer config = tgsContext.getConfig();
        Ticket tgt = tgsContext.getTgt();

        // Check primary realm.
        if ( !tgt.getRealm().equals( config.getPrimaryRealm() ) )
        {
            throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
        }

        String tgtServerName = tgt.getServerPrincipal().getName();
        String requestServerName = tgsContext.getRequest().getServerPrincipal().getName();

        /*
         * if (tgt.sname is not a TGT for local realm and is not req.sname)
         *     then error_out(KRB_AP_ERR_NOT_US);
         */
        if ( !tgtServerName.equals( config.getServicePrincipal().getName() )
            && !tgtServerName.equals( requestServerName ) )
        {
            throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
        }
    }
   
   
    private static void getTicketPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
    {
        KerberosPrincipal principal = tgsContext.getTgt().getServerPrincipal();
        PrincipalStore store = tgsContext.getStore();

        PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
        tgsContext.setTicketPrincipalEntry( entry );
    }


    private static void verifyTgtAuthHeader( TicketGrantingContext tgsContext ) throws KerberosException
    {
        ApplicationRequest authHeader = tgsContext.getAuthHeader();
        Ticket tgt = tgsContext.getTgt();
       
        boolean isValidate = tgsContext.getRequest().getKdcOptions().get( KdcOptions.VALIDATE );

        EncryptionType encryptionType = tgt.getEncPart().getEType();
        EncryptionKey serverKey = tgsContext.getTicketPrincipalEntry().getKeyMap().get( encryptionType );

        long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
        ReplayCache replayCache = tgsContext.getReplayCache();
        boolean emptyAddressesAllowed = tgsContext.getConfig().isEmptyAddressesAllowed();
        InetAddress clientAddress = tgsContext.getClientAddress();
        CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();

        Authenticator authenticator = KerberosUtils.verifyAuthHeader( authHeader, tgt, serverKey, clockSkew, replayCache,
            emptyAddressesAllowed, clientAddress, cipherTextHandler, KeyUsage.NUMBER7, isValidate );

        tgsContext.setAuthenticator( authenticator );
    }
   
   
    private static void verifyBodyChecksum( TicketGrantingContext tgsContext ) throws KerberosException
    {
        KdcServer config = tgsContext.getConfig();

        if ( config.isBodyChecksumVerified() )
        {
            byte[] bodyBytes = tgsContext.getRequest().getBodyBytes();
            Checksum authenticatorChecksum = tgsContext.getAuthenticator().getChecksum();

            if ( authenticatorChecksum == null || authenticatorChecksum.getChecksumType() == null
                || authenticatorChecksum.getChecksumValue() == null || bodyBytes == null )
            {
                throw new KerberosException( ErrorType.KRB_AP_ERR_INAPP_CKSUM );
            }

            LOG.debug( "Verifying body checksum type '{}'.", authenticatorChecksum.getChecksumType() );

            checksumHandler.verifyChecksum( authenticatorChecksum, bodyBytes, null, KeyUsage.NUMBER8 );
        }
    }
   

    public static void getRequestPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
    {
        KerberosPrincipal principal = tgsContext.getRequest().getServerPrincipal();
        PrincipalStore store = tgsContext.getStore();

        PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
        tgsContext.setRequestPrincipalEntry( entry );
    }

   
    private static void generateTicket( TicketGrantingContext tgsContext ) throws KerberosException
    {
        KdcRequest request = tgsContext.getRequest();
        Ticket tgt = tgsContext.getTgt();
        Authenticator authenticator = tgsContext.getAuthenticator();
        CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
        KerberosPrincipal ticketPrincipal = request.getServerPrincipal();

        EncryptionType encryptionType = tgsContext.getEncryptionType();
        EncryptionKey serverKey = tgsContext.getRequestPrincipalEntry().getKeyMap().get( encryptionType );

        KdcServer config = tgsContext.getConfig();

        EncTicketPartModifier newTicketBody = new EncTicketPartModifier();

        newTicketBody.setClientAddresses( tgt.getEncTicketPart().getClientAddresses() );

        processFlags( config, request, tgt, newTicketBody );

        EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( tgsContext.getEncryptionType() );
        newTicketBody.setSessionKey( sessionKey );

        newTicketBody.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );

        if ( request.getEncAuthorizationData() != null )
        {
            AuthorizationData authData = ( AuthorizationData ) cipherTextHandler.unseal( AuthorizationData.class,
                authenticator.getSubSessionKey(), request.getEncAuthorizationData(), KeyUsage.NUMBER4 );
            authData.add( tgt.getEncTicketPart().getAuthorizationData() );
            newTicketBody.setAuthorizationData( authData );
        }

        processTransited( newTicketBody, tgt );

        processTimes( config, request, newTicketBody, tgt );

        EncTicketPart ticketPart = newTicketBody.getEncTicketPart();

        if ( request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) )
        {
            /*
             * if (server not specified) then
             *         server = req.second_ticket.client;
             * endif
             *
             * if ((req.second_ticket is not a TGT) or
             *     (req.second_ticket.client != server)) then
             *         error_out(KDC_ERR_POLICY);
             * endif
             *
             * new_tkt.enc-part := encrypt OCTET STRING using etype_for_key(second-ticket.key), second-ticket.key;
             */
            throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
        }
        else
        {
            EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 );

            Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
            newTicket.setEncTicketPart( ticketPart );

            tgsContext.setNewTicket( newTicket );
        }
    }
   

    private static void buildReply( TicketGrantingContext tgsContext ) throws KerberosException
    {
        KdcRequest request = tgsContext.getRequest();
        Ticket tgt = tgsContext.getTgt();
        Ticket newTicket = tgsContext.getNewTicket();

        TicketGrantReply reply = new TicketGrantReply();
        reply.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );
        reply.setTicket( newTicket );
        reply.setKey( newTicket.getEncTicketPart().getSessionKey() );
        reply.setNonce( request.getNonce() );
        // TODO - resp.last-req := fetch_last_request_info(client); requires store
        reply.setLastRequest( new LastRequest() );
        reply.setFlags( newTicket.getEncTicketPart().getFlags() );
        reply.setClientAddresses( newTicket.getEncTicketPart().getClientAddresses() );
        reply.setAuthTime( newTicket.getEncTicketPart().getAuthTime() );
        reply.setStartTime( newTicket.getEncTicketPart().getStartTime() );
        reply.setEndTime( newTicket.getEncTicketPart().getEndTime() );
        reply.setServerPrincipal( newTicket.getServerPrincipal() );

        if ( newTicket.getEncTicketPart().getFlags().isRenewable() )
        {
            reply.setRenewTill( newTicket.getEncTicketPart().getRenewTill() );
        }

        tgsContext.setReply( reply );
    }
   
   
    private static void sealReply( TicketGrantingContext tgsContext ) throws KerberosException
    {
        TicketGrantReply reply = ( TicketGrantReply ) tgsContext.getReply();
        Ticket tgt = tgsContext.getTgt();
        CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
        Authenticator authenticator = tgsContext.getAuthenticator();

        EncryptedData encryptedData;

        if ( authenticator.getSubSessionKey() != null )
        {
            encryptedData = cipherTextHandler.seal( authenticator.getSubSessionKey(), reply, KeyUsage.NUMBER9 );
        }
        else
        {
            encryptedData = cipherTextHandler.seal( tgt.getEncTicketPart().getSessionKey(), reply, KeyUsage.NUMBER8 );
        }

        reply.setEncPart( encryptedData );
    }
   
   
   
    private static void monitorContext( TicketGrantingContext tgsContext )
    {
        try
        {
            Ticket tgt = tgsContext.getTgt();
            long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
            ChecksumType checksumType = tgsContext.getAuthenticator().getChecksum().getChecksumType();
            InetAddress clientAddress = tgsContext.getClientAddress();
            HostAddresses clientAddresses = tgt.getEncTicketPart().getClientAddresses();

            boolean caddrContainsSender = false;
            if ( tgt.getEncTicketPart().getClientAddresses() != null )
            {
                caddrContainsSender = tgt.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) );
            }

            StringBuffer sb = new StringBuffer();

            sb.append( "Monitoring " + SERVICE_NAME + " context:" );

            sb.append( "\n\t" + "clockSkew              " + clockSkew );
            sb.append( "\n\t" + "checksumType           " + checksumType );
            sb.append( "\n\t" + "clientAddress          " + clientAddress );
            sb.append( "\n\t" + "clientAddresses        " + clientAddresses );
            sb.append( "\n\t" + "caddr contains sender  " + caddrContainsSender );

            KerberosPrincipal requestServerPrincipal = tgsContext.getRequest().getServerPrincipal();
            PrincipalStoreEntry requestPrincipal = tgsContext.getRequestPrincipalEntry();

            sb.append( "\n\t" + "principal              " + requestServerPrincipal );
            sb.append( "\n\t" + "cn                     " + requestPrincipal.getCommonName() );
            sb.append( "\n\t" + "realm                  " + requestPrincipal.getRealmName() );
            sb.append( "\n\t" + "principal              " + requestPrincipal.getPrincipal() );
            sb.append( "\n\t" + "SAM type               " + requestPrincipal.getSamType() );

            KerberosPrincipal ticketServerPrincipal = tgsContext.getTgt().getServerPrincipal();
            PrincipalStoreEntry ticketPrincipal = tgsContext.getTicketPrincipalEntry();

            sb.append( "\n\t" + "principal              " + ticketServerPrincipal );
            sb.append( "\n\t" + "cn                     " + ticketPrincipal.getCommonName() );
            sb.append( "\n\t" + "realm                  " + ticketPrincipal.getRealmName() );
            sb.append( "\n\t" + "principal              " + ticketPrincipal.getPrincipal() );
            sb.append( "\n\t" + "SAM type               " + ticketPrincipal.getSamType() );

            EncryptionType encryptionType = tgsContext.getTgt().getEncPart().getEType();
            int keyVersion = ticketPrincipal.getKeyMap().get( encryptionType ).getKeyVersion();
            sb.append( "\n\t" + "Ticket key type        " + encryptionType );
            sb.append( "\n\t" + "Service key version    " + keyVersion );

            LOG.debug( sb.toString() );
        }
        catch ( Exception e )
        {
            // This is a monitor.  No exceptions should bubble up.
            LOG.error( I18n.err( I18n.ERR_154 ), e );
        }
    }

   
    private static void monitorReply( KdcContext kdcContext )
    {
        Object reply = kdcContext.getReply();

        if ( reply instanceof KdcReply )
        {
            KdcReply success = ( KdcReply ) reply;

            try
            {
                StringBuffer sb = new StringBuffer();

                sb.append( "Responding with " + SERVICE_NAME + " reply:" );
                sb.append( "\n\t" + "messageType:           " + success.getMessageType() );
                sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() );
                sb.append( "\n\t" + "nonce:                 " + success.getNonce() );
                sb.append( "\n\t" + "clientPrincipal:       " + success.getClientPrincipal() );
                sb.append( "\n\t" + "client realm:          " + success.getClientRealm() );
                sb.append( "\n\t" + "serverPrincipal:       " + success.getServerPrincipal() );
                sb.append( "\n\t" + "server realm:          " + success.getServerRealm() );
                sb.append( "\n\t" + "auth time:             " + success.getAuthTime() );
                sb.append( "\n\t" + "start time:            " + success.getStartTime() );
                sb.append( "\n\t" + "end time:              " + success.getEndTime() );
                sb.append( "\n\t" + "renew-till time:       " + success.getRenewTill() );
                sb.append( "\n\t" + "hostAddresses:         " + success.getClientAddresses() );

                LOG.debug( sb.toString() );
            }
            catch ( Exception e )
            {
                // This is a monitor.  No exceptions should bubble up.
                LOG.error( I18n.err( I18n.ERR_155 ), e );
            }
        }
    }
   

   
    private static void processFlags( KdcServer config, KdcRequest request, Ticket tgt,
        EncTicketPartModifier newTicketBody ) throws KerberosException
    {
        if ( tgt.getEncTicketPart().getFlags().isPreAuth() )
        {
            newTicketBody.setFlag( TicketFlag.PRE_AUTHENT );
        }

        if ( request.getOption( KdcOptions.FORWARDABLE ) )
        {
            if ( !config.isForwardableAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
            }

            newTicketBody.setFlag( TicketFlag.FORWARDABLE );
        }

        if ( request.getOption( KdcOptions.FORWARDED ) )
        {
            if ( !config.isForwardableAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
            }

            if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
                && request.getAddresses().getAddresses().length > 0 )
            {
                newTicketBody.setClientAddresses( request.getAddresses() );
            }
            else
            {
                if ( !config.isEmptyAddressesAllowed() )
                {
                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
                }
            }

            newTicketBody.setFlag( TicketFlag.FORWARDED );
        }

        if ( tgt.getEncTicketPart().getFlags().isForwarded() )
        {
            newTicketBody.setFlag( TicketFlag.FORWARDED );
        }

        if ( request.getOption( KdcOptions.PROXIABLE ) )
        {
            if ( !config.isProxiableAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
            }

            newTicketBody.setFlag( TicketFlag.PROXIABLE );
        }

        if ( request.getOption( KdcOptions.PROXY ) )
        {
            if ( !config.isProxiableAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
            }

            if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
                && request.getAddresses().getAddresses().length > 0 )
            {
                newTicketBody.setClientAddresses( request.getAddresses() );
            }
            else
            {
                if ( !config.isEmptyAddressesAllowed() )
                {
                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
                }
            }

            newTicketBody.setFlag( TicketFlag.PROXY );
        }

        if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) )
        {
            if ( !config.isPostdatedAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
            }

            newTicketBody.setFlag( TicketFlag.MAY_POSTDATE );
        }

        /*
         * "Otherwise, if the TGT has the MAY-POSTDATE flag set, then the resulting
         * ticket will be postdated, and the requested starttime is checked against
         * the policy of the local realm.  If acceptable, the ticket's starttime is
         * set as requested, and the INVALID flag is set.  The postdated ticket MUST
         * be validated before use by presenting it to the KDC after the starttime
         * has been reached.  However, in no case may the starttime, endtime, or
         * renew-till time of a newly-issued postdated ticket extend beyond the
         * renew-till time of the TGT."
         */
        if ( request.getOption( KdcOptions.POSTDATED ) )
        {
            if ( !config.isPostdatedAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
            }

            newTicketBody.setFlag( TicketFlag.POSTDATED );
            newTicketBody.setFlag( TicketFlag.INVALID );

            newTicketBody.setStartTime( request.getFrom() );
        }

        if ( request.getOption( KdcOptions.VALIDATE ) )
        {
            if ( !config.isPostdatedAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isInvalid() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            KerberosTime startTime = ( tgt.getEncTicketPart().getStartTime() != null ) ?
                    tgt.getEncTicketPart().getStartTime() :
                        tgt.getEncTicketPart().getAuthTime();

            if ( startTime.greaterThan( new KerberosTime() ) )
            {
                throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV );
            }

            echoTicket( newTicketBody, tgt );
            newTicketBody.clearFlag( TicketFlag.INVALID );
        }

        if ( request.getOption( KdcOptions.RESERVED ) )
        {
            throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
        }
    }


    private static void processTimes( KdcServer config, KdcRequest request, EncTicketPartModifier newTicketBody,
        Ticket tgt ) throws KerberosException
    {
        KerberosTime now = new KerberosTime();

        newTicketBody.setAuthTime( tgt.getEncTicketPart().getAuthTime() );

        KerberosTime startTime = request.getFrom();

        /*
         * "If the requested starttime is absent, indicates a time in the past,
         * or is within the window of acceptable clock skew for the KDC and the
         * POSTDATE option has not been specified, then the starttime of the
         * ticket is set to the authentication server's current time."
         */
        if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
            && !request.getOption( KdcOptions.POSTDATED ) )
        {
            startTime = now;
        }

        /*
         * "If it indicates a time in the future beyond the acceptable clock skew,
         * but the POSTDATED option has not been specified or the MAY-POSTDATE flag
         * is not set in the TGT, then the error KDC_ERR_CANNOT_POSTDATE is
         * returned."
         */
        if ( startTime != null && startTime.greaterThan( now )
            && !startTime.isInClockSkew( config.getAllowableClockSkew() )
            && ( !request.getOption( KdcOptions.POSTDATED ) || !tgt.getEncTicketPart().getFlags().isMayPosdate() ) )
        {
            throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
        }

        KerberosTime renewalTime = null;
        KerberosTime kerberosEndTime = null;

        if ( request.getOption( KdcOptions.RENEW ) )
        {
            if ( !config.isRenewableAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            if ( !tgt.getEncTicketPart().getFlags().isRenewable() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
            }

            if ( tgt.getEncTicketPart().getRenewTill().lessThan( now ) )
            {
                throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED );
            }

            echoTicket( newTicketBody, tgt );

            newTicketBody.setStartTime( now );

            KerberosTime tgtStartTime = ( tgt.getEncTicketPart().getStartTime() != null ) ?
                tgt.getEncTicketPart().getStartTime() :
                    tgt.getEncTicketPart().getAuthTime();

            long oldLife = tgt.getEncTicketPart().getEndTime().getTime() - tgtStartTime.getTime();

            kerberosEndTime = new KerberosTime( Math.min( tgt.getEncTicketPart().getRenewTill().getTime(), now.getTime() + oldLife ) );
            newTicketBody.setEndTime( kerberosEndTime );
        }
        else
        {
            if ( newTicketBody.getEncTicketPart().getStartTime() == null )
            {
                newTicketBody.setStartTime( now );
            }

            KerberosTime till;
            if ( request.getTill().isZero() )
            {
                till = KerberosTime.INFINITY;
            }
            else
            {
                till = request.getTill();
            }

            /*
             * The end time is the minimum of (a) the requested till time or (b)
             * the start time plus maximum lifetime as configured in policy or (c)
             * the end time of the TGT.
             */
            List<KerberosTime> minimizer = new ArrayList<KerberosTime>();
            minimizer.add( till );
            minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumTicketLifetime() ) );
            minimizer.add( tgt.getEncTicketPart().getEndTime() );
            kerberosEndTime = Collections.min( minimizer );

            newTicketBody.setEndTime( kerberosEndTime );

            if ( request.getOption( KdcOptions.RENEWABLE_OK ) && kerberosEndTime.lessThan( request.getTill() )
                && tgt.getEncTicketPart().getFlags().isRenewable() )
            {
                if ( !config.isRenewableAllowed() )
                {
                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
                }

                // We set the RENEWABLE option for later processing.                          
                request.setOption( KdcOptions.RENEWABLE );
                long rtime = Math.min( request.getTill().getTime(), tgt.getEncTicketPart().getRenewTill().getTime() );
                renewalTime = new KerberosTime( rtime );
            }
        }

        if ( renewalTime == null )
        {
            renewalTime = request.getRtime();
        }

        KerberosTime rtime;
        if ( renewalTime != null && renewalTime.isZero() )
        {
            rtime = KerberosTime.INFINITY;
        }
        else
        {
            rtime = renewalTime;
        }

        if ( request.getOption( KdcOptions.RENEWABLE ) && tgt.getEncTicketPart().getFlags().isRenewable() )
        {
            if ( !config.isRenewableAllowed() )
            {
                throw new KerberosException( ErrorType.KDC_ERR_POLICY );
            }

            newTicketBody.setFlag( TicketFlag.RENEWABLE );

            /*
             * The renew-till time is the minimum of (a) the requested renew-till
             * time or (b) the start time plus maximum renewable lifetime as
             * configured in policy or (c) the renew-till time of the TGT.
             */
            List<KerberosTime> minimizer = new ArrayList<KerberosTime>();

            /*
             * 'rtime' KerberosTime is OPTIONAL
             */
            if ( rtime != null )
            {
                minimizer.add( rtime );
            }

            minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumRenewableLifetime() ) );
            minimizer.add( tgt.getEncTicketPart().getRenewTill() );
            newTicketBody.setRenewTill( Collections.min( minimizer ) );
        }

        /*
         * "If the requested expiration time minus the starttime (as determined
         * above) is less than a site-determined minimum lifetime, an error
         * message with code KDC_ERR_NEVER_VALID is returned."
         */
        if ( kerberosEndTime.lessThan( startTime ) )
        {
            throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
        }

        long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
        if ( ticketLifeTime < config.getAllowableClockSkew() )
        {
            throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
        }
    }


    /*
     * if (realm_tgt_is_for(tgt) := tgt.realm) then
     *         // tgt issued by local realm
     *         new_tkt.transited := tgt.transited;
     * else
     *         // was issued for this realm by some other realm
     *         if (tgt.transited.tr-type not supported) then
     *                 error_out(KDC_ERR_TRTYPE_NOSUPP);
     *         endif
     *
     *         new_tkt.transited := compress_transited(tgt.transited + tgt.realm)
     * endif
     */   
    private static void processTransited( EncTicketPartModifier newTicketBody, Ticket tgt )
    {
        // TODO - currently no transited support other than local
        newTicketBody.setTransitedEncoding( tgt.getEncTicketPart().getTransitedEncoding() );
    }

   
    private static void echoTicket( EncTicketPartModifier newTicketBody, Ticket tgt )
    {
        EncTicketPart encTicketpart = tgt.getEncTicketPart();
        newTicketBody.setAuthorizationData( encTicketpart.getAuthorizationData() );
        newTicketBody.setAuthTime( encTicketpart.getAuthTime() );
        newTicketBody.setClientAddresses( encTicketpart.getClientAddresses() );
        newTicketBody.setClientPrincipal( encTicketpart.getClientPrincipal() );
        newTicketBody.setEndTime( encTicketpart.getEndTime() );
        newTicketBody.setFlags( encTicketpart.getFlags() );
        newTicketBody.setRenewTill( encTicketpart.getRenewTill() );
        newTicketBody.setSessionKey( encTicketpart.getSessionKey() );
        newTicketBody.setTransitedEncoding( encTicketpart.getTransitedEncoding() );
    }
}
TOP

Related Classes of org.apache.directory.server.kerberos.kdc.ticketgrant.TicketGrantingService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.