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.kerberos.kdc.authentication;
021    
022    
023    import java.io.IOException;
024    import java.net.InetAddress;
025    import java.util.Date;
026    import java.util.Set;
027    
028    import javax.security.auth.kerberos.KerberosKey;
029    import javax.security.auth.kerberos.KerberosPrincipal;
030    
031    import org.apache.directory.server.i18n.I18n;
032    import org.apache.directory.server.kerberos.kdc.KdcContext;
033    import org.apache.directory.server.kerberos.kdc.KdcServer;
034    import org.apache.directory.server.kerberos.sam.SamException;
035    import org.apache.directory.server.kerberos.sam.SamSubsystem;
036    import org.apache.directory.server.kerberos.shared.KerberosConstants;
037    import org.apache.directory.server.kerberos.shared.KerberosUtils;
038    import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
039    import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
040    import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
041    import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
042    import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
043    import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
044    import org.apache.directory.server.kerberos.shared.io.decoder.EncryptedDataDecoder;
045    import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionTypeInfoEncoder;
046    import org.apache.directory.server.kerberos.shared.io.encoder.PreAuthenticationDataEncoder;
047    import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
048    import org.apache.directory.server.kerberos.shared.messages.KdcReply;
049    import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
050    import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
051    import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
052    import org.apache.directory.server.kerberos.shared.messages.components.InvalidTicketException;
053    import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
054    import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
055    import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
056    import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
057    import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfoEntry;
058    import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
059    import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
060    import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
061    import org.apache.directory.server.kerberos.shared.messages.value.PaData;
062    import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
063    import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
064    import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
065    import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
066    import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
067    import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
068    import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
069    import org.slf4j.Logger;
070    import org.slf4j.LoggerFactory;
071    
072    
073    /**
074     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
075     * @version $Rev: 901657 $, $Date: 2010-01-21 12:27:15 +0100 (Thu, 21 Jan 2010) $
076     */
077    public class AuthenticationService
078    {
079        /** The log for this class. */
080        private static final Logger LOG = LoggerFactory.getLogger( AuthenticationService.class );
081    
082        private static final ReplayCache replayCache = new InMemoryReplayCache();
083        private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
084    
085        private static final String SERVICE_NAME = "Authentication Service (AS)";
086    
087    
088        public static void execute( AuthenticationContext authContext ) throws Exception
089        {
090            if ( LOG.isDebugEnabled() )
091            {
092                monitorRequest( authContext );
093            }
094            
095            authContext.setReplayCache( replayCache );
096            authContext.setCipherTextHandler( cipherTextHandler );
097    
098            if ( authContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
099            {
100                throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
101            }
102    
103            selectEncryptionType( authContext );
104            getClientEntry( authContext );
105            verifyPolicy( authContext );
106            verifySam( authContext );
107            verifyEncryptedTimestamp( authContext );
108            
109            if ( authContext.getClientKey() == null )
110            {
111                verifyEncryptedTimestamp( authContext );
112            }
113    
114            getServerEntry( authContext );
115            generateTicket( authContext );
116            buildReply( authContext );
117    
118            if ( LOG.isDebugEnabled() )
119            {
120                monitorContext( authContext );
121                monitorReply( ( KdcContext ) authContext );
122            }
123            
124            sealReply( authContext );
125        }
126    
127        
128        private static void selectEncryptionType( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
129        {
130            KdcContext kdcContext = ( KdcContext ) authContext;
131            KdcServer config = kdcContext.getConfig();
132    
133            Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType();
134    
135            EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
136    
137            LOG.debug( "Session will use encryption type {}.", bestType );
138    
139            if ( bestType == null )
140            {
141                throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
142            }
143    
144            kdcContext.setEncryptionType( bestType );
145        }
146    
147        
148        private static void getClientEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
149        {
150            KerberosPrincipal principal = authContext.getRequest().getClientPrincipal();
151            PrincipalStore store = authContext.getStore();
152    
153            PrincipalStoreEntry storeEntry = getEntry( principal, store, ErrorType.KDC_ERR_C_PRINCIPAL_UNKNOWN ); 
154            authContext.setClientEntry( storeEntry );
155        }
156        
157        
158        private static void verifyPolicy( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
159        {
160            PrincipalStoreEntry entry = authContext.getClientEntry();
161    
162            if ( entry.isDisabled() )
163            {
164                throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
165            }
166    
167            if ( entry.isLockedOut() )
168            {
169                throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
170            }
171    
172            if ( entry.getExpiration().getTime() < new Date().getTime() )
173            {
174                throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
175            }
176        }
177        
178        
179        private static void verifySam( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
180        {
181            LOG.debug( "Verifying using SAM subsystem." );
182            KdcRequest request = authContext.getRequest();
183            KdcServer config = authContext.getConfig();
184    
185            PrincipalStoreEntry clientEntry = authContext.getClientEntry();
186            String clientName = clientEntry.getPrincipal().getName();
187    
188            EncryptionKey clientKey = null;
189    
190            if ( clientEntry.getSamType() != null )
191            {
192                if ( LOG.isDebugEnabled() )
193                {
194                    LOG.debug( "Entry for client principal {} has a valid SAM type.  Invoking SAM subsystem for pre-authentication.", clientName );
195                }
196    
197                PaData[] preAuthData = request.getPreAuthData();
198    
199                if ( preAuthData == null || preAuthData.length == 0 )
200                {
201                    throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, preparePreAuthenticationError( config
202                        .getEncryptionTypes() ) );
203                }
204    
205                try
206                {
207                    for ( int ii = 0; ii < preAuthData.length; ii++ )
208                    {
209                        if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
210                        {
211                            KerberosKey samKey = SamSubsystem.getInstance().verify( clientEntry,
212                                preAuthData[ii].getPaDataValue() );
213                            clientKey = new EncryptionKey( EncryptionType.getTypeByOrdinal( samKey.getKeyType() ), samKey
214                                .getEncoded() );
215                        }
216                    }
217                }
218                catch ( SamException se )
219                {
220                    throw new KerberosException( ErrorType.KRB_ERR_GENERIC, se );
221                }
222    
223                authContext.setClientKey( clientKey );
224                authContext.setPreAuthenticated( true );
225    
226                if ( LOG.isDebugEnabled() )
227                {
228                    LOG.debug( "Pre-authentication using SAM subsystem successful for {}.", clientName );
229                }
230            }
231        }
232        
233        
234        private static void verifyEncryptedTimestamp( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
235        {
236            LOG.debug( "Verifying using encrypted timestamp." );
237            
238            KdcServer config = authContext.getConfig();
239            KdcRequest request = authContext.getRequest();
240            CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
241            PrincipalStoreEntry clientEntry = authContext.getClientEntry();
242            String clientName = clientEntry.getPrincipal().getName();
243    
244            EncryptionKey clientKey = null;
245    
246            if ( clientEntry.getSamType() == null )
247            {
248                if ( LOG.isDebugEnabled() )
249                {
250                    LOG.debug(
251                        "Entry for client principal {} has no SAM type.  Proceeding with standard pre-authentication.",
252                        clientName );
253                }
254    
255                EncryptionType encryptionType = authContext.getEncryptionType();
256                clientKey = clientEntry.getKeyMap().get( encryptionType );
257    
258                if ( clientKey == null )
259                {
260                    throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
261                }
262    
263                if ( config.isPaEncTimestampRequired() )
264                {
265                    PaData[] preAuthData = request.getPreAuthData();
266    
267                    if ( preAuthData == null )
268                    {
269                        throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
270                            preparePreAuthenticationError( config.getEncryptionTypes() ) );
271                    }
272    
273                    EncryptedTimeStamp timestamp = null;
274    
275                    for ( int ii = 0; ii < preAuthData.length; ii++ )
276                    {
277                        if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
278                        {
279                            EncryptedData dataValue;
280    
281                            try
282                            {
283                                dataValue = EncryptedDataDecoder.decode( preAuthData[ii].getPaDataValue() );
284                            }
285                            catch ( IOException ioe )
286                            {
287                                throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe );
288                            }
289                            catch ( ClassCastException cce )
290                            {
291                                throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce );
292                            }
293    
294                            timestamp = ( EncryptedTimeStamp ) cipherTextHandler.unseal( EncryptedTimeStamp.class,
295                                clientKey, dataValue, KeyUsage.NUMBER1 );
296                        }
297                    }
298    
299                    if ( preAuthData.length > 0 && timestamp == null )
300                    {
301                        throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
302                    }
303    
304                    if ( timestamp == null )
305                    {
306                        throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
307                            preparePreAuthenticationError( config.getEncryptionTypes() ) );
308                    }
309    
310                    if ( !timestamp.getTimeStamp().isInClockSkew( config.getAllowableClockSkew() ) )
311                    {
312                        throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_FAILED );
313                    }
314    
315                    /*
316                     * if(decrypted_enc_timestamp and usec is replay)
317                     *         error_out(KDC_ERR_PREAUTH_FAILED);
318                     * endif
319                     * 
320                     * add decrypted_enc_timestamp and usec to replay cache;
321                     */
322                }
323            }
324    
325            authContext.setClientKey( clientKey );
326            authContext.setPreAuthenticated( true );
327    
328            if ( LOG.isDebugEnabled() )
329            {
330                LOG.debug( "Pre-authentication by encrypted timestamp successful for {}.", clientName );
331            }
332        }
333        
334        
335        private static void getServerEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
336        {
337            KerberosPrincipal principal = authContext.getRequest().getServerPrincipal();
338            PrincipalStore store = authContext.getStore();
339        
340            authContext.setServerEntry( getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) );
341        }    
342        
343        
344        private static void generateTicket( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
345        {
346            KdcRequest request = authContext.getRequest();
347            CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
348            KerberosPrincipal serverPrincipal = request.getServerPrincipal();
349    
350            EncryptionType encryptionType = authContext.getEncryptionType();
351            EncryptionKey serverKey = authContext.getServerEntry().getKeyMap().get( encryptionType );
352    
353            KerberosPrincipal ticketPrincipal = request.getServerPrincipal();
354            EncTicketPartModifier newTicketBody = new EncTicketPartModifier();
355            KdcServer config = authContext.getConfig();
356    
357            // The INITIAL flag indicates that a ticket was issued using the AS protocol.
358            newTicketBody.setFlag( TicketFlag.INITIAL );
359    
360            // The PRE-AUTHENT flag indicates that the client used pre-authentication.
361            if ( authContext.isPreAuthenticated() )
362            {
363                newTicketBody.setFlag( TicketFlag.PRE_AUTHENT );
364            }
365    
366            if ( request.getOption( KdcOptions.FORWARDABLE ) )
367            {
368                if ( !config.isForwardableAllowed() )
369                {
370                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
371                }
372    
373                newTicketBody.setFlag( TicketFlag.FORWARDABLE );
374            }
375    
376            if ( request.getOption( KdcOptions.PROXIABLE ) )
377            {
378                if ( !config.isProxiableAllowed() )
379                {
380                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
381                }
382    
383                newTicketBody.setFlag( TicketFlag.PROXIABLE );
384            }
385    
386            if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) )
387            {
388                if ( !config.isPostdatedAllowed() )
389                {
390                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
391                }
392    
393                newTicketBody.setFlag( TicketFlag.MAY_POSTDATE );
394            }
395    
396            if ( request.getOption( KdcOptions.RENEW ) || request.getOption( KdcOptions.VALIDATE )
397                || request.getOption( KdcOptions.PROXY ) || request.getOption( KdcOptions.FORWARDED )
398                || request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) )
399            {
400                throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
401            }
402    
403            EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( authContext.getEncryptionType() );
404            newTicketBody.setSessionKey( sessionKey );
405    
406            newTicketBody.setClientPrincipal( request.getClientPrincipal() );
407            newTicketBody.setTransitedEncoding( new TransitedEncoding() );
408    
409            KerberosTime now = new KerberosTime();
410    
411            newTicketBody.setAuthTime( now );
412    
413            KerberosTime startTime = request.getFrom();
414    
415            /*
416             * "If the requested starttime is absent, indicates a time in the past,
417             * or is within the window of acceptable clock skew for the KDC and the
418             * POSTDATE option has not been specified, then the starttime of the
419             * ticket is set to the authentication server's current time."
420             */
421            if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
422                && !request.getOption( KdcOptions.POSTDATED ) )
423            {
424                startTime = now;
425            }
426    
427            /*
428             * "If it indicates a time in the future beyond the acceptable clock skew,
429             * but the POSTDATED option has not been specified, then the error
430             * KDC_ERR_CANNOT_POSTDATE is returned."
431             */
432            if ( startTime != null && startTime.greaterThan( now )
433                && !startTime.isInClockSkew( config.getAllowableClockSkew() ) && !request.getOption( KdcOptions.POSTDATED ) )
434            {
435                throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
436            }
437    
438            /*
439             * "Otherwise the requested starttime is checked against the policy of the
440             * local realm and if the ticket's starttime is acceptable, it is set as
441             * requested, and the INVALID flag is set in the new ticket."
442             */
443            if ( request.getOption( KdcOptions.POSTDATED ) )
444            {
445                if ( !config.isPostdatedAllowed() )
446                {
447                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
448                }
449    
450                newTicketBody.setFlag( TicketFlag.POSTDATED );
451                newTicketBody.setFlag( TicketFlag.INVALID );
452                newTicketBody.setStartTime( startTime );
453            }
454    
455            long till = 0;
456            
457            if ( request.getTill().getTime() == 0 )
458            {
459                till = Long.MAX_VALUE;
460            }
461            else
462            {
463                till = request.getTill().getTime();
464            }
465    
466            /*
467             * The end time is the minimum of (a) the requested till time or (b)
468             * the start time plus maximum lifetime as configured in policy.
469             */
470            long endTime = Math.min( till, startTime.getTime() + config.getMaximumTicketLifetime() );
471            KerberosTime kerberosEndTime = new KerberosTime( endTime );
472            newTicketBody.setEndTime( kerberosEndTime );
473    
474            /*
475             * "If the requested expiration time minus the starttime (as determined
476             * above) is less than a site-determined minimum lifetime, an error
477             * message with code KDC_ERR_NEVER_VALID is returned."
478             */
479            if ( kerberosEndTime.lessThan( startTime ) )
480            {
481                throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
482            }
483    
484            long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
485            
486            if ( ticketLifeTime < config.getAllowableClockSkew() )
487            {
488                throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
489            }
490    
491            /*
492             * "If the requested expiration time for the ticket exceeds what was determined
493             * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
494             * flag is set in the new ticket, and the renew-till value is set as if the
495             * 'RENEWABLE' option were requested."
496             */
497            KerberosTime tempRtime = request.getRtime();
498    
499            if ( request.getOption( KdcOptions.RENEWABLE_OK ) && request.getTill().greaterThan( kerberosEndTime ) )
500            {
501                if ( !config.isRenewableAllowed() )
502                {
503                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
504                }
505    
506                request.setOption( KdcOptions.RENEWABLE );
507                tempRtime = request.getTill();
508            }
509    
510            if ( request.getOption( KdcOptions.RENEWABLE ) )
511            {
512                if ( !config.isRenewableAllowed() )
513                {
514                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
515                }
516    
517                newTicketBody.setFlag( TicketFlag.RENEWABLE );
518    
519                if ( tempRtime == null || tempRtime.isZero() )
520                {
521                    tempRtime = KerberosTime.INFINITY;
522                }
523    
524                /*
525                 * The renew-till time is the minimum of (a) the requested renew-till
526                 * time or (b) the start time plus maximum renewable lifetime as
527                 * configured in policy.
528                 */
529                long renewTill = Math.min( tempRtime.getTime(), startTime.getTime() + config.getMaximumRenewableLifetime() );
530                newTicketBody.setRenewTill( new KerberosTime( renewTill ) );
531            }
532    
533            if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
534                && request.getAddresses().getAddresses().length > 0 )
535            {
536                newTicketBody.setClientAddresses( request.getAddresses() );
537            }
538            else
539            {
540                if ( !config.isEmptyAddressesAllowed() )
541                {
542                    throw new KerberosException( ErrorType.KDC_ERR_POLICY );
543                }
544            }
545    
546            EncTicketPart ticketPart = newTicketBody.getEncTicketPart();
547    
548            EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 );
549    
550            Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
551            newTicket.setEncTicketPart( ticketPart );
552    
553            if ( LOG.isDebugEnabled() )
554            {
555                LOG.debug( "Ticket will be issued for access to {}.", serverPrincipal.toString() );
556            }
557    
558            authContext.setTicket( newTicket );
559        }
560        
561        
562        private static void buildReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
563        {
564            KdcRequest request = authContext.getRequest();
565            Ticket ticket = authContext.getTicket();
566    
567            AuthenticationReply reply = new AuthenticationReply();
568    
569            reply.setClientPrincipal( request.getClientPrincipal() );
570            reply.setTicket( ticket );
571            reply.setKey( ticket.getEncTicketPart().getSessionKey() );
572    
573            // TODO - fetch lastReq for this client; requires store
574            reply.setLastRequest( new LastRequest() );
575            // TODO - resp.key-expiration := client.expiration; requires store
576    
577            reply.setNonce( request.getNonce() );
578    
579            reply.setFlags( ticket.getEncTicketPart().getFlags() );
580            reply.setAuthTime( ticket.getEncTicketPart().getAuthTime() );
581            reply.setStartTime( ticket.getEncTicketPart().getStartTime() );
582            reply.setEndTime( ticket.getEncTicketPart().getEndTime() );
583    
584            if ( ticket.getEncTicketPart().getFlags().isRenewable() )
585            {
586                reply.setRenewTill( ticket.getEncTicketPart().getRenewTill() );
587            }
588    
589            reply.setServerPrincipal( ticket.getServerPrincipal() );
590            reply.setClientAddresses( ticket.getEncTicketPart().getClientAddresses() );
591    
592            authContext.setReply( reply );
593        }
594        
595        
596        private static void sealReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
597        {
598            AuthenticationReply reply = ( AuthenticationReply ) authContext.getReply();
599            EncryptionKey clientKey = authContext.getClientKey();
600            CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
601    
602            EncryptedData encryptedData = cipherTextHandler.seal( clientKey, reply, KeyUsage.NUMBER3 );
603            reply.setEncPart( encryptedData );
604        }
605        
606        
607        private static void monitorRequest( KdcContext kdcContext )
608        {
609            KdcRequest request = kdcContext.getRequest();
610    
611            if ( LOG.isDebugEnabled() )
612            {
613                try
614                {
615                    String clientAddress = kdcContext.getClientAddress().getHostAddress();
616    
617                    StringBuffer sb = new StringBuffer();
618    
619                    sb.append( "Received " + SERVICE_NAME + " request:" );
620                    sb.append( "\n\t" + "messageType:           " + request.getMessageType() );
621                    sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
622                    sb.append( "\n\t" + "clientAddress:         " + clientAddress );
623                    sb.append( "\n\t" + "nonce:                 " + request.getNonce() );
624                    sb.append( "\n\t" + "kdcOptions:            " + request.getKdcOptions() );
625                    sb.append( "\n\t" + "clientPrincipal:       " + request.getClientPrincipal() );
626                    sb.append( "\n\t" + "serverPrincipal:       " + request.getServerPrincipal() );
627                    sb.append( "\n\t" + "encryptionType:        " + KerberosUtils.getEncryptionTypesString( request.getEType() ) );
628                    sb.append( "\n\t" + "realm:                 " + request.getRealm() );
629                    sb.append( "\n\t" + "from time:             " + request.getFrom() );
630                    sb.append( "\n\t" + "till time:             " + request.getTill() );
631                    sb.append( "\n\t" + "renew-till time:       " + request.getRtime() );
632                    sb.append( "\n\t" + "hostAddresses:         " + request.getAddresses() );
633    
634                    LOG.debug( sb.toString() );
635                }
636                catch ( Exception e )
637                {
638                    // This is a monitor.  No exceptions should bubble up.
639                    LOG.error( I18n.err( I18n.ERR_153 ), e );
640                }
641            }
642        }
643        
644        private static void monitorContext( AuthenticationContext authContext )
645        {
646            try
647            {
648                long clockSkew = authContext.getConfig().getAllowableClockSkew();
649                InetAddress clientAddress = authContext.getClientAddress();
650    
651                StringBuilder sb = new StringBuilder();
652    
653                sb.append( "Monitoring " + SERVICE_NAME + " context:" );
654    
655                sb.append( "\n\t" + "clockSkew              " + clockSkew );
656                sb.append( "\n\t" + "clientAddress          " + clientAddress );
657    
658                KerberosPrincipal clientPrincipal = authContext.getClientEntry().getPrincipal();
659                PrincipalStoreEntry clientEntry = authContext.getClientEntry();
660    
661                sb.append( "\n\t" + "principal              " + clientPrincipal );
662                sb.append( "\n\t" + "cn                     " + clientEntry.getCommonName() );
663                sb.append( "\n\t" + "realm                  " + clientEntry.getRealmName() );
664                sb.append( "\n\t" + "principal              " + clientEntry.getPrincipal() );
665                sb.append( "\n\t" + "SAM type               " + clientEntry.getSamType() );
666    
667                KerberosPrincipal serverPrincipal = authContext.getRequest().getServerPrincipal();
668                PrincipalStoreEntry serverEntry = authContext.getServerEntry();
669    
670                sb.append( "\n\t" + "principal              " + serverPrincipal );
671                sb.append( "\n\t" + "cn                     " + serverEntry.getCommonName() );
672                sb.append( "\n\t" + "realm                  " + serverEntry.getRealmName() );
673                sb.append( "\n\t" + "principal              " + serverEntry.getPrincipal() );
674                sb.append( "\n\t" + "SAM type               " + serverEntry.getSamType() );
675    
676                EncryptionType encryptionType = authContext.getEncryptionType();
677                int clientKeyVersion = clientEntry.getKeyMap().get( encryptionType ).getKeyVersion();
678                int serverKeyVersion = serverEntry.getKeyMap().get( encryptionType ).getKeyVersion();
679                sb.append( "\n\t" + "Request key type       " + encryptionType );
680                sb.append( "\n\t" + "Client key version     " + clientKeyVersion );
681                sb.append( "\n\t" + "Server key version     " + serverKeyVersion );
682    
683                LOG.debug( sb.toString() );
684            }
685            catch ( Exception e )
686            {
687                // This is a monitor.  No exceptions should bubble up.
688                LOG.error( I18n.err( I18n.ERR_154 ), e );
689            }
690        }
691        
692        
693        private static void monitorReply( KdcContext kdcContext )
694        {
695            Object reply = kdcContext.getReply();
696    
697            if ( LOG.isDebugEnabled() )
698            {
699                if ( reply instanceof KdcReply )
700                {
701                    KdcReply success = ( KdcReply ) reply;
702    
703                    try
704                    {
705                        StringBuffer sb = new StringBuffer();
706    
707                        sb.append( "Responding with " + SERVICE_NAME + " reply:" );
708                        sb.append( "\n\t" + "messageType:           " + success.getMessageType() );
709                        sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() );
710                        sb.append( "\n\t" + "nonce:                 " + success.getNonce() );
711                        sb.append( "\n\t" + "clientPrincipal:       " + success.getClientPrincipal() );
712                        sb.append( "\n\t" + "client realm:          " + success.getClientRealm() );
713                        sb.append( "\n\t" + "serverPrincipal:       " + success.getServerPrincipal() );
714                        sb.append( "\n\t" + "server realm:          " + success.getServerRealm() );
715                        sb.append( "\n\t" + "auth time:             " + success.getAuthTime() );
716                        sb.append( "\n\t" + "start time:            " + success.getStartTime() );
717                        sb.append( "\n\t" + "end time:              " + success.getEndTime() );
718                        sb.append( "\n\t" + "renew-till time:       " + success.getRenewTill() );
719                        sb.append( "\n\t" + "hostAddresses:         " + success.getClientAddresses() );
720    
721                        LOG.debug( sb.toString() );
722                    }
723                    catch ( Exception e )
724                    {
725                        // This is a monitor.  No exceptions should bubble up.
726                        LOG.error( I18n.err( I18n.ERR_155 ), e );
727                    }
728                }
729            }
730        }
731        
732        
733        /**
734         * Get a PrincipalStoreEntry given a principal.  The ErrorType is used to indicate
735         * whether any resulting error pertains to a server or client.
736         */
737        private static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType )
738            throws KerberosException
739        {
740            PrincipalStoreEntry entry = null;
741    
742            try
743            {
744                entry = store.getPrincipal( principal );
745            }
746            catch ( Exception e )
747            {
748                throw new KerberosException( errorType, e );
749            }
750    
751            if ( entry == null )
752            {
753                throw new KerberosException( errorType );
754            }
755    
756            if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() )
757            {
758                throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
759            }
760    
761            return entry;
762        }
763        
764        
765        /**
766         * Prepares a pre-authentication error message containing required
767         * encryption types.
768         *
769         * @param encryptionTypes
770         * @return The error message as bytes.
771         */
772        private static byte[] preparePreAuthenticationError( Set<EncryptionType> encryptionTypes )
773        {
774            PaData[] paDataSequence = new PaData[2];
775    
776            PaData paData = new PaData();
777            paData.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
778            paData.setPaDataValue( new byte[0] );
779    
780            paDataSequence[0] = paData;
781    
782            EncryptionTypeInfoEntry[] entries = new EncryptionTypeInfoEntry[ encryptionTypes.size() ];
783            int i = 0;
784            
785            for ( EncryptionType encryptionType:encryptionTypes )
786            {
787                entries[i++] = new EncryptionTypeInfoEntry( encryptionType, null );
788            }
789    
790            byte[] encTypeInfo = null;
791    
792            try
793            {
794                encTypeInfo = EncryptionTypeInfoEncoder.encode( entries );
795            }
796            catch ( IOException ioe )
797            {
798                return null;
799            }
800    
801            PaData encType = new PaData();
802            encType.setPaDataType( PaDataType.PA_ENCTYPE_INFO );
803            encType.setPaDataValue( encTypeInfo );
804    
805            paDataSequence[1] = encType;
806    
807            try
808            {
809                return PreAuthenticationDataEncoder.encode( paDataSequence );
810            }
811            catch ( IOException ioe )
812            {
813                return null;
814            }
815        }
816    }