1 /* 2 * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.jgss.krb5; 27 28 import org.ietf.jgss.*; 29 import sun.security.util.HexDumpEncoder; 30 import sun.security.jgss.GSSUtil; 31 import sun.security.jgss.GSSCaller; 32 import sun.security.jgss.spi.*; 33 import sun.security.jgss.TokenTracker; 34 import sun.security.krb5.*; 35 import java.io.InputStream; 36 import java.io.OutputStream; 37 import java.io.IOException; 38 import java.security.Provider; 39 import java.security.AccessController; 40 import java.security.AccessControlContext; 41 import java.security.Key; 42 import java.security.PrivilegedActionException; 43 import java.security.PrivilegedExceptionAction; 44 import javax.security.auth.Subject; 45 import javax.security.auth.kerberos.ServicePermission; 46 import javax.security.auth.kerberos.KerberosCredMessage; 47 import javax.security.auth.kerberos.KerberosPrincipal; 48 import javax.security.auth.kerberos.KerberosTicket; 49 import sun.security.krb5.internal.Ticket; 50 import sun.security.krb5.internal.AuthorizationData; 51 52 /** 53 * Implements the mechanism specific context class for the Kerberos v5 54 * GSS-API mechanism. 55 * 56 * @author Mayank Upadhyay 57 * @author Ram Marti 58 * @since 1.4 59 */ 60 class Krb5Context implements GSSContextSpi { 61 62 /* 63 * The different states that this context can be in. 64 */ 65 66 private static final int STATE_NEW = 1; 67 private static final int STATE_IN_PROCESS = 2; 68 private static final int STATE_DONE = 3; 69 private static final int STATE_DELETED = 4; 70 71 private int state = STATE_NEW; 72 73 public static final int SESSION_KEY = 0; 74 public static final int INITIATOR_SUBKEY = 1; 75 public static final int ACCEPTOR_SUBKEY = 2; 76 77 /* 78 * Optional features that the application can set and their default 79 * values. 80 */ 81 82 private boolean credDelegState = false; // now only useful at client 83 private boolean mutualAuthState = true; 84 private boolean replayDetState = true; 85 private boolean sequenceDetState = true; 86 private boolean confState = true; 87 private boolean integState = true; 88 private boolean delegPolicyState = false; 89 90 private boolean isConstrainedDelegationTried = false; 91 92 private int mySeqNumber; 93 private int peerSeqNumber; 94 private int keySrc; 95 private TokenTracker peerTokenTracker; 96 97 private CipherHelper cipherHelper = null; 98 99 /* 100 * Separate locks for the sequence numbers allow the application to 101 * receive tokens at the same time that it is sending tokens. Note 102 * that the application must synchronize the generation and 103 * transmission of tokens such that tokens are processed in the same 104 * order that they are generated. This is important when sequence 105 * checking of per-message tokens is enabled. 106 */ 107 108 private Object mySeqNumberLock = new Object(); 109 private Object peerSeqNumberLock = new Object(); 110 111 private EncryptionKey key; 112 private Krb5NameElement myName; 113 private Krb5NameElement peerName; 114 private int lifetime; 115 private boolean initiator; 116 private ChannelBinding channelBinding; 117 118 private Krb5CredElement myCred; 119 private Krb5CredElement delegatedCred; // Set only on acceptor side 120 121 // XXX See if the required info from these can be extracted and 122 // stored elsewhere 123 private Credentials tgt; 124 private Credentials serviceCreds; 125 private KrbApReq apReq; 126 Ticket serviceTicket; 127 final private GSSCaller caller; 128 private static final boolean DEBUG = Krb5Util.DEBUG; 129 130 /** 131 * Constructor for Krb5Context to be called on the context initiator's 132 * side. 133 */ 134 Krb5Context(GSSCaller caller, Krb5NameElement peerName, Krb5CredElement myCred, 135 int lifetime) 136 throws GSSException { 137 138 if (peerName == null) 139 throw new IllegalArgumentException("Cannot have null peer name"); 140 141 this.caller = caller; 142 this.peerName = peerName; 143 this.myCred = myCred; 144 this.lifetime = lifetime; 145 this.initiator = true; 146 } 147 148 /** 149 * Constructor for Krb5Context to be called on the context acceptor's 150 * side. 151 */ 152 Krb5Context(GSSCaller caller, Krb5CredElement myCred) 153 throws GSSException { 154 this.caller = caller; 155 this.myCred = myCred; 156 this.initiator = false; 157 } 158 159 /** 160 * Constructor for Krb5Context to import a previously exported context. 161 */ 162 public Krb5Context(GSSCaller caller, byte[] interProcessToken) 163 throws GSSException { 164 throw new GSSException(GSSException.UNAVAILABLE, 165 -1, "GSS Import Context not available"); 166 } 167 168 /** 169 * Method to determine if the context can be exported and then 170 * re-imported. 171 */ 172 public final boolean isTransferable() throws GSSException { 173 return false; 174 } 175 176 /** 177 * The lifetime remaining for this context. 178 */ 179 public final int getLifetime() { 180 // XXX Return service ticket lifetime 181 return GSSContext.INDEFINITE_LIFETIME; 182 } 183 184 /* 185 * Methods that may be invoked by the GSS framework in response 186 * to an application request for setting/getting these 187 * properties. 188 * 189 * These can only be called on the initiator side. 190 * 191 * Notice that an application can only request these 192 * properties. The mechanism may or may not support them. The 193 * application must make getXXX calls after context establishment 194 * to see if the mechanism implementations on both sides support 195 * these features. requestAnonymity is an exception where the 196 * application will want to call getAnonymityState prior to sending any 197 * GSS token during context establishment. 198 * 199 * Also note that the requests can only be placed before context 200 * establishment starts. i.e. when state is STATE_NEW 201 */ 202 203 /** 204 * Requests the desired lifetime. Can only be used on the context 205 * initiator's side. 206 */ 207 public void requestLifetime(int lifetime) throws GSSException { 208 if (state == STATE_NEW && isInitiator()) 209 this.lifetime = lifetime; 210 } 211 212 /** 213 * Requests that confidentiality be available. 214 */ 215 public final void requestConf(boolean value) throws GSSException { 216 if (state == STATE_NEW && isInitiator()) 217 confState = value; 218 } 219 220 /** 221 * Is confidentiality available? 222 */ 223 public final boolean getConfState() { 224 return confState; 225 } 226 227 /** 228 * Requests that integrity be available. 229 */ 230 public final void requestInteg(boolean value) throws GSSException { 231 if (state == STATE_NEW && isInitiator()) 232 integState = value; 233 } 234 235 /** 236 * Is integrity available? 237 */ 238 public final boolean getIntegState() { 239 return integState; 240 } 241 242 /** 243 * Requests that credential delegation be done during context 244 * establishment. 245 */ 246 public final void requestCredDeleg(boolean value) throws GSSException { 247 if (state == STATE_NEW && isInitiator()) { 248 if (myCred == null || !(myCred instanceof Krb5ProxyCredential)) { 249 credDelegState = value; 250 } 251 } 252 } 253 254 /** 255 * Is credential delegation enabled? 256 */ 257 public final boolean getCredDelegState() { 258 if (isInitiator()) { 259 return credDelegState; 260 } else { 261 // Server side deleg state is not flagged by credDelegState. 262 // It can use constrained delegation. 263 tryConstrainedDelegation(); 264 return delegatedCred != null; 265 } 266 } 267 268 /** 269 * Requests that mutual authentication be done during context 270 * establishment. Since this is fromm the client's perspective, it 271 * essentially requests that the server be authenticated. 272 */ 273 public final void requestMutualAuth(boolean value) throws GSSException { 274 if (state == STATE_NEW && isInitiator()) { 275 mutualAuthState = value; 276 } 277 } 278 279 /** 280 * Is mutual authentication enabled? Since this is from the client's 281 * perspective, it essentially meas that the server is being 282 * authenticated. 283 */ 284 public final boolean getMutualAuthState() { 285 return mutualAuthState; 286 } 287 288 /** 289 * Requests that replay detection be done on the GSS wrap and MIC 290 * tokens. 291 */ 292 public final void requestReplayDet(boolean value) throws GSSException { 293 if (state == STATE_NEW && isInitiator()) 294 replayDetState = value; 295 } 296 297 /** 298 * Is replay detection enabled on the GSS wrap and MIC tokens? 299 * We enable replay detection if sequence checking is enabled. 300 */ 301 public final boolean getReplayDetState() { 302 return replayDetState || sequenceDetState; 303 } 304 305 /** 306 * Requests that sequence checking be done on the GSS wrap and MIC 307 * tokens. 308 */ 309 public final void requestSequenceDet(boolean value) throws GSSException { 310 if (state == STATE_NEW && isInitiator()) 311 sequenceDetState = value; 312 } 313 314 /** 315 * Is sequence checking enabled on the GSS Wrap and MIC tokens? 316 * We enable sequence checking if replay detection is enabled. 317 */ 318 public final boolean getSequenceDetState() { 319 return sequenceDetState || replayDetState; 320 } 321 322 /** 323 * Requests that the deleg policy be respected. 324 */ 325 public final void requestDelegPolicy(boolean value) { 326 if (state == STATE_NEW && isInitiator()) 327 delegPolicyState = value; 328 } 329 330 /** 331 * Is deleg policy respected? 332 */ 333 public final boolean getDelegPolicyState() { 334 return delegPolicyState; 335 } 336 337 /* 338 * Anonymity is a little different in that after an application 339 * requests anonymity it will want to know whether the mechanism 340 * can support it or not, prior to sending any tokens across for 341 * context establishment. Since this is from the initiator's 342 * perspective, it essentially requests that the initiator be 343 * anonymous. 344 */ 345 346 public final void requestAnonymity(boolean value) throws GSSException { 347 // Ignore silently. Application will check back with 348 // getAnonymityState. 349 } 350 351 // RFC 2853 actually calls for this to be called after context 352 // establishment to get the right answer, but that is 353 // incorrect. The application may not want to send over any 354 // tokens if anonymity is not available. 355 public final boolean getAnonymityState() { 356 return false; 357 } 358 359 /* 360 * Package private methods invoked by other Krb5 plugin classes. 361 */ 362 363 /** 364 * Get the context specific DESCipher instance, invoked in 365 * MessageToken.init() 366 */ 367 final CipherHelper getCipherHelper(EncryptionKey ckey) throws GSSException { 368 EncryptionKey cipherKey = null; 369 if (cipherHelper == null) { 370 cipherKey = (getKey() == null) ? ckey: getKey(); 371 cipherHelper = new CipherHelper(cipherKey); 372 } 373 return cipherHelper; 374 } 375 376 final int incrementMySequenceNumber() { 377 int retVal; 378 synchronized (mySeqNumberLock) { 379 retVal = mySeqNumber; 380 mySeqNumber = retVal + 1; 381 } 382 return retVal; 383 } 384 385 final void resetMySequenceNumber(int seqNumber) { 386 if (DEBUG) { 387 System.out.println("Krb5Context setting mySeqNumber to: " 388 + seqNumber); 389 } 390 synchronized (mySeqNumberLock) { 391 mySeqNumber = seqNumber; 392 } 393 } 394 395 final void resetPeerSequenceNumber(int seqNumber) { 396 if (DEBUG) { 397 System.out.println("Krb5Context setting peerSeqNumber to: " 398 + seqNumber); 399 } 400 synchronized (peerSeqNumberLock) { 401 peerSeqNumber = seqNumber; 402 peerTokenTracker = new TokenTracker(peerSeqNumber); 403 } 404 } 405 406 final void setKey(int keySrc, EncryptionKey key) throws GSSException { 407 this.key = key; 408 this.keySrc = keySrc; 409 // %%% to do: should clear old cipherHelper first 410 cipherHelper = new CipherHelper(key); // Need to use new key 411 } 412 413 public final int getKeySrc() { 414 return keySrc; 415 } 416 417 private final EncryptionKey getKey() { 418 return key; 419 } 420 421 /** 422 * Called on the acceptor side to store the delegated credentials 423 * received in the AcceptSecContextToken. 424 */ 425 final void setDelegCred(Krb5CredElement delegatedCred) { 426 this.delegatedCred = delegatedCred; 427 } 428 429 /* 430 * While the application can only request the following features, 431 * other classes in the package can call the actual set methods 432 * for them. They are called as context establishment tokens are 433 * received on an acceptor side and the context feature list that 434 * the initiator wants becomes known. 435 */ 436 437 /* 438 * This method is also called by InitialToken.OverloadedChecksum if the 439 * TGT is not forwardable and the user requested delegation. 440 */ 441 final void setCredDelegState(boolean state) { 442 credDelegState = state; 443 } 444 445 final void setMutualAuthState(boolean state) { 446 mutualAuthState = state; 447 } 448 449 final void setReplayDetState(boolean state) { 450 replayDetState = state; 451 } 452 453 final void setSequenceDetState(boolean state) { 454 sequenceDetState = state; 455 } 456 457 final void setConfState(boolean state) { 458 confState = state; 459 } 460 461 final void setIntegState(boolean state) { 462 integState = state; 463 } 464 465 final void setDelegPolicyState(boolean state) { 466 delegPolicyState = state; 467 } 468 469 /** 470 * Sets the channel bindings to be used during context 471 * establishment. 472 */ 473 public final void setChannelBinding(ChannelBinding channelBinding) 474 throws GSSException { 475 this.channelBinding = channelBinding; 476 } 477 478 final ChannelBinding getChannelBinding() { 479 return channelBinding; 480 } 481 482 /** 483 * Returns the mechanism oid. 484 * 485 * @return the Oid of this context 486 */ 487 public final Oid getMech() { 488 return (Krb5MechFactory.GSS_KRB5_MECH_OID); 489 } 490 491 /** 492 * Returns the context initiator name. 493 * 494 * @return initiator name 495 * @exception GSSException 496 */ 497 public final GSSNameSpi getSrcName() throws GSSException { 498 return (isInitiator()? myName : peerName); 499 } 500 501 /** 502 * Returns the context acceptor. 503 * 504 * @return context acceptor(target) name 505 * @exception GSSException 506 */ 507 public final GSSNameSpi getTargName() throws GSSException { 508 return (!isInitiator()? myName : peerName); 509 } 510 511 /** 512 * Returns the delegated credential for the context. This 513 * is an optional feature of contexts which not all 514 * mechanisms will support. A context can be requested to 515 * support credential delegation by using the <b>CRED_DELEG</b>, 516 * or it can request for a constrained delegation. 517 * This is only valid on the acceptor side of the context. 518 * @return GSSCredentialSpi object for the delegated credential 519 * @exception GSSException 520 * @see GSSContext#getDelegCredState 521 */ 522 public final GSSCredentialSpi getDelegCred() throws GSSException { 523 if (state != STATE_IN_PROCESS && state != STATE_DONE) 524 throw new GSSException(GSSException.NO_CONTEXT); 525 if (isInitiator()) { 526 throw new GSSException(GSSException.NO_CRED); 527 } 528 tryConstrainedDelegation(); 529 if (delegatedCred == null) { 530 throw new GSSException(GSSException.NO_CRED); 531 } 532 return delegatedCred; 533 } 534 535 private void tryConstrainedDelegation() { 536 if (state != STATE_IN_PROCESS && state != STATE_DONE) { 537 return; 538 } 539 // We will only try constrained delegation once (if necessary). 540 if (!isConstrainedDelegationTried) { 541 if (delegatedCred == null) { 542 if (DEBUG) { 543 System.out.println(">>> Constrained deleg from " + caller); 544 } 545 // The constrained delegation part. The acceptor needs to have 546 // isInitiator=true in order to get a TGT, either earlier at 547 // logon stage, if useSubjectCredsOnly, or now. 548 try { 549 delegatedCred = new Krb5ProxyCredential( 550 Krb5InitCredential.getInstance( 551 GSSCaller.CALLER_ACCEPT, myName, lifetime), 552 peerName, serviceTicket); 553 } catch (GSSException gsse) { 554 // OK, delegatedCred is null then 555 } 556 } 557 isConstrainedDelegationTried = true; 558 } 559 } 560 /** 561 * Tests if this is the initiator side of the context. 562 * 563 * @return boolean indicating if this is initiator (true) 564 * or target (false) 565 */ 566 public final boolean isInitiator() { 567 return initiator; 568 } 569 570 /** 571 * Tests if the context can be used for per-message service. 572 * Context may allow the calls to the per-message service 573 * functions before being fully established. 574 * 575 * @return boolean indicating if per-message methods can 576 * be called. 577 */ 578 public final boolean isProtReady() { 579 return (state == STATE_DONE); 580 } 581 582 /** 583 * Initiator context establishment call. This method may be 584 * required to be called several times. A CONTINUE_NEEDED return 585 * call indicates that more calls are needed after the next token 586 * is received from the peer. 587 * 588 * @param is contains the token received from the peer. On the 589 * first call it will be ignored. 590 * @return any token required to be sent to the peer 591 * It is responsibility of the caller 592 * to send the token to its peer for processing. 593 * @exception GSSException 594 */ 595 public final byte[] initSecContext(InputStream is, int mechTokenSize) 596 throws GSSException { 597 598 byte[] retVal = null; 599 InitialToken token = null; 600 int errorCode = GSSException.FAILURE; 601 if (DEBUG) { 602 System.out.println("Entered Krb5Context.initSecContext with " + 603 "state=" + printState(state)); 604 } 605 if (!isInitiator()) { 606 throw new GSSException(GSSException.FAILURE, -1, 607 "initSecContext on an acceptor " + 608 "GSSContext"); 609 } 610 611 try { 612 if (state == STATE_NEW) { 613 state = STATE_IN_PROCESS; 614 615 errorCode = GSSException.NO_CRED; 616 617 if (myCred == null) { 618 myCred = Krb5InitCredential.getInstance(caller, myName, 619 GSSCredential.DEFAULT_LIFETIME); 620 } else if (!myCred.isInitiatorCredential()) { 621 throw new GSSException(errorCode, -1, 622 "No TGT available"); 623 } 624 myName = (Krb5NameElement) myCred.getName(); 625 final Krb5ProxyCredential second; 626 if (myCred instanceof Krb5InitCredential) { 627 second = null; 628 tgt = ((Krb5InitCredential) myCred).getKrb5Credentials(); 629 } else { 630 second = (Krb5ProxyCredential) myCred; 631 tgt = second.self.getKrb5Credentials(); 632 } 633 634 checkPermission(peerName.getKrb5PrincipalName().getName(), 635 "initiate"); 636 /* 637 * If useSubjectCredsonly is true then 638 * we check whether we already have the ticket 639 * for this service in the Subject and reuse it 640 */ 641 642 final AccessControlContext acc = 643 AccessController.getContext(); 644 645 if (GSSUtil.useSubjectCredsOnly(caller)) { 646 KerberosTicket kerbTicket = null; 647 try { 648 // get service ticket from caller's subject 649 kerbTicket = AccessController.doPrivileged( 650 new PrivilegedExceptionAction<KerberosTicket>() { 651 public KerberosTicket run() throws Exception { 652 // XXX to be cleaned 653 // highly consider just calling: 654 // Subject.getSubject 655 // SubjectComber.find 656 // instead of Krb5Util.getTicket 657 return Krb5Util.getTicket( 658 GSSCaller.CALLER_UNKNOWN, 659 // since it's useSubjectCredsOnly here, 660 // don't worry about the null 661 second == null ? 662 myName.getKrb5PrincipalName().getName(): 663 second.getName().getKrb5PrincipalName().getName(), 664 peerName.getKrb5PrincipalName().getName(), 665 acc); 666 }}); 667 } catch (PrivilegedActionException e) { 668 if (DEBUG) { 669 System.out.println("Attempt to obtain service" 670 + " ticket from the subject failed!"); 671 } 672 } 673 if (kerbTicket != null) { 674 if (DEBUG) { 675 System.out.println("Found service ticket in " + 676 "the subject" + 677 kerbTicket); 678 } 679 680 // convert Ticket to serviceCreds 681 // XXX Should merge these two object types 682 // avoid converting back and forth 683 serviceCreds = Krb5Util.ticketToCreds(kerbTicket); 684 } 685 } 686 if (serviceCreds == null) { 687 // either we did not find the serviceCreds in the 688 // Subject or useSubjectCreds is false 689 if (DEBUG) { 690 System.out.println("Service ticket not found in " + 691 "the subject"); 692 } 693 // Get Service ticket using the Kerberos protocols 694 if (second == null) { 695 serviceCreds = Credentials.acquireServiceCreds( 696 peerName.getKrb5PrincipalName().getName(), 697 tgt); 698 } else { 699 serviceCreds = Credentials.acquireS4U2proxyCreds( 700 peerName.getKrb5PrincipalName().getName(), 701 second.tkt, 702 second.getName().getKrb5PrincipalName(), 703 tgt); 704 } 705 if (GSSUtil.useSubjectCredsOnly(caller)) { 706 final Subject subject = 707 AccessController.doPrivileged( 708 new java.security.PrivilegedAction<Subject>() { 709 public Subject run() { 710 return (Subject.getSubject(acc)); 711 } 712 }); 713 if (subject != null && 714 !subject.isReadOnly()) { 715 /* 716 * Store the service credentials as 717 * javax.security.auth.kerberos.KerberosTicket in 718 * the Subject. We could wait until the context is 719 * successfully established; however it is easier 720 * to do it here and there is no harm. 721 */ 722 final KerberosTicket kt = 723 Krb5Util.credsToTicket(serviceCreds); 724 AccessController.doPrivileged ( 725 new java.security.PrivilegedAction<Void>() { 726 public Void run() { 727 subject.getPrivateCredentials().add(kt); 728 return null; 729 } 730 }); 731 } else { 732 // log it for debugging purpose 733 if (DEBUG) { 734 System.out.println("Subject is " + 735 "readOnly;Kerberos Service "+ 736 "ticket not stored"); 737 } 738 } 739 } 740 } 741 742 errorCode = GSSException.FAILURE; 743 token = new InitSecContextToken(this, tgt, serviceCreds); 744 apReq = ((InitSecContextToken)token).getKrbApReq(); 745 retVal = token.encode(); 746 myCred = null; 747 if (!getMutualAuthState()) { 748 state = STATE_DONE; 749 } 750 if (DEBUG) { 751 System.out.println("Created InitSecContextToken:\n"+ 752 new HexDumpEncoder().encodeBuffer(retVal)); 753 } 754 } else if (state == STATE_IN_PROCESS) { 755 // No need to write anything; 756 // just validate the incoming token 757 new AcceptSecContextToken(this, serviceCreds, apReq, is); 758 apReq = null; 759 state = STATE_DONE; 760 } else { 761 // XXX Use logging API? 762 if (DEBUG) { 763 System.out.println(state); 764 } 765 } 766 } catch (KrbException e) { 767 if (DEBUG) { 768 e.printStackTrace(); 769 } 770 GSSException gssException = 771 new GSSException(errorCode, -1, e.getMessage()); 772 gssException.initCause(e); 773 throw gssException; 774 } catch (IOException e) { 775 GSSException gssException = 776 new GSSException(errorCode, -1, e.getMessage()); 777 gssException.initCause(e); 778 throw gssException; 779 } 780 return retVal; 781 } 782 783 public final boolean isEstablished() { 784 return (state == STATE_DONE); 785 } 786 787 /** 788 * Acceptor's context establishment call. This method may be 789 * required to be called several times. A CONTINUE_NEEDED return 790 * call indicates that more calls are needed after the next token 791 * is received from the peer. 792 * 793 * @param is contains the token received from the peer. 794 * @return any token required to be sent to the peer 795 * It is responsibility of the caller 796 * to send the token to its peer for processing. 797 * @exception GSSException 798 */ 799 public final byte[] acceptSecContext(InputStream is, int mechTokenSize) 800 throws GSSException { 801 802 byte[] retVal = null; 803 804 if (DEBUG) { 805 System.out.println("Entered Krb5Context.acceptSecContext with " + 806 "state=" + printState(state)); 807 } 808 809 if (isInitiator()) { 810 throw new GSSException(GSSException.FAILURE, -1, 811 "acceptSecContext on an initiator " + 812 "GSSContext"); 813 } 814 try { 815 if (state == STATE_NEW) { 816 state = STATE_IN_PROCESS; 817 if (myCred == null) { 818 myCred = Krb5AcceptCredential.getInstance(caller, myName); 819 } else if (!myCred.isAcceptorCredential()) { 820 throw new GSSException(GSSException.NO_CRED, -1, 821 "No Secret Key available"); 822 } 823 myName = (Krb5NameElement) myCred.getName(); 824 825 // If there is already a bound name, check now 826 if (myName != null) { 827 Krb5MechFactory.checkAcceptCredPermission(myName, myName); 828 } 829 830 InitSecContextToken token = new InitSecContextToken(this, 831 (Krb5AcceptCredential) myCred, is); 832 PrincipalName clientName = token.getKrbApReq().getClient(); 833 peerName = Krb5NameElement.getInstance(clientName); 834 835 // If unbound, check after the bound name is found 836 if (myName == null) { 837 myName = Krb5NameElement.getInstance( 838 token.getKrbApReq().getCreds().getServer()); 839 Krb5MechFactory.checkAcceptCredPermission(myName, myName); 840 } 841 842 if (getMutualAuthState()) { 843 retVal = new AcceptSecContextToken(this, 844 token.getKrbApReq()).encode(); 845 } 846 serviceTicket = token.getKrbApReq().getCreds().getTicket(); 847 myCred = null; 848 state = STATE_DONE; 849 } else { 850 // XXX Use logging API? 851 if (DEBUG) { 852 System.out.println(state); 853 } 854 } 855 } catch (KrbException e) { 856 GSSException gssException = 857 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 858 gssException.initCause(e); 859 throw gssException; 860 } catch (IOException e) { 861 if (DEBUG) { 862 e.printStackTrace(); 863 } 864 GSSException gssException = 865 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 866 gssException.initCause(e); 867 throw gssException; 868 } 869 870 return retVal; 871 } 872 873 /** 874 * Queries the context for largest data size to accommodate 875 * the specified protection and be <= maxTokSize. 876 * 877 * @param qop the quality of protection that the context will be 878 * asked to provide. 879 * @param confReq a flag indicating whether confidentiality will be 880 * requested or not 881 * @param outputSize the maximum size of the output token 882 * @return the maximum size for the input message that can be 883 * provided to the wrap() method in order to guarantee that these 884 * requirements are met. 885 * @throws GSSException 886 */ 887 public final int getWrapSizeLimit(int qop, boolean confReq, 888 int maxTokSize) throws GSSException { 889 890 int retVal = 0; 891 if (cipherHelper.getProto() == 0) { 892 retVal = WrapToken.getSizeLimit(qop, confReq, maxTokSize, 893 getCipherHelper(null)); 894 } else if (cipherHelper.getProto() == 1) { 895 retVal = WrapToken_v2.getSizeLimit(qop, confReq, maxTokSize, 896 getCipherHelper(null)); 897 } 898 return retVal; 899 } 900 901 /* 902 * Per-message calls depend on the sequence number. The sequence number 903 * synchronization is at a finer granularity because wrap and getMIC 904 * care about the local sequence number (mySeqNumber) where are unwrap 905 * and verifyMIC care about the remote sequence number (peerSeqNumber). 906 */ 907 908 public final byte[] wrap(byte[] inBuf, int offset, int len, 909 MessageProp msgProp) throws GSSException { 910 if (DEBUG) { 911 System.out.println("Krb5Context.wrap: data=[" 912 + getHexBytes(inBuf, offset, len) 913 + "]"); 914 } 915 916 if (state != STATE_DONE) 917 throw new GSSException(GSSException.NO_CONTEXT, -1, 918 "Wrap called in invalid state!"); 919 920 byte[] encToken = null; 921 try { 922 if (cipherHelper.getProto() == 0) { 923 WrapToken token = 924 new WrapToken(this, msgProp, inBuf, offset, len); 925 encToken = token.encode(); 926 } else if (cipherHelper.getProto() == 1) { 927 WrapToken_v2 token = 928 new WrapToken_v2(this, msgProp, inBuf, offset, len); 929 encToken = token.encode(); 930 } 931 if (DEBUG) { 932 System.out.println("Krb5Context.wrap: token=[" 933 + getHexBytes(encToken, 0, encToken.length) 934 + "]"); 935 } 936 return encToken; 937 } catch (IOException e) { 938 encToken = null; 939 GSSException gssException = 940 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 941 gssException.initCause(e); 942 throw gssException; 943 } 944 } 945 946 public final int wrap(byte[] inBuf, int inOffset, int len, 947 byte[] outBuf, int outOffset, 948 MessageProp msgProp) throws GSSException { 949 950 if (state != STATE_DONE) 951 throw new GSSException(GSSException.NO_CONTEXT, -1, 952 "Wrap called in invalid state!"); 953 954 int retVal = 0; 955 try { 956 if (cipherHelper.getProto() == 0) { 957 WrapToken token = 958 new WrapToken(this, msgProp, inBuf, inOffset, len); 959 retVal = token.encode(outBuf, outOffset); 960 } else if (cipherHelper.getProto() == 1) { 961 WrapToken_v2 token = 962 new WrapToken_v2(this, msgProp, inBuf, inOffset, len); 963 retVal = token.encode(outBuf, outOffset); 964 } 965 if (DEBUG) { 966 System.out.println("Krb5Context.wrap: token=[" 967 + getHexBytes(outBuf, outOffset, retVal) 968 + "]"); 969 } 970 return retVal; 971 } catch (IOException e) { 972 retVal = 0; 973 GSSException gssException = 974 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 975 gssException.initCause(e); 976 throw gssException; 977 } 978 } 979 980 public final void wrap(byte[] inBuf, int offset, int len, 981 OutputStream os, MessageProp msgProp) 982 throws GSSException { 983 984 if (state != STATE_DONE) 985 throw new GSSException(GSSException.NO_CONTEXT, -1, 986 "Wrap called in invalid state!"); 987 988 byte[] encToken = null; 989 try { 990 if (cipherHelper.getProto() == 0) { 991 WrapToken token = 992 new WrapToken(this, msgProp, inBuf, offset, len); 993 token.encode(os); 994 if (DEBUG) { 995 encToken = token.encode(); 996 } 997 } else if (cipherHelper.getProto() == 1) { 998 WrapToken_v2 token = 999 new WrapToken_v2(this, msgProp, inBuf, offset, len); 1000 token.encode(os); 1001 if (DEBUG) { 1002 encToken = token.encode(); 1003 } 1004 } 1005 } catch (IOException e) { 1006 GSSException gssException = 1007 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1008 gssException.initCause(e); 1009 throw gssException; 1010 } 1011 1012 if (DEBUG) { 1013 System.out.println("Krb5Context.wrap: token=[" 1014 + getHexBytes(encToken, 0, encToken.length) 1015 + "]"); 1016 } 1017 } 1018 1019 public final void wrap(InputStream is, OutputStream os, 1020 MessageProp msgProp) throws GSSException { 1021 1022 byte[] data; 1023 try { 1024 data = new byte[is.available()]; 1025 is.read(data); 1026 } catch (IOException e) { 1027 GSSException gssException = 1028 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1029 gssException.initCause(e); 1030 throw gssException; 1031 } 1032 wrap(data, 0, data.length, os, msgProp); 1033 } 1034 1035 public final byte[] unwrap(byte[] inBuf, int offset, int len, 1036 MessageProp msgProp) 1037 throws GSSException { 1038 1039 if (DEBUG) { 1040 System.out.println("Krb5Context.unwrap: token=[" 1041 + getHexBytes(inBuf, offset, len) 1042 + "]"); 1043 } 1044 1045 if (state != STATE_DONE) { 1046 throw new GSSException(GSSException.NO_CONTEXT, -1, 1047 " Unwrap called in invalid state!"); 1048 } 1049 1050 byte[] data = null; 1051 if (cipherHelper.getProto() == 0) { 1052 WrapToken token = 1053 new WrapToken(this, inBuf, offset, len, msgProp); 1054 data = token.getData(); 1055 setSequencingAndReplayProps(token, msgProp); 1056 } else if (cipherHelper.getProto() == 1) { 1057 WrapToken_v2 token = 1058 new WrapToken_v2(this, inBuf, offset, len, msgProp); 1059 data = token.getData(); 1060 setSequencingAndReplayProps(token, msgProp); 1061 } 1062 1063 if (DEBUG) { 1064 System.out.println("Krb5Context.unwrap: data=[" 1065 + getHexBytes(data, 0, data.length) 1066 + "]"); 1067 } 1068 1069 return data; 1070 } 1071 1072 public final int unwrap(byte[] inBuf, int inOffset, int len, 1073 byte[] outBuf, int outOffset, 1074 MessageProp msgProp) throws GSSException { 1075 1076 if (state != STATE_DONE) 1077 throw new GSSException(GSSException.NO_CONTEXT, -1, 1078 "Unwrap called in invalid state!"); 1079 1080 if (cipherHelper.getProto() == 0) { 1081 WrapToken token = 1082 new WrapToken(this, inBuf, inOffset, len, msgProp); 1083 len = token.getData(outBuf, outOffset); 1084 setSequencingAndReplayProps(token, msgProp); 1085 } else if (cipherHelper.getProto() == 1) { 1086 WrapToken_v2 token = 1087 new WrapToken_v2(this, inBuf, inOffset, len, msgProp); 1088 len = token.getData(outBuf, outOffset); 1089 setSequencingAndReplayProps(token, msgProp); 1090 } 1091 return len; 1092 } 1093 1094 public final int unwrap(InputStream is, 1095 byte[] outBuf, int outOffset, 1096 MessageProp msgProp) throws GSSException { 1097 1098 if (state != STATE_DONE) 1099 throw new GSSException(GSSException.NO_CONTEXT, -1, 1100 "Unwrap called in invalid state!"); 1101 1102 int len = 0; 1103 if (cipherHelper.getProto() == 0) { 1104 WrapToken token = new WrapToken(this, is, msgProp); 1105 len = token.getData(outBuf, outOffset); 1106 setSequencingAndReplayProps(token, msgProp); 1107 } else if (cipherHelper.getProto() == 1) { 1108 WrapToken_v2 token = new WrapToken_v2(this, is, msgProp); 1109 len = token.getData(outBuf, outOffset); 1110 setSequencingAndReplayProps(token, msgProp); 1111 } 1112 return len; 1113 } 1114 1115 1116 public final void unwrap(InputStream is, OutputStream os, 1117 MessageProp msgProp) throws GSSException { 1118 1119 if (state != STATE_DONE) 1120 throw new GSSException(GSSException.NO_CONTEXT, -1, 1121 "Unwrap called in invalid state!"); 1122 1123 byte[] data = null; 1124 if (cipherHelper.getProto() == 0) { 1125 WrapToken token = new WrapToken(this, is, msgProp); 1126 data = token.getData(); 1127 setSequencingAndReplayProps(token, msgProp); 1128 } else if (cipherHelper.getProto() == 1) { 1129 WrapToken_v2 token = new WrapToken_v2(this, is, msgProp); 1130 data = token.getData(); 1131 setSequencingAndReplayProps(token, msgProp); 1132 } 1133 1134 try { 1135 os.write(data); 1136 } catch (IOException e) { 1137 GSSException gssException = 1138 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1139 gssException.initCause(e); 1140 throw gssException; 1141 } 1142 } 1143 1144 public final byte[] getMIC(byte[] inMsg, int offset, int len, 1145 MessageProp msgProp) 1146 throws GSSException { 1147 1148 byte[] micToken = null; 1149 try { 1150 if (cipherHelper.getProto() == 0) { 1151 MicToken token = 1152 new MicToken(this, msgProp, inMsg, offset, len); 1153 micToken = token.encode(); 1154 } else if (cipherHelper.getProto() == 1) { 1155 MicToken_v2 token = 1156 new MicToken_v2(this, msgProp, inMsg, offset, len); 1157 micToken = token.encode(); 1158 } 1159 return micToken; 1160 } catch (IOException e) { 1161 micToken = null; 1162 GSSException gssException = 1163 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1164 gssException.initCause(e); 1165 throw gssException; 1166 } 1167 } 1168 1169 private int getMIC(byte[] inMsg, int offset, int len, 1170 byte[] outBuf, int outOffset, 1171 MessageProp msgProp) 1172 throws GSSException { 1173 1174 int retVal = 0; 1175 try { 1176 if (cipherHelper.getProto() == 0) { 1177 MicToken token = 1178 new MicToken(this, msgProp, inMsg, offset, len); 1179 retVal = token.encode(outBuf, outOffset); 1180 } else if (cipherHelper.getProto() == 1) { 1181 MicToken_v2 token = 1182 new MicToken_v2(this, msgProp, inMsg, offset, len); 1183 retVal = token.encode(outBuf, outOffset); 1184 } 1185 return retVal; 1186 } catch (IOException e) { 1187 retVal = 0; 1188 GSSException gssException = 1189 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1190 gssException.initCause(e); 1191 throw gssException; 1192 } 1193 } 1194 1195 /* 1196 * Checksum calculation requires a byte[]. Hence might as well pass 1197 * a byte[] into the MicToken constructor. However, writing the 1198 * token can be optimized for cases where the application passed in 1199 * an OutputStream. 1200 */ 1201 1202 private void getMIC(byte[] inMsg, int offset, int len, 1203 OutputStream os, MessageProp msgProp) 1204 throws GSSException { 1205 1206 try { 1207 if (cipherHelper.getProto() == 0) { 1208 MicToken token = 1209 new MicToken(this, msgProp, inMsg, offset, len); 1210 token.encode(os); 1211 } else if (cipherHelper.getProto() == 1) { 1212 MicToken_v2 token = 1213 new MicToken_v2(this, msgProp, inMsg, offset, len); 1214 token.encode(os); 1215 } 1216 } catch (IOException e) { 1217 GSSException gssException = 1218 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1219 gssException.initCause(e); 1220 throw gssException; 1221 } 1222 } 1223 1224 public final void getMIC(InputStream is, OutputStream os, 1225 MessageProp msgProp) throws GSSException { 1226 byte[] data; 1227 try { 1228 data = new byte[is.available()]; 1229 is.read(data); 1230 } catch (IOException e) { 1231 GSSException gssException = 1232 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1233 gssException.initCause(e); 1234 throw gssException; 1235 } 1236 getMIC(data, 0, data.length, os, msgProp); 1237 } 1238 1239 public final void verifyMIC(byte[] inTok, int tokOffset, int tokLen, 1240 byte[] inMsg, int msgOffset, int msgLen, 1241 MessageProp msgProp) 1242 throws GSSException { 1243 1244 if (cipherHelper.getProto() == 0) { 1245 MicToken token = 1246 new MicToken(this, inTok, tokOffset, tokLen, msgProp); 1247 token.verify(inMsg, msgOffset, msgLen); 1248 setSequencingAndReplayProps(token, msgProp); 1249 } else if (cipherHelper.getProto() == 1) { 1250 MicToken_v2 token = 1251 new MicToken_v2(this, inTok, tokOffset, tokLen, msgProp); 1252 token.verify(inMsg, msgOffset, msgLen); 1253 setSequencingAndReplayProps(token, msgProp); 1254 } 1255 } 1256 1257 private void verifyMIC(InputStream is, 1258 byte[] inMsg, int msgOffset, int msgLen, 1259 MessageProp msgProp) 1260 throws GSSException { 1261 1262 if (cipherHelper.getProto() == 0) { 1263 MicToken token = new MicToken(this, is, msgProp); 1264 token.verify(inMsg, msgOffset, msgLen); 1265 setSequencingAndReplayProps(token, msgProp); 1266 } else if (cipherHelper.getProto() == 1) { 1267 MicToken_v2 token = new MicToken_v2(this, is, msgProp); 1268 token.verify(inMsg, msgOffset, msgLen); 1269 setSequencingAndReplayProps(token, msgProp); 1270 } 1271 } 1272 1273 public final void verifyMIC(InputStream is, InputStream msgStr, 1274 MessageProp mProp) throws GSSException { 1275 byte[] msg; 1276 try { 1277 msg = new byte[msgStr.available()]; 1278 msgStr.read(msg); 1279 } catch (IOException e) { 1280 GSSException gssException = 1281 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 1282 gssException.initCause(e); 1283 throw gssException; 1284 } 1285 verifyMIC(is, msg, 0, msg.length, mProp); 1286 } 1287 1288 /** 1289 * Produces a token representing this context. After this call 1290 * the context will no longer be usable until an import is 1291 * performed on the returned token. 1292 * 1293 * @param os the output token will be written to this stream 1294 * @exception GSSException 1295 */ 1296 public final byte[] export() throws GSSException { 1297 throw new GSSException(GSSException.UNAVAILABLE, -1, 1298 "GSS Export Context not available"); 1299 } 1300 1301 /** 1302 * Releases context resources and terminates the 1303 * context between 2 peer. 1304 * 1305 * @exception GSSException with major codes NO_CONTEXT, FAILURE. 1306 */ 1307 1308 public final void dispose() throws GSSException { 1309 state = STATE_DELETED; 1310 delegatedCred = null; 1311 tgt = null; 1312 serviceCreds = null; 1313 key = null; 1314 } 1315 1316 public final Provider getProvider() { 1317 return Krb5MechFactory.PROVIDER; 1318 } 1319 1320 /** 1321 * Sets replay and sequencing information for a message token received 1322 * form the peer. 1323 */ 1324 private void setSequencingAndReplayProps(MessageToken token, 1325 MessageProp prop) { 1326 if (replayDetState || sequenceDetState) { 1327 int seqNum = token.getSequenceNumber(); 1328 peerTokenTracker.getProps(seqNum, prop); 1329 } 1330 } 1331 1332 /** 1333 * Sets replay and sequencing information for a message token received 1334 * form the peer. 1335 */ 1336 private void setSequencingAndReplayProps(MessageToken_v2 token, 1337 MessageProp prop) { 1338 if (replayDetState || sequenceDetState) { 1339 int seqNum = token.getSequenceNumber(); 1340 peerTokenTracker.getProps(seqNum, prop); 1341 } 1342 } 1343 1344 private void checkPermission(String principal, String action) { 1345 SecurityManager sm = System.getSecurityManager(); 1346 if (sm != null) { 1347 ServicePermission perm = 1348 new ServicePermission(principal, action); 1349 sm.checkPermission(perm); 1350 } 1351 } 1352 1353 private static String getHexBytes(byte[] bytes, int pos, int len) { 1354 1355 StringBuilder sb = new StringBuilder(); 1356 for (int i = 0; i < len; i++) { 1357 1358 int b1 = (bytes[i]>>4) & 0x0f; 1359 int b2 = bytes[i] & 0x0f; 1360 1361 sb.append(Integer.toHexString(b1)); 1362 sb.append(Integer.toHexString(b2)); 1363 sb.append(' '); 1364 } 1365 return sb.toString(); 1366 } 1367 1368 private static String printState(int state) { 1369 switch (state) { 1370 case STATE_NEW: 1371 return ("STATE_NEW"); 1372 case STATE_IN_PROCESS: 1373 return ("STATE_IN_PROCESS"); 1374 case STATE_DONE: 1375 return ("STATE_DONE"); 1376 case STATE_DELETED: 1377 return ("STATE_DELETED"); 1378 default: 1379 return ("Unknown state " + state); 1380 } 1381 } 1382 1383 GSSCaller getCaller() { 1384 // Currently used by InitialToken only 1385 return caller; 1386 } 1387 1388 /** 1389 * The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY) 1390 */ 1391 static class KerberosSessionKey implements Key { 1392 private static final long serialVersionUID = 699307378954123869L; 1393 1394 @SuppressWarnings("serial") // Not statically typed as Serializable 1395 private final EncryptionKey key; 1396 1397 KerberosSessionKey(EncryptionKey key) { 1398 this.key = key; 1399 } 1400 1401 @Override 1402 public String getAlgorithm() { 1403 return Integer.toString(key.getEType()); 1404 } 1405 1406 @Override 1407 public String getFormat() { 1408 return "RAW"; 1409 } 1410 1411 @Override 1412 public byte[] getEncoded() { 1413 return key.getBytes().clone(); 1414 } 1415 1416 @Override 1417 public String toString() { 1418 return "Kerberos session key: etype: " + key.getEType() + "\n" + 1419 new HexDumpEncoder().encodeBuffer(key.getBytes()); 1420 } 1421 } 1422 1423 /** 1424 * Return the mechanism-specific attribute associated with {@code type}. 1425 */ 1426 public Object inquireSecContext(String type) 1427 throws GSSException { 1428 if (!isEstablished()) { 1429 throw new GSSException(GSSException.NO_CONTEXT, -1, 1430 "Security context not established."); 1431 } 1432 switch (type) { 1433 case "KRB5_GET_SESSION_KEY": 1434 return new KerberosSessionKey(key); 1435 case "KRB5_GET_SESSION_KEY_EX": 1436 return new javax.security.auth.kerberos.EncryptionKey( 1437 key.getBytes(), key.getEType()); 1438 case "KRB5_GET_TKT_FLAGS": 1439 return tktFlags.clone(); 1440 case "KRB5_GET_AUTHZ_DATA": 1441 if (isInitiator()) { 1442 throw new GSSException(GSSException.UNAVAILABLE, -1, 1443 "AuthzData not available on initiator side."); 1444 } else { 1445 return authzData; 1446 } 1447 case "KRB5_GET_AUTHTIME": 1448 return authTime; 1449 case "KRB5_GET_KRB_CRED": 1450 if (!isInitiator()) { 1451 throw new GSSException(GSSException.UNAVAILABLE, -1, 1452 "KRB_CRED not available on acceptor side."); 1453 } 1454 KerberosPrincipal sender = new KerberosPrincipal( 1455 myName.getKrb5PrincipalName().getName()); 1456 KerberosPrincipal recipient = new KerberosPrincipal( 1457 peerName.getKrb5PrincipalName().getName()); 1458 try { 1459 byte[] krbCred = new KrbCred(tgt, serviceCreds, key) 1460 .getMessage(); 1461 return new KerberosCredMessage( 1462 sender, recipient, krbCred); 1463 } catch (KrbException | IOException e) { 1464 GSSException gsse = new GSSException(GSSException.UNAVAILABLE, -1, 1465 "KRB_CRED not generated correctly."); 1466 gsse.initCause(e); 1467 throw gsse; 1468 } 1469 } 1470 throw new GSSException(GSSException.UNAVAILABLE, -1, 1471 "Inquire type not supported."); 1472 } 1473 1474 // Helpers for inquireSecContext 1475 private boolean[] tktFlags; 1476 private String authTime; 1477 private AuthorizationData authzData; 1478 1479 public void setTktFlags(boolean[] tktFlags) { 1480 this.tktFlags = tktFlags; 1481 } 1482 1483 public void setAuthTime(String authTime) { 1484 this.authTime = authTime; 1485 } 1486 1487 public void setAuthzData(AuthorizationData authzData) { 1488 this.authzData = authzData; 1489 } 1490 1491 }