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 }