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.net.InetAddress;
24  import java.nio.ByteBuffer;
25  import java.util.Date;
26  import java.util.List;
27  import java.util.Set;
28  
29  import javax.security.auth.kerberos.KerberosKey;
30  import javax.security.auth.kerberos.KerberosPrincipal;
31  
32  import org.apache.directory.api.asn1.EncoderException;
33  import org.apache.directory.api.ldap.model.constants.Loggers;
34  import org.apache.directory.server.i18n.I18n;
35  import org.apache.directory.server.kerberos.KerberosConfig;
36  import org.apache.directory.server.kerberos.kdc.KdcContext;
37  import org.apache.directory.server.kerberos.sam.SamException;
38  import org.apache.directory.server.kerberos.sam.SamSubsystem;
39  import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
40  import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
41  import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
42  import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
43  import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
44  import org.apache.directory.shared.kerberos.KerberosConstants;
45  import org.apache.directory.shared.kerberos.KerberosTime;
46  import org.apache.directory.shared.kerberos.KerberosUtils;
47  import org.apache.directory.shared.kerberos.codec.KerberosDecoder;
48  import org.apache.directory.shared.kerberos.codec.options.KdcOptions;
49  import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
50  import org.apache.directory.shared.kerberos.codec.types.LastReqType;
51  import org.apache.directory.shared.kerberos.codec.types.PaDataType;
52  import org.apache.directory.shared.kerberos.components.ETypeInfo;
53  import org.apache.directory.shared.kerberos.components.ETypeInfo2;
54  import org.apache.directory.shared.kerberos.components.ETypeInfo2Entry;
55  import org.apache.directory.shared.kerberos.components.ETypeInfoEntry;
56  import org.apache.directory.shared.kerberos.components.EncKdcRepPart;
57  import org.apache.directory.shared.kerberos.components.EncTicketPart;
58  import org.apache.directory.shared.kerberos.components.EncryptedData;
59  import org.apache.directory.shared.kerberos.components.EncryptionKey;
60  import org.apache.directory.shared.kerberos.components.KdcReq;
61  import org.apache.directory.shared.kerberos.components.KdcReqBody;
62  import org.apache.directory.shared.kerberos.components.LastReq;
63  import org.apache.directory.shared.kerberos.components.LastReqEntry;
64  import org.apache.directory.shared.kerberos.components.MethodData;
65  import org.apache.directory.shared.kerberos.components.PaData;
66  import org.apache.directory.shared.kerberos.components.PaEncTsEnc;
67  import org.apache.directory.shared.kerberos.components.PrincipalName;
68  import org.apache.directory.shared.kerberos.components.TransitedEncoding;
69  import org.apache.directory.shared.kerberos.exceptions.ErrorType;
70  import org.apache.directory.shared.kerberos.exceptions.KerberosException;
71  import org.apache.directory.shared.kerberos.flags.TicketFlag;
72  import org.apache.directory.shared.kerberos.flags.TicketFlags;
73  import org.apache.directory.shared.kerberos.messages.AsRep;
74  import org.apache.directory.shared.kerberos.messages.EncAsRepPart;
75  import org.apache.directory.shared.kerberos.messages.Ticket;
76  import org.slf4j.Logger;
77  import org.slf4j.LoggerFactory;
78  
79  
80  /**
81   * Subsystem in charge of authenticating the incoming users.
82   * 
83   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
84   */
85  public final class AuthenticationService
86  {
87      /** The log for this class. */
88      private static final Logger LOG_KRB = LoggerFactory.getLogger( Loggers.KERBEROS_LOG.getName() );
89  
90      /** The module responsible for encryption and decryption */
91      private static final CipherTextHandler/crypto/encryption/CipherTextHandler.html#CipherTextHandler">CipherTextHandler CIPHER_TEXT_HANDLER = new CipherTextHandler();
92  
93      /** The service name */
94      private static final String SERVICE_NAME = "Authentication Service (AS)";
95  
96  
97      private AuthenticationService()
98      {
99      }
100 
101 
102     /**
103      * Handle the authentication, given a specific context
104      *
105      * @param authContext The authentication context
106      * @throws Exception If the authentication failed
107      */
108     public static void execute( AuthenticationContext authContext ) throws Exception
109     {
110         if ( LOG_KRB.isDebugEnabled() )
111         {
112             monitorRequest( authContext );
113         }
114 
115         authContext.setCipherTextHandler( CIPHER_TEXT_HANDLER );
116 
117         int kerberosVersion = authContext.getRequest().getProtocolVersionNumber();
118 
119         if ( kerberosVersion != KerberosConstants.KERBEROS_V5 )
120         {
121             LOG_KRB.error( "Kerberos V{} is not supported", kerberosVersion );
122             throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
123         }
124 
125         selectEncryptionType( authContext );
126         getClientEntry( authContext );
127         verifyPolicy( authContext );
128         verifySam( authContext );
129         verifyEncryptedTimestamp( authContext );
130 
131         getServerEntry( authContext );
132         generateTicket( authContext );
133         buildReply( authContext );
134     }
135 
136 
137     /**
138      * 
139      * @param authContext
140      * @throws KerberosException
141      */
142     private static void selectEncryptionType( AuthenticationContext authContext ) throws KerberosException
143     {
144 
145         LOG_KRB.debug( "--> Selecting the EncryptionType" );
146         KdcContext kdcContext = authContext;
147         KerberosConfig config = kdcContext.getConfig();
148 
149         Set<EncryptionType> requestedTypes = kdcContext.getRequest().getKdcReqBody().getEType();
150         LOG_KRB.debug( "Encryption types requested by client {}.", requestedTypes );
151 
152         EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
153 
154         LOG_KRB.debug( "Session will use encryption type {}.", bestType );
155 
156         if ( bestType == null )
157         {
158             LOG_KRB.error( "No encryptionType selected !" );
159             throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
160         }
161 
162         kdcContext.setEncryptionType( bestType );
163     }
164 
165 
166     private static void getClientEntry( AuthenticationContext authContext ) throws KerberosException
167     {
168         LOG_KRB.debug( "--> Getting the client Entry" );
169         KdcReqBody kdcReqBody = authContext.getRequest().getKdcReqBody();
170         KerberosPrincipal principal = KerberosUtils.getKerberosPrincipal(
171             kdcReqBody.getCName(),
172             kdcReqBody.getRealm() );
173         PrincipalStore store = authContext.getStore();
174 
175         try
176         {
177             PrincipalStoreEntry storeEntry = KerberosUtils.getEntry( principal, store,
178                 ErrorType.KDC_ERR_C_PRINCIPAL_UNKNOWN );
179             authContext.setClientEntry( storeEntry );
180 
181             LOG_KRB.debug( "Found entry {} for principal {}", storeEntry.getDistinguishedName(), principal );
182         }
183         catch ( KerberosException ke )
184         {
185             LOG_KRB.error( "Error while searching for client {} : {}", principal, ke.getMessage() );
186             throw ke;
187         }
188     }
189 
190 
191     private static void verifyPolicy( AuthenticationContext authContext ) throws KerberosException
192     {
193         LOG_KRB.debug( "--> Verifying the policy" );
194         PrincipalStoreEntry entry = authContext.getClientEntry();
195 
196         if ( entry.isDisabled() )
197         {
198             LOG_KRB.error( "The entry {} is disabled", entry.getDistinguishedName() );
199             throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
200         }
201 
202         if ( entry.isLockedOut() )
203         {
204             LOG_KRB.error( "The entry {} is locked out", entry.getDistinguishedName() );
205             throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
206         }
207 
208         if ( entry.getExpiration().getTime() < new Date().getTime() )
209         {
210             LOG_KRB.error( "The entry {} has been revoked", entry.getDistinguishedName() );
211             throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
212         }
213     }
214 
215 
216     private static void verifySam( AuthenticationContext authContext ) throws KerberosException
217     {
218         LOG_KRB.debug( "--> Verifying using SAM subsystem." );
219         KdcReq request = authContext.getRequest();
220         KerberosConfig config = authContext.getConfig();
221 
222         PrincipalStoreEntry clientEntry = authContext.getClientEntry();
223         String clientName = clientEntry.getPrincipal().getName();
224 
225         EncryptionKey clientKey = null;
226 
227         if ( clientEntry.getSamType() != null )
228         {
229             if ( LOG_KRB.isDebugEnabled() )
230             {
231                 LOG_KRB
232                     .debug(
233                         "Entry for client principal {} has a valid SAM type.  Invoking SAM subsystem for pre-authentication.",
234                         clientName );
235             }
236 
237             List<PaData> preAuthData = request.getPaData();
238 
239             if ( ( preAuthData == null ) || preAuthData.isEmpty() )
240             {
241                 LOG_KRB.debug( "No PreAuth Data" );
242                 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, preparePreAuthenticationError(
243                     authContext.getEncryptionType(), config
244                         .getEncryptionTypes() ) );
245             }
246 
247             try
248             {
249                 for ( PaData paData : preAuthData )
250                 {
251                     if ( paData.getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
252                     {
253                         KerberosKey samKey = SamSubsystem.getInstance().verify( clientEntry,
254                             paData.getPaDataValue() );
255                         clientKey = new EncryptionKey( EncryptionType.getTypeByValue( samKey.getKeyType() ), samKey
256                             .getEncoded() );
257                     }
258                 }
259             }
260             catch ( SamException se )
261             {
262                 LOG_KRB.error( "Error : {}", se.getMessage() );
263                 throw new KerberosException( ErrorType.KRB_ERR_GENERIC, se );
264             }
265 
266             authContext.setClientKey( clientKey );
267             authContext.setPreAuthenticated( true );
268 
269             if ( LOG_KRB.isDebugEnabled() )
270             {
271                 LOG_KRB.debug( "Pre-authentication using SAM subsystem successful for {}.", clientName );
272             }
273         }
274     }
275 
276 
277     private static void verifyEncryptedTimestamp( AuthenticationContext authContext ) throws KerberosException
278     {
279         LOG_KRB.debug( "--> Verifying using encrypted timestamp." );
280 
281         KerberosConfig config = authContext.getConfig();
282         KdcReq request = authContext.getRequest();
283         CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
284         PrincipalStoreEntry clientEntry = authContext.getClientEntry();
285         String clientName = clientEntry.getPrincipal().getName();
286 
287         EncryptionKey clientKey = null;
288 
289         if ( clientEntry.getSamType() == null )
290         {
291             LOG_KRB.debug(
292                 "Entry for client principal {} has no SAM type.  Proceeding with standard pre-authentication.",
293                 clientName );
294 
295             EncryptionType encryptionType = authContext.getEncryptionType();
296             clientKey = clientEntry.getKeyMap().get( encryptionType );
297 
298             if ( clientKey == null )
299             {
300                 LOG_KRB.error( "No key for client {}", clientEntry.getDistinguishedName() );
301                 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
302             }
303 
304             if ( config.isPaEncTimestampRequired() )
305             {
306                 List<PaData> preAuthData = request.getPaData();
307 
308                 if ( preAuthData == null )
309                 {
310                     LOG_KRB.debug( "PRE_AUTH required..." );
311                     throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
312                         preparePreAuthenticationError( authContext.getEncryptionType(), config.getEncryptionTypes() ) );
313                 }
314 
315                 PaEncTsEnc timestamp = null;
316 
317                 for ( PaData paData : preAuthData )
318                 {
319                     if ( paData.getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
320                     {
321                         EncryptedData dataValue = KerberosDecoder.decodeEncryptedData( paData.getPaDataValue() );
322                         byte[] decryptedData = cipherTextHandler.decrypt( clientKey, dataValue,
323                             KeyUsage.AS_REQ_PA_ENC_TIMESTAMP_WITH_CKEY );
324                         timestamp = KerberosDecoder.decodePaEncTsEnc( decryptedData );
325                     }
326                 }
327 
328                 if ( timestamp == null )
329                 {
330                     LOG_KRB.error( "No timestamp found" );
331                     throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
332                         preparePreAuthenticationError( authContext.getEncryptionType(), config.getEncryptionTypes() ) );
333                 }
334 
335                 if ( !timestamp.getPaTimestamp().isInClockSkew( config.getAllowableClockSkew() ) )
336                 {
337                     LOG_KRB.error( "Timestamp not in delay" );
338 
339                     throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_FAILED );
340                 }
341             }
342         }
343 
344         authContext.setClientKey( clientKey );
345         authContext.setPreAuthenticated( true );
346 
347         if ( LOG_KRB.isDebugEnabled() )
348         {
349             LOG_KRB.debug( "Pre-authentication by encrypted timestamp successful for {}.", clientName );
350         }
351     }
352 
353 
354     private static void getServerEntry( AuthenticationContext authContext ) throws KerberosException
355     {
356         PrincipalName principal = authContext.getRequest().getKdcReqBody().getSName();
357         PrincipalStore store = authContext.getStore();
358 
359         LOG_KRB.debug( "--> Getting the server entry for {}", principal );
360 
361         KerberosPrincipal principalWithRealm = new KerberosPrincipal( principal.getNameString() + "@"
362             + authContext.getRequest().getKdcReqBody().getRealm() );
363         authContext.setServerEntry( KerberosUtils.getEntry( principalWithRealm, store,
364             ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) );
365     }
366 
367 
368     private static void generateTicket( AuthenticationContext authContext ) throws KerberosException
369     {
370         KdcReq request = authContext.getRequest();
371         CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
372         PrincipalName serverPrincipal = request.getKdcReqBody().getSName();
373 
374         LOG_KRB.debug( "--> Generating ticket for {}", serverPrincipal );
375 
376         EncryptionType encryptionType = authContext.getEncryptionType();
377         EncryptionKey serverKey = authContext.getServerEntry().getKeyMap().get( encryptionType );
378 
379         PrincipalName ticketPrincipal = request.getKdcReqBody().getSName();
380 
381         EncTicketPartros/components/EncTicketPart.html#EncTicketPart">EncTicketPart encTicketPart = new EncTicketPart();
382         KerberosConfig config = authContext.getConfig();
383 
384         // The INITIAL flag indicates that a ticket was issued using the AS protocol.
385         TicketFlagserberos/flags/TicketFlags.html#TicketFlags">TicketFlags ticketFlags = new TicketFlags();
386         encTicketPart.setFlags( ticketFlags );
387         ticketFlags.setFlag( TicketFlag.INITIAL );
388 
389         // The PRE-AUTHENT flag indicates that the client used pre-authentication.
390         if ( authContext.isPreAuthenticated() )
391         {
392             ticketFlags.setFlag( TicketFlag.PRE_AUTHENT );
393         }
394 
395         if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.FORWARDABLE ) )
396         {
397             if ( !config.isForwardableAllowed() )
398             {
399                 LOG_KRB.error( "Ticket cannot be generated, because Forwadable is not allowed" );
400                 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
401             }
402 
403             ticketFlags.setFlag( TicketFlag.FORWARDABLE );
404         }
405 
406         if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.PROXIABLE ) )
407         {
408             if ( !config.isProxiableAllowed() )
409             {
410                 LOG_KRB.error( "Ticket cannot be generated, because proxyiable is not allowed" );
411                 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
412             }
413 
414             ticketFlags.setFlag( TicketFlag.PROXIABLE );
415         }
416 
417         if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.ALLOW_POSTDATE ) )
418         {
419             if ( !config.isPostdatedAllowed() )
420             {
421                 LOG_KRB.error( "Ticket cannot be generated, because Posdate is not allowed" );
422                 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
423             }
424 
425             ticketFlags.setFlag( TicketFlag.MAY_POSTDATE );
426         }
427 
428         KdcOptions kdcOptions = request.getKdcReqBody().getKdcOptions();
429 
430         if ( kdcOptions.get( KdcOptions.RENEW )
431             || kdcOptions.get( KdcOptions.VALIDATE )
432             || kdcOptions.get( KdcOptions.PROXY )
433             || kdcOptions.get( KdcOptions.FORWARDED )
434             || kdcOptions.get( KdcOptions.ENC_TKT_IN_SKEY ) )
435         {
436             String msg = "";
437             
438             if ( kdcOptions.get( KdcOptions.RENEW ) )
439             {
440                 msg = "Ticket cannot be generated, as it's a renew";
441             }
442             
443             if ( kdcOptions.get( KdcOptions.VALIDATE ) )
444             {
445                 msg = "Ticket cannot be generated, as it's a validate";
446             }
447             
448             if ( kdcOptions.get( KdcOptions.PROXY ) )
449             {
450                 msg = "Ticket cannot be generated, as it's a proxy";
451             }
452             
453             if ( kdcOptions.get( KdcOptions.FORWARDED ) )
454             {
455                 msg = "Ticket cannot be generated, as it's forwarded";
456             }
457             
458             if ( kdcOptions.get( KdcOptions.ENC_TKT_IN_SKEY ) )
459             {
460                 msg = "Ticket cannot be generated, as it's a user-to-user ";
461             }
462             
463             if ( LOG_KRB.isDebugEnabled() )
464             {
465                 LOG_KRB.debug( msg );
466             }
467 
468             throw new KerberosException( ErrorType.KDC_ERR_BADOPTION, msg );
469         }
470 
471         EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( authContext.getEncryptionType() );
472         encTicketPart.setKey( sessionKey );
473 
474         encTicketPart.setCName( request.getKdcReqBody().getCName() );
475         encTicketPart.setCRealm( request.getKdcReqBody().getRealm() );
476         encTicketPart.setTransited( new TransitedEncoding() );
477         String serverRealm = request.getKdcReqBody().getRealm();
478 
479         KerberosTimehared/kerberos/KerberosTime.html#KerberosTime">KerberosTime now = new KerberosTime();
480 
481         encTicketPart.setAuthTime( now );
482 
483         KerberosTime startTime = request.getKdcReqBody().getFrom();
484 
485         /*
486          * "If the requested starttime is absent, indicates a time in the past,
487          * or is within the window of acceptable clock skew for the KDC and the
488          * POSTDATE option has not been specified, then the starttime of the
489          * ticket is set to the authentication server's current time."
490          */
491         if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
492             && !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) )
493         {
494             startTime = now;
495         }
496 
497         /*
498          * "If it indicates a time in the future beyond the acceptable clock skew,
499          * but the POSTDATED option has not been specified, then the error
500          * KDC_ERR_CANNOT_POSTDATE is returned."
501          */
502         if ( ( startTime != null ) && startTime.greaterThan( now )
503             && !startTime.isInClockSkew( config.getAllowableClockSkew() )
504             && !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) )
505         {
506             String msg = "Ticket cannot be generated, as it's in the future and the POSTDATED option is not set in the request";
507             LOG_KRB.error( msg );
508             throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE, msg );
509         }
510 
511         /*
512          * "Otherwise the requested starttime is checked against the policy of the
513          * local realm and if the ticket's starttime is acceptable, it is set as
514          * requested, and the INVALID flag is set in the new ticket."
515          */
516         if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) )
517         {
518             if ( !config.isPostdatedAllowed() )
519             {
520                 String msg = "Ticket cannot be generated, cause issuing POSTDATED tickets is not allowed";
521                 LOG_KRB.error( msg );
522                 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg );
523             }
524 
525             ticketFlags.setFlag( TicketFlag.POSTDATED );
526             ticketFlags.setFlag( TicketFlag.INVALID );
527         }
528 
529         encTicketPart.setStartTime( startTime );
530         
531         long till = 0;
532 
533         if ( request.getKdcReqBody().getTill().getTime() == 0 )
534         {
535             till = Long.MAX_VALUE;
536         }
537         else
538         {
539             till = request.getKdcReqBody().getTill().getTime();
540         }
541 
542         /*
543          * The end time is the minimum of (a) the requested till time or (b)
544          * the start time plus maximum lifetime as configured in policy.
545          */
546         long endTime = Math.min( till, startTime.getTime() + config.getMaximumTicketLifetime() );
547         KerberosTimeos/KerberosTime.html#KerberosTime">KerberosTime kerberosEndTime = new KerberosTime( endTime );
548         encTicketPart.setEndTime( kerberosEndTime );
549 
550         /*
551          * "If the requested expiration time minus the starttime (as determined
552          * above) is less than a site-determined minimum lifetime, an error
553          * message with code KDC_ERR_NEVER_VALID is returned."
554          */
555         if ( kerberosEndTime.lessThan( startTime ) )
556         {
557             String msg = "Ticket cannot be generated, as the endTime is below the startTime";
558             LOG_KRB.error( msg );
559             throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID, msg );
560         }
561 
562         long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
563 
564         if ( ticketLifeTime < config.getMinimumTicketLifetime() )
565         {
566             String msg = "Ticket cannot be generated, as the Lifetime is too small";
567             LOG_KRB.error( msg );
568             throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID, msg );
569         }
570 
571         /*
572          * "If the requested expiration time for the ticket exceeds what was determined
573          * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
574          * flag is set in the new ticket, and the renew-till value is set as if the
575          * 'RENEWABLE' option were requested."
576          */
577         KerberosTime tempRtime = request.getKdcReqBody().getRTime();
578 
579         if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE_OK )
580             && request.getKdcReqBody().getTill().greaterThan( kerberosEndTime ) )
581         {
582             if ( !config.isRenewableAllowed() )
583             {
584                 String msg = "Ticket cannot be generated, as the renew date is exceeded";
585                 LOG_KRB.error( msg );
586                 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg );
587             }
588 
589             request.getKdcReqBody().getKdcOptions().set( KdcOptions.RENEWABLE );
590             tempRtime = request.getKdcReqBody().getTill();
591         }
592 
593         if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE ) )
594         {
595             if ( !config.isRenewableAllowed() )
596             {
597                 String msg = "Ticket cannot be generated, as Renewable is not allowed";
598                 LOG_KRB.error( msg );
599                 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg );
600             }
601 
602             ticketFlags.setFlag( TicketFlag.RENEWABLE );
603 
604             if ( tempRtime == null || tempRtime.isZero() )
605             {
606                 tempRtime = KerberosTime.INFINITY;
607             }
608 
609             /*
610              * The renew-till time is the minimum of (a) the requested renew-till
611              * time or (b) the start time plus maximum renewable lifetime as
612              * configured in policy.
613              */
614             long renewTill = Math.min( tempRtime.getTime(), startTime.getTime() + config.getMaximumRenewableLifetime() );
615             encTicketPart.setRenewTill( new KerberosTime( renewTill ) );
616         }
617 
618         if ( request.getKdcReqBody().getAddresses() != null
619             && request.getKdcReqBody().getAddresses().getAddresses() != null
620             && request.getKdcReqBody().getAddresses().getAddresses().length > 0 )
621         {
622             encTicketPart.setClientAddresses( request.getKdcReqBody().getAddresses() );
623         }
624         else
625         {
626             if ( !config.isEmptyAddressesAllowed() )
627             {
628                 String msg = "Ticket cannot be generated, as the addresses are null, and it's not allowed";
629                 LOG_KRB.error( msg );
630                 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg );
631             }
632         }
633 
634         EncryptedData encryptedData = cipherTextHandler.seal( serverKey, encTicketPart,
635             KeyUsage.AS_OR_TGS_REP_TICKET_WITH_SRVKEY );
636 
637         Tickethared/kerberos/messages/Ticket.html#Ticket">Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
638 
639         newTicket.setRealm( serverRealm );
640         newTicket.setEncTicketPart( encTicketPart );
641 
642         if ( LOG_KRB.isDebugEnabled() )
643         {
644             LOG_KRB.debug( "Ticket will be issued for access to {}.", serverPrincipal );
645         }
646 
647         authContext.setTicket( newTicket );
648     }
649 
650 
651     private static void buildReply( AuthenticationContext authContext ) throws KerberosException
652     {
653         LOG_KRB.debug( "--> Building reply" );
654         KdcReq request = authContext.getRequest();
655         Ticket ticket = authContext.getTicket();
656 
657         AsRepory/shared/kerberos/messages/AsRep.html#AsRep">AsRep reply = new AsRep();
658 
659         reply.setCName( request.getKdcReqBody().getCName() );
660         reply.setCRealm( request.getKdcReqBody().getRealm() );
661         reply.setTicket( ticket );
662 
663         EncKdcRepPartros/components/EncKdcRepPart.html#EncKdcRepPart">EncKdcRepPart encKdcRepPart = new EncKdcRepPart();
664         //session key
665         encKdcRepPart.setKey( ticket.getEncTicketPart().getKey() );
666 
667         // TODO - fetch lastReq for this client; requires store
668         // FIXME temporary fix, IMO we should create some new ATs to store this info in DIT
669         LastReqshared/kerberos/components/LastReq.html#LastReq">LastReq lastReq = new LastReq();
670         lastReq.addEntry( new LastReqEntry( LastReqType.TIME_OF_INITIAL_REQ, new KerberosTime() ) );
671         encKdcRepPart.setLastReq( lastReq );
672         // TODO - resp.key-expiration := client.expiration; requires store
673 
674         encKdcRepPart.setNonce( request.getKdcReqBody().getNonce() );
675 
676         encKdcRepPart.setFlags( ticket.getEncTicketPart().getFlags() );
677         encKdcRepPart.setAuthTime( ticket.getEncTicketPart().getAuthTime() );
678         encKdcRepPart.setStartTime( ticket.getEncTicketPart().getStartTime() );
679         encKdcRepPart.setEndTime( ticket.getEncTicketPart().getEndTime() );
680 
681         if ( ticket.getEncTicketPart().getFlags().isRenewable() )
682         {
683             encKdcRepPart.setRenewTill( ticket.getEncTicketPart().getRenewTill() );
684         }
685 
686         encKdcRepPart.setSName( ticket.getSName() );
687         encKdcRepPart.setSRealm( ticket.getRealm() );
688         encKdcRepPart.setClientAddresses( ticket.getEncTicketPart().getClientAddresses() );
689 
690         EncAsRepPartberos/messages/EncAsRepPart.html#EncAsRepPart">EncAsRepPart encAsRepPart = new EncAsRepPart();
691         encAsRepPart.setEncKdcRepPart( encKdcRepPart );
692 
693         if ( LOG_KRB.isDebugEnabled() )
694         {
695             monitorContext( authContext );
696             monitorReply( reply, encKdcRepPart );
697         }
698 
699         EncryptionKey clientKey = authContext.getClientKey();
700         EncryptedData encryptedData = CIPHER_TEXT_HANDLER.seal( clientKey, encAsRepPart,
701             KeyUsage.AS_REP_ENC_PART_WITH_CKEY );
702         reply.setEncPart( encryptedData );
703         //FIXME the below setter is useless, remove it
704         reply.setEncKdcRepPart( encKdcRepPart );
705 
706         authContext.setReply( reply );
707     }
708 
709 
710     private static void monitorRequest( KdcContext kdcContext )
711     {
712         KdcReq request = kdcContext.getRequest();
713 
714         if ( LOG_KRB.isDebugEnabled() )
715         {
716             try
717             {
718                 String clientAddress = kdcContext.getClientAddress().getHostAddress();
719 
720                 StringBuilder sb = new StringBuilder();
721 
722                 sb.append( "Received " ).append( SERVICE_NAME ).append( " request:" );
723                 sb.append( "\n\tmessageType:           " ).append( request.getMessageType() );
724                 sb.append( "\n\tprotocolVersionNumber: " ).append( request.getProtocolVersionNumber() );
725                 sb.append( "\n\tclientAddress:         " ).append( clientAddress );
726                 sb.append( "\n\tnonce:                 " ).append( request.getKdcReqBody().getNonce() );
727                 sb.append( "\n\tkdcOptions:            " ).append( request.getKdcReqBody().getKdcOptions() );
728                 sb.append( "\n\tclientPrincipal:       " ).append( request.getKdcReqBody().getCName() );
729                 sb.append( "\n\tserverPrincipal:       " ).append( request.getKdcReqBody().getSName() );
730                 sb.append( "\n\tencryptionType:        "
731                     + KerberosUtils.getEncryptionTypesString( request.getKdcReqBody().getEType() ) );
732                 sb.append( "\n\trealm:                 " ).append( request.getKdcReqBody().getRealm() );
733                 sb.append( "\n\tfrom time:             " ).append( request.getKdcReqBody().getFrom() );
734                 sb.append( "\n\ttill time:             " ).append( request.getKdcReqBody().getTill() );
735                 sb.append( "\n\trenew-till time:       " ).append( request.getKdcReqBody().getRTime() );
736                 sb.append( "\n\thostAddresses:         " ).append( request.getKdcReqBody().getAddresses() );
737 
738                 String message = sb.toString();
739                 LOG_KRB.debug( message );
740             }
741             catch ( Exception e )
742             {
743                 // This is a monitor.  No exceptions should bubble up.
744                 LOG_KRB.error( I18n.err( I18n.ERR_153 ), e );
745             }
746         }
747     }
748 
749 
750     private static void monitorContext( AuthenticationContext authContext )
751     {
752         if ( LOG_KRB.isDebugEnabled() )
753         {
754             try
755             {
756                 long clockSkew = authContext.getConfig().getAllowableClockSkew();
757                 InetAddress clientAddress = authContext.getClientAddress();
758     
759                 StringBuilder sb = new StringBuilder();
760     
761                 sb.append( "Monitoring " ).append( SERVICE_NAME ).append( " context:" );
762     
763                 sb.append( "\n\tclockSkew              " ).append( clockSkew );
764                 sb.append( "\n\tclientAddress          " ).append( clientAddress );
765     
766                 KerberosPrincipal clientPrincipal = authContext.getClientEntry().getPrincipal();
767                 PrincipalStoreEntry clientEntry = authContext.getClientEntry();
768     
769                 sb.append( "\n\tprincipal              " ).append( clientPrincipal );
770                 sb.append( "\n\tcn                     " ).append( clientEntry.getCommonName() );
771                 sb.append( "\n\trealm                  " ).append( clientEntry.getRealmName() );
772                 sb.append( "\n\tprincipal              " ).append( clientEntry.getPrincipal() );
773                 sb.append( "\n\tSAM type               " ).append( clientEntry.getSamType() );
774     
775                 PrincipalName serverPrincipal = authContext.getRequest().getKdcReqBody().getSName();
776                 PrincipalStoreEntry serverEntry = authContext.getServerEntry();
777     
778                 sb.append( "\n\tprincipal              " ).append( serverPrincipal );
779                 sb.append( "\n\tcn                     " ).append( serverEntry.getCommonName() );
780                 sb.append( "\n\trealm                  " ).append( serverEntry.getRealmName() );
781                 sb.append( "\n\tprincipal              " ).append( serverEntry.getPrincipal() );
782                 sb.append( "\n\tSAM type               " ).append( serverEntry.getSamType() );
783     
784                 EncryptionType encryptionType = authContext.getEncryptionType();
785                 int clientKeyVersion = clientEntry.getKeyMap().get( encryptionType ).getKeyVersion();
786                 int serverKeyVersion = serverEntry.getKeyMap().get( encryptionType ).getKeyVersion();
787                 sb.append( "\n\tRequest key type       " ).append( encryptionType );
788                 sb.append( "\n\tClient key version     " ).append( clientKeyVersion );
789                 sb.append( "\n\tServer key version     " ).append( serverKeyVersion );
790     
791                 String message = sb.toString();
792     
793                 LOG_KRB.debug( message );
794             }
795             catch ( Exception e )
796             {
797                 // This is a monitor.  No exceptions should bubble up.
798                 LOG_KRB.error( I18n.err( I18n.ERR_154 ), e );
799             }
800         }
801     }
802 
803 
804     private static void monitorReply( AsRep reply, EncKdcRepPart part )
805     {
806         if ( LOG_KRB.isDebugEnabled() )
807         {
808             try
809             {
810                 StringBuilder sb = new StringBuilder();
811 
812                 sb.append( "Responding with " ).append( SERVICE_NAME ).append( " reply:" );
813                 sb.append( "\n\tmessageType:           " ).append( reply.getMessageType() );
814                 sb.append( "\n\tprotocolVersionNumber: " ).append( reply.getProtocolVersionNumber() );
815                 sb.append( "\n\tnonce:                 " ).append( part.getNonce() );
816                 sb.append( "\n\tclientPrincipal:       " ).append( reply.getCName() );
817                 sb.append( "\n\tclient realm:          " ).append( reply.getCRealm() );
818                 sb.append( "\n\tserverPrincipal:       " ).append( part.getSName() );
819                 sb.append( "\n\tserver realm:          " ).append( part.getSRealm() );
820                 sb.append( "\n\tauth time:             " ).append( part.getAuthTime() );
821                 sb.append( "\n\tstart time:            " ).append( part.getStartTime() );
822                 sb.append( "\n\tend time:              " ).append( part.getEndTime() );
823                 sb.append( "\n\trenew-till time:       " ).append( part.getRenewTill() );
824                 sb.append( "\n\thostAddresses:         " ).append( part.getClientAddresses() );
825 
826                 String message = sb.toString();
827 
828                 LOG_KRB.debug( message );
829             }
830             catch ( Exception e )
831             {
832                 // This is a monitor.  No exceptions should bubble up.
833                 LOG_KRB.error( I18n.err( I18n.ERR_155 ), e );
834             }
835         }
836     }
837 
838 
839     /**
840      * Prepares a pre-authentication error message containing required
841      * encryption types.
842      *
843      * @param encryptionTypes
844      * @return The error message as bytes.
845      */
846     private static byte[] preparePreAuthenticationError( EncryptionType requestedType,
847         Set<EncryptionType> encryptionTypes )
848     {
849         boolean isNewEtype = KerberosUtils.isNewEncryptionType( requestedType );
850 
851         ETypeInfo2/kerberos/components/ETypeInfo2.html#ETypeInfo2">ETypeInfo2 eTypeInfo2 = new ETypeInfo2();
852 
853         ETypeInfoed/kerberos/components/ETypeInfo.html#ETypeInfo">ETypeInfo eTypeInfo = new ETypeInfo();
854 
855         for ( EncryptionType encryptionType : encryptionTypes )
856         {
857             if ( !isNewEtype )
858             {
859                 ETypeInfoEntrys/components/ETypeInfoEntry.html#ETypeInfoEntry">ETypeInfoEntry etypeInfoEntry = new ETypeInfoEntry( encryptionType, null );
860                 eTypeInfo.addETypeInfoEntry( etypeInfoEntry );
861             }
862 
863             ETypeInfo2Entrycomponents/ETypeInfo2Entry.html#ETypeInfo2Entry">ETypeInfo2Entry etypeInfo2Entry = new ETypeInfo2Entry( encryptionType );
864             eTypeInfo2.addETypeInfo2Entry( etypeInfo2Entry );
865         }
866 
867         byte[] encTypeInfo = null;
868         byte[] encTypeInfo2 = null;
869         try
870         {
871             if ( !isNewEtype )
872             {
873                 ByteBuffer buffer = ByteBuffer.allocate( eTypeInfo.computeLength() );
874                 encTypeInfo = eTypeInfo.encode( buffer ).array();
875             }
876 
877             ByteBuffer buffer = ByteBuffer.allocate( eTypeInfo2.computeLength() );
878             encTypeInfo2 = eTypeInfo2.encode( buffer ).array();
879         }
880         catch ( EncoderException ioe )
881         {
882             return null;
883         }
884 
885         MethodData/kerberos/components/MethodData.html#MethodData">MethodData methodData = new MethodData();
886 
887         methodData.addPaData( new PaData( PaDataType.PA_ENC_TIMESTAMP, null ) );
888 
889         if ( !isNewEtype )
890         {
891             methodData.addPaData( new PaData( PaDataType.PA_ENCTYPE_INFO, encTypeInfo ) );
892         }
893 
894         methodData.addPaData( new PaData( PaDataType.PA_ENCTYPE_INFO2, encTypeInfo2 ) );
895 
896         try
897         {
898             ByteBuffer buffer = ByteBuffer.allocate( methodData.computeLength() );
899             return methodData.encode( buffer ).array();
900         }
901         catch ( EncoderException ee )
902         {
903             LOG_KRB.warn( "Failed to encode the etype information", ee );
904             return null;
905         }
906     }
907 }