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