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.authentication; 021 022 023import java.net.InetAddress; 024import java.nio.ByteBuffer; 025import java.util.Date; 026import java.util.List; 027import java.util.Set; 028 029import javax.security.auth.kerberos.KerberosKey; 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.sam.SamException; 038import org.apache.directory.server.kerberos.sam.SamSubsystem; 039import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler; 040import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage; 041import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory; 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.ETypeInfo; 053import org.apache.directory.shared.kerberos.components.ETypeInfo2; 054import org.apache.directory.shared.kerberos.components.ETypeInfo2Entry; 055import org.apache.directory.shared.kerberos.components.ETypeInfoEntry; 056import org.apache.directory.shared.kerberos.components.EncKdcRepPart; 057import org.apache.directory.shared.kerberos.components.EncTicketPart; 058import org.apache.directory.shared.kerberos.components.EncryptedData; 059import org.apache.directory.shared.kerberos.components.EncryptionKey; 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.MethodData; 065import org.apache.directory.shared.kerberos.components.PaData; 066import org.apache.directory.shared.kerberos.components.PaEncTsEnc; 067import org.apache.directory.shared.kerberos.components.PrincipalName; 068import org.apache.directory.shared.kerberos.components.TransitedEncoding; 069import org.apache.directory.shared.kerberos.exceptions.ErrorType; 070import org.apache.directory.shared.kerberos.exceptions.InvalidTicketException; 071import org.apache.directory.shared.kerberos.exceptions.KerberosException; 072import org.apache.directory.shared.kerberos.flags.TicketFlag; 073import org.apache.directory.shared.kerberos.flags.TicketFlags; 074import org.apache.directory.shared.kerberos.messages.AsRep; 075import org.apache.directory.shared.kerberos.messages.EncAsRepPart; 076import org.apache.directory.shared.kerberos.messages.Ticket; 077import org.slf4j.Logger; 078import org.slf4j.LoggerFactory; 079 080 081/** 082 * Subsystem in charge of authenticating the incoming users. 083 * 084 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 085 */ 086public final class AuthenticationService 087{ 088 /** The log for this class. */ 089 private static final Logger LOG_KRB = LoggerFactory.getLogger( Loggers.KERBEROS_LOG.getName() ); 090 091 /** The module responsible for encryption and decryption */ 092 private static final CipherTextHandler CIPHER_TEXT_HANDLER = new CipherTextHandler(); 093 094 /** The service name */ 095 private static final String SERVICE_NAME = "Authentication Service (AS)"; 096 097 098 private AuthenticationService() 099 { 100 } 101 102 103 /** 104 * Handle the authentication, given a specific context 105 * 106 * @param authContext The authentication context 107 * @throws Exception If the authentication failed 108 */ 109 public static void execute( AuthenticationContext authContext ) throws Exception 110 { 111 if ( LOG_KRB.isDebugEnabled() ) 112 { 113 monitorRequest( authContext ); 114 } 115 116 authContext.setCipherTextHandler( CIPHER_TEXT_HANDLER ); 117 118 int kerberosVersion = authContext.getRequest().getProtocolVersionNumber(); 119 120 if ( kerberosVersion != KerberosConstants.KERBEROS_V5 ) 121 { 122 LOG_KRB.error( "Kerberos V{} is not supported", kerberosVersion ); 123 throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO ); 124 } 125 126 selectEncryptionType( authContext ); 127 getClientEntry( authContext ); 128 verifyPolicy( authContext ); 129 verifySam( authContext ); 130 verifyEncryptedTimestamp( authContext ); 131 132 getServerEntry( authContext ); 133 generateTicket( authContext ); 134 buildReply( authContext ); 135 } 136 137 138 /** 139 * 140 * @param authContext 141 * @throws KerberosException 142 * @throws InvalidTicketException 143 */ 144 private static void selectEncryptionType( AuthenticationContext authContext ) throws KerberosException, 145 InvalidTicketException 146 { 147 148 LOG_KRB.debug( "--> Selecting the EncryptionType" ); 149 KdcContext kdcContext = authContext; 150 KerberosConfig config = kdcContext.getConfig(); 151 152 Set<EncryptionType> requestedTypes = kdcContext.getRequest().getKdcReqBody().getEType(); 153 LOG_KRB.debug( "Encryption types requested by client {}.", requestedTypes ); 154 155 EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() ); 156 157 LOG_KRB.debug( "Session will use encryption type {}.", bestType ); 158 159 if ( bestType == null ) 160 { 161 LOG_KRB.error( "No encryptionType selected !" ); 162 throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP ); 163 } 164 165 kdcContext.setEncryptionType( bestType ); 166 } 167 168 169 private static void getClientEntry( AuthenticationContext authContext ) throws KerberosException, 170 InvalidTicketException 171 { 172 LOG_KRB.debug( "--> Getting the client Entry" ); 173 KdcReqBody kdcReqBody = authContext.getRequest().getKdcReqBody(); 174 KerberosPrincipal principal = KerberosUtils.getKerberosPrincipal( 175 kdcReqBody.getCName(), 176 kdcReqBody.getRealm() ); 177 PrincipalStore store = authContext.getStore(); 178 179 try 180 { 181 PrincipalStoreEntry storeEntry = KerberosUtils.getEntry( principal, store, 182 ErrorType.KDC_ERR_C_PRINCIPAL_UNKNOWN ); 183 authContext.setClientEntry( storeEntry ); 184 185 LOG_KRB.debug( "Found entry {} for principal {}", storeEntry.getDistinguishedName(), principal ); 186 } 187 catch ( KerberosException ke ) 188 { 189 LOG_KRB.error( "Error while searching for client {} : {}", principal, ke.getMessage() ); 190 throw ke; 191 } 192 } 193 194 195 private static void verifyPolicy( AuthenticationContext authContext ) throws KerberosException, 196 InvalidTicketException 197 { 198 LOG_KRB.debug( "--> Verifying the policy" ); 199 PrincipalStoreEntry entry = authContext.getClientEntry(); 200 201 if ( entry.isDisabled() ) 202 { 203 LOG_KRB.error( "The entry {} is disabled", entry.getDistinguishedName() ); 204 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED ); 205 } 206 207 if ( entry.isLockedOut() ) 208 { 209 LOG_KRB.error( "The entry {} is locked out", entry.getDistinguishedName() ); 210 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED ); 211 } 212 213 if ( entry.getExpiration().getTime() < new Date().getTime() ) 214 { 215 LOG_KRB.error( "The entry {} has been revoked", entry.getDistinguishedName() ); 216 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED ); 217 } 218 } 219 220 221 private static void verifySam( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 222 { 223 LOG_KRB.debug( "--> Verifying using SAM subsystem." ); 224 KdcReq request = authContext.getRequest(); 225 KerberosConfig config = authContext.getConfig(); 226 227 PrincipalStoreEntry clientEntry = authContext.getClientEntry(); 228 String clientName = clientEntry.getPrincipal().getName(); 229 230 EncryptionKey clientKey = null; 231 232 if ( clientEntry.getSamType() != null ) 233 { 234 if ( LOG_KRB.isDebugEnabled() ) 235 { 236 LOG_KRB 237 .debug( 238 "Entry for client principal {} has a valid SAM type. Invoking SAM subsystem for pre-authentication.", 239 clientName ); 240 } 241 242 List<PaData> preAuthData = request.getPaData(); 243 244 if ( ( preAuthData == null ) || ( preAuthData.size() == 0 ) ) 245 { 246 LOG_KRB.debug( "No PreAuth Data" ); 247 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, preparePreAuthenticationError( 248 authContext.getEncryptionType(), config 249 .getEncryptionTypes() ) ); 250 } 251 252 try 253 { 254 for ( PaData paData : preAuthData ) 255 { 256 if ( paData.getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) ) 257 { 258 KerberosKey samKey = SamSubsystem.getInstance().verify( clientEntry, 259 paData.getPaDataValue() ); 260 clientKey = new EncryptionKey( EncryptionType.getTypeByValue( samKey.getKeyType() ), samKey 261 .getEncoded() ); 262 } 263 } 264 } 265 catch ( SamException se ) 266 { 267 LOG_KRB.error( "Error : {}", se.getMessage() ); 268 throw new KerberosException( ErrorType.KRB_ERR_GENERIC, se ); 269 } 270 271 authContext.setClientKey( clientKey ); 272 authContext.setPreAuthenticated( true ); 273 274 if ( LOG_KRB.isDebugEnabled() ) 275 { 276 LOG_KRB.debug( "Pre-authentication using SAM subsystem successful for {}.", clientName ); 277 } 278 } 279 } 280 281 282 private static void verifyEncryptedTimestamp( AuthenticationContext authContext ) throws KerberosException, 283 InvalidTicketException 284 { 285 LOG_KRB.debug( "--> Verifying using encrypted timestamp." ); 286 287 KerberosConfig config = authContext.getConfig(); 288 KdcReq request = authContext.getRequest(); 289 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler(); 290 PrincipalStoreEntry clientEntry = authContext.getClientEntry(); 291 String clientName = clientEntry.getPrincipal().getName(); 292 293 EncryptionKey clientKey = null; 294 295 if ( clientEntry.getSamType() == null ) 296 { 297 LOG_KRB.debug( 298 "Entry for client principal {} has no SAM type. Proceeding with standard pre-authentication.", 299 clientName ); 300 301 EncryptionType encryptionType = authContext.getEncryptionType(); 302 clientKey = clientEntry.getKeyMap().get( encryptionType ); 303 304 if ( clientKey == null ) 305 { 306 LOG_KRB.error( "No key for client {}", clientEntry.getDistinguishedName() ); 307 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY ); 308 } 309 310 if ( config.isPaEncTimestampRequired() ) 311 { 312 List<PaData> preAuthData = request.getPaData(); 313 314 if ( preAuthData == null ) 315 { 316 LOG_KRB.debug( "PRE_AUTH required..." ); 317 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, 318 preparePreAuthenticationError( authContext.getEncryptionType(), config.getEncryptionTypes() ) ); 319 } 320 321 PaEncTsEnc timestamp = null; 322 323 for ( PaData paData : preAuthData ) 324 { 325 if ( paData.getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) ) 326 { 327 EncryptedData dataValue = KerberosDecoder.decodeEncryptedData( paData.getPaDataValue() ); 328 byte[] decryptedData = cipherTextHandler.decrypt( clientKey, dataValue, 329 KeyUsage.AS_REQ_PA_ENC_TIMESTAMP_WITH_CKEY ); 330 timestamp = KerberosDecoder.decodePaEncTsEnc( decryptedData ); 331 } 332 } 333 334 if ( timestamp == null ) 335 { 336 LOG_KRB.error( "No timestamp found" ); 337 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, 338 preparePreAuthenticationError( authContext.getEncryptionType(), config.getEncryptionTypes() ) ); 339 } 340 341 if ( !timestamp.getPaTimestamp().isInClockSkew( config.getAllowableClockSkew() ) ) 342 { 343 LOG_KRB.error( "Timestamp not in delay" ); 344 345 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_FAILED ); 346 } 347 348 /* 349 * if(decrypted_enc_timestamp and usec is replay) 350 * error_out(KDC_ERR_PREAUTH_FAILED); 351 * endif 352 * 353 * add decrypted_enc_timestamp and usec to replay cache; 354 */ 355 } 356 } 357 358 authContext.setClientKey( clientKey ); 359 authContext.setPreAuthenticated( true ); 360 361 if ( LOG_KRB.isDebugEnabled() ) 362 { 363 LOG_KRB.debug( "Pre-authentication by encrypted timestamp successful for {}.", clientName ); 364 } 365 } 366 367 368 private static void getServerEntry( AuthenticationContext authContext ) throws KerberosException, 369 InvalidTicketException 370 { 371 PrincipalName principal = authContext.getRequest().getKdcReqBody().getSName(); 372 PrincipalStore store = authContext.getStore(); 373 374 LOG_KRB.debug( "--> Getting the server entry for {}" + principal ); 375 376 KerberosPrincipal principalWithRealm = new KerberosPrincipal( principal.getNameString() + "@" 377 + authContext.getRequest().getKdcReqBody().getRealm() ); 378 authContext.setServerEntry( KerberosUtils.getEntry( principalWithRealm, store, 379 ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) ); 380 } 381 382 383 private static void generateTicket( AuthenticationContext authContext ) throws KerberosException, 384 InvalidTicketException 385 { 386 KdcReq request = authContext.getRequest(); 387 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler(); 388 PrincipalName serverPrincipal = request.getKdcReqBody().getSName(); 389 390 LOG_KRB.debug( "--> Generating ticket for {}", serverPrincipal ); 391 392 EncryptionType encryptionType = authContext.getEncryptionType(); 393 EncryptionKey serverKey = authContext.getServerEntry().getKeyMap().get( encryptionType ); 394 395 PrincipalName ticketPrincipal = request.getKdcReqBody().getSName(); 396 397 EncTicketPart encTicketPart = new EncTicketPart(); 398 KerberosConfig config = authContext.getConfig(); 399 400 // The INITIAL flag indicates that a ticket was issued using the AS protocol. 401 TicketFlags ticketFlags = new TicketFlags(); 402 encTicketPart.setFlags( ticketFlags ); 403 ticketFlags.setFlag( TicketFlag.INITIAL ); 404 405 // The PRE-AUTHENT flag indicates that the client used pre-authentication. 406 if ( authContext.isPreAuthenticated() ) 407 { 408 ticketFlags.setFlag( TicketFlag.PRE_AUTHENT ); 409 } 410 411 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.FORWARDABLE ) ) 412 { 413 if ( !config.isForwardableAllowed() ) 414 { 415 LOG_KRB.error( "Ticket cannot be generated, because Forwadable is not allowed" ); 416 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 417 } 418 419 ticketFlags.setFlag( TicketFlag.FORWARDABLE ); 420 } 421 422 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.PROXIABLE ) ) 423 { 424 if ( !config.isProxiableAllowed() ) 425 { 426 LOG_KRB.error( "Ticket cannot be generated, because proxyiable is not allowed" ); 427 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 428 } 429 430 ticketFlags.setFlag( TicketFlag.PROXIABLE ); 431 } 432 433 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.ALLOW_POSTDATE ) ) 434 { 435 if ( !config.isPostdatedAllowed() ) 436 { 437 LOG_KRB.error( "Ticket cannot be generated, because Posdate is not allowed" ); 438 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 439 } 440 441 ticketFlags.setFlag( TicketFlag.MAY_POSTDATE ); 442 } 443 444 KdcOptions kdcOptions = request.getKdcReqBody().getKdcOptions(); 445 446 if ( kdcOptions.get( KdcOptions.RENEW ) 447 || kdcOptions.get( KdcOptions.VALIDATE ) 448 || kdcOptions.get( KdcOptions.PROXY ) 449 || kdcOptions.get( KdcOptions.FORWARDED ) 450 || kdcOptions.get( KdcOptions.ENC_TKT_IN_SKEY ) ) 451 { 452 String msg = ""; 453 454 if ( kdcOptions.get( KdcOptions.RENEW ) ) 455 { 456 msg = "Ticket cannot be generated, as it's a renew"; 457 } 458 459 if ( kdcOptions.get( KdcOptions.VALIDATE ) ) 460 { 461 msg = "Ticket cannot be generated, as it's a validate"; 462 } 463 464 if ( kdcOptions.get( KdcOptions.PROXY ) ) 465 { 466 msg = "Ticket cannot be generated, as it's a proxy"; 467 } 468 469 if ( kdcOptions.get( KdcOptions.FORWARDED ) ) 470 { 471 msg = "Ticket cannot be generated, as it's forwarded"; 472 } 473 474 if ( kdcOptions.get( KdcOptions.ENC_TKT_IN_SKEY ) ) 475 { 476 msg = "Ticket cannot be generated, as it's a user-to-user "; 477 } 478 479 if ( LOG_KRB.isDebugEnabled() ) 480 { 481 LOG_KRB.debug( msg ); 482 } 483 484 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION, msg ); 485 } 486 487 EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( authContext.getEncryptionType() ); 488 encTicketPart.setKey( sessionKey ); 489 490 encTicketPart.setCName( request.getKdcReqBody().getCName() ); 491 encTicketPart.setCRealm( request.getKdcReqBody().getRealm() ); 492 encTicketPart.setTransited( new TransitedEncoding() ); 493 String serverRealm = request.getKdcReqBody().getRealm(); 494 495 KerberosTime now = new KerberosTime(); 496 497 encTicketPart.setAuthTime( now ); 498 499 KerberosTime startTime = request.getKdcReqBody().getFrom(); 500 501 /* 502 * "If the requested starttime is absent, indicates a time in the past, 503 * or is within the window of acceptable clock skew for the KDC and the 504 * POSTDATE option has not been specified, then the starttime of the 505 * ticket is set to the authentication server's current time." 506 */ 507 if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() ) 508 && !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) ) 509 { 510 startTime = now; 511 } 512 513 /* 514 * "If it indicates a time in the future beyond the acceptable clock skew, 515 * but the POSTDATED option has not been specified, then the error 516 * KDC_ERR_CANNOT_POSTDATE is returned." 517 */ 518 if ( ( startTime != null ) && startTime.greaterThan( now ) 519 && !startTime.isInClockSkew( config.getAllowableClockSkew() ) 520 && !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) ) 521 { 522 String msg = "Ticket cannot be generated, as it's in the future and the POSTDATED option is not set in the request"; 523 LOG_KRB.error( msg ); 524 throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE, msg ); 525 } 526 527 /* 528 * "Otherwise the requested starttime is checked against the policy of the 529 * local realm and if the ticket's starttime is acceptable, it is set as 530 * requested, and the INVALID flag is set in the new ticket." 531 */ 532 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) ) 533 { 534 if ( !config.isPostdatedAllowed() ) 535 { 536 String msg = "Ticket cannot be generated, cause issuing POSTDATED tickets is not allowed"; 537 LOG_KRB.error( msg ); 538 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg ); 539 } 540 541 ticketFlags.setFlag( TicketFlag.POSTDATED ); 542 ticketFlags.setFlag( TicketFlag.INVALID ); 543 } 544 545 encTicketPart.setStartTime( startTime ); 546 547 long till = 0; 548 549 if ( request.getKdcReqBody().getTill().getTime() == 0 ) 550 { 551 till = Long.MAX_VALUE; 552 } 553 else 554 { 555 till = request.getKdcReqBody().getTill().getTime(); 556 } 557 558 /* 559 * The end time is the minimum of (a) the requested till time or (b) 560 * the start time plus maximum lifetime as configured in policy. 561 */ 562 long endTime = Math.min( till, startTime.getTime() + config.getMaximumTicketLifetime() ); 563 KerberosTime kerberosEndTime = new KerberosTime( endTime ); 564 encTicketPart.setEndTime( kerberosEndTime ); 565 566 /* 567 * "If the requested expiration time minus the starttime (as determined 568 * above) is less than a site-determined minimum lifetime, an error 569 * message with code KDC_ERR_NEVER_VALID is returned." 570 */ 571 if ( kerberosEndTime.lessThan( startTime ) ) 572 { 573 String msg = "Ticket cannot be generated, as the endTime is below the startTime"; 574 LOG_KRB.error( msg ); 575 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID, msg ); 576 } 577 578 long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() ); 579 580 if ( ticketLifeTime < config.getMinimumTicketLifetime() ) 581 { 582 String msg = "Ticket cannot be generated, as the Lifetime is too small"; 583 LOG_KRB.error( msg ); 584 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID, msg ); 585 } 586 587 /* 588 * "If the requested expiration time for the ticket exceeds what was determined 589 * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE' 590 * flag is set in the new ticket, and the renew-till value is set as if the 591 * 'RENEWABLE' option were requested." 592 */ 593 KerberosTime tempRtime = request.getKdcReqBody().getRTime(); 594 595 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE_OK ) 596 && request.getKdcReqBody().getTill().greaterThan( kerberosEndTime ) ) 597 { 598 if ( !config.isRenewableAllowed() ) 599 { 600 String msg = "Ticket cannot be generated, as the renew date is exceeded"; 601 LOG_KRB.error( msg ); 602 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg ); 603 } 604 605 request.getKdcReqBody().getKdcOptions().set( KdcOptions.RENEWABLE ); 606 tempRtime = request.getKdcReqBody().getTill(); 607 } 608 609 if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE ) ) 610 { 611 if ( !config.isRenewableAllowed() ) 612 { 613 String msg = "Ticket cannot be generated, as Renewable is not allowed"; 614 LOG_KRB.error( msg ); 615 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg ); 616 } 617 618 ticketFlags.setFlag( TicketFlag.RENEWABLE ); 619 620 if ( tempRtime == null || tempRtime.isZero() ) 621 { 622 tempRtime = KerberosTime.INFINITY; 623 } 624 625 /* 626 * The renew-till time is the minimum of (a) the requested renew-till 627 * time or (b) the start time plus maximum renewable lifetime as 628 * configured in policy. 629 */ 630 long renewTill = Math.min( tempRtime.getTime(), startTime.getTime() + config.getMaximumRenewableLifetime() ); 631 encTicketPart.setRenewTill( new KerberosTime( renewTill ) ); 632 } 633 634 if ( request.getKdcReqBody().getAddresses() != null 635 && request.getKdcReqBody().getAddresses().getAddresses() != null 636 && request.getKdcReqBody().getAddresses().getAddresses().length > 0 ) 637 { 638 encTicketPart.setClientAddresses( request.getKdcReqBody().getAddresses() ); 639 } 640 else 641 { 642 if ( !config.isEmptyAddressesAllowed() ) 643 { 644 String msg = "Ticket cannot be generated, as the addresses are null, and it's not allowed"; 645 LOG_KRB.error( msg ); 646 throw new KerberosException( ErrorType.KDC_ERR_POLICY, msg ); 647 } 648 } 649 650 EncryptedData encryptedData = cipherTextHandler.seal( serverKey, encTicketPart, 651 KeyUsage.AS_OR_TGS_REP_TICKET_WITH_SRVKEY ); 652 653 Ticket newTicket = new Ticket( ticketPrincipal, encryptedData ); 654 655 newTicket.setRealm( serverRealm ); 656 newTicket.setEncTicketPart( encTicketPart ); 657 658 LOG_KRB.debug( "Ticket will be issued for access to {}.", serverPrincipal.toString() ); 659 660 authContext.setTicket( newTicket ); 661 } 662 663 664 private static void buildReply( AuthenticationContext authContext ) throws KerberosException, 665 InvalidTicketException 666 { 667 LOG_KRB.debug( "--> Building reply" ); 668 KdcReq request = authContext.getRequest(); 669 Ticket ticket = authContext.getTicket(); 670 671 AsRep reply = new AsRep(); 672 673 reply.setCName( request.getKdcReqBody().getCName() ); 674 reply.setCRealm( request.getKdcReqBody().getRealm() ); 675 reply.setTicket( ticket ); 676 677 EncKdcRepPart encKdcRepPart = new EncKdcRepPart(); 678 //session key 679 encKdcRepPart.setKey( ticket.getEncTicketPart().getKey() ); 680 681 // TODO - fetch lastReq for this client; requires store 682 // FIXME temporary fix, IMO we should create some new ATs to store this info in DIT 683 LastReq lastReq = new LastReq(); 684 lastReq.addEntry( new LastReqEntry( LastReqType.TIME_OF_INITIAL_REQ, new KerberosTime() ) ); 685 encKdcRepPart.setLastReq( lastReq ); 686 // TODO - resp.key-expiration := client.expiration; requires store 687 688 encKdcRepPart.setNonce( request.getKdcReqBody().getNonce() ); 689 690 encKdcRepPart.setFlags( ticket.getEncTicketPart().getFlags() ); 691 encKdcRepPart.setAuthTime( ticket.getEncTicketPart().getAuthTime() ); 692 encKdcRepPart.setStartTime( ticket.getEncTicketPart().getStartTime() ); 693 encKdcRepPart.setEndTime( ticket.getEncTicketPart().getEndTime() ); 694 695 if ( ticket.getEncTicketPart().getFlags().isRenewable() ) 696 { 697 encKdcRepPart.setRenewTill( ticket.getEncTicketPart().getRenewTill() ); 698 } 699 700 encKdcRepPart.setSName( ticket.getSName() ); 701 encKdcRepPart.setSRealm( ticket.getRealm() ); 702 encKdcRepPart.setClientAddresses( ticket.getEncTicketPart().getClientAddresses() ); 703 704 EncAsRepPart encAsRepPart = new EncAsRepPart(); 705 encAsRepPart.setEncKdcRepPart( encKdcRepPart ); 706 707 if ( LOG_KRB.isDebugEnabled() ) 708 { 709 monitorContext( authContext ); 710 monitorReply( reply, encKdcRepPart ); 711 } 712 713 EncryptionKey clientKey = authContext.getClientKey(); 714 EncryptedData encryptedData = CIPHER_TEXT_HANDLER.seal( clientKey, encAsRepPart, 715 KeyUsage.AS_REP_ENC_PART_WITH_CKEY ); 716 reply.setEncPart( encryptedData ); 717 //FIXME the below setter is useless, remove it 718 reply.setEncKdcRepPart( encKdcRepPart ); 719 720 authContext.setReply( reply ); 721 } 722 723 724 private static void monitorRequest( KdcContext kdcContext ) 725 { 726 KdcReq request = kdcContext.getRequest(); 727 728 if ( LOG_KRB.isDebugEnabled() ) 729 { 730 try 731 { 732 String clientAddress = kdcContext.getClientAddress().getHostAddress(); 733 734 StringBuffer sb = new StringBuffer(); 735 736 sb.append( "Received " + SERVICE_NAME + " request:" ); 737 sb.append( "\n\t" + "messageType: " + request.getMessageType() ); 738 sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() ); 739 sb.append( "\n\t" + "clientAddress: " + clientAddress ); 740 sb.append( "\n\t" + "nonce: " + request.getKdcReqBody().getNonce() ); 741 sb.append( "\n\t" + "kdcOptions: " + request.getKdcReqBody().getKdcOptions() ); 742 sb.append( "\n\t" + "clientPrincipal: " + request.getKdcReqBody().getCName() ); 743 sb.append( "\n\t" + "serverPrincipal: " + request.getKdcReqBody().getSName() ); 744 sb.append( "\n\t" + "encryptionType: " 745 + KerberosUtils.getEncryptionTypesString( request.getKdcReqBody().getEType() ) ); 746 sb.append( "\n\t" + "realm: " + request.getKdcReqBody().getRealm() ); 747 sb.append( "\n\t" + "from time: " + request.getKdcReqBody().getFrom() ); 748 sb.append( "\n\t" + "till time: " + request.getKdcReqBody().getTill() ); 749 sb.append( "\n\t" + "renew-till time: " + request.getKdcReqBody().getRTime() ); 750 sb.append( "\n\t" + "hostAddresses: " + request.getKdcReqBody().getAddresses() ); 751 752 String message = sb.toString(); 753 LOG_KRB.debug( message ); 754 } 755 catch ( Exception e ) 756 { 757 // This is a monitor. No exceptions should bubble up. 758 LOG_KRB.error( I18n.err( I18n.ERR_153 ), e ); 759 } 760 } 761 } 762 763 764 private static void monitorContext( AuthenticationContext authContext ) 765 { 766 try 767 { 768 long clockSkew = authContext.getConfig().getAllowableClockSkew(); 769 InetAddress clientAddress = authContext.getClientAddress(); 770 771 StringBuilder sb = new StringBuilder(); 772 773 sb.append( "Monitoring " + SERVICE_NAME + " context:" ); 774 775 sb.append( "\n\t" + "clockSkew " + clockSkew ); 776 sb.append( "\n\t" + "clientAddress " + clientAddress ); 777 778 KerberosPrincipal clientPrincipal = authContext.getClientEntry().getPrincipal(); 779 PrincipalStoreEntry clientEntry = authContext.getClientEntry(); 780 781 sb.append( "\n\t" + "principal " + clientPrincipal ); 782 sb.append( "\n\t" + "cn " + clientEntry.getCommonName() ); 783 sb.append( "\n\t" + "realm " + clientEntry.getRealmName() ); 784 sb.append( "\n\t" + "principal " + clientEntry.getPrincipal() ); 785 sb.append( "\n\t" + "SAM type " + clientEntry.getSamType() ); 786 787 PrincipalName serverPrincipal = authContext.getRequest().getKdcReqBody().getSName(); 788 PrincipalStoreEntry serverEntry = authContext.getServerEntry(); 789 790 sb.append( "\n\t" + "principal " + serverPrincipal ); 791 sb.append( "\n\t" + "cn " + serverEntry.getCommonName() ); 792 sb.append( "\n\t" + "realm " + serverEntry.getRealmName() ); 793 sb.append( "\n\t" + "principal " + serverEntry.getPrincipal() ); 794 sb.append( "\n\t" + "SAM type " + serverEntry.getSamType() ); 795 796 EncryptionType encryptionType = authContext.getEncryptionType(); 797 int clientKeyVersion = clientEntry.getKeyMap().get( encryptionType ).getKeyVersion(); 798 int serverKeyVersion = serverEntry.getKeyMap().get( encryptionType ).getKeyVersion(); 799 sb.append( "\n\t" + "Request key type " + encryptionType ); 800 sb.append( "\n\t" + "Client key version " + clientKeyVersion ); 801 sb.append( "\n\t" + "Server key version " + serverKeyVersion ); 802 803 String message = sb.toString(); 804 805 LOG_KRB.debug( message ); 806 } 807 catch ( Exception e ) 808 { 809 // This is a monitor. No exceptions should bubble up. 810 LOG_KRB.error( I18n.err( I18n.ERR_154 ), e ); 811 } 812 } 813 814 815 private static void monitorReply( AsRep reply, EncKdcRepPart part ) 816 { 817 if ( LOG_KRB.isDebugEnabled() ) 818 { 819 try 820 { 821 StringBuffer sb = new StringBuffer(); 822 823 sb.append( "Responding with " + SERVICE_NAME + " reply:" ); 824 sb.append( "\n\t" + "messageType: " + reply.getMessageType() ); 825 sb.append( "\n\t" + "protocolVersionNumber: " + reply.getProtocolVersionNumber() ); 826 sb.append( "\n\t" + "nonce: " + part.getNonce() ); 827 sb.append( "\n\t" + "clientPrincipal: " + reply.getCName() ); 828 sb.append( "\n\t" + "client realm: " + reply.getCRealm() ); 829 sb.append( "\n\t" + "serverPrincipal: " + part.getSName() ); 830 sb.append( "\n\t" + "server realm: " + part.getSRealm() ); 831 sb.append( "\n\t" + "auth time: " + part.getAuthTime() ); 832 sb.append( "\n\t" + "start time: " + part.getStartTime() ); 833 sb.append( "\n\t" + "end time: " + part.getEndTime() ); 834 sb.append( "\n\t" + "renew-till time: " + part.getRenewTill() ); 835 sb.append( "\n\t" + "hostAddresses: " + part.getClientAddresses() ); 836 837 String message = sb.toString(); 838 839 LOG_KRB.debug( message ); 840 } 841 catch ( Exception e ) 842 { 843 // This is a monitor. No exceptions should bubble up. 844 LOG_KRB.error( I18n.err( I18n.ERR_155 ), e ); 845 } 846 } 847 } 848 849 850 /** 851 * Prepares a pre-authentication error message containing required 852 * encryption types. 853 * 854 * @param encryptionTypes 855 * @return The error message as bytes. 856 */ 857 private static byte[] preparePreAuthenticationError( EncryptionType requestedType, 858 Set<EncryptionType> encryptionTypes ) 859 { 860 boolean isNewEtype = KerberosUtils.isNewEncryptionType( requestedType ); 861 862 ETypeInfo2 eTypeInfo2 = new ETypeInfo2(); 863 864 ETypeInfo eTypeInfo = new ETypeInfo(); 865 866 for ( EncryptionType encryptionType : encryptionTypes ) 867 { 868 if ( !isNewEtype ) 869 { 870 ETypeInfoEntry etypeInfoEntry = new ETypeInfoEntry( encryptionType, null ); 871 eTypeInfo.addETypeInfoEntry( etypeInfoEntry ); 872 } 873 874 ETypeInfo2Entry etypeInfo2Entry = new ETypeInfo2Entry( encryptionType ); 875 eTypeInfo2.addETypeInfo2Entry( etypeInfo2Entry ); 876 } 877 878 byte[] encTypeInfo = null; 879 byte[] encTypeInfo2 = null; 880 try 881 { 882 if ( !isNewEtype ) 883 { 884 ByteBuffer buffer = ByteBuffer.allocate( eTypeInfo.computeLength() ); 885 encTypeInfo = eTypeInfo.encode( buffer ).array(); 886 } 887 888 ByteBuffer buffer = ByteBuffer.allocate( eTypeInfo2.computeLength() ); 889 encTypeInfo2 = eTypeInfo2.encode( buffer ).array(); 890 } 891 catch ( EncoderException ioe ) 892 { 893 return null; 894 } 895 896 MethodData methodData = new MethodData(); 897 898 methodData.addPaData( new PaData( PaDataType.PA_ENC_TIMESTAMP, null ) ); 899 900 if ( !isNewEtype ) 901 { 902 methodData.addPaData( new PaData( PaDataType.PA_ENCTYPE_INFO, encTypeInfo ) ); 903 } 904 905 methodData.addPaData( new PaData( PaDataType.PA_ENCTYPE_INFO2, encTypeInfo2 ) ); 906 907 try 908 { 909 ByteBuffer buffer = ByteBuffer.allocate( methodData.computeLength() ); 910 return methodData.encode( buffer ).array(); 911 } 912 catch ( EncoderException ee ) 913 { 914 LOG_KRB.warn( "Failed to encode the etype information", ee ); 915 return null; 916 } 917 } 918}