001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.kerberos.kdc.ticketgrant;
021
022
023 import java.net.InetAddress;
024 import java.util.ArrayList;
025 import java.util.Collections;
026 import java.util.List;
027 import java.util.Set;
028
029 import javax.security.auth.kerberos.KerberosPrincipal;
030
031 import org.apache.directory.server.i18n.I18n;
032 import org.apache.directory.server.kerberos.kdc.KdcContext;
033 import org.apache.directory.server.kerberos.kdc.KdcServer;
034 import org.apache.directory.server.kerberos.shared.KerberosConstants;
035 import org.apache.directory.server.kerberos.shared.KerberosUtils;
036 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumHandler;
037 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
038 import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
039 import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
040 import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
041 import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
042 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
043 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
044 import org.apache.directory.server.kerberos.shared.io.decoder.ApplicationRequestDecoder;
045 import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
046 import org.apache.directory.server.kerberos.shared.messages.KdcReply;
047 import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
048 import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
049 import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
050 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
051 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
052 import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
053 import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
054 import org.apache.directory.server.kerberos.shared.messages.value.Checksum;
055 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
056 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
057 import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
058 import org.apache.directory.server.kerberos.shared.messages.value.HostAddresses;
059 import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
060 import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
061 import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
062 import org.apache.directory.server.kerberos.shared.messages.value.PaData;
063 import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
064 import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
065 import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
066 import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
067 import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
068 import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
069 import org.slf4j.Logger;
070 import org.slf4j.LoggerFactory;
071
072
073 /**
074 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
075 * @version $Rev: 583938 $, $Date: 2007-10-11 21:57:20 +0200 (Thu, 11 Oct 2007) $
076 */
077 public class TicketGrantingService
078 {
079
080 /** the log for this class */
081 private static final Logger LOG = LoggerFactory.getLogger( TicketGrantingService.class );
082
083 private static final InMemoryReplayCache replayCache = new InMemoryReplayCache();
084 private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
085
086 private static final String SERVICE_NAME = "Ticket-Granting Service (TGS)";
087
088 private static final ChecksumHandler checksumHandler = new ChecksumHandler();
089
090 public static void execute( TicketGrantingContext tgsContext ) throws Exception
091 {
092 if ( LOG.isDebugEnabled() )
093 {
094 monitorRequest( tgsContext );
095 }
096
097 configureTicketGranting( tgsContext);
098 selectEncryptionType( tgsContext );
099 getAuthHeader( tgsContext );
100 verifyTgt( tgsContext );
101 getTicketPrincipalEntry( tgsContext );
102 verifyTgtAuthHeader( tgsContext );
103 verifyBodyChecksum( tgsContext );
104 getRequestPrincipalEntry( tgsContext );
105 generateTicket( tgsContext );
106 buildReply( tgsContext );
107
108 if ( LOG.isDebugEnabled() )
109 {
110 monitorContext( tgsContext );
111 monitorReply( tgsContext );
112 }
113
114 sealReply( tgsContext );
115 }
116
117
118 private static void configureTicketGranting( TicketGrantingContext tgsContext ) throws KerberosException
119 {
120 KdcServer config = tgsContext.getConfig();
121 long clockSkew = config.getAllowableClockSkew();
122 replayCache.setClockSkew( clockSkew );
123 tgsContext.setReplayCache( replayCache );
124
125 tgsContext.setCipherTextHandler( cipherTextHandler );
126
127 if ( tgsContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
128 {
129 throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
130 }
131 }
132
133
134 private static void monitorRequest( KdcContext kdcContext ) throws Exception
135 {
136 KdcRequest request = kdcContext.getRequest();
137
138 try
139 {
140 String clientAddress = kdcContext.getClientAddress().getHostAddress();
141
142 StringBuffer sb = new StringBuffer();
143
144 sb.append( "Received " + SERVICE_NAME + " request:" );
145 sb.append( "\n\t" + "messageType: " + request.getMessageType() );
146 sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
147 sb.append( "\n\t" + "clientAddress: " + clientAddress );
148 sb.append( "\n\t" + "nonce: " + request.getNonce() );
149 sb.append( "\n\t" + "kdcOptions: " + request.getKdcOptions() );
150 sb.append( "\n\t" + "clientPrincipal: " + request.getClientPrincipal() );
151 sb.append( "\n\t" + "serverPrincipal: " + request.getServerPrincipal() );
152 sb.append( "\n\t" + "encryptionType: " + KerberosUtils.getEncryptionTypesString( request.getEType() ) );
153 sb.append( "\n\t" + "realm: " + request.getRealm() );
154 sb.append( "\n\t" + "from time: " + request.getFrom() );
155 sb.append( "\n\t" + "till time: " + request.getTill() );
156 sb.append( "\n\t" + "renew-till time: " + request.getRtime() );
157 sb.append( "\n\t" + "hostAddresses: " + request.getAddresses() );
158
159 LOG.debug( sb.toString() );
160 }
161 catch ( Exception e )
162 {
163 // This is a monitor. No exceptions should bubble up.
164 LOG.error( I18n.err( I18n.ERR_153 ), e );
165 }
166 }
167
168
169 private static void selectEncryptionType( TicketGrantingContext tgsContext ) throws Exception
170 {
171 KdcContext kdcContext = (KdcContext)tgsContext;
172 KdcServer config = kdcContext.getConfig();
173
174 Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType();
175
176 EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
177
178 LOG.debug( "Session will use encryption type {}.", bestType );
179
180 if ( bestType == null )
181 {
182 throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
183 }
184
185 kdcContext.setEncryptionType( bestType );
186 }
187
188
189 private static void getAuthHeader( TicketGrantingContext tgsContext ) throws Exception
190 {
191 KdcRequest request = tgsContext.getRequest();
192
193 PaData[] preAuthData = request.getPreAuthData();
194
195 if ( preAuthData == null || preAuthData.length < 1 )
196 {
197 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
198 }
199
200 byte[] undecodedAuthHeader = null;
201
202 for ( int ii = 0; ii < preAuthData.length; ii++ )
203 {
204 if ( preAuthData[ii].getPaDataType() == PaDataType.PA_TGS_REQ )
205 {
206 undecodedAuthHeader = preAuthData[ii].getPaDataValue();
207 }
208 }
209
210 if ( undecodedAuthHeader == null )
211 {
212 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
213 }
214
215 ApplicationRequestDecoder decoder = new ApplicationRequestDecoder();
216 ApplicationRequest authHeader = decoder.decode( undecodedAuthHeader );
217
218 Ticket tgt = authHeader.getTicket();
219
220 tgsContext.setAuthHeader( authHeader );
221 tgsContext.setTgt( tgt );
222 }
223
224
225 public static void verifyTgt( TicketGrantingContext tgsContext ) throws KerberosException
226 {
227 KdcServer config = tgsContext.getConfig();
228 Ticket tgt = tgsContext.getTgt();
229
230 // Check primary realm.
231 if ( !tgt.getRealm().equals( config.getPrimaryRealm() ) )
232 {
233 throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
234 }
235
236 String tgtServerName = tgt.getServerPrincipal().getName();
237 String requestServerName = tgsContext.getRequest().getServerPrincipal().getName();
238
239 /*
240 * if (tgt.sname is not a TGT for local realm and is not req.sname)
241 * then error_out(KRB_AP_ERR_NOT_US);
242 */
243 if ( !tgtServerName.equals( config.getServicePrincipal().getName() )
244 && !tgtServerName.equals( requestServerName ) )
245 {
246 throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US );
247 }
248 }
249
250
251 private static void getTicketPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
252 {
253 KerberosPrincipal principal = tgsContext.getTgt().getServerPrincipal();
254 PrincipalStore store = tgsContext.getStore();
255
256 PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
257 tgsContext.setTicketPrincipalEntry( entry );
258 }
259
260
261 private static void verifyTgtAuthHeader( TicketGrantingContext tgsContext ) throws KerberosException
262 {
263 ApplicationRequest authHeader = tgsContext.getAuthHeader();
264 Ticket tgt = tgsContext.getTgt();
265
266 boolean isValidate = tgsContext.getRequest().getKdcOptions().get( KdcOptions.VALIDATE );
267
268 EncryptionType encryptionType = tgt.getEncPart().getEType();
269 EncryptionKey serverKey = tgsContext.getTicketPrincipalEntry().getKeyMap().get( encryptionType );
270
271 long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
272 ReplayCache replayCache = tgsContext.getReplayCache();
273 boolean emptyAddressesAllowed = tgsContext.getConfig().isEmptyAddressesAllowed();
274 InetAddress clientAddress = tgsContext.getClientAddress();
275 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
276
277 Authenticator authenticator = KerberosUtils.verifyAuthHeader( authHeader, tgt, serverKey, clockSkew, replayCache,
278 emptyAddressesAllowed, clientAddress, cipherTextHandler, KeyUsage.NUMBER7, isValidate );
279
280 tgsContext.setAuthenticator( authenticator );
281 }
282
283
284 private static void verifyBodyChecksum( TicketGrantingContext tgsContext ) throws KerberosException
285 {
286 KdcServer config = tgsContext.getConfig();
287
288 if ( config.isBodyChecksumVerified() )
289 {
290 byte[] bodyBytes = tgsContext.getRequest().getBodyBytes();
291 Checksum authenticatorChecksum = tgsContext.getAuthenticator().getChecksum();
292
293 if ( authenticatorChecksum == null || authenticatorChecksum.getChecksumType() == null
294 || authenticatorChecksum.getChecksumValue() == null || bodyBytes == null )
295 {
296 throw new KerberosException( ErrorType.KRB_AP_ERR_INAPP_CKSUM );
297 }
298
299 LOG.debug( "Verifying body checksum type '{}'.", authenticatorChecksum.getChecksumType() );
300
301 checksumHandler.verifyChecksum( authenticatorChecksum, bodyBytes, null, KeyUsage.NUMBER8 );
302 }
303 }
304
305
306 public static void getRequestPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException
307 {
308 KerberosPrincipal principal = tgsContext.getRequest().getServerPrincipal();
309 PrincipalStore store = tgsContext.getStore();
310
311 PrincipalStoreEntry entry = KerberosUtils.getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN );
312 tgsContext.setRequestPrincipalEntry( entry );
313 }
314
315
316 private static void generateTicket( TicketGrantingContext tgsContext ) throws KerberosException
317 {
318 KdcRequest request = tgsContext.getRequest();
319 Ticket tgt = tgsContext.getTgt();
320 Authenticator authenticator = tgsContext.getAuthenticator();
321 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
322 KerberosPrincipal ticketPrincipal = request.getServerPrincipal();
323
324 EncryptionType encryptionType = tgsContext.getEncryptionType();
325 EncryptionKey serverKey = tgsContext.getRequestPrincipalEntry().getKeyMap().get( encryptionType );
326
327 KdcServer config = tgsContext.getConfig();
328
329 EncTicketPartModifier newTicketBody = new EncTicketPartModifier();
330
331 newTicketBody.setClientAddresses( tgt.getEncTicketPart().getClientAddresses() );
332
333 processFlags( config, request, tgt, newTicketBody );
334
335 EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( tgsContext.getEncryptionType() );
336 newTicketBody.setSessionKey( sessionKey );
337
338 newTicketBody.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );
339
340 if ( request.getEncAuthorizationData() != null )
341 {
342 AuthorizationData authData = ( AuthorizationData ) cipherTextHandler.unseal( AuthorizationData.class,
343 authenticator.getSubSessionKey(), request.getEncAuthorizationData(), KeyUsage.NUMBER4 );
344 authData.add( tgt.getEncTicketPart().getAuthorizationData() );
345 newTicketBody.setAuthorizationData( authData );
346 }
347
348 processTransited( newTicketBody, tgt );
349
350 processTimes( config, request, newTicketBody, tgt );
351
352 EncTicketPart ticketPart = newTicketBody.getEncTicketPart();
353
354 if ( request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) )
355 {
356 /*
357 * if (server not specified) then
358 * server = req.second_ticket.client;
359 * endif
360 *
361 * if ((req.second_ticket is not a TGT) or
362 * (req.second_ticket.client != server)) then
363 * error_out(KDC_ERR_POLICY);
364 * endif
365 *
366 * new_tkt.enc-part := encrypt OCTET STRING using etype_for_key(second-ticket.key), second-ticket.key;
367 */
368 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
369 }
370 else
371 {
372 EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 );
373
374 Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
375 newTicket.setEncTicketPart( ticketPart );
376
377 tgsContext.setNewTicket( newTicket );
378 }
379 }
380
381
382 private static void buildReply( TicketGrantingContext tgsContext ) throws KerberosException
383 {
384 KdcRequest request = tgsContext.getRequest();
385 Ticket tgt = tgsContext.getTgt();
386 Ticket newTicket = tgsContext.getNewTicket();
387
388 TicketGrantReply reply = new TicketGrantReply();
389 reply.setClientPrincipal( tgt.getEncTicketPart().getClientPrincipal() );
390 reply.setTicket( newTicket );
391 reply.setKey( newTicket.getEncTicketPart().getSessionKey() );
392 reply.setNonce( request.getNonce() );
393 // TODO - resp.last-req := fetch_last_request_info(client); requires store
394 reply.setLastRequest( new LastRequest() );
395 reply.setFlags( newTicket.getEncTicketPart().getFlags() );
396 reply.setClientAddresses( newTicket.getEncTicketPart().getClientAddresses() );
397 reply.setAuthTime( newTicket.getEncTicketPart().getAuthTime() );
398 reply.setStartTime( newTicket.getEncTicketPart().getStartTime() );
399 reply.setEndTime( newTicket.getEncTicketPart().getEndTime() );
400 reply.setServerPrincipal( newTicket.getServerPrincipal() );
401
402 if ( newTicket.getEncTicketPart().getFlags().isRenewable() )
403 {
404 reply.setRenewTill( newTicket.getEncTicketPart().getRenewTill() );
405 }
406
407 tgsContext.setReply( reply );
408 }
409
410
411 private static void sealReply( TicketGrantingContext tgsContext ) throws KerberosException
412 {
413 TicketGrantReply reply = ( TicketGrantReply ) tgsContext.getReply();
414 Ticket tgt = tgsContext.getTgt();
415 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler();
416 Authenticator authenticator = tgsContext.getAuthenticator();
417
418 EncryptedData encryptedData;
419
420 if ( authenticator.getSubSessionKey() != null )
421 {
422 encryptedData = cipherTextHandler.seal( authenticator.getSubSessionKey(), reply, KeyUsage.NUMBER9 );
423 }
424 else
425 {
426 encryptedData = cipherTextHandler.seal( tgt.getEncTicketPart().getSessionKey(), reply, KeyUsage.NUMBER8 );
427 }
428
429 reply.setEncPart( encryptedData );
430 }
431
432
433
434 private static void monitorContext( TicketGrantingContext tgsContext )
435 {
436 try
437 {
438 Ticket tgt = tgsContext.getTgt();
439 long clockSkew = tgsContext.getConfig().getAllowableClockSkew();
440 ChecksumType checksumType = tgsContext.getAuthenticator().getChecksum().getChecksumType();
441 InetAddress clientAddress = tgsContext.getClientAddress();
442 HostAddresses clientAddresses = tgt.getEncTicketPart().getClientAddresses();
443
444 boolean caddrContainsSender = false;
445 if ( tgt.getEncTicketPart().getClientAddresses() != null )
446 {
447 caddrContainsSender = tgt.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) );
448 }
449
450 StringBuffer sb = new StringBuffer();
451
452 sb.append( "Monitoring " + SERVICE_NAME + " context:" );
453
454 sb.append( "\n\t" + "clockSkew " + clockSkew );
455 sb.append( "\n\t" + "checksumType " + checksumType );
456 sb.append( "\n\t" + "clientAddress " + clientAddress );
457 sb.append( "\n\t" + "clientAddresses " + clientAddresses );
458 sb.append( "\n\t" + "caddr contains sender " + caddrContainsSender );
459
460 KerberosPrincipal requestServerPrincipal = tgsContext.getRequest().getServerPrincipal();
461 PrincipalStoreEntry requestPrincipal = tgsContext.getRequestPrincipalEntry();
462
463 sb.append( "\n\t" + "principal " + requestServerPrincipal );
464 sb.append( "\n\t" + "cn " + requestPrincipal.getCommonName() );
465 sb.append( "\n\t" + "realm " + requestPrincipal.getRealmName() );
466 sb.append( "\n\t" + "principal " + requestPrincipal.getPrincipal() );
467 sb.append( "\n\t" + "SAM type " + requestPrincipal.getSamType() );
468
469 KerberosPrincipal ticketServerPrincipal = tgsContext.getTgt().getServerPrincipal();
470 PrincipalStoreEntry ticketPrincipal = tgsContext.getTicketPrincipalEntry();
471
472 sb.append( "\n\t" + "principal " + ticketServerPrincipal );
473 sb.append( "\n\t" + "cn " + ticketPrincipal.getCommonName() );
474 sb.append( "\n\t" + "realm " + ticketPrincipal.getRealmName() );
475 sb.append( "\n\t" + "principal " + ticketPrincipal.getPrincipal() );
476 sb.append( "\n\t" + "SAM type " + ticketPrincipal.getSamType() );
477
478 EncryptionType encryptionType = tgsContext.getTgt().getEncPart().getEType();
479 int keyVersion = ticketPrincipal.getKeyMap().get( encryptionType ).getKeyVersion();
480 sb.append( "\n\t" + "Ticket key type " + encryptionType );
481 sb.append( "\n\t" + "Service key version " + keyVersion );
482
483 LOG.debug( sb.toString() );
484 }
485 catch ( Exception e )
486 {
487 // This is a monitor. No exceptions should bubble up.
488 LOG.error( I18n.err( I18n.ERR_154 ), e );
489 }
490 }
491
492
493 private static void monitorReply( KdcContext kdcContext )
494 {
495 Object reply = kdcContext.getReply();
496
497 if ( reply instanceof KdcReply )
498 {
499 KdcReply success = ( KdcReply ) reply;
500
501 try
502 {
503 StringBuffer sb = new StringBuffer();
504
505 sb.append( "Responding with " + SERVICE_NAME + " reply:" );
506 sb.append( "\n\t" + "messageType: " + success.getMessageType() );
507 sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() );
508 sb.append( "\n\t" + "nonce: " + success.getNonce() );
509 sb.append( "\n\t" + "clientPrincipal: " + success.getClientPrincipal() );
510 sb.append( "\n\t" + "client realm: " + success.getClientRealm() );
511 sb.append( "\n\t" + "serverPrincipal: " + success.getServerPrincipal() );
512 sb.append( "\n\t" + "server realm: " + success.getServerRealm() );
513 sb.append( "\n\t" + "auth time: " + success.getAuthTime() );
514 sb.append( "\n\t" + "start time: " + success.getStartTime() );
515 sb.append( "\n\t" + "end time: " + success.getEndTime() );
516 sb.append( "\n\t" + "renew-till time: " + success.getRenewTill() );
517 sb.append( "\n\t" + "hostAddresses: " + success.getClientAddresses() );
518
519 LOG.debug( sb.toString() );
520 }
521 catch ( Exception e )
522 {
523 // This is a monitor. No exceptions should bubble up.
524 LOG.error( I18n.err( I18n.ERR_155 ), e );
525 }
526 }
527 }
528
529
530
531 private static void processFlags( KdcServer config, KdcRequest request, Ticket tgt,
532 EncTicketPartModifier newTicketBody ) throws KerberosException
533 {
534 if ( tgt.getEncTicketPart().getFlags().isPreAuth() )
535 {
536 newTicketBody.setFlag( TicketFlag.PRE_AUTHENT );
537 }
538
539 if ( request.getOption( KdcOptions.FORWARDABLE ) )
540 {
541 if ( !config.isForwardableAllowed() )
542 {
543 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
544 }
545
546 if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
547 {
548 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
549 }
550
551 newTicketBody.setFlag( TicketFlag.FORWARDABLE );
552 }
553
554 if ( request.getOption( KdcOptions.FORWARDED ) )
555 {
556 if ( !config.isForwardableAllowed() )
557 {
558 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
559 }
560
561 if ( !tgt.getEncTicketPart().getFlags().isForwardable() )
562 {
563 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
564 }
565
566 if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
567 && request.getAddresses().getAddresses().length > 0 )
568 {
569 newTicketBody.setClientAddresses( request.getAddresses() );
570 }
571 else
572 {
573 if ( !config.isEmptyAddressesAllowed() )
574 {
575 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
576 }
577 }
578
579 newTicketBody.setFlag( TicketFlag.FORWARDED );
580 }
581
582 if ( tgt.getEncTicketPart().getFlags().isForwarded() )
583 {
584 newTicketBody.setFlag( TicketFlag.FORWARDED );
585 }
586
587 if ( request.getOption( KdcOptions.PROXIABLE ) )
588 {
589 if ( !config.isProxiableAllowed() )
590 {
591 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
592 }
593
594 if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
595 {
596 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
597 }
598
599 newTicketBody.setFlag( TicketFlag.PROXIABLE );
600 }
601
602 if ( request.getOption( KdcOptions.PROXY ) )
603 {
604 if ( !config.isProxiableAllowed() )
605 {
606 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
607 }
608
609 if ( !tgt.getEncTicketPart().getFlags().isProxiable() )
610 {
611 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
612 }
613
614 if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
615 && request.getAddresses().getAddresses().length > 0 )
616 {
617 newTicketBody.setClientAddresses( request.getAddresses() );
618 }
619 else
620 {
621 if ( !config.isEmptyAddressesAllowed() )
622 {
623 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
624 }
625 }
626
627 newTicketBody.setFlag( TicketFlag.PROXY );
628 }
629
630 if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) )
631 {
632 if ( !config.isPostdatedAllowed() )
633 {
634 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
635 }
636
637 if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
638 {
639 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
640 }
641
642 newTicketBody.setFlag( TicketFlag.MAY_POSTDATE );
643 }
644
645 /*
646 * "Otherwise, if the TGT has the MAY-POSTDATE flag set, then the resulting
647 * ticket will be postdated, and the requested starttime is checked against
648 * the policy of the local realm. If acceptable, the ticket's starttime is
649 * set as requested, and the INVALID flag is set. The postdated ticket MUST
650 * be validated before use by presenting it to the KDC after the starttime
651 * has been reached. However, in no case may the starttime, endtime, or
652 * renew-till time of a newly-issued postdated ticket extend beyond the
653 * renew-till time of the TGT."
654 */
655 if ( request.getOption( KdcOptions.POSTDATED ) )
656 {
657 if ( !config.isPostdatedAllowed() )
658 {
659 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
660 }
661
662 if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() )
663 {
664 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
665 }
666
667 newTicketBody.setFlag( TicketFlag.POSTDATED );
668 newTicketBody.setFlag( TicketFlag.INVALID );
669
670 newTicketBody.setStartTime( request.getFrom() );
671 }
672
673 if ( request.getOption( KdcOptions.VALIDATE ) )
674 {
675 if ( !config.isPostdatedAllowed() )
676 {
677 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
678 }
679
680 if ( !tgt.getEncTicketPart().getFlags().isInvalid() )
681 {
682 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
683 }
684
685 KerberosTime startTime = ( tgt.getEncTicketPart().getStartTime() != null ) ?
686 tgt.getEncTicketPart().getStartTime() :
687 tgt.getEncTicketPart().getAuthTime();
688
689 if ( startTime.greaterThan( new KerberosTime() ) )
690 {
691 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV );
692 }
693
694 echoTicket( newTicketBody, tgt );
695 newTicketBody.clearFlag( TicketFlag.INVALID );
696 }
697
698 if ( request.getOption( KdcOptions.RESERVED ) )
699 {
700 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
701 }
702 }
703
704
705 private static void processTimes( KdcServer config, KdcRequest request, EncTicketPartModifier newTicketBody,
706 Ticket tgt ) throws KerberosException
707 {
708 KerberosTime now = new KerberosTime();
709
710 newTicketBody.setAuthTime( tgt.getEncTicketPart().getAuthTime() );
711
712 KerberosTime startTime = request.getFrom();
713
714 /*
715 * "If the requested starttime is absent, indicates a time in the past,
716 * or is within the window of acceptable clock skew for the KDC and the
717 * POSTDATE option has not been specified, then the starttime of the
718 * ticket is set to the authentication server's current time."
719 */
720 if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
721 && !request.getOption( KdcOptions.POSTDATED ) )
722 {
723 startTime = now;
724 }
725
726 /*
727 * "If it indicates a time in the future beyond the acceptable clock skew,
728 * but the POSTDATED option has not been specified or the MAY-POSTDATE flag
729 * is not set in the TGT, then the error KDC_ERR_CANNOT_POSTDATE is
730 * returned."
731 */
732 if ( startTime != null && startTime.greaterThan( now )
733 && !startTime.isInClockSkew( config.getAllowableClockSkew() )
734 && ( !request.getOption( KdcOptions.POSTDATED ) || !tgt.getEncTicketPart().getFlags().isMayPosdate() ) )
735 {
736 throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
737 }
738
739 KerberosTime renewalTime = null;
740 KerberosTime kerberosEndTime = null;
741
742 if ( request.getOption( KdcOptions.RENEW ) )
743 {
744 if ( !config.isRenewableAllowed() )
745 {
746 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
747 }
748
749 if ( !tgt.getEncTicketPart().getFlags().isRenewable() )
750 {
751 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
752 }
753
754 if ( tgt.getEncTicketPart().getRenewTill().lessThan( now ) )
755 {
756 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED );
757 }
758
759 echoTicket( newTicketBody, tgt );
760
761 newTicketBody.setStartTime( now );
762
763 KerberosTime tgtStartTime = ( tgt.getEncTicketPart().getStartTime() != null ) ?
764 tgt.getEncTicketPart().getStartTime() :
765 tgt.getEncTicketPart().getAuthTime();
766
767 long oldLife = tgt.getEncTicketPart().getEndTime().getTime() - tgtStartTime.getTime();
768
769 kerberosEndTime = new KerberosTime( Math.min( tgt.getEncTicketPart().getRenewTill().getTime(), now.getTime() + oldLife ) );
770 newTicketBody.setEndTime( kerberosEndTime );
771 }
772 else
773 {
774 if ( newTicketBody.getEncTicketPart().getStartTime() == null )
775 {
776 newTicketBody.setStartTime( now );
777 }
778
779 KerberosTime till;
780 if ( request.getTill().isZero() )
781 {
782 till = KerberosTime.INFINITY;
783 }
784 else
785 {
786 till = request.getTill();
787 }
788
789 /*
790 * The end time is the minimum of (a) the requested till time or (b)
791 * the start time plus maximum lifetime as configured in policy or (c)
792 * the end time of the TGT.
793 */
794 List<KerberosTime> minimizer = new ArrayList<KerberosTime>();
795 minimizer.add( till );
796 minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumTicketLifetime() ) );
797 minimizer.add( tgt.getEncTicketPart().getEndTime() );
798 kerberosEndTime = Collections.min( minimizer );
799
800 newTicketBody.setEndTime( kerberosEndTime );
801
802 if ( request.getOption( KdcOptions.RENEWABLE_OK ) && kerberosEndTime.lessThan( request.getTill() )
803 && tgt.getEncTicketPart().getFlags().isRenewable() )
804 {
805 if ( !config.isRenewableAllowed() )
806 {
807 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
808 }
809
810 // We set the RENEWABLE option for later processing.
811 request.setOption( KdcOptions.RENEWABLE );
812 long rtime = Math.min( request.getTill().getTime(), tgt.getEncTicketPart().getRenewTill().getTime() );
813 renewalTime = new KerberosTime( rtime );
814 }
815 }
816
817 if ( renewalTime == null )
818 {
819 renewalTime = request.getRtime();
820 }
821
822 KerberosTime rtime;
823 if ( renewalTime != null && renewalTime.isZero() )
824 {
825 rtime = KerberosTime.INFINITY;
826 }
827 else
828 {
829 rtime = renewalTime;
830 }
831
832 if ( request.getOption( KdcOptions.RENEWABLE ) && tgt.getEncTicketPart().getFlags().isRenewable() )
833 {
834 if ( !config.isRenewableAllowed() )
835 {
836 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
837 }
838
839 newTicketBody.setFlag( TicketFlag.RENEWABLE );
840
841 /*
842 * The renew-till time is the minimum of (a) the requested renew-till
843 * time or (b) the start time plus maximum renewable lifetime as
844 * configured in policy or (c) the renew-till time of the TGT.
845 */
846 List<KerberosTime> minimizer = new ArrayList<KerberosTime>();
847
848 /*
849 * 'rtime' KerberosTime is OPTIONAL
850 */
851 if ( rtime != null )
852 {
853 minimizer.add( rtime );
854 }
855
856 minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumRenewableLifetime() ) );
857 minimizer.add( tgt.getEncTicketPart().getRenewTill() );
858 newTicketBody.setRenewTill( Collections.min( minimizer ) );
859 }
860
861 /*
862 * "If the requested expiration time minus the starttime (as determined
863 * above) is less than a site-determined minimum lifetime, an error
864 * message with code KDC_ERR_NEVER_VALID is returned."
865 */
866 if ( kerberosEndTime.lessThan( startTime ) )
867 {
868 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
869 }
870
871 long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
872 if ( ticketLifeTime < config.getAllowableClockSkew() )
873 {
874 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
875 }
876 }
877
878
879 /*
880 * if (realm_tgt_is_for(tgt) := tgt.realm) then
881 * // tgt issued by local realm
882 * new_tkt.transited := tgt.transited;
883 * else
884 * // was issued for this realm by some other realm
885 * if (tgt.transited.tr-type not supported) then
886 * error_out(KDC_ERR_TRTYPE_NOSUPP);
887 * endif
888 *
889 * new_tkt.transited := compress_transited(tgt.transited + tgt.realm)
890 * endif
891 */
892 private static void processTransited( EncTicketPartModifier newTicketBody, Ticket tgt )
893 {
894 // TODO - currently no transited support other than local
895 newTicketBody.setTransitedEncoding( tgt.getEncTicketPart().getTransitedEncoding() );
896 }
897
898
899 private static void echoTicket( EncTicketPartModifier newTicketBody, Ticket tgt )
900 {
901 EncTicketPart encTicketpart = tgt.getEncTicketPart();
902 newTicketBody.setAuthorizationData( encTicketpart.getAuthorizationData() );
903 newTicketBody.setAuthTime( encTicketpart.getAuthTime() );
904 newTicketBody.setClientAddresses( encTicketpart.getClientAddresses() );
905 newTicketBody.setClientPrincipal( encTicketpart.getClientPrincipal() );
906 newTicketBody.setEndTime( encTicketpart.getEndTime() );
907 newTicketBody.setFlags( encTicketpart.getFlags() );
908 newTicketBody.setRenewTill( encTicketpart.getRenewTill() );
909 newTicketBody.setSessionKey( encTicketpart.getSessionKey() );
910 newTicketBody.setTransitedEncoding( encTicketpart.getTransitedEncoding() );
911 }
912 }