View Javadoc

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