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 */ 020package org.apache.directory.server.kerberos.kdc.ticketgrant; 021 022 023import java.net.InetAddress; 024import java.nio.ByteBuffer; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.List; 028import java.util.Set; 029 030import javax.security.auth.kerberos.KerberosPrincipal; 031 032import org.apache.directory.api.asn1.EncoderException; 033import org.apache.directory.api.ldap.model.constants.Loggers; 034import org.apache.directory.server.i18n.I18n; 035import org.apache.directory.server.kerberos.KerberosConfig; 036import org.apache.directory.server.kerberos.kdc.KdcContext; 037import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumHandler; 038import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler; 039import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage; 040import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory; 041import org.apache.directory.server.kerberos.shared.replay.ReplayCache; 042import org.apache.directory.server.kerberos.shared.store.PrincipalStore; 043import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry; 044import org.apache.directory.shared.kerberos.KerberosConstants; 045import org.apache.directory.shared.kerberos.KerberosTime; 046import org.apache.directory.shared.kerberos.KerberosUtils; 047import org.apache.directory.shared.kerberos.codec.KerberosDecoder; 048import org.apache.directory.shared.kerberos.codec.options.KdcOptions; 049import org.apache.directory.shared.kerberos.codec.types.EncryptionType; 050import org.apache.directory.shared.kerberos.codec.types.LastReqType; 051import org.apache.directory.shared.kerberos.codec.types.PaDataType; 052import org.apache.directory.shared.kerberos.components.AuthorizationData; 053import org.apache.directory.shared.kerberos.components.Checksum; 054import org.apache.directory.shared.kerberos.components.EncKdcRepPart; 055import org.apache.directory.shared.kerberos.components.EncTicketPart; 056import org.apache.directory.shared.kerberos.components.EncryptedData; 057import org.apache.directory.shared.kerberos.components.EncryptionKey; 058import org.apache.directory.shared.kerberos.components.HostAddress; 059import org.apache.directory.shared.kerberos.components.HostAddresses; 060import org.apache.directory.shared.kerberos.components.KdcReq; 061import org.apache.directory.shared.kerberos.components.KdcReqBody; 062import org.apache.directory.shared.kerberos.components.LastReq; 063import org.apache.directory.shared.kerberos.components.LastReqEntry; 064import org.apache.directory.shared.kerberos.components.PaData; 065import org.apache.directory.shared.kerberos.components.PrincipalName; 066import org.apache.directory.shared.kerberos.crypto.checksum.ChecksumType; 067import org.apache.directory.shared.kerberos.exceptions.ErrorType; 068import org.apache.directory.shared.kerberos.exceptions.InvalidTicketException; 069import org.apache.directory.shared.kerberos.exceptions.KerberosException; 070import org.apache.directory.shared.kerberos.flags.TicketFlag; 071import org.apache.directory.shared.kerberos.messages.ApReq; 072import org.apache.directory.shared.kerberos.messages.Authenticator; 073import org.apache.directory.shared.kerberos.messages.EncTgsRepPart; 074import org.apache.directory.shared.kerberos.messages.TgsRep; 075import org.apache.directory.shared.kerberos.messages.Ticket; 076import org.slf4j.Logger; 077import org.slf4j.LoggerFactory; 078 079 080/** 081 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 082 */ 083public final class TicketGrantingService 084{ 085 086 /** the log for this class */ 087 private static final Logger LOG_KRB = LoggerFactory.getLogger( Loggers.KERBEROS_LOG.getName() ); 088 089 private static final CipherTextHandler CIPHER_TEXT_HANDLER = new CipherTextHandler(); 090 091 private static final String SERVICE_NAME = "Ticket-Granting Service (TGS)"; 092 093 private static final ChecksumHandler CHRECKSUM_HANDLER = new ChecksumHandler(); 094 095 096 private TicketGrantingService() 097 { 098 } 099 100 101 public static void execute( TicketGrantingContext tgsContext ) throws Exception 102 { 103 if ( LOG_KRB.isDebugEnabled() ) 104 { 105 monitorRequest( tgsContext ); 106 } 107 108 configureTicketGranting( tgsContext ); 109 selectEncryptionType( tgsContext ); 110 getAuthHeader( tgsContext ); 111 // commenting to allow cross-realm auth 112 //verifyTgt( tgsContext ); 113 getTicketPrincipalEntry( tgsContext ); 114 verifyTgtAuthHeader( tgsContext ); 115 verifyBodyChecksum( tgsContext ); 116 getRequestPrincipalEntry( tgsContext ); 117 generateTicket( tgsContext ); 118 buildReply( tgsContext ); 119 } 120 121 122 private static void configureTicketGranting( TicketGrantingContext tgsContext ) throws KerberosException 123 { 124 tgsContext.setCipherTextHandler( CIPHER_TEXT_HANDLER ); 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 KdcReq 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.getKdcReqBody().getNonce() ); 148 sb.append( "\n\t" + "kdcOptions: " + request.getKdcReqBody().getKdcOptions() ); 149 sb.append( "\n\t" + "clientPrincipal: " + request.getKdcReqBody().getCName() ); 150 sb.append( "\n\t" + "serverPrincipal: " + request.getKdcReqBody().getSName() ); 151 sb.append( "\n\t" + "encryptionType: " 152 + KerberosUtils.getEncryptionTypesString( request.getKdcReqBody().getEType() ) ); 153 sb.append( "\n\t" + "realm: " + request.getKdcReqBody().getRealm() ); 154 sb.append( "\n\t" + "from time: " + request.getKdcReqBody().getFrom() ); 155 sb.append( "\n\t" + "till time: " + request.getKdcReqBody().getTill() ); 156 sb.append( "\n\t" + "renew-till time: " + request.getKdcReqBody().getRTime() ); 157 sb.append( "\n\t" + "hostAddresses: " + request.getKdcReqBody().getAddresses() ); 158 159 LOG_KRB.debug( sb.toString() ); 160 } 161 catch ( Exception e ) 162 { 163 // This is a monitor. No exceptions should bubble up. 164 LOG_KRB.error( I18n.err( I18n.ERR_153 ), e ); 165 } 166 } 167 168 169 private static void selectEncryptionType( TicketGrantingContext tgsContext ) throws Exception 170 { 171 KdcContext kdcContext = tgsContext; 172 KerberosConfig config = kdcContext.getConfig(); 173 174 Set<EncryptionType> requestedTypes = kdcContext.getRequest().getKdcReqBody().getEType(); 175 176 EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() ); 177 178 LOG_KRB.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 KdcReq request = tgsContext.getRequest(); 192 193 if ( ( request.getPaData() == null ) || ( request.getPaData().size() < 1 ) ) 194 { 195 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP ); 196 } 197 198 byte[] undecodedAuthHeader = null; 199 200 for ( PaData paData : request.getPaData() ) 201 { 202 if ( paData.getPaDataType() == PaDataType.PA_TGS_REQ ) 203 { 204 undecodedAuthHeader = paData.getPaDataValue(); 205 } 206 } 207 208 if ( undecodedAuthHeader == null ) 209 { 210 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP ); 211 } 212 213 ApReq authHeader = KerberosDecoder.decodeApReq( undecodedAuthHeader ); 214 215 Ticket tgt = authHeader.getTicket(); 216 217 tgsContext.setAuthHeader( authHeader ); 218 tgsContext.setTgt( tgt ); 219 } 220 221 222 public static void verifyTgt( TicketGrantingContext tgsContext ) throws KerberosException 223 { 224 KerberosConfig config = tgsContext.getConfig(); 225 Ticket tgt = tgsContext.getTgt(); 226 227 // Check primary realm. 228 if ( !tgt.getRealm().equals( config.getPrimaryRealm() ) ) 229 { 230 throw new KerberosException( ErrorType.KRB_AP_ERR_NOT_US ); 231 } 232 233 String tgtServerName = KerberosUtils.getKerberosPrincipal( tgt.getSName(), tgt.getRealm() ).getName(); 234 String requestServerName = KerberosUtils.getKerberosPrincipal( 235 tgsContext.getRequest().getKdcReqBody().getSName(), tgsContext.getRequest().getKdcReqBody().getRealm() ) 236 .getName(); 237 238 /* 239 * if (tgt.sname is not a TGT for local realm and is not req.sname) 240 * then error_out(KRB_AP_ERR_NOT_US); 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 PrincipalName principal = tgsContext.getTgt().getSName(); 253 PrincipalStore store = tgsContext.getStore(); 254 255 KerberosPrincipal principalWithRealm = KerberosUtils.getKerberosPrincipal( principal, tgsContext.getTgt() 256 .getRealm() ); 257 PrincipalStoreEntry entry = getEntry( principalWithRealm, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ); 258 tgsContext.setTicketPrincipalEntry( entry ); 259 } 260 261 262 private static void verifyTgtAuthHeader( TicketGrantingContext tgsContext ) throws KerberosException 263 { 264 ApReq authHeader = tgsContext.getAuthHeader(); 265 Ticket tgt = tgsContext.getTgt(); 266 267 KdcOptions kdcOptions = tgsContext.getRequest().getKdcReqBody().getKdcOptions(); 268 boolean isValidate = kdcOptions.get( KdcOptions.VALIDATE ); 269 270 EncryptionType encryptionType = tgt.getEncPart().getEType(); 271 EncryptionKey serverKey = tgsContext.getTicketPrincipalEntry().getKeyMap().get( encryptionType ); 272 273 long clockSkew = tgsContext.getConfig().getAllowableClockSkew(); 274 ReplayCache replayCache = tgsContext.getReplayCache(); 275 boolean emptyAddressesAllowed = tgsContext.getConfig().isEmptyAddressesAllowed(); 276 InetAddress clientAddress = tgsContext.getClientAddress(); 277 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler(); 278 279 Authenticator authenticator = KerberosUtils.verifyAuthHeader( authHeader, tgt, serverKey, clockSkew, 280 replayCache, 281 emptyAddressesAllowed, clientAddress, cipherTextHandler, 282 KeyUsage.TGS_REQ_PA_TGS_REQ_PADATA_AP_REQ_TGS_SESS_KEY, isValidate ); 283 284 tgsContext.setAuthenticator( authenticator ); 285 } 286 287 288 /** 289 * RFC4120 290 * <li>Section 3.3.2. Receipt of KRB_TGS_REQ Message -> 2nd paragraph 291 * <li>Section 5.5.1. KRB_AP_REQ Definition -> Authenticator -> cksum 292 */ 293 private static void verifyBodyChecksum( TicketGrantingContext tgsContext ) throws KerberosException 294 { 295 KerberosConfig config = tgsContext.getConfig(); 296 297 if ( config.isBodyChecksumVerified() ) 298 { 299 KdcReqBody body = tgsContext.getRequest().getKdcReqBody(); 300 // FIXME how this byte[] is computed?? 301 // is it full ASN.1 encoded bytes OR just the bytes of all the values alone? 302 // for now am using the ASN.1 encoded value 303 ByteBuffer buf = ByteBuffer.allocate( body.computeLength() ); 304 try 305 { 306 body.encode( buf ); 307 } 308 catch ( EncoderException e ) 309 { 310 throw new KerberosException( ErrorType.KRB_AP_ERR_INAPP_CKSUM ); 311 } 312 313 byte[] bodyBytes = buf.array(); 314 Checksum authenticatorChecksum = tgsContext.getAuthenticator().getCksum(); 315 316 if ( authenticatorChecksum != null ) 317 { 318 // we need the session key 319 Ticket tgt = tgsContext.getTgt(); 320 EncTicketPart encTicketPart = tgt.getEncTicketPart(); 321 EncryptionKey sessionKey = encTicketPart.getKey(); 322 323 if ( authenticatorChecksum == null || authenticatorChecksum.getChecksumType() == null 324 || authenticatorChecksum.getChecksumValue() == null || bodyBytes == null ) 325 { 326 throw new KerberosException( ErrorType.KRB_AP_ERR_INAPP_CKSUM ); 327 } 328 329 LOG_KRB.debug( "Verifying body checksum type '{}'.", authenticatorChecksum.getChecksumType() ); 330 331 CHRECKSUM_HANDLER.verifyChecksum( authenticatorChecksum, bodyBytes, sessionKey.getKeyValue(), 332 KeyUsage.TGS_REQ_PA_TGS_REQ_PADATA_AP_REQ_AUTHNT_CKSUM_TGS_SESS_KEY ); 333 } 334 } 335 } 336 337 338 public static void getRequestPrincipalEntry( TicketGrantingContext tgsContext ) throws KerberosException 339 { 340 KerberosPrincipal principal = KerberosUtils.getKerberosPrincipal( 341 tgsContext.getRequest().getKdcReqBody().getSName(), tgsContext.getRequest().getKdcReqBody().getRealm() ); 342 PrincipalStore store = tgsContext.getStore(); 343 344 PrincipalStoreEntry entry = getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ); 345 tgsContext.setRequestPrincipalEntry( entry ); 346 } 347 348 349 private static void generateTicket( TicketGrantingContext tgsContext ) throws KerberosException, 350 InvalidTicketException 351 { 352 KdcReq request = tgsContext.getRequest(); 353 Ticket tgt = tgsContext.getTgt(); 354 Authenticator authenticator = tgsContext.getAuthenticator(); 355 CipherTextHandler cipherTextHandler = tgsContext.getCipherTextHandler(); 356 KerberosPrincipal ticketPrincipal = KerberosUtils.getKerberosPrincipal( 357 request.getKdcReqBody().getSName(), request.getKdcReqBody().getRealm() ); 358 359 EncryptionType encryptionType = tgsContext.getEncryptionType(); 360 EncryptionKey serverKey = tgsContext.getRequestPrincipalEntry().getKeyMap().get( encryptionType ); 361 362 KerberosConfig config = tgsContext.getConfig(); 363 364 tgsContext.getRequest().getKdcReqBody().getAdditionalTickets(); 365 366 EncTicketPart newTicketPart = new EncTicketPart(); 367 368 newTicketPart.setClientAddresses( tgt.getEncTicketPart().getClientAddresses() ); 369 370 processFlags( config, request, tgt, newTicketPart ); 371 372 EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( tgsContext.getEncryptionType() ); 373 newTicketPart.setKey( sessionKey ); 374 375 newTicketPart.setCName( tgt.getEncTicketPart().getCName() ); 376 newTicketPart.setCRealm( tgt.getEncTicketPart().getCRealm() ); 377 378 if ( request.getKdcReqBody().getEncAuthorizationData() != null ) 379 { 380 byte[] authorizationData = cipherTextHandler.decrypt( authenticator.getSubKey(), request.getKdcReqBody() 381 .getEncAuthorizationData(), KeyUsage.TGS_REQ_KDC_REQ_BODY_AUTHZ_DATA_ENC_WITH_TGS_SESS_KEY ); 382 AuthorizationData authData = KerberosDecoder.decodeAuthorizationData( authorizationData ); 383 authData.addEntry( tgt.getEncTicketPart().getAuthorizationData().getCurrentAD() ); 384 newTicketPart.setAuthorizationData( authData ); 385 } 386 387 processTransited( newTicketPart, tgt ); 388 389 processTimes( config, request, newTicketPart, tgt ); 390 391 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.ENC_TKT_IN_SKEY ) ) 392 { 393 Ticket[] additionalTkts = tgsContext.getRequest().getKdcReqBody().getAdditionalTickets(); 394 395 if ( additionalTkts == null || additionalTkts.length == 0 ) 396 { 397 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 398 } 399 400 Ticket additionalTgt = additionalTkts[0]; 401 // reject if it is not a TGT 402 if ( !additionalTgt.getEncTicketPart().getFlags().isInitial() ) 403 { 404 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 405 } 406 407 serverKey = additionalTgt.getEncTicketPart().getKey(); 408 /* 409 * if (server not specified) then 410 * server = req.second_ticket.client; 411 * endif 412 * 413 * if ((req.second_ticket is not a TGT) or 414 * (req.second_ticket.client != server)) then 415 * error_out(KDC_ERR_POLICY); 416 * endif 417 * 418 * new_tkt.enc-part := encrypt OCTET STRING using etype_for_key(second-ticket.key), second-ticket.key; 419 */ 420 //throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 421 } 422 423 EncryptedData encryptedData = cipherTextHandler.seal( serverKey, newTicketPart, 424 KeyUsage.AS_OR_TGS_REP_TICKET_WITH_SRVKEY ); 425 426 Ticket newTicket = new Ticket( request.getKdcReqBody().getSName(), encryptedData ); 427 newTicket.setEncTicketPart( newTicketPart ); 428 newTicket.setRealm( request.getKdcReqBody().getRealm() ); 429 430 tgsContext.setNewTicket( newTicket ); 431 } 432 433 434 private static void buildReply( TicketGrantingContext tgsContext ) throws KerberosException 435 { 436 KdcReq request = tgsContext.getRequest(); 437 Ticket tgt = tgsContext.getTgt(); 438 Ticket newTicket = tgsContext.getNewTicket(); 439 440 TgsRep reply = new TgsRep(); 441 442 reply.setCName( tgt.getEncTicketPart().getCName() ); 443 reply.setCRealm( tgt.getEncTicketPart().getCRealm() ); 444 reply.setTicket( newTicket ); 445 446 EncKdcRepPart encKdcRepPart = new EncKdcRepPart(); 447 448 encKdcRepPart.setKey( newTicket.getEncTicketPart().getKey() ); 449 encKdcRepPart.setNonce( request.getKdcReqBody().getNonce() ); 450 // TODO - resp.last-req := fetch_last_request_info(client); requires store 451 // FIXME temporary fix, IMO we should create some new ATs to store this info in DIT 452 LastReq lastReq = new LastReq(); 453 lastReq.addEntry( new LastReqEntry( LastReqType.TIME_OF_INITIAL_REQ, new KerberosTime() ) ); 454 encKdcRepPart.setLastReq( lastReq ); 455 456 encKdcRepPart.setFlags( newTicket.getEncTicketPart().getFlags() ); 457 encKdcRepPart.setClientAddresses( newTicket.getEncTicketPart().getClientAddresses() ); 458 encKdcRepPart.setAuthTime( newTicket.getEncTicketPart().getAuthTime() ); 459 encKdcRepPart.setStartTime( newTicket.getEncTicketPart().getStartTime() ); 460 encKdcRepPart.setEndTime( newTicket.getEncTicketPart().getEndTime() ); 461 encKdcRepPart.setSName( newTicket.getSName() ); 462 encKdcRepPart.setSRealm( newTicket.getRealm() ); 463 464 if ( newTicket.getEncTicketPart().getFlags().isRenewable() ) 465 { 466 encKdcRepPart.setRenewTill( newTicket.getEncTicketPart().getRenewTill() ); 467 } 468 469 if ( LOG_KRB.isDebugEnabled() ) 470 { 471 monitorContext( tgsContext ); 472 monitorReply( reply, encKdcRepPart ); 473 } 474 475 EncTgsRepPart encTgsRepPart = new EncTgsRepPart(); 476 encTgsRepPart.setEncKdcRepPart( encKdcRepPart ); 477 478 Authenticator authenticator = tgsContext.getAuthenticator(); 479 480 EncryptedData encryptedData; 481 482 if ( authenticator.getSubKey() != null ) 483 { 484 encryptedData = CIPHER_TEXT_HANDLER.seal( authenticator.getSubKey(), encTgsRepPart, 485 KeyUsage.TGS_REP_ENC_PART_TGS_AUTHNT_SUB_KEY ); 486 } 487 else 488 { 489 encryptedData = CIPHER_TEXT_HANDLER.seal( tgt.getEncTicketPart().getKey(), encTgsRepPart, 490 KeyUsage.TGS_REP_ENC_PART_TGS_SESS_KEY ); 491 } 492 493 reply.setEncPart( encryptedData ); 494 reply.setEncKdcRepPart( encKdcRepPart ); 495 496 tgsContext.setReply( reply ); 497 } 498 499 500 private static void monitorContext( TicketGrantingContext tgsContext ) 501 { 502 try 503 { 504 Ticket tgt = tgsContext.getTgt(); 505 long clockSkew = tgsContext.getConfig().getAllowableClockSkew(); 506 507 Checksum cksum = tgsContext.getAuthenticator().getCksum(); 508 509 ChecksumType checksumType = null; 510 if ( cksum != null ) 511 { 512 checksumType = cksum.getChecksumType(); 513 } 514 515 InetAddress clientAddress = tgsContext.getClientAddress(); 516 HostAddresses clientAddresses = tgt.getEncTicketPart().getClientAddresses(); 517 518 boolean caddrContainsSender = false; 519 if ( tgt.getEncTicketPart().getClientAddresses() != null ) 520 { 521 caddrContainsSender = tgt.getEncTicketPart().getClientAddresses() 522 .contains( new HostAddress( clientAddress ) ); 523 } 524 525 StringBuffer sb = new StringBuffer(); 526 527 sb.append( "Monitoring " + SERVICE_NAME + " context:" ); 528 529 sb.append( "\n\t" + "clockSkew " + clockSkew ); 530 sb.append( "\n\t" + "checksumType " + checksumType ); 531 sb.append( "\n\t" + "clientAddress " + clientAddress ); 532 sb.append( "\n\t" + "clientAddresses " + clientAddresses ); 533 sb.append( "\n\t" + "caddr contains sender " + caddrContainsSender ); 534 535 PrincipalName requestServerPrincipal = tgsContext.getRequest().getKdcReqBody().getSName(); 536 PrincipalStoreEntry requestPrincipal = tgsContext.getRequestPrincipalEntry(); 537 538 sb.append( "\n\t" + "principal " + requestServerPrincipal ); 539 sb.append( "\n\t" + "cn " + requestPrincipal.getCommonName() ); 540 sb.append( "\n\t" + "realm " + requestPrincipal.getRealmName() ); 541 sb.append( "\n\t" + "principal " + requestPrincipal.getPrincipal() ); 542 sb.append( "\n\t" + "SAM type " + requestPrincipal.getSamType() ); 543 544 PrincipalName ticketServerPrincipal = tgsContext.getTgt().getSName(); 545 PrincipalStoreEntry ticketPrincipal = tgsContext.getTicketPrincipalEntry(); 546 547 sb.append( "\n\t" + "principal " + ticketServerPrincipal ); 548 sb.append( "\n\t" + "cn " + ticketPrincipal.getCommonName() ); 549 sb.append( "\n\t" + "realm " + ticketPrincipal.getRealmName() ); 550 sb.append( "\n\t" + "principal " + ticketPrincipal.getPrincipal() ); 551 sb.append( "\n\t" + "SAM type " + ticketPrincipal.getSamType() ); 552 553 EncryptionType encryptionType = tgsContext.getTgt().getEncPart().getEType(); 554 int keyVersion = ticketPrincipal.getKeyMap().get( encryptionType ).getKeyVersion(); 555 sb.append( "\n\t" + "Ticket key type " + encryptionType ); 556 sb.append( "\n\t" + "Service key version " + keyVersion ); 557 558 LOG_KRB.debug( sb.toString() ); 559 } 560 catch ( Exception e ) 561 { 562 // This is a monitor. No exceptions should bubble up. 563 LOG_KRB.error( I18n.err( I18n.ERR_154 ), e ); 564 } 565 } 566 567 568 private static void monitorReply( TgsRep success, EncKdcRepPart part ) 569 { 570 try 571 { 572 StringBuffer sb = new StringBuffer(); 573 574 sb.append( "Responding with " + SERVICE_NAME + " reply:" ); 575 sb.append( "\n\t" + "messageType: " + success.getMessageType() ); 576 sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() ); 577 sb.append( "\n\t" + "nonce: " + part.getNonce() ); 578 sb.append( "\n\t" + "clientPrincipal: " + success.getCName() ); 579 sb.append( "\n\t" + "client realm: " + success.getCRealm() ); 580 sb.append( "\n\t" + "serverPrincipal: " + part.getSName() ); 581 sb.append( "\n\t" + "server realm: " + part.getSRealm() ); 582 sb.append( "\n\t" + "auth time: " + part.getAuthTime() ); 583 sb.append( "\n\t" + "start time: " + part.getStartTime() ); 584 sb.append( "\n\t" + "end time: " + part.getEndTime() ); 585 sb.append( "\n\t" + "renew-till time: " + part.getRenewTill() ); 586 sb.append( "\n\t" + "hostAddresses: " + part.getClientAddresses() ); 587 588 LOG_KRB.debug( sb.toString() ); 589 } 590 catch ( Exception e ) 591 { 592 // This is a monitor. No exceptions should bubble up. 593 LOG_KRB.error( I18n.err( I18n.ERR_155 ), e ); 594 } 595 } 596 597 598 private static void processFlags( KerberosConfig config, KdcReq request, Ticket tgt, 599 EncTicketPart newTicketPart ) throws KerberosException 600 { 601 if ( tgt.getEncTicketPart().getFlags().isPreAuth() ) 602 { 603 newTicketPart.setFlag( TicketFlag.PRE_AUTHENT ); 604 } 605 606 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.FORWARDABLE ) ) 607 { 608 if ( !config.isForwardableAllowed() ) 609 { 610 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 611 } 612 613 if ( !tgt.getEncTicketPart().getFlags().isForwardable() ) 614 { 615 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 616 } 617 618 newTicketPart.setFlag( TicketFlag.FORWARDABLE ); 619 } 620 621 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.FORWARDED ) ) 622 { 623 if ( !config.isForwardableAllowed() ) 624 { 625 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 626 } 627 628 if ( !tgt.getEncTicketPart().getFlags().isForwardable() ) 629 { 630 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 631 } 632 633 if ( request.getKdcReqBody().getAddresses() != null 634 && request.getKdcReqBody().getAddresses().getAddresses() != null 635 && request.getKdcReqBody().getAddresses().getAddresses().length > 0 ) 636 { 637 newTicketPart.setClientAddresses( request.getKdcReqBody().getAddresses() ); 638 } 639 else 640 { 641 if ( !config.isEmptyAddressesAllowed() ) 642 { 643 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 644 } 645 } 646 647 newTicketPart.setFlag( TicketFlag.FORWARDED ); 648 } 649 650 if ( tgt.getEncTicketPart().getFlags().isForwarded() ) 651 { 652 newTicketPart.setFlag( TicketFlag.FORWARDED ); 653 } 654 655 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.PROXIABLE ) ) 656 { 657 if ( !config.isProxiableAllowed() ) 658 { 659 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 660 } 661 662 if ( !tgt.getEncTicketPart().getFlags().isProxiable() ) 663 { 664 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 665 } 666 667 newTicketPart.setFlag( TicketFlag.PROXIABLE ); 668 } 669 670 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.PROXY ) ) 671 { 672 if ( !config.isProxiableAllowed() ) 673 { 674 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 675 } 676 677 if ( !tgt.getEncTicketPart().getFlags().isProxiable() ) 678 { 679 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 680 } 681 682 if ( request.getKdcReqBody().getAddresses() != null 683 && request.getKdcReqBody().getAddresses().getAddresses() != null 684 && request.getKdcReqBody().getAddresses().getAddresses().length > 0 ) 685 { 686 newTicketPart.setClientAddresses( request.getKdcReqBody().getAddresses() ); 687 } 688 else 689 { 690 if ( !config.isEmptyAddressesAllowed() ) 691 { 692 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 693 } 694 } 695 696 newTicketPart.setFlag( TicketFlag.PROXY ); 697 } 698 699 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.ALLOW_POSTDATE ) ) 700 { 701 if ( !config.isPostdatedAllowed() ) 702 { 703 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 704 } 705 706 if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() ) 707 { 708 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 709 } 710 711 newTicketPart.setFlag( TicketFlag.MAY_POSTDATE ); 712 } 713 714 /* 715 * "Otherwise, if the TGT has the MAY-POSTDATE flag set, then the resulting 716 * ticket will be postdated, and the requested starttime is checked against 717 * the policy of the local realm. If acceptable, the ticket's starttime is 718 * set as requested, and the INVALID flag is set. The postdated ticket MUST 719 * be validated before use by presenting it to the KDC after the starttime 720 * has been reached. However, in no case may the starttime, endtime, or 721 * renew-till time of a newly-issued postdated ticket extend beyond the 722 * renew-till time of the TGT." 723 */ 724 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) ) 725 { 726 if ( !config.isPostdatedAllowed() ) 727 { 728 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 729 } 730 731 if ( !tgt.getEncTicketPart().getFlags().isMayPosdate() ) 732 { 733 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 734 } 735 736 newTicketPart.setFlag( TicketFlag.POSTDATED ); 737 newTicketPart.setFlag( TicketFlag.INVALID ); 738 739 newTicketPart.setStartTime( request.getKdcReqBody().getFrom() ); 740 } 741 742 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.VALIDATE ) ) 743 { 744 if ( !config.isPostdatedAllowed() ) 745 { 746 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 747 } 748 749 if ( !tgt.getEncTicketPart().getFlags().isInvalid() ) 750 { 751 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 752 } 753 754 KerberosTime startTime = ( tgt.getEncTicketPart().getStartTime() != null ) 755 ? tgt.getEncTicketPart().getStartTime() 756 : tgt.getEncTicketPart().getAuthTime(); 757 758 if ( startTime.greaterThan( new KerberosTime() ) ) 759 { 760 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV ); 761 } 762 763 echoTicket( newTicketPart, tgt ); 764 newTicketPart.getFlags().clearFlag( TicketFlag.INVALID ); 765 } 766 767 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_0 ) 768 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_7 ) 769 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_9 ) 770 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_10 ) 771 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_11 ) 772 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_12 ) 773 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_13 ) 774 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_14 ) 775 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_15 ) 776 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_16 ) 777 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_17 ) 778 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_18 ) 779 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_19 ) 780 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_20 ) 781 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_21 ) 782 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_22 ) 783 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_23 ) 784 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_24 ) 785 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_25 ) 786 || request.getKdcReqBody().getKdcOptions().get( KdcOptions.RESERVED_29 ) ) 787 { 788 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 789 } 790 } 791 792 793 private static void processTimes( KerberosConfig config, KdcReq request, EncTicketPart newTicketPart, 794 Ticket tgt ) throws KerberosException 795 { 796 KerberosTime now = new KerberosTime(); 797 798 newTicketPart.setAuthTime( tgt.getEncTicketPart().getAuthTime() ); 799 800 KerberosTime startTime = request.getKdcReqBody().getFrom(); 801 802 /* 803 * "If the requested starttime is absent, indicates a time in the past, 804 * or is within the window of acceptable clock skew for the KDC and the 805 * POSTDATE option has not been specified, then the starttime of the 806 * ticket is set to the authentication server's current time." 807 */ 808 if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() ) 809 && !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) ) 810 { 811 startTime = now; 812 } 813 814 /* 815 * "If it indicates a time in the future beyond the acceptable clock skew, 816 * but the POSTDATED option has not been specified or the MAY-POSTDATE flag 817 * is not set in the TGT, then the error KDC_ERR_CANNOT_POSTDATE is 818 * returned." 819 */ 820 if ( startTime != null 821 && startTime.greaterThan( now ) 822 && !startTime.isInClockSkew( config.getAllowableClockSkew() ) 823 && ( !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) || !tgt.getEncTicketPart() 824 .getFlags().isMayPosdate() ) ) 825 { 826 throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE ); 827 } 828 829 KerberosTime renewalTime = null; 830 KerberosTime kerberosEndTime = null; 831 832 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEW ) ) 833 { 834 if ( !config.isRenewableAllowed() ) 835 { 836 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 837 } 838 839 if ( !tgt.getEncTicketPart().getFlags().isRenewable() ) 840 { 841 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 842 } 843 844 if ( tgt.getEncTicketPart().getRenewTill().lessThan( now ) ) 845 { 846 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED ); 847 } 848 849 echoTicket( newTicketPart, tgt ); 850 851 newTicketPart.setStartTime( now ); 852 853 KerberosTime tgtStartTime = ( tgt.getEncTicketPart().getStartTime() != null ) 854 ? tgt.getEncTicketPart().getStartTime() 855 : tgt.getEncTicketPart().getAuthTime(); 856 857 long oldLife = tgt.getEncTicketPart().getEndTime().getTime() - tgtStartTime.getTime(); 858 859 kerberosEndTime = new KerberosTime( Math.min( tgt.getEncTicketPart().getRenewTill().getTime(), 860 now.getTime() + oldLife ) ); 861 newTicketPart.setEndTime( kerberosEndTime ); 862 } 863 else 864 { 865 if ( newTicketPart.getStartTime() == null ) 866 { 867 newTicketPart.setStartTime( now ); 868 } 869 870 KerberosTime till; 871 if ( request.getKdcReqBody().getTill().isZero() ) 872 { 873 till = KerberosTime.INFINITY; 874 } 875 else 876 { 877 till = request.getKdcReqBody().getTill(); 878 } 879 880 /* 881 * The end time is the minimum of (a) the requested till time or (b) 882 * the start time plus maximum lifetime as configured in policy or (c) 883 * the end time of the TGT. 884 */ 885 List<KerberosTime> minimizer = new ArrayList<KerberosTime>(); 886 minimizer.add( till ); 887 minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumTicketLifetime() ) ); 888 minimizer.add( tgt.getEncTicketPart().getEndTime() ); 889 kerberosEndTime = Collections.min( minimizer ); 890 891 newTicketPart.setEndTime( kerberosEndTime ); 892 893 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE_OK ) 894 && kerberosEndTime.lessThan( request.getKdcReqBody().getTill() ) 895 && tgt.getEncTicketPart().getFlags().isRenewable() ) 896 { 897 if ( !config.isRenewableAllowed() ) 898 { 899 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 900 } 901 902 // We set the RENEWABLE option for later processing. 903 request.getKdcReqBody().getKdcOptions().set( KdcOptions.RENEWABLE ); 904 long rtime = Math.min( request.getKdcReqBody().getTill().getTime(), tgt.getEncTicketPart() 905 .getRenewTill().getTime() ); 906 renewalTime = new KerberosTime( rtime ); 907 } 908 } 909 910 if ( renewalTime == null ) 911 { 912 renewalTime = request.getKdcReqBody().getRTime(); 913 } 914 915 KerberosTime rtime; 916 if ( renewalTime != null && renewalTime.isZero() ) 917 { 918 rtime = KerberosTime.INFINITY; 919 } 920 else 921 { 922 rtime = renewalTime; 923 } 924 925 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE ) 926 && tgt.getEncTicketPart().getFlags().isRenewable() ) 927 { 928 if ( !config.isRenewableAllowed() ) 929 { 930 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 931 } 932 933 newTicketPart.setFlag( TicketFlag.RENEWABLE ); 934 935 /* 936 * The renew-till time is the minimum of (a) the requested renew-till 937 * time or (b) the start time plus maximum renewable lifetime as 938 * configured in policy or (c) the renew-till time of the TGT. 939 */ 940 List<KerberosTime> minimizer = new ArrayList<KerberosTime>(); 941 942 /* 943 * 'rtime' KerberosTime is OPTIONAL 944 */ 945 if ( rtime != null ) 946 { 947 minimizer.add( rtime ); 948 } 949 950 minimizer.add( new KerberosTime( startTime.getTime() + config.getMaximumRenewableLifetime() ) ); 951 minimizer.add( tgt.getEncTicketPart().getRenewTill() ); 952 newTicketPart.setRenewTill( Collections.min( minimizer ) ); 953 } 954 955 /* 956 * "If the requested expiration time minus the starttime (as determined 957 * above) is less than a site-determined minimum lifetime, an error 958 * message with code KDC_ERR_NEVER_VALID is returned." 959 */ 960 if ( kerberosEndTime.lessThan( startTime ) ) 961 { 962 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID ); 963 } 964 965 long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() ); 966 if ( ticketLifeTime < config.getAllowableClockSkew() ) 967 { 968 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID ); 969 } 970 } 971 972 973 /* 974 * if (realm_tgt_is_for(tgt) := tgt.realm) then 975 * // tgt issued by local realm 976 * new_tkt.transited := tgt.transited; 977 * else 978 * // was issued for this realm by some other realm 979 * if (tgt.transited.tr-type not supported) then 980 * error_out(KDC_ERR_TRTYPE_NOSUPP); 981 * endif 982 * 983 * new_tkt.transited := compress_transited(tgt.transited + tgt.realm) 984 * endif 985 */ 986 private static void processTransited( EncTicketPart newTicketPart, Ticket tgt ) 987 { 988 // TODO - currently no transited support other than local 989 newTicketPart.setTransited( tgt.getEncTicketPart().getTransited() ); 990 } 991 992 993 private static void echoTicket( EncTicketPart newTicketPart, Ticket tgt ) 994 { 995 EncTicketPart encTicketpart = tgt.getEncTicketPart(); 996 newTicketPart.setAuthorizationData( encTicketpart.getAuthorizationData() ); 997 newTicketPart.setAuthTime( encTicketpart.getAuthTime() ); 998 newTicketPart.setClientAddresses( encTicketpart.getClientAddresses() ); 999 newTicketPart.setCName( encTicketpart.getCName() ); 1000 newTicketPart.setEndTime( encTicketpart.getEndTime() ); 1001 newTicketPart.setFlags( encTicketpart.getFlags() ); 1002 newTicketPart.setRenewTill( encTicketpart.getRenewTill() ); 1003 newTicketPart.setKey( encTicketpart.getKey() ); 1004 newTicketPart.setTransited( encTicketpart.getTransited() ); 1005 } 1006 1007 1008 /** 1009 * Get a PrincipalStoreEntry given a principal. The ErrorType is used to indicate 1010 * whether any resulting error pertains to a server or client. 1011 * 1012 * @param principal The KerberosPrincipal instance 1013 * @param store The Principal store 1014 * @param errorType The type of error 1015 * @return The PrincipalStoreEntry 1016 * @throws KerberosException If teh entry can't be retrieve 1017 */ 1018 public static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType ) 1019 throws KerberosException 1020 { 1021 PrincipalStoreEntry entry = null; 1022 1023 try 1024 { 1025 entry = store.getPrincipal( principal ); 1026 } 1027 catch ( Exception e ) 1028 { 1029 throw new KerberosException( errorType, e ); 1030 } 1031 1032 if ( entry == null ) 1033 { 1034 throw new KerberosException( errorType ); 1035 } 1036 1037 if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() ) 1038 { 1039 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY ); 1040 } 1041 1042 return entry; 1043 } 1044 1045}