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