1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.kerberos.kdc.ticketgrant;
21
22
23 import java.net.InetAddress;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Set;
28
29 import javax.security.auth.kerberos.KerberosPrincipal;
30
31 import org.apache.directory.server.kerberos.kdc.KdcContext;
32 import org.apache.directory.server.kerberos.kdc.KdcServer;
33 import org.apache.directory.server.kerberos.shared.KerberosConstants;
34 import org.apache.directory.server.kerberos.shared.KerberosUtils;
35 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumHandler;
36 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
37 import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
38 import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
39 import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
40 import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
41 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
42 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
43 import org.apache.directory.server.kerberos.shared.io.decoder.ApplicationRequestDecoder;
44 import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
45 import org.apache.directory.server.kerberos.shared.messages.KdcReply;
46 import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
47 import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
48 import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
49 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
50 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
51 import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
52 import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
53 import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
54 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
55 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
56 import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
57 import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
58 import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
59 import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
60 import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
61 import org.apache.directory.server.kerberos.shared.messages.value.PaData;
62 import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
63 import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
64 import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
65 import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
66 import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
67 import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74
75
76 public class TicketGrantingService
77 {
78
79
80 private static final Logger LOG = LoggerFactory.getLogger( TicketGrantingService.class );
81
82 private static final InMemoryReplayCache replayCache = new InMemoryReplayCache();
83 private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
84
85 private static final String SERVICE_NAME = "Ticket-Granting Service (TGS)";
86
87 private static final ChecksumHandler checksumHandler = new ChecksumHandler();
88
89 public static void execute( TicketGrantingContext tgsContext ) throws Exception
90 {
91 if ( LOG.isDebugEnabled() )
92 {
93 monitorRequest( tgsContext );
94 }
95
96 configureTicketGranting( tgsContext);
97 selectEncryptionType( tgsContext );
98 getAuthHeader( tgsContext );
99 verifyTgt( tgsContext );
100 getTicketPrincipalEntry( tgsContext );
101 verifyTgtAuthHeader( tgsContext );
102 verifyBodyChecksum( tgsContext );
103 getRequestPrincipalEntry( tgsContext );
104 generateTicket( tgsContext );
105 buildReply( tgsContext );
106
107 if ( LOG.isDebugEnabled() )
108 {
109 monitorContext( tgsContext );
110 monitorReply( tgsContext );
111 }
112
113 sealReply( tgsContext );
114 }
115
116
117 private static void configureTicketGranting( TicketGrantingContext tgsContext ) throws KerberosException
118 {
119 KdcServer config = tgsContext.getConfig();
120 long clockSkew = config.getAllowableClockSkew();
121 replayCache.setClockSkew( clockSkew );
122 tgsContext.setReplayCache( replayCache );
123
124 tgsContext.setCipherTextHandler( cipherTextHandler );
125
126 if ( tgsContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
127 {
128 throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
129 }
130 }
131
132
133 private static void monitorRequest( KdcContext kdcContext ) throws Exception
134 {
135 KdcRequest request = kdcContext.getRequest();
136
137 try
138 {
139 String clientAddress = kdcContext.getClientAddress().getHostAddress();
140
141 StringBuffer sb = new StringBuffer();
142
143 sb.append( "Received " + SERVICE_NAME + " request:" );
144 sb.append( "\n\t" + "messageType: " + request.getMessageType() );
145 sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
146 sb.append( "\n\t" + "clientAddress: " + clientAddress );
147 sb.append( "\n\t" + "nonce: " + request.getNonce() );
148 sb.append( "\n\t" + "kdcOptions: " + request.getKdcOptions() );
149 sb.append( "\n\t" + "clientPrincipal: " + request.getClientPrincipal() );
150 sb.append( "\n\t" + "serverPrincipal: " + request.getServerPrincipal() );
151 sb.append( "\n\t" + "encryptionType: " + KerberosUtils.getEncryptionTypesString( request.getEType() ) );
152 sb.append( "\n\t" + "realm: " + request.getRealm() );
153 sb.append( "\n\t" + "from time: " + request.getFrom() );
154 sb.append( "\n\t" + "till time: " + request.getTill() );
155 sb.append( "\n\t" + "renew-till time: " + request.getRtime() );
156 sb.append( "\n\t" + "hostAddresses: " + request.getAddresses() );
157
158 LOG.debug( sb.toString() );
159 }
160 catch ( Exception e )
161 {
162
163 LOG.error( "Error in request monitor", e );
164 }
165 }
166
167
168 private static void selectEncryptionType( TicketGrantingContext tgsContext ) throws Exception
169 {
170 KdcContext kdcContext = (KdcContext)tgsContext;
171 KdcServer config = kdcContext.getConfig();
172
173 Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType();
174
175 EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
176
177 LOG.debug( "Session will use encryption type {}.", bestType );
178
179 if ( bestType == null )
180 {
181 throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
182 }
183
184 kdcContext.setEncryptionType( bestType );
185 }
186
187
188 private static void getAuthHeader( TicketGrantingContext tgsContext ) throws Exception
189 {
190 KdcRequest request = tgsContext.getRequest();
191
192 PaData[] preAuthData = request.getPreAuthData();
193
194 if ( preAuthData == null || preAuthData.length < 1 )
195 {
196 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
197 }
198
199 byte[] undecodedAuthHeader = null;
200
201 for ( int ii = 0; ii < preAuthData.length; ii++ )
202 {
203 if ( preAuthData[ii].getPaDataType() == PaDataType.PA_TGS_REQ )
204 {
205 undecodedAuthHeader = preAuthData[ii].getPaDataValue();
206 }
207 }
208
209 if ( undecodedAuthHeader == null )
210 {
211 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
212 }
213
214 ApplicationRequestDecoder decoder = new ApplicationRequestDecoder();
215 ApplicationRequest authHeader = decoder.decode( undecodedAuthHeader );
216
217 Ticket tgt = authHeader.getTicket();
218
219 tgsContext.setAuthHeader( authHeader );
220 tgsContext.setTgt( tgt );
221 }
222
223
224 public static void verifyTgt( TicketGrantingContext tgsContext ) throws KerberosException
225 {
226 KdcServer config = tgsContext.getConfig();
227 Ticket tgt = tgsContext.getTgt();
228
229
230 if ( !tgt.getRealm().equals( config.getPrimaryRealm() ) )
231 {
232 throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
233 }
234
235 String tgtServerName = tgt.getServerPrincipal().getName();
236 String requestServerName = tgsContext.getRequest().getServerPrincipal().getName();
237
238
239
240
241
242 if ( !tgtServerName.equals( config.getServicePrincipal().getName() )
243 && !tgtServerName.equals( requestServerName ) )
244 {
245 throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
246 }
247 }
248
249
250 private static void getTicketPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
251 {
252 KerberosPrincipal principal = tgsContext.getTgt().getServerPrincipal();
253 PrincipalStore store = tgsContext.getStore();
254
255 PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
256 tgsContext.setTicketPrincipalEntry( entry );
257 }
258
259
260 private static void verifyTgtAuthHeader( TicketGrantingContext tgsContext ) throws KerberosException
261 {
262 ApplicationRequest authHeader = tgsContext.getAuthHeader();
263 Ticket tgt = tgsContext.getTgt();
264
265 boolean isValidate = tgsContext.getRequest().getKdcOptions().get( KdcOptions.VALIDATE );
266
267 EncryptionType encryptionType = tgt.getEncPart().getEType();
268 EncryptionKey serverKey = tgsContext.getTicketPrincipalEntry().getKeyMap().get( encryptionType );
269
270 long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
271 ReplayCache replayCache = tgsContext.getReplayCache();
272 boolean emptyAddressesAllowed = tgsContext.getConfig().isEmptyAddressesAllowed();
273 InetAddress clientAddress = tgsContext.getClientAddress();
274 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
275
276 Authenticator authenticator = KerberosUtils.verifyAuthHeader( authHeader, tgt, serverKey, clockSkew, replayCache,
277 emptyAddressesAllowed, clientAddress, cipherTextHandler, KeyUsage.NUMBER7, isValidate );
278
279 tgsContext.setAuthenticator( authenticator );
280 }
281
282
283 private static void verifyBodyChecksum( TicketGrantingContext tgsContext ) throws KerberosException
284 {
285 KdcServer config = tgsContext.getConfig();
286
287 if ( config.isBodyChecksumVerified() )
288 {
289 byte[] bodyBytes = tgsContext.getRequest().getBodyBytes();
290 Checksum authenticatorChecksum = tgsContext.getAuthenticator().getChecksum();
291
292 if ( authenticatorChecksum == null || authenticatorChecksum.getChecksumType() == null
293 || authenticatorChecksum.getChecksumValue() == null || bodyBytes == null )
294 {
295 throw new KerberosException( ErrorType.KRB_AP_ERR_INAPP_CKSUM );
296 }
297
298 LOG.debug( "Verifying body checksum type '{}'.", authenticatorChecksum.getChecksumType() );
299
300 checksumHandler.verifyChecksum( authenticatorChecksum, bodyBytes, null, KeyUsage.NUMBER8 );
301 }
302 }
303
304
305 public static void getRequestPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
306 {
307 KerberosPrincipal principal = tgsContext.getRequest().getServerPrincipal();
308 PrincipalStore store = tgsContext.getStore();
309
310 PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
311 tgsContext.setRequestPrincipalEntry( entry );
312 }
313
314
315 private static void generateTicket( TicketGrantingContext tgsContext ) throws KerberosException
316 {
317 KdcRequest request = tgsContext.getRequest();
318 Ticket tgt = tgsContext.getTgt();
319 Authenticator authenticator = tgsContext.getAuthenticator();
320 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
321 KerberosPrincipal ticketPrincipal = request.getServerPrincipal();
322
323 EncryptionType encryptionType = tgsContext.getEncryptionType();
324 EncryptionKey serverKey = tgsContext.getRequestPrincipalEntry().getKeyMap().get( encryptionType );
325
326 KdcServer config = tgsContext.getConfig();
327
328 EncTicketPartModifier newTicketBody = new EncTicketPartModifier();
329
330 newTicketBody.setClientAddresses( tgt.getEncTicketPart().getClientAddresses() );
331
332 processFlags( config, request, tgt, newTicketBody );
333
334 EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( tgsContext.getEncryptionType() );
335 newTicketBody.setSessionKey( sessionKey );
336
337 newTicketBody.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );
338
339 if ( request.getEncAuthorizationData() != null )
340 {
341 AuthorizationData authData = ( AuthorizationData ) cipherTextHandler.unseal( AuthorizationData.class,
342 authenticator.getSubSessionKey(), request.getEncAuthorizationData(), KeyUsage.NUMBER4 );
343 authData.add( tgt.getEncTicketPart().getAuthorizationData() );
344 newTicketBody.setAuthorizationData( authData );
345 }
346
347 processTransited( newTicketBody, tgt );
348
349 processTimes( config, request, newTicketBody, tgt );
350
351 EncTicketPart ticketPart = newTicketBody.getEncTicketPart();
352
353 if ( request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) )
354 {
355
356
357
358
359
360
361
362
363
364
365
366
367 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
368 }
369 else
370 {
371 EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 );
372
373 Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
374 newTicket.setEncTicketPart( ticketPart );
375
376 tgsContext.setNewTicket( newTicket );
377 }
378 }
379
380
381 private static void buildReply( TicketGrantingContext tgsContext ) throws KerberosException
382 {
383 KdcRequest request = tgsContext.getRequest();
384 Ticket tgt = tgsContext.getTgt();
385 Ticket newTicket = tgsContext.getNewTicket();
386
387 TicketGrantReply reply = new TicketGrantReply();
388 reply.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );
389 reply.setTicket( newTicket );
390 reply.setKey( newTicket.getEncTicketPart().getSessionKey() );
391 reply.setNonce( request.getNonce() );
392
393 reply.setLastRequest( new LastRequest() );
394 reply.setFlags( newTicket.getEncTicketPart().getFlags() );
395 reply.setClientAddresses( newTicket.getEncTicketPart().getClientAddresses() );
396 reply.setAuthTime( newTicket.getEncTicketPart().getAuthTime() );
397 reply.setStartTime( newTicket.getEncTicketPart().getStartTime() );
398 reply.setEndTime( newTicket.getEncTicketPart().getEndTime() );
399 reply.setServerPrincipal( newTicket.getServerPrincipal() );
400
401 if ( newTicket.getEncTicketPart().getFlags().isRenewable() )
402 {
403 reply.setRenewTill( newTicket.getEncTicketPart().getRenewTill() );
404 }
405
406 tgsContext.setReply( reply );
407 }
408
409
410 private static void sealReply( TicketGrantingContext tgsContext ) throws KerberosException
411 {
412 TicketGrantReply reply = ( TicketGrantReply ) tgsContext.getReply();
413 Ticket tgt = tgsContext.getTgt();
414 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
415 Authenticator authenticator = tgsContext.getAuthenticator();
416
417 EncryptedData encryptedData;
418
419 if ( authenticator.getSubSessionKey() != null )
420 {
421 encryptedData = cipherTextHandler.seal( authenticator.getSubSessionKey(), reply, KeyUsage.NUMBER9 );
422 }
423 else
424 {
425 encryptedData = cipherTextHandler.seal( tgt.getEncTicketPart().getSessionKey(), reply, KeyUsage.NUMBER8 );
426 }
427
428 reply.setEncPart( encryptedData );
429 }
430
431
432
433 private static void monitorContext( TicketGrantingContext tgsContext )
434 {
435 try
436 {
437 Ticket tgt = tgsContext.getTgt();
438 long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
439 ChecksumType checksumType = tgsContext.getAuthenticator().getChecksum().getChecksumType();
440 InetAddress clientAddress = tgsContext.getClientAddress();
441 HostAddresses clientAddresses = tgt.getEncTicketPart().getClientAddresses();
442
443 boolean caddrContainsSender = false;
444 if ( tgt.getEncTicketPart().getClientAddresses() != null )
445 {
446 caddrContainsSender = tgt.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) );
447 }
448
449 StringBuffer sb = new StringBuffer();
450
451 sb.append( "Monitoring " + SERVICE_NAME + " context:" );
452
453 sb.append( "\n\t" + "clockSkew " + clockSkew );
454 sb.append( "\n\t" + "checksumType " + checksumType );
455 sb.append( "\n\t" + "clientAddress " + clientAddress );
456 sb.append( "\n\t" + "clientAddresses " + clientAddresses );
457 sb.append( "\n\t" + "caddr contains sender " + caddrContainsSender );
458
459 KerberosPrincipal requestServerPrincipal = tgsContext.getRequest().getServerPrincipal();
460 PrincipalStoreEntry requestPrincipal = tgsContext.getRequestPrincipalEntry();
461
462 sb.append( "\n\t" + "principal " + requestServerPrincipal );
463 sb.append( "\n\t" + "cn " + requestPrincipal.getCommonName() );
464 sb.append( "\n\t" + "realm " + requestPrincipal.getRealmName() );
465 sb.append( "\n\t" + "principal " + requestPrincipal.getPrincipal() );
466 sb.append( "\n\t" + "SAM type " + requestPrincipal.getSamType() );
467
468 KerberosPrincipal ticketServerPrincipal = tgsContext.getTgt().getServerPrincipal();
469 PrincipalStoreEntry ticketPrincipal = tgsContext.getTicketPrincipalEntry();
470
471 sb.append( "\n\t" + "principal " + ticketServerPrincipal );
472 sb.append( "\n\t" + "cn " + ticketPrincipal.getCommonName() );
473 sb.append( "\n\t" + "realm " + ticketPrincipal.getRealmName() );
474 sb.append( "\n\t" + "principal " + ticketPrincipal.getPrincipal() );
475 sb.append( "\n\t" + "SAM type " + ticketPrincipal.getSamType() );
476
477 EncryptionType encryptionType = tgsContext.getTgt().getEncPart().getEType();
478 int keyVersion = ticketPrincipal.getKeyMap().get( encryptionType ).getKeyVersion();
479 sb.append( "\n\t" + "Ticket key type " + encryptionType );
480 sb.append( "\n\t" + "Service key version " + keyVersion );
481
482 LOG.debug( sb.toString() );
483 }
484 catch ( Exception e )
485 {
486
487 LOG.error( "Error in context monitor", e );
488 }
489 }
490
491
492 private static void monitorReply( KdcContext kdcContext )
493 {
494 Object reply = kdcContext.getReply();
495
496 if ( reply instanceof KdcReply )
497 {
498 KdcReply success = ( KdcReply ) reply;
499
500 try
501 {
502 StringBuffer sb = new StringBuffer();
503
504 sb.append( "Responding with " + SERVICE_NAME + " reply:" );
505 sb.append( "\n\t" + "messageType: " + success.getMessageType() );
506 sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() );
507 sb.append( "\n\t" + "nonce: " + success.getNonce() );
508 sb.append( "\n\t" + "clientPrincipal: " + success.getClientPrincipal() );
509 sb.append( "\n\t" + "client realm: " + success.getClientRealm() );
510 sb.append( "\n\t" + "serverPrincipal: " + success.getServerPrincipal() );
511 sb.append( "\n\t" + "server realm: " + success.getServerRealm() );
512 sb.append( "\n\t" + "auth time: " + success.getAuthTime() );
513 sb.append( "\n\t" + "start time: " + success.getStartTime() );
514 sb.append( "\n\t" + "end time: " + success.getEndTime() );
515 sb.append( "\n\t" + "renew-till time: " + success.getRenewTill() );
516 sb.append( "\n\t" + "hostAddresses: " + success.getClientAddresses() );
517
518 LOG.debug( sb.toString() );
519 }
520 catch ( Exception e )
521 {
522
523 LOG.error( "Error in reply monitor", e );
524 }
525 }
526 }
527
528
529
530 private static void processFlags( KdcServer config, KdcRequest request, Ticket tgt,
531 EncTicketPartModifier newTicketBody ) throws KerberosException
532 {
533 if ( tgt.getEncTicketPart().getFlags().isPreAuth() )
534 {
535 newTicketBody.setFlag( TicketFlag.PRE_AUTHENT );
536 }
537
538 if ( request.getOption( KdcOptions.FORWARDABLE ) )
539 {
540 if ( !config.isForwardableAllowed() )
541 {
542 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
543 }
544
545 if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
546 {
547 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
548 }
549
550 newTicketBody.setFlag( TicketFlag.FORWARDABLE );
551 }
552
553 if ( request.getOption( KdcOptions.FORWARDED ) )
554 {
555 if ( !config.isForwardableAllowed() )
556 {
557 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
558 }
559
560 if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
561 {
562 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
563 }
564
565 if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
566 && request.getAddresses().getAddresses().length > 0 )
567 {
568 newTicketBody.setClientAddresses( request.getAddresses() );
569 }
570 else
571 {
572 if ( !config.isEmptyAddressesAllowed() )
573 {
574 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
575 }
576 }
577
578 newTicketBody.setFlag( TicketFlag.FORWARDED );
579 }
580
581 if ( tgt.getEncTicketPart().getFlags().isForwarded() )
582 {
583 newTicketBody.setFlag( TicketFlag.FORWARDED );
584 }
585
586 if ( request.getOption( KdcOptions.PROXIABLE ) )
587 {
588 if ( !config.isProxiableAllowed() )
589 {
590 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
591 }
592
593 if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
594 {
595 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
596 }
597
598 newTicketBody.setFlag( TicketFlag.PROXIABLE );
599 }
600
601 if ( request.getOption( KdcOptions.PROXY ) )
602 {
603 if ( !config.isProxiableAllowed() )
604 {
605 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
606 }
607
608 if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
609 {
610 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
611 }
612
613 if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
614 && request.getAddresses().getAddresses().length > 0 )
615 {
616 newTicketBody.setClientAddresses( request.getAddresses() );
617 }
618 else
619 {
620 if ( !config.isEmptyAddressesAllowed() )
621 {
622 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
623 }
624 }
625
626 newTicketBody.setFlag( TicketFlag.PROXY );
627 }
628
629 if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) )
630 {
631 if ( !config.isPostdatedAllowed() )
632 {
633 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
634 }
635
636 if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
637 {
638 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
639 }
640
641 newTicketBody.setFlag( TicketFlag.MAY_POSTDATE );
642 }
643
644
645
646
647
648
649
650
651
652
653
654 if ( request.getOption( KdcOptions.POSTDATED ) )
655 {
656 if ( !config.isPostdatedAllowed() )
657 {
658 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
659 }
660
661 if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
662 {
663 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
664 }
665
666 newTicketBody.setFlag( TicketFlag.POSTDATED );
667 newTicketBody.setFlag( TicketFlag.INVALID );
668
669 newTicketBody.setStartTime( request.getFrom() );
670 }
671
672 if ( request.getOption( KdcOptions.VALIDATE ) )
673 {
674 if ( !config.isPostdatedAllowed() )
675 {
676 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
677 }
678
679 if ( !tgt.getEncTicketPart().getFlags().isInvalid() )
680 {
681 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
682 }
683
684 KerberosTime startTime = ( tgt.getEncTicketPart().getStartTime() != null ) ?
685 tgt.getEncTicketPart().getStartTime() :
686 tgt.getEncTicketPart().getAuthTime();
687
688 if ( startTime.greaterThan( new KerberosTime() ) )
689 {
690 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV );
691 }
692
693 echoTicket( newTicketBody, tgt );
694 newTicketBody.clearFlag( TicketFlag.INVALID );
695 }
696
697 if ( request.getOption( KdcOptions.RESERVED ) )
698 {
699 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
700 }
701 }
702
703
704 private static void processTimes( KdcServer config, KdcRequest request, EncTicketPartModifier newTicketBody,
705 Ticket tgt ) throws KerberosException
706 {
707 KerberosTime now = new KerberosTime();
708
709 newTicketBody.setAuthTime( tgt.getEncTicketPart().getAuthTime() );
710
711 KerberosTime startTime = request.getFrom();
712
713
714
715
716
717
718
719 if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
720 && !request.getOption( KdcOptions.POSTDATED ) )
721 {
722 startTime = now;
723 }
724
725
726
727
728
729
730
731 if ( startTime != null && startTime.greaterThan( now )
732 && !startTime.isInClockSkew( config.getAllowableClockSkew() )
733 && ( !request.getOption( KdcOptions.POSTDATED ) || !tgt.getEncTicketPart().getFlags().isMayPosdate() ) )
734 {
735 throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
736 }
737
738 KerberosTime renewalTime = null;
739 KerberosTime kerberosEndTime = null;
740
741 if ( request.getOption( KdcOptions.RENEW ) )
742 {
743 if ( !config.isRenewableAllowed() )
744 {
745 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
746 }
747
748 if ( !tgt.getEncTicketPart().getFlags().isRenewable() )
749 {
750 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
751 }
752
753 if ( tgt.getEncTicketPart().getRenewTill().lessThan( now ) )
754 {
755 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED );
756 }
757
758 echoTicket( newTicketBody, tgt );
759
760 newTicketBody.setStartTime( now );
761
762 KerberosTime tgtStartTime = ( tgt.getEncTicketPart().getStartTime() != null ) ?
763 tgt.getEncTicketPart().getStartTime() :
764 tgt.getEncTicketPart().getAuthTime();
765
766 long oldLife = tgt.getEncTicketPart().getEndTime().getTime() - tgtStartTime.getTime();
767
768 kerberosEndTime = new KerberosTime( Math.min( tgt.getEncTicketPart().getRenewTill().getTime(), now.getTime() + oldLife ) );
769 newTicketBody.setEndTime( kerberosEndTime );
770 }
771 else
772 {
773 if ( newTicketBody.getEncTicketPart().getStartTime() == null )
774 {
775 newTicketBody.setStartTime( now );
776 }
777
778 KerberosTime till;
779 if ( request.getTill().isZero() )
780 {
781 till = KerberosTime.INFINITY;
782 }
783 else
784 {
785 till = request.getTill();
786 }
787
788
789
790
791
792
793 List<KerberosTime> minimizer = new ArrayList<KerberosTime>();
794 minimizer.add( till );
795 minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumTicketLifetime() ) );
796 minimizer.add( tgt.getEncTicketPart().getEndTime() );
797 kerberosEndTime = Collections.min( minimizer );
798
799 newTicketBody.setEndTime( kerberosEndTime );
800
801 if ( request.getOption( KdcOptions.RENEWABLE_OK ) && kerberosEndTime.lessThan( request.getTill() )
802 && tgt.getEncTicketPart().getFlags().isRenewable() )
803 {
804 if ( !config.isRenewableAllowed() )
805 {
806 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
807 }
808
809
810 request.setOption( KdcOptions.RENEWABLE );
811 long rtime = Math.min( request.getTill().getTime(), tgt.getEncTicketPart().getRenewTill().getTime() );
812 renewalTime = new KerberosTime( rtime );
813 }
814 }
815
816 if ( renewalTime == null )
817 {
818 renewalTime = request.getRtime();
819 }
820
821 KerberosTime rtime;
822 if ( renewalTime != null && renewalTime.isZero() )
823 {
824 rtime = KerberosTime.INFINITY;
825 }
826 else
827 {
828 rtime = renewalTime;
829 }
830
831 if ( request.getOption( KdcOptions.RENEWABLE ) && tgt.getEncTicketPart().getFlags().isRenewable() )
832 {
833 if ( !config.isRenewableAllowed() )
834 {
835 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
836 }
837
838 newTicketBody.setFlag( TicketFlag.RENEWABLE );
839
840
841
842
843
844
845 List<KerberosTime> minimizer = new ArrayList<KerberosTime>();
846
847
848
849
850 if ( rtime != null )
851 {
852 minimizer.add( rtime );
853 }
854
855 minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumRenewableLifetime() ) );
856 minimizer.add( tgt.getEncTicketPart().getRenewTill() );
857 newTicketBody.setRenewTill( Collections.min( minimizer ) );
858 }
859
860
861
862
863
864
865 if ( kerberosEndTime.lessThan( startTime ) )
866 {
867 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
868 }
869
870 long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
871 if ( ticketLifeTime < config.getAllowableClockSkew() )
872 {
873 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
874 }
875 }
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891 private static void processTransited( EncTicketPartModifier newTicketBody, Ticket tgt )
892 {
893
894 newTicketBody.setTransitedEncoding( tgt.getEncTicketPart().getTransitedEncoding() );
895 }
896
897
898 private static void echoTicket( EncTicketPartModifier newTicketBody, Ticket tgt )
899 {
900 EncTicketPart encTicketpart = tgt.getEncTicketPart();
901 newTicketBody.setAuthorizationData( encTicketpart.getAuthorizationData() );
902 newTicketBody.setAuthTime( encTicketpart.getAuthTime() );
903 newTicketBody.setClientAddresses( encTicketpart.getClientAddresses() );
904 newTicketBody.setClientPrincipal( encTicketpart.getClientPrincipal() );
905 newTicketBody.setEndTime( encTicketpart.getEndTime() );
906 newTicketBody.setFlags( encTicketpart.getFlags() );
907 newTicketBody.setRenewTill( encTicketpart.getRenewTill() );
908 newTicketBody.setSessionKey( encTicketpart.getSessionKey() );
909 newTicketBody.setTransitedEncoding( encTicketpart.getTransitedEncoding() );
910 }
911 }