001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.server.kerberos.kdc.authentication; 021 022 023 import java.io.IOException; 024 import java.net.InetAddress; 025 import java.util.Date; 026 import java.util.Set; 027 028 import javax.security.auth.kerberos.KerberosKey; 029 import javax.security.auth.kerberos.KerberosPrincipal; 030 031 import org.apache.directory.server.i18n.I18n; 032 import org.apache.directory.server.kerberos.kdc.KdcContext; 033 import org.apache.directory.server.kerberos.kdc.KdcServer; 034 import org.apache.directory.server.kerberos.sam.SamException; 035 import org.apache.directory.server.kerberos.sam.SamSubsystem; 036 import org.apache.directory.server.kerberos.shared.KerberosConstants; 037 import org.apache.directory.server.kerberos.shared.KerberosUtils; 038 import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler; 039 import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType; 040 import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage; 041 import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory; 042 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType; 043 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException; 044 import org.apache.directory.server.kerberos.shared.io.decoder.EncryptedDataDecoder; 045 import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionTypeInfoEncoder; 046 import org.apache.directory.server.kerberos.shared.io.encoder.PreAuthenticationDataEncoder; 047 import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply; 048 import org.apache.directory.server.kerberos.shared.messages.KdcReply; 049 import org.apache.directory.server.kerberos.shared.messages.KdcRequest; 050 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart; 051 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier; 052 import org.apache.directory.server.kerberos.shared.messages.components.InvalidTicketException; 053 import org.apache.directory.server.kerberos.shared.messages.components.Ticket; 054 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData; 055 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp; 056 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey; 057 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfoEntry; 058 import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions; 059 import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime; 060 import org.apache.directory.server.kerberos.shared.messages.value.LastRequest; 061 import org.apache.directory.server.kerberos.shared.messages.value.PaData; 062 import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding; 063 import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag; 064 import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType; 065 import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache; 066 import org.apache.directory.server.kerberos.shared.replay.ReplayCache; 067 import org.apache.directory.server.kerberos.shared.store.PrincipalStore; 068 import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry; 069 import org.slf4j.Logger; 070 import org.slf4j.LoggerFactory; 071 072 073 /** 074 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 075 * @version $Rev: 901657 $, $Date: 2010-01-21 12:27:15 +0100 (Thu, 21 Jan 2010) $ 076 */ 077 public class AuthenticationService 078 { 079 /** The log for this class. */ 080 private static final Logger LOG = LoggerFactory.getLogger( AuthenticationService.class ); 081 082 private static final ReplayCache replayCache = new InMemoryReplayCache(); 083 private static final CipherTextHandler cipherTextHandler = new CipherTextHandler(); 084 085 private static final String SERVICE_NAME = "Authentication Service (AS)"; 086 087 088 public static void execute( AuthenticationContext authContext ) throws Exception 089 { 090 if ( LOG.isDebugEnabled() ) 091 { 092 monitorRequest( authContext ); 093 } 094 095 authContext.setReplayCache( replayCache ); 096 authContext.setCipherTextHandler( cipherTextHandler ); 097 098 if ( authContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 ) 099 { 100 throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO ); 101 } 102 103 selectEncryptionType( authContext ); 104 getClientEntry( authContext ); 105 verifyPolicy( authContext ); 106 verifySam( authContext ); 107 verifyEncryptedTimestamp( authContext ); 108 109 if ( authContext.getClientKey() == null ) 110 { 111 verifyEncryptedTimestamp( authContext ); 112 } 113 114 getServerEntry( authContext ); 115 generateTicket( authContext ); 116 buildReply( authContext ); 117 118 if ( LOG.isDebugEnabled() ) 119 { 120 monitorContext( authContext ); 121 monitorReply( ( KdcContext ) authContext ); 122 } 123 124 sealReply( authContext ); 125 } 126 127 128 private static void selectEncryptionType( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 129 { 130 KdcContext kdcContext = ( KdcContext ) authContext; 131 KdcServer config = kdcContext.getConfig(); 132 133 Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType(); 134 135 EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() ); 136 137 LOG.debug( "Session will use encryption type {}.", bestType ); 138 139 if ( bestType == null ) 140 { 141 throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP ); 142 } 143 144 kdcContext.setEncryptionType( bestType ); 145 } 146 147 148 private static void getClientEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 149 { 150 KerberosPrincipal principal = authContext.getRequest().getClientPrincipal(); 151 PrincipalStore store = authContext.getStore(); 152 153 PrincipalStoreEntry storeEntry = getEntry( principal, store, ErrorType.KDC_ERR_C_PRINCIPAL_UNKNOWN ); 154 authContext.setClientEntry( storeEntry ); 155 } 156 157 158 private static void verifyPolicy( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 159 { 160 PrincipalStoreEntry entry = authContext.getClientEntry(); 161 162 if ( entry.isDisabled() ) 163 { 164 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED ); 165 } 166 167 if ( entry.isLockedOut() ) 168 { 169 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED ); 170 } 171 172 if ( entry.getExpiration().getTime() < new Date().getTime() ) 173 { 174 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED ); 175 } 176 } 177 178 179 private static void verifySam( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 180 { 181 LOG.debug( "Verifying using SAM subsystem." ); 182 KdcRequest request = authContext.getRequest(); 183 KdcServer config = authContext.getConfig(); 184 185 PrincipalStoreEntry clientEntry = authContext.getClientEntry(); 186 String clientName = clientEntry.getPrincipal().getName(); 187 188 EncryptionKey clientKey = null; 189 190 if ( clientEntry.getSamType() != null ) 191 { 192 if ( LOG.isDebugEnabled() ) 193 { 194 LOG.debug( "Entry for client principal {} has a valid SAM type. Invoking SAM subsystem for pre-authentication.", clientName ); 195 } 196 197 PaData[] preAuthData = request.getPreAuthData(); 198 199 if ( preAuthData == null || preAuthData.length == 0 ) 200 { 201 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, preparePreAuthenticationError( config 202 .getEncryptionTypes() ) ); 203 } 204 205 try 206 { 207 for ( int ii = 0; ii < preAuthData.length; ii++ ) 208 { 209 if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) ) 210 { 211 KerberosKey samKey = SamSubsystem.getInstance().verify( clientEntry, 212 preAuthData[ii].getPaDataValue() ); 213 clientKey = new EncryptionKey( EncryptionType.getTypeByOrdinal( samKey.getKeyType() ), samKey 214 .getEncoded() ); 215 } 216 } 217 } 218 catch ( SamException se ) 219 { 220 throw new KerberosException( ErrorType.KRB_ERR_GENERIC, se ); 221 } 222 223 authContext.setClientKey( clientKey ); 224 authContext.setPreAuthenticated( true ); 225 226 if ( LOG.isDebugEnabled() ) 227 { 228 LOG.debug( "Pre-authentication using SAM subsystem successful for {}.", clientName ); 229 } 230 } 231 } 232 233 234 private static void verifyEncryptedTimestamp( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 235 { 236 LOG.debug( "Verifying using encrypted timestamp." ); 237 238 KdcServer config = authContext.getConfig(); 239 KdcRequest request = authContext.getRequest(); 240 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler(); 241 PrincipalStoreEntry clientEntry = authContext.getClientEntry(); 242 String clientName = clientEntry.getPrincipal().getName(); 243 244 EncryptionKey clientKey = null; 245 246 if ( clientEntry.getSamType() == null ) 247 { 248 if ( LOG.isDebugEnabled() ) 249 { 250 LOG.debug( 251 "Entry for client principal {} has no SAM type. Proceeding with standard pre-authentication.", 252 clientName ); 253 } 254 255 EncryptionType encryptionType = authContext.getEncryptionType(); 256 clientKey = clientEntry.getKeyMap().get( encryptionType ); 257 258 if ( clientKey == null ) 259 { 260 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY ); 261 } 262 263 if ( config.isPaEncTimestampRequired() ) 264 { 265 PaData[] preAuthData = request.getPreAuthData(); 266 267 if ( preAuthData == null ) 268 { 269 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, 270 preparePreAuthenticationError( config.getEncryptionTypes() ) ); 271 } 272 273 EncryptedTimeStamp timestamp = null; 274 275 for ( int ii = 0; ii < preAuthData.length; ii++ ) 276 { 277 if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) ) 278 { 279 EncryptedData dataValue; 280 281 try 282 { 283 dataValue = EncryptedDataDecoder.decode( preAuthData[ii].getPaDataValue() ); 284 } 285 catch ( IOException ioe ) 286 { 287 throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe ); 288 } 289 catch ( ClassCastException cce ) 290 { 291 throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce ); 292 } 293 294 timestamp = ( EncryptedTimeStamp ) cipherTextHandler.unseal( EncryptedTimeStamp.class, 295 clientKey, dataValue, KeyUsage.NUMBER1 ); 296 } 297 } 298 299 if ( preAuthData.length > 0 && timestamp == null ) 300 { 301 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP ); 302 } 303 304 if ( timestamp == null ) 305 { 306 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, 307 preparePreAuthenticationError( config.getEncryptionTypes() ) ); 308 } 309 310 if ( !timestamp.getTimeStamp().isInClockSkew( config.getAllowableClockSkew() ) ) 311 { 312 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_FAILED ); 313 } 314 315 /* 316 * if(decrypted_enc_timestamp and usec is replay) 317 * error_out(KDC_ERR_PREAUTH_FAILED); 318 * endif 319 * 320 * add decrypted_enc_timestamp and usec to replay cache; 321 */ 322 } 323 } 324 325 authContext.setClientKey( clientKey ); 326 authContext.setPreAuthenticated( true ); 327 328 if ( LOG.isDebugEnabled() ) 329 { 330 LOG.debug( "Pre-authentication by encrypted timestamp successful for {}.", clientName ); 331 } 332 } 333 334 335 private static void getServerEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 336 { 337 KerberosPrincipal principal = authContext.getRequest().getServerPrincipal(); 338 PrincipalStore store = authContext.getStore(); 339 340 authContext.setServerEntry( getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) ); 341 } 342 343 344 private static void generateTicket( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 345 { 346 KdcRequest request = authContext.getRequest(); 347 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler(); 348 KerberosPrincipal serverPrincipal = request.getServerPrincipal(); 349 350 EncryptionType encryptionType = authContext.getEncryptionType(); 351 EncryptionKey serverKey = authContext.getServerEntry().getKeyMap().get( encryptionType ); 352 353 KerberosPrincipal ticketPrincipal = request.getServerPrincipal(); 354 EncTicketPartModifier newTicketBody = new EncTicketPartModifier(); 355 KdcServer config = authContext.getConfig(); 356 357 // The INITIAL flag indicates that a ticket was issued using the AS protocol. 358 newTicketBody.setFlag( TicketFlag.INITIAL ); 359 360 // The PRE-AUTHENT flag indicates that the client used pre-authentication. 361 if ( authContext.isPreAuthenticated() ) 362 { 363 newTicketBody.setFlag( TicketFlag.PRE_AUTHENT ); 364 } 365 366 if ( request.getOption( KdcOptions.FORWARDABLE ) ) 367 { 368 if ( !config.isForwardableAllowed() ) 369 { 370 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 371 } 372 373 newTicketBody.setFlag( TicketFlag.FORWARDABLE ); 374 } 375 376 if ( request.getOption( KdcOptions.PROXIABLE ) ) 377 { 378 if ( !config.isProxiableAllowed() ) 379 { 380 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 381 } 382 383 newTicketBody.setFlag( TicketFlag.PROXIABLE ); 384 } 385 386 if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) ) 387 { 388 if ( !config.isPostdatedAllowed() ) 389 { 390 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 391 } 392 393 newTicketBody.setFlag( TicketFlag.MAY_POSTDATE ); 394 } 395 396 if ( request.getOption( KdcOptions.RENEW ) || request.getOption( KdcOptions.VALIDATE ) 397 || request.getOption( KdcOptions.PROXY ) || request.getOption( KdcOptions.FORWARDED ) 398 || request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) ) 399 { 400 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION ); 401 } 402 403 EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( authContext.getEncryptionType() ); 404 newTicketBody.setSessionKey( sessionKey ); 405 406 newTicketBody.setClientPrincipal( request.getClientPrincipal() ); 407 newTicketBody.setTransitedEncoding( new TransitedEncoding() ); 408 409 KerberosTime now = new KerberosTime(); 410 411 newTicketBody.setAuthTime( now ); 412 413 KerberosTime startTime = request.getFrom(); 414 415 /* 416 * "If the requested starttime is absent, indicates a time in the past, 417 * or is within the window of acceptable clock skew for the KDC and the 418 * POSTDATE option has not been specified, then the starttime of the 419 * ticket is set to the authentication server's current time." 420 */ 421 if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() ) 422 && !request.getOption( KdcOptions.POSTDATED ) ) 423 { 424 startTime = now; 425 } 426 427 /* 428 * "If it indicates a time in the future beyond the acceptable clock skew, 429 * but the POSTDATED option has not been specified, then the error 430 * KDC_ERR_CANNOT_POSTDATE is returned." 431 */ 432 if ( startTime != null && startTime.greaterThan( now ) 433 && !startTime.isInClockSkew( config.getAllowableClockSkew() ) && !request.getOption( KdcOptions.POSTDATED ) ) 434 { 435 throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE ); 436 } 437 438 /* 439 * "Otherwise the requested starttime is checked against the policy of the 440 * local realm and if the ticket's starttime is acceptable, it is set as 441 * requested, and the INVALID flag is set in the new ticket." 442 */ 443 if ( request.getOption( KdcOptions.POSTDATED ) ) 444 { 445 if ( !config.isPostdatedAllowed() ) 446 { 447 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 448 } 449 450 newTicketBody.setFlag( TicketFlag.POSTDATED ); 451 newTicketBody.setFlag( TicketFlag.INVALID ); 452 newTicketBody.setStartTime( startTime ); 453 } 454 455 long till = 0; 456 457 if ( request.getTill().getTime() == 0 ) 458 { 459 till = Long.MAX_VALUE; 460 } 461 else 462 { 463 till = request.getTill().getTime(); 464 } 465 466 /* 467 * The end time is the minimum of (a) the requested till time or (b) 468 * the start time plus maximum lifetime as configured in policy. 469 */ 470 long endTime = Math.min( till, startTime.getTime() + config.getMaximumTicketLifetime() ); 471 KerberosTime kerberosEndTime = new KerberosTime( endTime ); 472 newTicketBody.setEndTime( kerberosEndTime ); 473 474 /* 475 * "If the requested expiration time minus the starttime (as determined 476 * above) is less than a site-determined minimum lifetime, an error 477 * message with code KDC_ERR_NEVER_VALID is returned." 478 */ 479 if ( kerberosEndTime.lessThan( startTime ) ) 480 { 481 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID ); 482 } 483 484 long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() ); 485 486 if ( ticketLifeTime < config.getAllowableClockSkew() ) 487 { 488 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID ); 489 } 490 491 /* 492 * "If the requested expiration time for the ticket exceeds what was determined 493 * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE' 494 * flag is set in the new ticket, and the renew-till value is set as if the 495 * 'RENEWABLE' option were requested." 496 */ 497 KerberosTime tempRtime = request.getRtime(); 498 499 if ( request.getOption( KdcOptions.RENEWABLE_OK ) && request.getTill().greaterThan( kerberosEndTime ) ) 500 { 501 if ( !config.isRenewableAllowed() ) 502 { 503 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 504 } 505 506 request.setOption( KdcOptions.RENEWABLE ); 507 tempRtime = request.getTill(); 508 } 509 510 if ( request.getOption( KdcOptions.RENEWABLE ) ) 511 { 512 if ( !config.isRenewableAllowed() ) 513 { 514 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 515 } 516 517 newTicketBody.setFlag( TicketFlag.RENEWABLE ); 518 519 if ( tempRtime == null || tempRtime.isZero() ) 520 { 521 tempRtime = KerberosTime.INFINITY; 522 } 523 524 /* 525 * The renew-till time is the minimum of (a) the requested renew-till 526 * time or (b) the start time plus maximum renewable lifetime as 527 * configured in policy. 528 */ 529 long renewTill = Math.min( tempRtime.getTime(), startTime.getTime() + config.getMaximumRenewableLifetime() ); 530 newTicketBody.setRenewTill( new KerberosTime( renewTill ) ); 531 } 532 533 if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null 534 && request.getAddresses().getAddresses().length > 0 ) 535 { 536 newTicketBody.setClientAddresses( request.getAddresses() ); 537 } 538 else 539 { 540 if ( !config.isEmptyAddressesAllowed() ) 541 { 542 throw new KerberosException( ErrorType.KDC_ERR_POLICY ); 543 } 544 } 545 546 EncTicketPart ticketPart = newTicketBody.getEncTicketPart(); 547 548 EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 ); 549 550 Ticket newTicket = new Ticket( ticketPrincipal, encryptedData ); 551 newTicket.setEncTicketPart( ticketPart ); 552 553 if ( LOG.isDebugEnabled() ) 554 { 555 LOG.debug( "Ticket will be issued for access to {}.", serverPrincipal.toString() ); 556 } 557 558 authContext.setTicket( newTicket ); 559 } 560 561 562 private static void buildReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 563 { 564 KdcRequest request = authContext.getRequest(); 565 Ticket ticket = authContext.getTicket(); 566 567 AuthenticationReply reply = new AuthenticationReply(); 568 569 reply.setClientPrincipal( request.getClientPrincipal() ); 570 reply.setTicket( ticket ); 571 reply.setKey( ticket.getEncTicketPart().getSessionKey() ); 572 573 // TODO - fetch lastReq for this client; requires store 574 reply.setLastRequest( new LastRequest() ); 575 // TODO - resp.key-expiration := client.expiration; requires store 576 577 reply.setNonce( request.getNonce() ); 578 579 reply.setFlags( ticket.getEncTicketPart().getFlags() ); 580 reply.setAuthTime( ticket.getEncTicketPart().getAuthTime() ); 581 reply.setStartTime( ticket.getEncTicketPart().getStartTime() ); 582 reply.setEndTime( ticket.getEncTicketPart().getEndTime() ); 583 584 if ( ticket.getEncTicketPart().getFlags().isRenewable() ) 585 { 586 reply.setRenewTill( ticket.getEncTicketPart().getRenewTill() ); 587 } 588 589 reply.setServerPrincipal( ticket.getServerPrincipal() ); 590 reply.setClientAddresses( ticket.getEncTicketPart().getClientAddresses() ); 591 592 authContext.setReply( reply ); 593 } 594 595 596 private static void sealReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException 597 { 598 AuthenticationReply reply = ( AuthenticationReply ) authContext.getReply(); 599 EncryptionKey clientKey = authContext.getClientKey(); 600 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler(); 601 602 EncryptedData encryptedData = cipherTextHandler.seal( clientKey, reply, KeyUsage.NUMBER3 ); 603 reply.setEncPart( encryptedData ); 604 } 605 606 607 private static void monitorRequest( KdcContext kdcContext ) 608 { 609 KdcRequest request = kdcContext.getRequest(); 610 611 if ( LOG.isDebugEnabled() ) 612 { 613 try 614 { 615 String clientAddress = kdcContext.getClientAddress().getHostAddress(); 616 617 StringBuffer sb = new StringBuffer(); 618 619 sb.append( "Received " + SERVICE_NAME + " request:" ); 620 sb.append( "\n\t" + "messageType: " + request.getMessageType() ); 621 sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() ); 622 sb.append( "\n\t" + "clientAddress: " + clientAddress ); 623 sb.append( "\n\t" + "nonce: " + request.getNonce() ); 624 sb.append( "\n\t" + "kdcOptions: " + request.getKdcOptions() ); 625 sb.append( "\n\t" + "clientPrincipal: " + request.getClientPrincipal() ); 626 sb.append( "\n\t" + "serverPrincipal: " + request.getServerPrincipal() ); 627 sb.append( "\n\t" + "encryptionType: " + KerberosUtils.getEncryptionTypesString( request.getEType() ) ); 628 sb.append( "\n\t" + "realm: " + request.getRealm() ); 629 sb.append( "\n\t" + "from time: " + request.getFrom() ); 630 sb.append( "\n\t" + "till time: " + request.getTill() ); 631 sb.append( "\n\t" + "renew-till time: " + request.getRtime() ); 632 sb.append( "\n\t" + "hostAddresses: " + request.getAddresses() ); 633 634 LOG.debug( sb.toString() ); 635 } 636 catch ( Exception e ) 637 { 638 // This is a monitor. No exceptions should bubble up. 639 LOG.error( I18n.err( I18n.ERR_153 ), e ); 640 } 641 } 642 } 643 644 private static void monitorContext( AuthenticationContext authContext ) 645 { 646 try 647 { 648 long clockSkew = authContext.getConfig().getAllowableClockSkew(); 649 InetAddress clientAddress = authContext.getClientAddress(); 650 651 StringBuilder sb = new StringBuilder(); 652 653 sb.append( "Monitoring " + SERVICE_NAME + " context:" ); 654 655 sb.append( "\n\t" + "clockSkew " + clockSkew ); 656 sb.append( "\n\t" + "clientAddress " + clientAddress ); 657 658 KerberosPrincipal clientPrincipal = authContext.getClientEntry().getPrincipal(); 659 PrincipalStoreEntry clientEntry = authContext.getClientEntry(); 660 661 sb.append( "\n\t" + "principal " + clientPrincipal ); 662 sb.append( "\n\t" + "cn " + clientEntry.getCommonName() ); 663 sb.append( "\n\t" + "realm " + clientEntry.getRealmName() ); 664 sb.append( "\n\t" + "principal " + clientEntry.getPrincipal() ); 665 sb.append( "\n\t" + "SAM type " + clientEntry.getSamType() ); 666 667 KerberosPrincipal serverPrincipal = authContext.getRequest().getServerPrincipal(); 668 PrincipalStoreEntry serverEntry = authContext.getServerEntry(); 669 670 sb.append( "\n\t" + "principal " + serverPrincipal ); 671 sb.append( "\n\t" + "cn " + serverEntry.getCommonName() ); 672 sb.append( "\n\t" + "realm " + serverEntry.getRealmName() ); 673 sb.append( "\n\t" + "principal " + serverEntry.getPrincipal() ); 674 sb.append( "\n\t" + "SAM type " + serverEntry.getSamType() ); 675 676 EncryptionType encryptionType = authContext.getEncryptionType(); 677 int clientKeyVersion = clientEntry.getKeyMap().get( encryptionType ).getKeyVersion(); 678 int serverKeyVersion = serverEntry.getKeyMap().get( encryptionType ).getKeyVersion(); 679 sb.append( "\n\t" + "Request key type " + encryptionType ); 680 sb.append( "\n\t" + "Client key version " + clientKeyVersion ); 681 sb.append( "\n\t" + "Server key version " + serverKeyVersion ); 682 683 LOG.debug( sb.toString() ); 684 } 685 catch ( Exception e ) 686 { 687 // This is a monitor. No exceptions should bubble up. 688 LOG.error( I18n.err( I18n.ERR_154 ), e ); 689 } 690 } 691 692 693 private static void monitorReply( KdcContext kdcContext ) 694 { 695 Object reply = kdcContext.getReply(); 696 697 if ( LOG.isDebugEnabled() ) 698 { 699 if ( reply instanceof KdcReply ) 700 { 701 KdcReply success = ( KdcReply ) reply; 702 703 try 704 { 705 StringBuffer sb = new StringBuffer(); 706 707 sb.append( "Responding with " + SERVICE_NAME + " reply:" ); 708 sb.append( "\n\t" + "messageType: " + success.getMessageType() ); 709 sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() ); 710 sb.append( "\n\t" + "nonce: " + success.getNonce() ); 711 sb.append( "\n\t" + "clientPrincipal: " + success.getClientPrincipal() ); 712 sb.append( "\n\t" + "client realm: " + success.getClientRealm() ); 713 sb.append( "\n\t" + "serverPrincipal: " + success.getServerPrincipal() ); 714 sb.append( "\n\t" + "server realm: " + success.getServerRealm() ); 715 sb.append( "\n\t" + "auth time: " + success.getAuthTime() ); 716 sb.append( "\n\t" + "start time: " + success.getStartTime() ); 717 sb.append( "\n\t" + "end time: " + success.getEndTime() ); 718 sb.append( "\n\t" + "renew-till time: " + success.getRenewTill() ); 719 sb.append( "\n\t" + "hostAddresses: " + success.getClientAddresses() ); 720 721 LOG.debug( sb.toString() ); 722 } 723 catch ( Exception e ) 724 { 725 // This is a monitor. No exceptions should bubble up. 726 LOG.error( I18n.err( I18n.ERR_155 ), e ); 727 } 728 } 729 } 730 } 731 732 733 /** 734 * Get a PrincipalStoreEntry given a principal. The ErrorType is used to indicate 735 * whether any resulting error pertains to a server or client. 736 */ 737 private static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType ) 738 throws KerberosException 739 { 740 PrincipalStoreEntry entry = null; 741 742 try 743 { 744 entry = store.getPrincipal( principal ); 745 } 746 catch ( Exception e ) 747 { 748 throw new KerberosException( errorType, e ); 749 } 750 751 if ( entry == null ) 752 { 753 throw new KerberosException( errorType ); 754 } 755 756 if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() ) 757 { 758 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY ); 759 } 760 761 return entry; 762 } 763 764 765 /** 766 * Prepares a pre-authentication error message containing required 767 * encryption types. 768 * 769 * @param encryptionTypes 770 * @return The error message as bytes. 771 */ 772 private static byte[] preparePreAuthenticationError( Set<EncryptionType> encryptionTypes ) 773 { 774 PaData[] paDataSequence = new PaData[2]; 775 776 PaData paData = new PaData(); 777 paData.setPaDataType( PaDataType.PA_ENC_TIMESTAMP ); 778 paData.setPaDataValue( new byte[0] ); 779 780 paDataSequence[0] = paData; 781 782 EncryptionTypeInfoEntry[] entries = new EncryptionTypeInfoEntry[ encryptionTypes.size() ]; 783 int i = 0; 784 785 for ( EncryptionType encryptionType:encryptionTypes ) 786 { 787 entries[i++] = new EncryptionTypeInfoEntry( encryptionType, null ); 788 } 789 790 byte[] encTypeInfo = null; 791 792 try 793 { 794 encTypeInfo = EncryptionTypeInfoEncoder.encode( entries ); 795 } 796 catch ( IOException ioe ) 797 { 798 return null; 799 } 800 801 PaData encType = new PaData(); 802 encType.setPaDataType( PaDataType.PA_ENCTYPE_INFO ); 803 encType.setPaDataValue( encTypeInfo ); 804 805 paDataSequence[1] = encType; 806 807 try 808 { 809 return PreAuthenticationDataEncoder.encode( paDataSequence ); 810 } 811 catch ( IOException ioe ) 812 { 813 return null; 814 } 815 } 816 }