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.shared.kerberos; 021 022 023import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES3_CBC_MD5; 024import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES3_CBC_SHA1; 025import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES3_CBC_SHA1_KD; 026import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_CBC_CRC; 027import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_CBC_MD4; 028import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_CBC_MD5; 029import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_EDE3_CBC_ENV_OID; 030import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DSAWITHSHA1_CMSOID; 031import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.MD5WITHRSAENCRYPTION_CMSOID; 032import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RC2CBC_ENVOID; 033import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RC4_HMAC; 034import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RSAENCRYPTION_ENVOID; 035import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RSAES_OAEP_ENV_OID; 036import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.SHA1WITHRSAENCRYPTION_CMSOID; 037 038import java.net.InetAddress; 039import java.text.ParseException; 040import java.text.SimpleDateFormat; 041import java.util.ArrayList; 042import java.util.HashSet; 043import java.util.LinkedHashMap; 044import java.util.LinkedHashSet; 045import java.util.List; 046import java.util.Locale; 047import java.util.Map; 048import java.util.Set; 049import java.util.TimeZone; 050 051import javax.security.auth.kerberos.KerberosPrincipal; 052 053import org.apache.directory.api.util.Strings; 054import org.apache.directory.server.i18n.I18n; 055import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler; 056import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage; 057import org.apache.directory.server.kerberos.shared.replay.ReplayCache; 058import org.apache.directory.server.kerberos.shared.store.PrincipalStore; 059import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry; 060import org.apache.directory.shared.kerberos.codec.KerberosDecoder; 061import org.apache.directory.shared.kerberos.codec.options.ApOptions; 062import org.apache.directory.shared.kerberos.codec.types.EncryptionType; 063import org.apache.directory.shared.kerberos.components.EncTicketPart; 064import org.apache.directory.shared.kerberos.components.EncryptionKey; 065import org.apache.directory.shared.kerberos.components.HostAddress; 066import org.apache.directory.shared.kerberos.components.PrincipalName; 067import org.apache.directory.shared.kerberos.exceptions.ErrorType; 068import org.apache.directory.shared.kerberos.exceptions.KerberosException; 069import org.apache.directory.shared.kerberos.messages.ApReq; 070import org.apache.directory.shared.kerberos.messages.Authenticator; 071import org.apache.directory.shared.kerberos.messages.Ticket; 072 073 074/** 075 * An utility class for Kerberos. 076 * 077 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 078 */ 079public class KerberosUtils 080{ 081 /** A constant for integer optional values */ 082 public static final int NULL = -1; 083 084 /** An empty list of principal names */ 085 public static final List<String> EMPTY_PRINCIPAL_NAME = new ArrayList<>(); 086 087 /** 088 * an order preserved map containing cipher names to the corresponding algorithm 089 * names in the descending order of strength 090 */ 091 private static final Map<String, String> cipherAlgoMap = new LinkedHashMap<>(); 092 093 public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" ); 094 095 /** Defines a default date format with a "yyyyMMddHHmmss'Z'" pattern */ 096 public static final SimpleDateFormat UTC_DATE_FORMAT = new SimpleDateFormat( "yyyyMMddHHmmss'Z'", Locale.ROOT ); 097 098 private static final Set<EncryptionType> oldEncTypes = new HashSet<>(); 099 100 static 101 { 102 UTC_DATE_FORMAT.setTimeZone( UTC_TIME_ZONE ); 103 104 cipherAlgoMap.put( "rc4", "ArcFourHmac" ); 105 cipherAlgoMap.put( "aes256", "AES256" ); 106 cipherAlgoMap.put( "aes128", "AES128" ); 107 cipherAlgoMap.put( "des3", "DESede" ); 108 cipherAlgoMap.put( "des", "DES" ); 109 110 oldEncTypes.add( DES_CBC_CRC ); 111 oldEncTypes.add( DES_CBC_MD4 ); 112 oldEncTypes.add( DES_CBC_MD5 ); 113 oldEncTypes.add( DES_EDE3_CBC_ENV_OID ); 114 oldEncTypes.add( DES3_CBC_MD5 ); 115 oldEncTypes.add( DES3_CBC_SHA1 ); 116 oldEncTypes.add( DES3_CBC_SHA1_KD ); 117 oldEncTypes.add( DSAWITHSHA1_CMSOID ); 118 oldEncTypes.add( MD5WITHRSAENCRYPTION_CMSOID ); 119 oldEncTypes.add( SHA1WITHRSAENCRYPTION_CMSOID ); 120 oldEncTypes.add( RC2CBC_ENVOID ); 121 oldEncTypes.add( RSAENCRYPTION_ENVOID ); 122 oldEncTypes.add( RSAES_OAEP_ENV_OID ); 123 oldEncTypes.add( RC4_HMAC ); 124 } 125 126 127 /** 128 * Parse a KerberosPrincipal instance and return the names. The Principal name 129 * is described in RFC 1964 : <br> 130 * <br> 131 * This name type corresponds to the single-string representation of a<br> 132 * Kerberos name. (Within the MIT Kerberos V5 implementation, such<br> 133 * names are parseable with the krb5_parse_name() function.) The<br> 134 * elements included within this name representation are as follows,<br> 135 * proceeding from the beginning of the string:<br> 136 * <br> 137 * (1) One or more principal name components; if more than one<br> 138 * principal name component is included, the components are<br> 139 * separated by `/`. Arbitrary octets may be included within<br> 140 * principal name components, with the following constraints and<br> 141 * special considerations:<br> 142 * <br> 143 * (1a) Any occurrence of the characters `@` or `/` within a<br> 144 * name component must be immediately preceded by the `\`<br> 145 * quoting character, to prevent interpretation as a component<br> 146 * or realm separator.<br> 147 * <br> 148 * (1b) The ASCII newline, tab, backspace, and null characters<br> 149 * may occur directly within the component or may be<br> 150 * represented, respectively, by `\n`, `\t`, `\b`, or `\0`.<br> 151 * <br> 152 * (1c) If the `\` quoting character occurs outside the contexts<br> 153 * described in (1a) and (1b) above, the following character is<br> 154 * interpreted literally. As a special case, this allows the<br> 155 * doubled representation `\\` to represent a single occurrence<br> 156 * of the quoting character.<br> 157 * <br> 158 * (1d) An occurrence of the `\` quoting character as the last<br> 159 * character of a component is illegal.<br> 160 * <br> 161 * (2) Optionally, a `@` character, signifying that a realm name<br> 162 * immediately follows. If no realm name element is included, the<br> 163 * local realm name is assumed. The `/` , `:`, and null characters<br> 164 * may not occur within a realm name; the `@`, newline, tab, and<br> 165 * backspace characters may be included using the quoting<br> 166 * conventions described in (1a), (1b), and (1c) above.<br> 167 * 168 * @param principal The principal to be parsed 169 * @return The names as a List of nameComponent 170 * 171 * @throws ParseException if the name is not valid 172 */ 173 public static List<String> getNames( KerberosPrincipal principal ) throws ParseException 174 { 175 if ( principal == null ) 176 { 177 return EMPTY_PRINCIPAL_NAME; 178 } 179 180 String names = principal.getName(); 181 182 if ( Strings.isEmpty( names ) ) 183 { 184 // Empty name... 185 return EMPTY_PRINCIPAL_NAME; 186 } 187 188 return getNames( names ); 189 } 190 191 192 /** 193 * Parse a PrincipalName and return the names. 194 */ 195 public static List<String> getNames( String principalNames ) throws ParseException 196 { 197 if ( principalNames == null ) 198 { 199 return EMPTY_PRINCIPAL_NAME; 200 } 201 202 List<String> nameComponents = new ArrayList<>(); 203 204 // Start the parsing. Another State Machine :) 205 char[] chars = principalNames.toCharArray(); 206 207 boolean escaped = false; 208 boolean done = false; 209 int start = 0; 210 int pos = 0; 211 212 for ( int i = 0; i < chars.length; i++ ) 213 { 214 pos = i; 215 216 switch ( chars[i] ) 217 { 218 case '\\': 219 escaped = !escaped; 220 break; 221 222 case '/': 223 if ( escaped ) 224 { 225 escaped = false; 226 } 227 else 228 { 229 // We have a new name component 230 if ( i - start > 0 ) 231 { 232 String nameComponent = new String( chars, start, i - start ); 233 nameComponents.add( nameComponent ); 234 start = i + 1; 235 } 236 else 237 { 238 throw new ParseException( I18n.err( I18n.ERR_628 ), i ); 239 } 240 } 241 242 break; 243 244 case '@': 245 if ( escaped ) 246 { 247 escaped = false; 248 } 249 else 250 { 251 // We have reached the realm : let's get out 252 done = true; 253 } 254 255 break; 256 257 default: 258 } 259 260 if ( done ) 261 { 262 // We have a new name component 263 if ( i - start > 0 ) 264 { 265 String nameComponent = new String( chars, start, i - start ); 266 nameComponents.add( nameComponent ); 267 start = i + 1; 268 } 269 else 270 { 271 throw new ParseException( I18n.err( I18n.ERR_628 ), i ); 272 } 273 274 break; 275 } 276 else if ( i + 1 == chars.length ) 277 { 278 // We have a new name component 279 String nameComponent = new String( chars, start, i - start + 1 ); 280 nameComponents.add( nameComponent ); 281 282 break; 283 } 284 } 285 286 if ( escaped ) 287 { 288 throw new ParseException( I18n.err( I18n.ERR_629 ), pos ); 289 } 290 291 return nameComponents; 292 } 293 294 295 /** 296 * Constructs a KerberosPrincipal from a PrincipalName and an 297 * optional realm 298 * 299 * @param principal The principal name and type 300 * @param realm The optional realm 301 * 302 * @return A KerberosPrincipal 303 */ 304 public static KerberosPrincipal getKerberosPrincipal( PrincipalName principal, String realm ) 305 { 306 String name = principal.getNameString(); 307 308 if ( !Strings.isEmpty( realm ) ) 309 { 310 name += '@' + realm; 311 } 312 313 return new KerberosPrincipal( name, principal.getNameType().getValue() ); 314 } 315 316 317 /** 318 * Get the matching encryption type from the configured types, searching 319 * into the requested types. We returns the first we find. 320 * 321 * @param requestedTypes The client encryption types 322 * @param configuredTypes The configured encryption types 323 * @return The first matching encryption type. 324 */ 325 public static EncryptionType getBestEncryptionType( Set<EncryptionType> requestedTypes, 326 Set<EncryptionType> configuredTypes ) 327 { 328 for ( EncryptionType encryptionType : configuredTypes ) 329 { 330 if ( requestedTypes.contains( encryptionType ) ) 331 { 332 return encryptionType; 333 } 334 } 335 336 return null; 337 } 338 339 340 /** 341 * Build a list of encryptionTypes 342 * 343 * @param encryptionTypes The encryptionTypes 344 * @return A list comma separated of the encryptionTypes 345 */ 346 public static String getEncryptionTypesString( Set<EncryptionType> encryptionTypes ) 347 { 348 StringBuilder sb = new StringBuilder(); 349 boolean isFirst = true; 350 351 for ( EncryptionType etype : encryptionTypes ) 352 { 353 if ( isFirst ) 354 { 355 isFirst = false; 356 } 357 else 358 { 359 sb.append( ", " ); 360 } 361 362 sb.append( etype ); 363 } 364 365 return sb.toString(); 366 } 367 368 369 public static boolean isKerberosString( byte[] value ) 370 { 371 if ( value == null ) 372 { 373 return false; 374 } 375 376 for ( byte b : value ) 377 { 378 if ( ( b < 0x20 ) || ( b > 0x7E ) ) 379 { 380 return false; 381 } 382 } 383 384 return true; 385 } 386 387 388 public static String getAlgoNameFromEncType( EncryptionType encType ) 389 { 390 String cipherName = Strings.toLowerCaseAscii( encType.getName() ); 391 392 for ( String c : cipherAlgoMap.keySet() ) 393 { 394 if ( cipherName.startsWith( c ) ) 395 { 396 return cipherAlgoMap.get( c ); 397 } 398 } 399 400 throw new IllegalArgumentException( "Unknown algorithm name for the encryption type " + encType ); 401 } 402 403 404 /** 405 * Order a list of EncryptionType in a decreasing strength order 406 * 407 * @param etypes The ETypes to order 408 * @return A list of ordered ETypes. he strongest is on the left. 409 */ 410 public static Set<EncryptionType> orderEtypesByStrength( Set<EncryptionType> etypes ) 411 { 412 Set<EncryptionType> ordered = new LinkedHashSet<>( etypes.size() ); 413 414 for ( String algo : cipherAlgoMap.values() ) 415 { 416 for ( EncryptionType encType : etypes ) 417 { 418 String foundAlgo = getAlgoNameFromEncType( encType ); 419 420 if ( algo.equals( foundAlgo ) ) 421 { 422 ordered.add( encType ); 423 } 424 } 425 } 426 427 return ordered; 428 } 429 430 431 /** 432 * Get a PrincipalStoreEntry given a principal. The ErrorType is used to indicate 433 * whether any resulting error pertains to a server or client. 434 */ 435 public static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType ) 436 throws KerberosException 437 { 438 PrincipalStoreEntry entry = null; 439 440 try 441 { 442 entry = store.getPrincipal( principal ); 443 } 444 catch ( Exception e ) 445 { 446 throw new KerberosException( errorType, e ); 447 } 448 449 if ( entry == null ) 450 { 451 throw new KerberosException( errorType ); 452 } 453 454 if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() ) 455 { 456 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY ); 457 } 458 459 return entry; 460 } 461 462 463 /** 464 * Verifies an AuthHeader using guidelines from RFC 1510 section A.10., "KRB_AP_REQ verification." 465 * 466 * @param authHeader 467 * @param ticket 468 * @param serverKey 469 * @param clockSkew 470 * @param replayCache 471 * @param emptyAddressesAllowed 472 * @param clientAddress 473 * @param lockBox 474 * @param authenticatorKeyUsage 475 * @param isValidate 476 * @return The authenticator. 477 * @throws KerberosException 478 */ 479 public static Authenticator verifyAuthHeader( ApReq authHeader, Ticket ticket, EncryptionKey serverKey, 480 long clockSkew, ReplayCache replayCache, boolean emptyAddressesAllowed, InetAddress clientAddress, 481 CipherTextHandler lockBox, KeyUsage authenticatorKeyUsage, boolean isValidate ) throws KerberosException 482 { 483 if ( authHeader.getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 ) 484 { 485 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION ); 486 } 487 488 if ( authHeader.getMessageType() != KerberosMessageType.AP_REQ ) 489 { 490 throw new KerberosException( ErrorType.KRB_AP_ERR_MSG_TYPE ); 491 } 492 493 if ( authHeader.getTicket().getTktVno() != KerberosConstants.KERBEROS_V5 ) 494 { 495 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION ); 496 } 497 498 EncryptionKey ticketKey = null; 499 500 if ( authHeader.getOption( ApOptions.USE_SESSION_KEY ) ) 501 { 502 ticketKey = authHeader.getTicket().getEncTicketPart().getKey(); 503 } 504 else 505 { 506 ticketKey = serverKey; 507 } 508 509 if ( ticketKey == null ) 510 { 511 // TODO - check server key version number, skvno; requires store 512 // if ( false ) 513 // { 514 // throw new KerberosException( ErrorType.KRB_AP_ERR_BADKEYVER ); 515 // } 516 517 throw new KerberosException( ErrorType.KRB_AP_ERR_NOKEY ); 518 } 519 520 byte[] encTicketPartData = lockBox.decrypt( ticketKey, ticket.getEncPart(), 521 KeyUsage.AS_OR_TGS_REP_TICKET_WITH_SRVKEY ); 522 EncTicketPart encPart = KerberosDecoder.decodeEncTicketPart( encTicketPartData ); 523 ticket.setEncTicketPart( encPart ); 524 525 byte[] authenticatorData = lockBox.decrypt( ticket.getEncTicketPart().getKey(), authHeader.getAuthenticator(), 526 authenticatorKeyUsage ); 527 528 Authenticator authenticator = KerberosDecoder.decodeAuthenticator( authenticatorData ); 529 530 if ( !authenticator.getCName().getNameString().equals( ticket.getEncTicketPart().getCName().getNameString() ) ) 531 { 532 throw new KerberosException( ErrorType.KRB_AP_ERR_BADMATCH ); 533 } 534 535 if ( ticket.getEncTicketPart().getClientAddresses() != null ) 536 { 537 if ( !ticket.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) ) ) 538 { 539 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR ); 540 } 541 } 542 else 543 { 544 if ( !emptyAddressesAllowed ) 545 { 546 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR ); 547 } 548 } 549 550 KerberosPrincipal serverPrincipal = getKerberosPrincipal( ticket.getSName(), ticket.getRealm() ); 551 KerberosPrincipal clientPrincipal = getKerberosPrincipal( authenticator.getCName(), authenticator.getCRealm() ); 552 KerberosTime clientTime = authenticator.getCtime(); 553 int clientMicroSeconds = authenticator.getCusec(); 554 555 if ( replayCache != null ) 556 { 557 if ( replayCache.isReplay( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) ) 558 { 559 throw new KerberosException( ErrorType.KRB_AP_ERR_REPEAT ); 560 } 561 562 replayCache.save( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ); 563 } 564 565 if ( !authenticator.getCtime().isInClockSkew( clockSkew ) ) 566 { 567 throw new KerberosException( ErrorType.KRB_AP_ERR_SKEW ); 568 } 569 570 /* 571 * "The server computes the age of the ticket: local (server) time minus 572 * the starttime inside the Ticket. If the starttime is later than the 573 * current time by more than the allowable clock skew, or if the INVALID 574 * flag is set in the ticket, the KRB_AP_ERR_TKT_NYV error is returned." 575 */ 576 KerberosTime startTime = ( ticket.getEncTicketPart().getStartTime() != null ) ? ticket.getEncTicketPart() 577 .getStartTime() : ticket.getEncTicketPart().getAuthTime(); 578 579 KerberosTime now = new KerberosTime(); 580 boolean isValidStartTime = startTime.lessThan( now ); 581 582 if ( !isValidStartTime || ( ticket.getEncTicketPart().getFlags().isInvalid() && !isValidate ) ) 583 { 584 // it hasn't yet become valid 585 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV ); 586 } 587 588 // TODO - doesn't take into account skew 589 if ( !ticket.getEncTicketPart().getEndTime().greaterThan( now ) ) 590 { 591 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED ); 592 } 593 594 authHeader.getApOptions().set( ApOptions.MUTUAL_REQUIRED ); 595 596 return authenticator; 597 } 598 599 600 /** 601 * checks if the given encryption type is *new* (ref sec#3.1.3 of rfc4120) 602 * 603 * @param eType the encryption type 604 * @return true if the encryption type is new, false otherwise 605 */ 606 public static boolean isNewEncryptionType( EncryptionType eType ) 607 { 608 return !oldEncTypes.contains( eType ); 609 } 610 611 /** 612 * Verifies an AuthHeader using guidelines from RFC 1510 section A.10., "KRB_AP_REQ verification." 613 * 614 * @param authHeader 615 * @param ticket 616 * @param serverKey 617 * @param clockSkew 618 * @param replayCache 619 * @param emptyAddressesAllowed 620 * @param clientAddress 621 * @param lockBox 622 * @param authenticatorKeyUsage 623 * @param isValidate 624 * @return The authenticator. 625 * @throws KerberosException 626 * 627 public static Authenticator verifyAuthHeader( ApplicationRequest authHeader, Ticket ticket, EncryptionKey serverKey, 628 long clockSkew, ReplayCache replayCache, boolean emptyAddressesAllowed, InetAddress clientAddress, 629 CipherTextHandler lockBox, KeyUsage authenticatorKeyUsage, boolean isValidate ) throws KerberosException 630 { 631 if ( authHeader.getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 ) 632 { 633 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION ); 634 } 635 636 if ( authHeader.getMessageType() != KerberosMessageType.AP_REQ ) 637 { 638 throw new KerberosException( ErrorType.KRB_AP_ERR_MSG_TYPE ); 639 } 640 641 if ( authHeader.getTicket().getTktVno() != KerberosConstants.KERBEROS_V5 ) 642 { 643 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION ); 644 } 645 646 EncryptionKey ticketKey = null; 647 648 if ( authHeader.getOption( ApOptions.USE_SESSION_KEY ) ) 649 { 650 ticketKey = authHeader.getTicket().getEncTicketPart().getSessionKey(); 651 } 652 else 653 { 654 ticketKey = serverKey; 655 } 656 657 if ( ticketKey == null ) 658 { 659 // TODO - check server key version number, skvno; requires store 660 if ( false ) 661 { 662 throw new KerberosException( ErrorType.KRB_AP_ERR_BADKEYVER ); 663 } 664 665 throw new KerberosException( ErrorType.KRB_AP_ERR_NOKEY ); 666 } 667 668 EncTicketPart encPart = ( EncTicketPart ) lockBox.unseal( EncTicketPart.class, ticketKey, ticket.getEncPart(), 669 KeyUsage.NUMBER2 ); 670 ticket.setEncTicketPart( encPart ); 671 672 Authenticator authenticator = ( Authenticator ) lockBox.unseal( Authenticator.class, ticket.getEncTicketPart().getSessionKey(), 673 authHeader.getEncPart(), authenticatorKeyUsage ); 674 675 if ( !authenticator.getClientPrincipal().getName().equals( ticket.getEncTicketPart().getClientPrincipal().getName() ) ) 676 { 677 throw new KerberosException( ErrorType.KRB_AP_ERR_BADMATCH ); 678 } 679 680 if ( ticket.getEncTicketPart().getClientAddresses() != null ) 681 { 682 if ( !ticket.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) ) ) 683 { 684 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR ); 685 } 686 } 687 else 688 { 689 if ( !emptyAddressesAllowed ) 690 { 691 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR ); 692 } 693 } 694 695 KerberosPrincipal serverPrincipal = ticket.getServerPrincipal(); 696 KerberosPrincipal clientPrincipal = authenticator.getClientPrincipal(); 697 KerberosTime clientTime = authenticator.getClientTime(); 698 int clientMicroSeconds = authenticator.getClientMicroSecond(); 699 700 if ( replayCache.isReplay( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) ) 701 { 702 throw new KerberosException( ErrorType.KRB_AP_ERR_REPEAT ); 703 } 704 705 replayCache.save( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ); 706 707 if ( !authenticator.getClientTime().isInClockSkew( clockSkew ) ) 708 { 709 throw new KerberosException( ErrorType.KRB_AP_ERR_SKEW ); 710 } 711 712 /* 713 * "The server computes the age of the ticket: local (server) time minus 714 * the starttime inside the Ticket. If the starttime is later than the 715 * current time by more than the allowable clock skew, or if the INVALID 716 * flag is set in the ticket, the KRB_AP_ERR_TKT_NYV error is returned." 717 * 718 KerberosTime startTime = ( ticket.getEncTicketPart().getStartTime() != null ) ? ticket.getEncTicketPart().getStartTime() : ticket.getEncTicketPart().getAuthTime(); 719 720 KerberosTime now = new KerberosTime(); 721 boolean isValidStartTime = startTime.lessThan( now ); 722 723 if ( !isValidStartTime || ( ticket.getEncTicketPart().getFlags().isInvalid() && !isValidate ) ) 724 { 725 // it hasn't yet become valid 726 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV ); 727 } 728 729 // TODO - doesn't take into account skew 730 if ( !ticket.getEncTicketPart().getEndTime().greaterThan( now ) ) 731 { 732 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED ); 733 } 734 735 authHeader.setOption( ApOptions.MUTUAL_REQUIRED ); 736 737 return authenticator; 738 }*/ 739}