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.protocol;
21  
22  
23  import java.util.HashSet;
24  import java.util.Set;
25  
26  import javax.security.auth.kerberos.KerberosPrincipal;
27  
28  import org.apache.directory.server.kerberos.kdc.KdcServer;
29  import org.apache.directory.server.kerberos.shared.KerberosMessageType;
30  import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
31  import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
32  import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
33  import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
34  import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
35  import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
36  import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
37  import org.apache.directory.server.kerberos.shared.messages.value.PaData;
38  import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
39  import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
40  
41  
42  /**
43   * Tests the Authentication Service (AS) via the {@link KerberosProtocolHandler}.
44   *
45   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
46   * @version $Rev$, $Date$
47   */
48  public class AuthenticationServiceTest extends AbstractAuthenticationServiceTest
49  {
50      private KdcServer config;
51      private PrincipalStore store;
52      private KerberosProtocolHandler handler;
53      private DummySession session;
54  
55  
56      /**
57       * Creates a new instance of {@link AuthenticationServiceTest}.
58       */
59      public AuthenticationServiceTest()
60      {
61          config = new KdcServer();
62          store = new MapPrincipalStoreImpl();
63          handler = new KerberosProtocolHandler( config, store );
64          session = new DummySession();
65          lockBox = new CipherTextHandler();
66      }
67  
68  
69      /**
70       * Tests the default minimum request, which consists of as little as the
71       * client name, realm, till time, nonce, and encryption types.
72       * 
73       * This is the request archetype.
74       */
75      public void testRequestArchetype()
76      {
77          RequestBodyModifier modifier = new RequestBodyModifier();
78          modifier.setClientName( getPrincipalName( "hnelson" ) );
79          modifier.setServerName( getPrincipalName( "hnelson" ) );
80          modifier.setRealm( "EXAMPLE.COM" );
81          modifier.setEType( config.getEncryptionTypes() );
82  
83          KerberosTime till = new KerberosTime();
84          modifier.setTill( till );
85  
86          KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
87  
88          handler.messageReceived( session, message );
89  
90          ErrorMessage error = ( ErrorMessage ) session.getMessage();
91  
92          assertEquals( "Additional pre-authentication required", 25, error.getErrorCode() );
93      }
94  
95  
96      /**
97       * Tests the protocol version number, which must be '5'.
98       */
99      public void testProtocolVersionNumber()
100     {
101         RequestBodyModifier modifier = new RequestBodyModifier();
102         modifier.setClientName( getPrincipalName( "hnelson" ) );
103         modifier.setServerName( getPrincipalName( "hnelson" ) );
104         modifier.setRealm( "EXAMPLE.COM" );
105         modifier.setEType( config.getEncryptionTypes() );
106 
107         KdcRequest message = new KdcRequest( 4, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
108 
109         handler.messageReceived( session, message );
110 
111         ErrorMessage error = ( ErrorMessage ) session.getMessage();
112         assertEquals( "Requested protocol version number not supported", 3, error.getErrorCode() );
113     }
114 
115 
116     /**
117      * Tests that Kerberos reply messages sent to the KDC will be rejected with the
118      * correct error message.
119      */
120     public void testIncorrectMessageDirection()
121     {
122         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REP, null, null );
123 
124         handler.messageReceived( session, message );
125 
126         ErrorMessage error = ( ErrorMessage ) session.getMessage();
127         assertEquals( "Incorrect message direction", 47, error.getErrorCode() );
128 
129         message = new KdcRequest( 5, KerberosMessageType.TGS_REP, null, null );
130 
131         handler.messageReceived( session, message );
132 
133         error = ( ErrorMessage ) session.getMessage();
134         assertEquals( "Incorrect message direction", 47, error.getErrorCode() );
135     }
136 
137 
138     /**
139      * Tests that a non-existent client principal returns the correct error message.
140      * 
141      * "If the requested client principal named in the request is
142      * unknown because it doesn't exist in the KDC's principal database,
143      * then an error message with a KDC_ERR_C_PRINCIPAL_UNKNOWN is returned."
144      */
145     public void testClientNotFound()
146     {
147         RequestBodyModifier modifier = new RequestBodyModifier();
148         modifier.setClientName( getPrincipalName( "baduser" ) );
149         modifier.setServerName( getPrincipalName( "hnelson" ) );
150         modifier.setRealm( "EXAMPLE.COM" );
151         modifier.setEType( config.getEncryptionTypes() );
152 
153         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
154 
155         handler.messageReceived( session, message );
156 
157         ErrorMessage error = ( ErrorMessage ) session.getMessage();
158         assertEquals( "Client not found in Kerberos database", 6, error.getErrorCode() );
159     }
160 
161 
162     /**
163      * Test when an unsupported encryption type is requested, that the request is
164      * rejected with the correct error message.
165      * 
166      * "If the server cannot accommodate any encryption type requested by the
167      * client, an error message with code KDC_ERR_ETYPE_NOSUPP is returned."
168      * 
169      * @throws Exception 
170      */
171     public void testEncryptionTypeNoSupport() throws Exception
172     {
173         RequestBodyModifier modifier = new RequestBodyModifier();
174         modifier.setClientName( getPrincipalName( "hnelson" ) );
175         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
176         modifier.setRealm( "EXAMPLE.COM" );
177 
178         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
179         encryptionTypes.add( EncryptionType.DES3_CBC_MD5 );
180 
181         modifier.setEType( encryptionTypes );
182 
183         modifier.setKdcOptions( new KdcOptions() );
184 
185         long now = System.currentTimeMillis();
186 
187         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
188         modifier.setTill( requestedEndTime );
189 
190         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
191 
192         String passPhrase = "secret";
193         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
194 
195         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
196 
197         handler.messageReceived( session, message );
198 
199         ErrorMessage error = ( ErrorMessage ) session.getMessage();
200         assertEquals( "KDC has no support for encryption type", 14, error.getErrorCode() );
201     }
202 
203 
204     /**
205      * Tests that a non-existent server principal returns the correct error message.
206      * 
207      * @throws Exception 
208      */
209     public void testServerNotFound() throws Exception
210     {
211         RequestBodyModifier modifier = new RequestBodyModifier();
212         modifier.setClientName( getPrincipalName( "hnelson" ) );
213         modifier.setServerName( getPrincipalName( "badserver" ) );
214         modifier.setRealm( "EXAMPLE.COM" );
215         modifier.setEType( config.getEncryptionTypes() );
216 
217         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
218 
219         String passPhrase = "secret";
220         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
221 
222         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
223 
224         handler.messageReceived( session, message );
225 
226         ErrorMessage error = ( ErrorMessage ) session.getMessage();
227         assertEquals( "Server not found in Kerberos database", 7, error.getErrorCode() );
228     }
229 
230 
231     /**
232      * Tests that when a client principal is not configured with Kerberos keys that
233      * the correct error message is returned.
234      */
235     public void testClientNullKey()
236     {
237         RequestBodyModifier modifier = new RequestBodyModifier();
238         modifier.setClientName( getPrincipalName( "tquist" ) );
239         modifier.setServerName( getPrincipalName( "hnelson" ) );
240         modifier.setRealm( "EXAMPLE.COM" );
241         modifier.setEType( config.getEncryptionTypes() );
242 
243         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, null, modifier.getRequestBody() );
244 
245         handler.messageReceived( session, message );
246 
247         ErrorMessage error = ( ErrorMessage ) session.getMessage();
248         assertEquals( "The client or server has a null key", 9, error.getErrorCode() );
249     }
250 
251 
252     /**
253      * Tests that when a server principal is not configured with Kerberos keys that
254      * the correct error message is returned.
255      * 
256      * @throws Exception 
257      */
258     public void testServerNullKey() throws Exception
259     {
260         RequestBodyModifier modifier = new RequestBodyModifier();
261         modifier.setClientName( getPrincipalName( "hnelson" ) );
262         modifier.setServerName( getPrincipalName( "tquist" ) );
263         modifier.setRealm( "EXAMPLE.COM" );
264         modifier.setEType( config.getEncryptionTypes() );
265 
266         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
267 
268         String passPhrase = "secret";
269         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
270 
271         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
272 
273         handler.messageReceived( session, message );
274 
275         ErrorMessage error = ( ErrorMessage ) session.getMessage();
276         assertEquals( "The client or server has a null key", 9, error.getErrorCode() );
277     }
278 
279 
280     /**
281      * Tests when the starttime is absent and the POSTDATED option has not been
282      * specified, that the starttime of the ticket is set to the authentication
283      * server's current time.
284      * 
285      * "If the requested starttime is absent, indicates a time in the past,
286      * or is within the window of acceptable clock skew for the KDC and the
287      * POSTDATE option has not been specified, then the starttime of the
288      * ticket is set to the authentication server's current time."
289      * 
290      * @throws Exception 
291      */
292     public void testStartTimeAbsentNoPostdate() throws Exception
293     {
294         RequestBodyModifier modifier = new RequestBodyModifier();
295         modifier.setClientName( getPrincipalName( "hnelson" ) );
296         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
297         modifier.setRealm( "EXAMPLE.COM" );
298         modifier.setEType( config.getEncryptionTypes() );
299 
300         modifier.setKdcOptions( new KdcOptions() );
301 
302         long now = System.currentTimeMillis();
303 
304         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
305         modifier.setTill( requestedEndTime );
306 
307         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
308 
309         String passPhrase = "secret";
310         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
311 
312         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
313 
314         handler.messageReceived( session, message );
315 
316         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
317 
318         KerberosTime expectedStartTime = new KerberosTime( now );
319         boolean isClose = reply.getStartTime() == null
320             || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
321         assertTrue( "Expected start time", isClose );
322     }
323 
324 
325     /**
326      * Tests when the starttime indicates a time in the past and the POSTDATED option
327      * has not been specified, that the starttime of the ticket is set to the
328      * authentication server's current time.
329      * 
330      * "If the requested starttime is absent, indicates a time in the past,
331      * or is within the window of acceptable clock skew for the KDC and the
332      * POSTDATE option has not been specified, then the starttime of the
333      * ticket is set to the authentication server's current time."
334      * 
335      * @throws Exception 
336      */
337     public void testStartTimeInThePastNoPostdate() throws Exception
338     {
339         RequestBodyModifier modifier = new RequestBodyModifier();
340         modifier.setClientName( getPrincipalName( "hnelson" ) );
341         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
342         modifier.setRealm( "EXAMPLE.COM" );
343         modifier.setEType( config.getEncryptionTypes() );
344 
345         modifier.setKdcOptions( new KdcOptions() );
346 
347         long now = System.currentTimeMillis();
348 
349         KerberosTime requestedStartTime = new KerberosTime( now + -1 * KerberosTime.DAY );
350         modifier.setFrom( requestedStartTime );
351 
352         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
353         modifier.setTill( requestedEndTime );
354 
355         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
356 
357         String passPhrase = "secret";
358         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
359 
360         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
361 
362         handler.messageReceived( session, message );
363 
364         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
365 
366         KerberosTime expectedStartTime = new KerberosTime( now );
367         boolean isClose = reply.getStartTime() == null
368             || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
369         assertTrue( "Expected start time", isClose );
370     }
371 
372 
373     /**
374      * Tests when the starttime is within the window of acceptable clock skew for
375      * the KDC and the POSTDATED option has not been specified, that the starttime
376      * of the ticket is set to the authentication server's current time.
377      * 
378      * "If the requested starttime is absent, indicates a time in the past,
379      * or is within the window of acceptable clock skew for the KDC and the
380      * POSTDATE option has not been specified, then the starttime of the
381      * ticket is set to the authentication server's current time."
382      * 
383      * @throws Exception 
384      */
385     public void testStartTimeAcceptableClockSkewNoPostdate() throws Exception
386     {
387         RequestBodyModifier modifier = new RequestBodyModifier();
388         modifier.setClientName( getPrincipalName( "hnelson" ) );
389         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
390         modifier.setRealm( "EXAMPLE.COM" );
391         modifier.setEType( config.getEncryptionTypes() );
392 
393         modifier.setKdcOptions( new KdcOptions() );
394 
395         long now = System.currentTimeMillis();
396 
397         KerberosTime requestedStartTime = new KerberosTime( now );
398         modifier.setFrom( requestedStartTime );
399 
400         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
401         modifier.setTill( requestedEndTime );
402 
403         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
404 
405         String passPhrase = "secret";
406         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
407 
408         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
409 
410         handler.messageReceived( session, message );
411 
412         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
413 
414         KerberosTime expectedStartTime = new KerberosTime( now );
415         boolean isClose = reply.getStartTime() == null
416             || Math.abs( reply.getStartTime().getTime() - expectedStartTime.getTime() ) < 5000;
417         assertTrue( "Expected start time", isClose );
418     }
419 
420 
421     /**
422      * Tests when a start time is after an end time that the request is rejected with the
423      * correct error message.
424      * 
425      * "If the requested expiration time minus the starttime (as determined above)
426      * is less than a site-determined minimum lifetime, an error message with code
427      * KDC_ERR_NEVER_VALID is returned."
428      *
429      * @throws Exception
430      */
431     public void testStartTimeOrderNeverValid() throws Exception
432     {
433         RequestBodyModifier modifier = new RequestBodyModifier();
434         modifier.setClientName( getPrincipalName( "hnelson" ) );
435         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
436         modifier.setRealm( "EXAMPLE.COM" );
437         modifier.setEType( config.getEncryptionTypes() );
438 
439         KdcOptions kdcOptions = new KdcOptions();
440         kdcOptions.set( KdcOptions.POSTDATED );
441         modifier.setKdcOptions( kdcOptions );
442 
443         long now = System.currentTimeMillis();
444 
445         KerberosTime requestedStartTime = new KerberosTime( now + KerberosTime.DAY );
446         modifier.setFrom( requestedStartTime );
447 
448         KerberosTime requestedEndTime = new KerberosTime( now );
449         modifier.setTill( requestedEndTime );
450 
451         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
452         String passPhrase = "secret";
453         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
454 
455         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
456 
457         handler.messageReceived( session, message );
458 
459         ErrorMessage error = ( ErrorMessage ) session.getMessage();
460         assertEquals( "Requested start time is later than end time", 11, error.getErrorCode() );
461     }
462 
463 
464     /**
465      * Tests when the absolute value of the difference between the start time is
466      * and the end time is less than a configured minimum, that the request is
467      * rejected with the correct error message.
468      * 
469      * "If the requested expiration time minus the starttime (as determined above)
470      * is less than a site-determined minimum lifetime, an error message with code
471      * KDC_ERR_NEVER_VALID is returned."
472      *
473      * @throws Exception
474      */
475     public void testStartTimeMinimumNeverValid() throws Exception
476     {
477         RequestBodyModifier modifier = new RequestBodyModifier();
478         modifier.setClientName( getPrincipalName( "hnelson" ) );
479         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
480         modifier.setRealm( "EXAMPLE.COM" );
481         modifier.setEType( config.getEncryptionTypes() );
482 
483         modifier.setKdcOptions( new KdcOptions() );
484 
485         long now = System.currentTimeMillis();
486 
487         KerberosTime requestedStartTime = new KerberosTime( now );
488         modifier.setFrom( requestedStartTime );
489 
490         KerberosTime requestedEndTime = new KerberosTime( now + 4 * KerberosTime.MINUTE );
491         modifier.setTill( requestedEndTime );
492 
493         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
494         String passPhrase = "secret";
495         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
496 
497         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
498 
499         handler.messageReceived( session, message );
500 
501         ErrorMessage error = ( ErrorMessage ) session.getMessage();
502         assertEquals( "Requested start time is later than end time", 11, error.getErrorCode() );
503     }
504 
505 
506     /**
507      * Tests when a valid starttime is specified but the POSTDATE flag is not set,
508      * that the request is rejected with the correct error message.
509      * 
510      * "If it indicates a time in the future beyond the acceptable clock skew, but
511      * the POSTDATED option has not been specified, then the error
512      * KDC_ERR_CANNOT_POSTDATE is returned."
513      * 
514      * @throws Exception 
515      */
516     public void testStartTimeNoPostdated() throws Exception
517     {
518         RequestBodyModifier modifier = new RequestBodyModifier();
519         modifier.setClientName( getPrincipalName( "hnelson" ) );
520         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
521         modifier.setRealm( "EXAMPLE.COM" );
522         modifier.setEType( config.getEncryptionTypes() );
523 
524         modifier.setKdcOptions( new KdcOptions() );
525 
526         long now = System.currentTimeMillis();
527 
528         KerberosTime requestedStartTime = new KerberosTime( now + 10 * KerberosTime.MINUTE );
529         modifier.setFrom( requestedStartTime );
530 
531         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
532         modifier.setTill( requestedEndTime );
533 
534         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
535         String passPhrase = "secret";
536         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
537 
538         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
539 
540         handler.messageReceived( session, message );
541 
542         ErrorMessage error = ( ErrorMessage ) session.getMessage();
543         assertEquals( "Ticket not eligible for postdating", 10, error.getErrorCode() );
544     }
545 
546 
547     /**
548      * Tests that a user-specified start time is honored when that start time does not
549      * violate policy.
550      * 
551      * "Otherwise the requested starttime is checked against the policy of the local
552      * realm (the administrator might decide to prohibit certain types or ranges of
553      * postdated tickets), and if the ticket's starttime is acceptable, it is set as
554      * requested, and the INVALID flag is set in the new ticket.  The postdated
555      * ticket MUST be validated before use by presenting it to the KDC after the
556      * starttime has been reached."
557      * 
558      * "If the new ticket is postdated (the starttime is in the future), its
559      * INVALID flag will also be set."
560      * 
561      * "The flags field of the new ticket will have the following options set
562      * if they have been requested and if the policy of the local realm
563      * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
564      * 
565      * @throws Exception
566      */
567     public void testSpecificStartTime() throws Exception
568     {
569         RequestBodyModifier modifier = new RequestBodyModifier();
570         modifier.setClientName( getPrincipalName( "hnelson" ) );
571         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
572         modifier.setRealm( "EXAMPLE.COM" );
573         modifier.setEType( config.getEncryptionTypes() );
574 
575         KdcOptions kdcOptions = new KdcOptions();
576         kdcOptions.set( KdcOptions.POSTDATED );
577         modifier.setKdcOptions( kdcOptions );
578 
579         long now = System.currentTimeMillis();
580 
581         KerberosTime requestedStartTime = new KerberosTime( now + KerberosTime.DAY );
582         modifier.setFrom( requestedStartTime );
583 
584         KerberosTime requestedEndTime = new KerberosTime( now + 2 * KerberosTime.DAY );
585         modifier.setTill( requestedEndTime );
586 
587         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
588         String passPhrase = "secret";
589         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
590 
591         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
592 
593         handler.messageReceived( session, message );
594 
595         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
596 
597         assertTrue( "Requested start time", requestedStartTime.equals( reply.getStartTime() ) );
598         assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
599         assertTrue( "POSTDATED flag", reply.getFlags().isPostdated() );
600         assertTrue( "INVALID flag", reply.getFlags().isInvalid() );
601 
602         assertTrue( "Requested start time", requestedStartTime.equals( reply.getTicket().getEncTicketPart().getStartTime() ) );
603         assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
604         assertTrue( "POSTDATED flag", reply.getTicket().getEncTicketPart().getFlags().isPostdated() );
605         assertTrue( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
606 
607         assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
608     }
609 
610 
611     /**
612      * Tests that a user-specified end time is honored when that end time does not
613      * violate policy.
614      * 
615      * "The expiration time of the ticket will be set to the earlier of the
616      * requested endtime and a time determined by local policy, possibly by
617      * using realm- or principal-specific factors."
618      *
619      * @throws Exception
620      */
621     public void testSpecificEndTime() throws Exception
622     {
623         RequestBodyModifier modifier = new RequestBodyModifier();
624         modifier.setClientName( getPrincipalName( "hnelson" ) );
625         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
626         modifier.setRealm( "EXAMPLE.COM" );
627         modifier.setEType( config.getEncryptionTypes() );
628 
629         modifier.setKdcOptions( new KdcOptions() );
630 
631         long now = System.currentTimeMillis();
632 
633         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY / 2 );
634         modifier.setTill( requestedEndTime );
635 
636         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
637 
638         String passPhrase = "secret";
639         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
640 
641         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
642 
643         handler.messageReceived( session, message );
644 
645         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
646 
647         assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
648 
649         assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
650     }
651 
652 
653     /**
654      * Tests when an end time is requested that exceeds the maximum end time as 
655      * configured in policy that the maximum allowable end time is returned instead
656      * of the requested end time.
657      * 
658      * "The expiration time of the ticket will be set to the earlier of the
659      * requested endtime and a time determined by local policy, possibly by
660      * using realm- or principal-specific factors."
661      *
662      * @throws Exception
663      */
664     public void testEndTimeExceedsMaximumAllowable() throws Exception
665     {
666         RequestBodyModifier modifier = new RequestBodyModifier();
667         modifier.setClientName( getPrincipalName( "hnelson" ) );
668         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
669         modifier.setRealm( "EXAMPLE.COM" );
670         modifier.setEType( config.getEncryptionTypes() );
671 
672         modifier.setKdcOptions( new KdcOptions() );
673 
674         long now = System.currentTimeMillis();
675 
676         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
677         modifier.setTill( requestedEndTime );
678 
679         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
680 
681         String passPhrase = "secret";
682         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
683 
684         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
685 
686         handler.messageReceived( session, message );
687 
688         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
689 
690         KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
691         boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
692         assertTrue( "Expected end time", isClose );
693     }
694 
695 
696     /**
697      * Tests that a requested zulu end time of the epoch ("19700101000000Z") results
698      * in the maximum endtime permitted according to KDC policy.  The zulu epoch is
699      * the same as '0' (zero) milliseconds in Java.
700      * 
701      * @throws Exception
702      */
703     public void testEpochEndTime() throws Exception
704     {
705         RequestBodyModifier modifier = new RequestBodyModifier();
706         modifier.setClientName( getPrincipalName( "hnelson" ) );
707         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
708         modifier.setRealm( "EXAMPLE.COM" );
709         modifier.setEType( config.getEncryptionTypes() );
710 
711         modifier.setKdcOptions( new KdcOptions() );
712 
713         String epoch = "19700101000000Z";
714         KerberosTime requestedEndTime = KerberosTime.getTime( epoch );
715         modifier.setTill( requestedEndTime );
716 
717         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
718 
719         String passPhrase = "secret";
720         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
721 
722         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
723 
724         handler.messageReceived( session, message );
725 
726         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
727 
728         long now = System.currentTimeMillis();
729         KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
730         boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
731         assertTrue( "Expected end time", isClose );
732     }
733 
734 
735     /**
736      * Tests that a service ticket can be requested without the use of a TGT.  The
737      * returned service ticket will have the INITIAL flag set.
738      * 
739      * @throws Exception
740      */
741     public void testInitialServiceTicket() throws Exception
742     {
743         String servicePrincipalName = "ldap/ldap.example.com@EXAMPLE.COM";
744 
745         RequestBodyModifier modifier = new RequestBodyModifier();
746         modifier.setClientName( getPrincipalName( "hnelson" ) );
747         modifier.setServerName( getPrincipalName( servicePrincipalName ) );
748         modifier.setRealm( "EXAMPLE.COM" );
749         modifier.setEType( config.getEncryptionTypes() );
750 
751         modifier.setKdcOptions( new KdcOptions() );
752 
753         long now = System.currentTimeMillis();
754         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
755         modifier.setTill( requestedEndTime );
756 
757         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
758         String passPhrase = "secret";
759         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
760 
761         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
762 
763         handler.messageReceived( session, message );
764 
765         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
766 
767         assertTrue( "INITIAL flag", reply.getFlags().isInitial() );
768         assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
769 
770         assertTrue( "INITIAL flag", reply.getTicket().getEncTicketPart().getFlags().isInitial() );
771         assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
772 
773         assertEquals( "Service principal name", reply.getServerPrincipal().getName(), servicePrincipalName );
774         assertEquals( "Service principal name", reply.getTicket().getServerPrincipal().getName(), servicePrincipalName );
775     }
776 
777 
778     /**
779      * Tests whether a renewable ticket will be accepted in lieu of a non-renewable
780      * ticket if the requested ticket expiration date cannot be satisfied by a
781      * non-renewable ticket (due to configuration constraints).
782      * 
783      * "If the requested expiration time for the ticket exceeds what was determined
784      * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
785      * flag is set in the new ticket, and the renew-till value is set as if the
786      * 'RENEWABLE' option were requested (the field and option names are described
787      * fully in Section 5.4.1).
788      * 
789      * @throws Exception 
790      */
791     public void testRenewableOk() throws Exception
792     {
793         RequestBodyModifier modifier = new RequestBodyModifier();
794         modifier.setClientName( getPrincipalName( "hnelson" ) );
795         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
796         modifier.setRealm( "EXAMPLE.COM" );
797         modifier.setEType( config.getEncryptionTypes() );
798 
799         KdcOptions kdcOptions = new KdcOptions();
800         kdcOptions.set( KdcOptions.RENEWABLE_OK );
801         modifier.setKdcOptions( kdcOptions );
802 
803         long now = System.currentTimeMillis();
804 
805         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.WEEK );
806         modifier.setTill( requestedEndTime );
807 
808         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
809 
810         String passPhrase = "secret";
811         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
812 
813         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
814 
815         handler.messageReceived( session, message );
816 
817         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
818 
819         KerberosTime expectedEndTime = new KerberosTime( now + KerberosTime.DAY );
820         boolean isClose = Math.abs( reply.getEndTime().getTime() - expectedEndTime.getTime() ) < 5000;
821         assertTrue( "Expected end time", isClose );
822 
823         assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
824         assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
825 
826         KerberosTime expectedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK );
827         isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
828         assertTrue( "Expected renew-till time", isClose );
829     }
830 
831 
832     /**
833      * Tests forwardable tickets.
834      * 
835      * "The flags field of the new ticket will have the following options set
836      * if they have been requested and if the policy of the local realm
837      * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
838      * 
839      * @throws Exception 
840      */
841     public void testForwardableTicket() throws Exception
842     {
843         RequestBodyModifier modifier = new RequestBodyModifier();
844         modifier.setClientName( getPrincipalName( "hnelson" ) );
845         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
846         modifier.setRealm( "EXAMPLE.COM" );
847         modifier.setEType( config.getEncryptionTypes() );
848 
849         KdcOptions kdcOptions = new KdcOptions();
850         kdcOptions.set( KdcOptions.FORWARDABLE );
851         modifier.setKdcOptions( kdcOptions );
852 
853         long now = System.currentTimeMillis();
854 
855         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
856         modifier.setTill( requestedEndTime );
857 
858         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
859         String passPhrase = "secret";
860         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
861 
862         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
863 
864         handler.messageReceived( session, message );
865 
866         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
867 
868         assertTrue( "FORWARDABLE flag", reply.getFlags().isForwardable() );
869         assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
870 
871         assertTrue( "FORWARDABLE flag", reply.getTicket().getEncTicketPart().getFlags().isForwardable() );
872         assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
873     }
874 
875 
876     /**
877      * Tests allow postdating of derivative tickets.
878      * 
879      * "The flags field of the new ticket will have the following options set
880      * if they have been requested and if the policy of the local realm
881      * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
882      * 
883      * @throws Exception 
884      */
885     public void testAllowPostdate() throws Exception
886     {
887         RequestBodyModifier modifier = new RequestBodyModifier();
888         modifier.setClientName( getPrincipalName( "hnelson" ) );
889         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
890         modifier.setRealm( "EXAMPLE.COM" );
891         modifier.setEType( config.getEncryptionTypes() );
892 
893         KdcOptions kdcOptions = new KdcOptions();
894         kdcOptions.set( KdcOptions.ALLOW_POSTDATE );
895         modifier.setKdcOptions( kdcOptions );
896 
897         long now = System.currentTimeMillis();
898 
899         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
900         modifier.setTill( requestedEndTime );
901 
902         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
903         String passPhrase = "secret";
904         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
905 
906         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
907 
908         handler.messageReceived( session, message );
909 
910         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
911 
912         assertTrue( "MAY_POSTDATE flag", reply.getFlags().isMayPosdate() );
913         assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
914 
915         assertTrue( "MAY_POSTDATE flag", reply.getTicket().getEncTicketPart().getFlags().isMayPosdate() );
916         assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
917     }
918 
919 
920     /**
921      * Tests proxiable tickets.
922      * 
923      * "The flags field of the new ticket will have the following options set
924      * if they have been requested and if the policy of the local realm
925      * allows:  FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE."
926      * 
927      * @throws Exception 
928      */
929     public void testProxiableTicket() throws Exception
930     {
931         RequestBodyModifier modifier = new RequestBodyModifier();
932         modifier.setClientName( getPrincipalName( "hnelson" ) );
933         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
934         modifier.setRealm( "EXAMPLE.COM" );
935         modifier.setEType( config.getEncryptionTypes() );
936 
937         KdcOptions kdcOptions = new KdcOptions();
938         kdcOptions.set( KdcOptions.PROXIABLE );
939         modifier.setKdcOptions( kdcOptions );
940 
941         long now = System.currentTimeMillis();
942 
943         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
944         modifier.setTill( requestedEndTime );
945 
946         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
947         String passPhrase = "secret";
948         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
949 
950         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
951 
952         handler.messageReceived( session, message );
953 
954         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
955 
956         assertTrue( "PROXIABLE flag", reply.getFlags().isProxiable() );
957         assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
958 
959         assertTrue( "PROXIABLE flag", reply.getTicket().getEncTicketPart().getFlags().isProxiable() );
960         assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
961     }
962 
963 
964     /**
965      * Tests that a user-specified renew-till time is honored when that renew-till
966      * time does not violate policy.
967      * 
968      * "If the RENEWABLE option has been requested or if the RENEWABLE-OK
969      * option has been set and a renewable ticket is to be issued, then the
970      * renew-till field MAY be set to the earliest of ... its requested value [or]
971      * the starttime of the ticket plus the maximum renewable lifetime
972      * set by the policy of the local realm."
973      * 
974      * @throws Exception 
975      */
976     public void testRenewableTicket() throws Exception
977     {
978         RequestBodyModifier modifier = new RequestBodyModifier();
979         modifier.setClientName( getPrincipalName( "hnelson" ) );
980         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
981         modifier.setRealm( "EXAMPLE.COM" );
982         modifier.setEType( config.getEncryptionTypes() );
983 
984         KdcOptions kdcOptions = new KdcOptions();
985         kdcOptions.set( KdcOptions.RENEWABLE );
986         modifier.setKdcOptions( kdcOptions );
987 
988         long now = System.currentTimeMillis();
989 
990         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
991         modifier.setTill( requestedEndTime );
992 
993         KerberosTime requestedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK / 2 );
994         modifier.setRtime( requestedRenewTillTime );
995 
996         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
997         String passPhrase = "secret";
998         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
999 
1000         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
1001 
1002         handler.messageReceived( session, message );
1003 
1004         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
1005 
1006         assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
1007         assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
1008 
1009         assertTrue( "RENEWABLE flag", reply.getTicket().getEncTicketPart().getFlags().isRenewable() );
1010         assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
1011 
1012         assertTrue( "Requested renew-till time", requestedRenewTillTime.equals( reply.getRenewTill() ) );
1013     }
1014 
1015 
1016     /**
1017      * Tests when a renew-till time is requested that exceeds the maximum renew-till
1018      * time as configured in policy that the maximum allowable renew-till time is
1019      * returned instead of the requested renew-till time.
1020      * 
1021      * "If the RENEWABLE option has been requested or if the RENEWABLE-OK
1022      * option has been set and a renewable ticket is to be issued, then the
1023      * renew-till field MAY be set to the earliest of ... its requested value [or]
1024      * the starttime of the ticket plus the maximum renewable lifetime
1025      * set by the policy of the local realm."
1026      * 
1027      * @throws Exception 
1028      */
1029     public void testRenewableTicketExceedsMaximumAllowable() throws Exception
1030     {
1031         RequestBodyModifier modifier = new RequestBodyModifier();
1032         modifier.setClientName( getPrincipalName( "hnelson" ) );
1033         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
1034         modifier.setRealm( "EXAMPLE.COM" );
1035         modifier.setEType( config.getEncryptionTypes() );
1036 
1037         KdcOptions kdcOptions = new KdcOptions();
1038         kdcOptions.set( KdcOptions.RENEWABLE );
1039         modifier.setKdcOptions( kdcOptions );
1040 
1041         long now = System.currentTimeMillis();
1042 
1043         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
1044         modifier.setTill( requestedEndTime );
1045 
1046         KerberosTime requestedRenewTillTime = new KerberosTime( now + 2 * KerberosTime.WEEK );
1047         modifier.setRtime( requestedRenewTillTime );
1048 
1049         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
1050         String passPhrase = "secret";
1051         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
1052 
1053         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
1054 
1055         handler.messageReceived( session, message );
1056 
1057         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
1058 
1059         assertTrue( "RENEWABLE flag", reply.getFlags().isRenewable() );
1060         assertFalse( "INVALID flag", reply.getFlags().isInvalid() );
1061 
1062         assertTrue( "RENEWABLE flag", reply.getTicket().getEncTicketPart().getFlags().isRenewable() );
1063         assertFalse( "INVALID flag", reply.getTicket().getEncTicketPart().getFlags().isInvalid() );
1064 
1065         KerberosTime expectedRenewTillTime = new KerberosTime( now + KerberosTime.WEEK );
1066         boolean isClose = Math.abs( reply.getRenewTill().getTime() - expectedRenewTillTime.getTime() ) < 5000;
1067         assertTrue( "Expected renew-till time", isClose );
1068     }
1069 
1070 
1071     /**
1072      * Tests that the option RENEW, which is bad for an AS_REQ, is rejected
1073      * with the correct error message.
1074      *
1075      * @throws Exception
1076      */
1077     public void testBadOptionRenew() throws Exception
1078     {
1079         RequestBodyModifier modifier = new RequestBodyModifier();
1080         modifier.setClientName( getPrincipalName( "hnelson" ) );
1081         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
1082         modifier.setRealm( "EXAMPLE.COM" );
1083         modifier.setEType( config.getEncryptionTypes() );
1084 
1085         KdcOptions kdcOptions = new KdcOptions();
1086         kdcOptions.set( KdcOptions.RENEW );
1087         modifier.setKdcOptions( kdcOptions );
1088 
1089         long now = System.currentTimeMillis();
1090 
1091         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
1092         modifier.setTill( requestedEndTime );
1093 
1094         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
1095         String passPhrase = "secret";
1096         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
1097 
1098         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
1099 
1100         handler.messageReceived( session, message );
1101 
1102         ErrorMessage error = ( ErrorMessage ) session.getMessage();
1103         assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
1104     }
1105 
1106 
1107     /**
1108      * Tests that the option VALIDATE, which is bad for an AS_REQ, is rejected
1109      * with the correct error message.
1110      *
1111      * @throws Exception
1112      */
1113     public void testBadOptionValidate() throws Exception
1114     {
1115         RequestBodyModifier modifier = new RequestBodyModifier();
1116         modifier.setClientName( getPrincipalName( "hnelson" ) );
1117         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
1118         modifier.setRealm( "EXAMPLE.COM" );
1119         modifier.setEType( config.getEncryptionTypes() );
1120 
1121         KdcOptions kdcOptions = new KdcOptions();
1122         kdcOptions.set( KdcOptions.VALIDATE );
1123         modifier.setKdcOptions( kdcOptions );
1124 
1125         long now = System.currentTimeMillis();
1126 
1127         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
1128         modifier.setTill( requestedEndTime );
1129 
1130         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
1131         String passPhrase = "secret";
1132         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
1133 
1134         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
1135 
1136         handler.messageReceived( session, message );
1137 
1138         ErrorMessage error = ( ErrorMessage ) session.getMessage();
1139         assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
1140     }
1141 
1142 
1143     /**
1144      * Tests that the option PROXY, which is bad for an AS_REQ, is rejected
1145      * with the correct error message.
1146      *
1147      * @throws Exception
1148      */
1149     public void testBadOptionProxy() throws Exception
1150     {
1151         RequestBodyModifier modifier = new RequestBodyModifier();
1152         modifier.setClientName( getPrincipalName( "hnelson" ) );
1153         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
1154         modifier.setRealm( "EXAMPLE.COM" );
1155         modifier.setEType( config.getEncryptionTypes() );
1156 
1157         KdcOptions kdcOptions = new KdcOptions();
1158         kdcOptions.set( KdcOptions.PROXY );
1159         modifier.setKdcOptions( kdcOptions );
1160 
1161         long now = System.currentTimeMillis();
1162 
1163         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
1164         modifier.setTill( requestedEndTime );
1165 
1166         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
1167         String passPhrase = "secret";
1168         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
1169 
1170         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
1171 
1172         handler.messageReceived( session, message );
1173 
1174         ErrorMessage error = ( ErrorMessage ) session.getMessage();
1175         assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
1176     }
1177 
1178 
1179     /**
1180      * Tests that the option FORWARDED, which is bad for an AS_REQ, is rejected
1181      * with the correct error message.
1182      *
1183      * @throws Exception
1184      */
1185     public void testBadOptionForwarded() throws Exception
1186     {
1187         RequestBodyModifier modifier = new RequestBodyModifier();
1188         modifier.setClientName( getPrincipalName( "hnelson" ) );
1189         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
1190         modifier.setRealm( "EXAMPLE.COM" );
1191         modifier.setEType( config.getEncryptionTypes() );
1192 
1193         KdcOptions kdcOptions = new KdcOptions();
1194         kdcOptions.set( KdcOptions.FORWARDED );
1195         modifier.setKdcOptions( kdcOptions );
1196 
1197         long now = System.currentTimeMillis();
1198 
1199         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
1200         modifier.setTill( requestedEndTime );
1201 
1202         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
1203         String passPhrase = "secret";
1204         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
1205 
1206         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
1207 
1208         handler.messageReceived( session, message );
1209 
1210         ErrorMessage error = ( ErrorMessage ) session.getMessage();
1211         assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
1212     }
1213 
1214 
1215     /**
1216      * Tests that the option ENC_TKT_IN_SKEY, which is bad for an AS_REQ, is rejected
1217      * with the correct error message.
1218      *
1219      * @throws Exception
1220      */
1221     public void testBadOptionEncTktInSkey() throws Exception
1222     {
1223         RequestBodyModifier modifier = new RequestBodyModifier();
1224         modifier.setClientName( getPrincipalName( "hnelson" ) );
1225         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
1226         modifier.setRealm( "EXAMPLE.COM" );
1227         modifier.setEType( config.getEncryptionTypes() );
1228 
1229         KdcOptions kdcOptions = new KdcOptions();
1230         kdcOptions.set( KdcOptions.ENC_TKT_IN_SKEY );
1231         modifier.setKdcOptions( kdcOptions );
1232 
1233         long now = System.currentTimeMillis();
1234 
1235         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
1236         modifier.setTill( requestedEndTime );
1237 
1238         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
1239         String passPhrase = "secret";
1240         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
1241 
1242         KdcRequest message = new KdcRequest( 5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
1243 
1244         handler.messageReceived( session, message );
1245 
1246         ErrorMessage error = ( ErrorMessage ) session.getMessage();
1247         assertEquals( "KDC cannot accommodate requested option", 13, error.getErrorCode() );
1248     }
1249 }