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