1 /* 2 * Copyright (c) 2005, 2013, 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.spnego; 27 28 import java.io.*; 29 import java.security.Provider; 30 import org.ietf.jgss.*; 31 import sun.security.jgss.*; 32 import sun.security.jgss.spi.*; 33 import sun.security.util.*; 34 35 /** 36 * Implements the mechanism specific context class for SPNEGO 37 * GSS-API mechanism 38 * 39 * @author Seema Malkani 40 * @since 1.6 41 */ 42 public class SpNegoContext implements GSSContextSpi { 43 44 /* 45 * The different states that this context can be in. 46 */ 47 private static final int STATE_NEW = 1; 48 private static final int STATE_IN_PROCESS = 2; 49 private static final int STATE_DONE = 3; 50 private static final int STATE_DELETED = 4; 51 52 private int state = STATE_NEW; 53 54 /* 55 * Optional features that the application can set and their default 56 * values. 57 */ 58 private boolean credDelegState = false; 59 private boolean mutualAuthState = true; 60 private boolean replayDetState = true; 61 private boolean sequenceDetState = true; 62 private boolean confState = true; 63 private boolean integState = true; 64 private boolean delegPolicyState = false; 65 66 private GSSNameSpi peerName = null; 67 private GSSNameSpi myName = null; 68 private SpNegoCredElement myCred = null; 69 70 private GSSContext mechContext = null; 71 private byte[] DER_mechTypes = null; 72 73 private int lifetime; 74 private ChannelBinding channelBinding; 75 private boolean initiator; 76 77 // the underlying negotiated mechanism 78 private Oid internal_mech = null; 79 80 // the SpNegoMechFactory that creates this context 81 final private SpNegoMechFactory factory; 82 83 // debug property 84 static final boolean DEBUG = 85 java.security.AccessController.doPrivileged( 86 new sun.security.action.GetBooleanAction 87 ("sun.security.spnego.debug")).booleanValue(); 88 89 /** 90 * Constructor for SpNegoContext to be called on the context initiator's 91 * side. 92 */ 93 public SpNegoContext(SpNegoMechFactory factory, GSSNameSpi peerName, 94 GSSCredentialSpi myCred, 95 int lifetime) throws GSSException { 96 97 if (peerName == null) 98 throw new IllegalArgumentException("Cannot have null peer name"); 99 if ((myCred != null) && !(myCred instanceof SpNegoCredElement)) { 100 throw new IllegalArgumentException("Wrong cred element type"); 101 } 102 this.peerName = peerName; 103 this.myCred = (SpNegoCredElement) myCred; 104 this.lifetime = lifetime; 105 this.initiator = true; 106 this.factory = factory; 107 } 108 109 /** 110 * Constructor for SpNegoContext to be called on the context acceptor's 111 * side. 112 */ 113 public SpNegoContext(SpNegoMechFactory factory, GSSCredentialSpi myCred) 114 throws GSSException { 115 if ((myCred != null) && !(myCred instanceof SpNegoCredElement)) { 116 throw new IllegalArgumentException("Wrong cred element type"); 117 } 118 this.myCred = (SpNegoCredElement) myCred; 119 this.initiator = false; 120 this.factory = factory; 121 } 122 123 /** 124 * Constructor for SpNegoContext to import a previously exported context. 125 */ 126 public SpNegoContext(SpNegoMechFactory factory, byte [] interProcessToken) 127 throws GSSException { 128 throw new GSSException(GSSException.UNAVAILABLE, 129 -1, "GSS Import Context not available"); 130 } 131 132 /** 133 * Requests that confidentiality be available. 134 */ 135 public final void requestConf(boolean value) throws GSSException { 136 if (state == STATE_NEW && isInitiator()) 137 confState = value; 138 } 139 140 /** 141 * Is confidentiality available? 142 */ 143 public final boolean getConfState() { 144 return confState; 145 } 146 147 /** 148 * Requests that integrity be available. 149 */ 150 public final void requestInteg(boolean value) throws GSSException { 151 if (state == STATE_NEW && isInitiator()) 152 integState = value; 153 } 154 155 /** 156 * Requests that deleg policy be respected. 157 */ 158 public final void requestDelegPolicy(boolean value) throws GSSException { 159 if (state == STATE_NEW && isInitiator()) 160 delegPolicyState = value; 161 } 162 163 /** 164 * Is integrity available? 165 */ 166 public final boolean getIntegState() { 167 return integState; 168 } 169 170 /** 171 * Is deleg policy respected? 172 */ 173 public final boolean getDelegPolicyState() { 174 if (isInitiator() && mechContext != null && 175 mechContext instanceof GSSContextImpl && 176 (state == STATE_IN_PROCESS || state == STATE_DONE)) { 177 return ((GSSContextImpl)mechContext).getDelegPolicyState(); 178 } else { 179 return delegPolicyState; 180 } 181 } 182 183 /** 184 * Requests that credential delegation be done during context 185 * establishment. 186 */ 187 public final void requestCredDeleg(boolean value) throws GSSException { 188 if (state == STATE_NEW && isInitiator()) 189 credDelegState = value; 190 } 191 192 /** 193 * Is credential delegation enabled? 194 */ 195 public final boolean getCredDelegState() { 196 if (isInitiator() && mechContext != null && 197 (state == STATE_IN_PROCESS || state == STATE_DONE)) { 198 return mechContext.getCredDelegState(); 199 } else { 200 return credDelegState; 201 } 202 } 203 204 /** 205 * Requests that mutual authentication be done during context 206 * establishment. Since this is fromm the client's perspective, it 207 * essentially requests that the server be authenticated. 208 */ 209 public final void requestMutualAuth(boolean value) throws GSSException { 210 if (state == STATE_NEW && isInitiator()) { 211 mutualAuthState = value; 212 } 213 } 214 215 /** 216 * Is mutual authentication enabled? Since this is from the client's 217 * perspective, it essentially meas that the server is being 218 * authenticated. 219 */ 220 public final boolean getMutualAuthState() { 221 return mutualAuthState; 222 } 223 224 /** 225 * Returns the mechanism oid. 226 * 227 * @return the Oid of this context 228 */ 229 public final Oid getMech() { 230 if (isEstablished()) { 231 return getNegotiatedMech(); 232 } 233 return (SpNegoMechFactory.GSS_SPNEGO_MECH_OID); 234 } 235 236 public final Oid getNegotiatedMech() { 237 return (internal_mech); 238 } 239 240 public final Provider getProvider() { 241 return SpNegoMechFactory.PROVIDER; 242 } 243 244 public final void dispose() throws GSSException { 245 mechContext = null; 246 state = STATE_DELETED; 247 } 248 249 /** 250 * Tests if this is the initiator side of the context. 251 * 252 * @return boolean indicating if this is initiator (true) 253 * or target (false) 254 */ 255 public final boolean isInitiator() { 256 return initiator; 257 } 258 259 /** 260 * Tests if the context can be used for per-message service. 261 * Context may allow the calls to the per-message service 262 * functions before being fully established. 263 * 264 * @return boolean indicating if per-message methods can 265 * be called. 266 */ 267 public final boolean isProtReady() { 268 return (state == STATE_DONE); 269 } 270 271 /** 272 * Initiator context establishment call. This method may be 273 * required to be called several times. A CONTINUE_NEEDED return 274 * call indicates that more calls are needed after the next token 275 * is received from the peer. 276 * 277 * @param is contains the token received from the peer. On the 278 * first call it will be ignored. 279 * @return any token required to be sent to the peer 280 * It is responsibility of the caller to send the token 281 * to its peer for processing. 282 * @exception GSSException 283 */ 284 public final byte[] initSecContext(InputStream is, int mechTokenSize) 285 throws GSSException { 286 287 byte[] retVal = null; 288 NegTokenInit initToken = null; 289 byte[] mechToken = null; 290 int errorCode = GSSException.FAILURE; 291 292 if (DEBUG) { 293 System.out.println("Entered SpNego.initSecContext with " + 294 "state=" + printState(state)); 295 } 296 if (!isInitiator()) { 297 throw new GSSException(GSSException.FAILURE, -1, 298 "initSecContext on an acceptor GSSContext"); 299 } 300 301 try { 302 if (state == STATE_NEW) { 303 state = STATE_IN_PROCESS; 304 305 errorCode = GSSException.NO_CRED; 306 307 // determine available mech set 308 Oid[] mechList = getAvailableMechs(); 309 DER_mechTypes = getEncodedMechs(mechList); 310 311 // pull out first mechanism 312 internal_mech = mechList[0]; 313 314 // get the token for first mechanism 315 mechToken = GSS_initSecContext(null); 316 317 errorCode = GSSException.DEFECTIVE_TOKEN; 318 // generate SPNEGO token 319 initToken = new NegTokenInit(DER_mechTypes, getContextFlags(), 320 mechToken, null); 321 if (DEBUG) { 322 System.out.println("SpNegoContext.initSecContext: " + 323 "sending token of type = " + 324 SpNegoToken.getTokenName(initToken.getType())); 325 } 326 // get the encoded token 327 retVal = initToken.getEncoded(); 328 329 } else if (state == STATE_IN_PROCESS) { 330 331 errorCode = GSSException.FAILURE; 332 if (is == null) { 333 throw new GSSException(errorCode, -1, 334 "No token received from peer!"); 335 } 336 337 errorCode = GSSException.DEFECTIVE_TOKEN; 338 byte[] server_token = new byte[is.available()]; 339 SpNegoToken.readFully(is, server_token); 340 if (DEBUG) { 341 System.out.println("SpNegoContext.initSecContext: " + 342 "process received token = " + 343 SpNegoToken.getHexBytes(server_token)); 344 } 345 346 // read the SPNEGO token 347 // token will be validated when parsing 348 NegTokenTarg targToken = new NegTokenTarg(server_token); 349 350 if (DEBUG) { 351 System.out.println("SpNegoContext.initSecContext: " + 352 "received token of type = " + 353 SpNegoToken.getTokenName(targToken.getType())); 354 } 355 356 // pull out mechanism 357 internal_mech = targToken.getSupportedMech(); 358 if (internal_mech == null) { 359 // return wth failure 360 throw new GSSException(errorCode, -1, 361 "supported mechanism from server is null"); 362 } 363 364 // get the negotiated result 365 SpNegoToken.NegoResult negoResult = null; 366 int result = targToken.getNegotiatedResult(); 367 switch (result) { 368 case 0: 369 negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; 370 state = STATE_DONE; 371 break; 372 case 1: 373 negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; 374 state = STATE_IN_PROCESS; 375 break; 376 case 2: 377 negoResult = SpNegoToken.NegoResult.REJECT; 378 state = STATE_DELETED; 379 break; 380 default: 381 state = STATE_DONE; 382 break; 383 } 384 385 errorCode = GSSException.BAD_MECH; 386 387 if (negoResult == SpNegoToken.NegoResult.REJECT) { 388 throw new GSSException(errorCode, -1, 389 internal_mech.toString()); 390 } 391 392 errorCode = GSSException.DEFECTIVE_TOKEN; 393 394 if ((negoResult == SpNegoToken.NegoResult.ACCEPT_COMPLETE) || 395 (negoResult == SpNegoToken.NegoResult.ACCEPT_INCOMPLETE)) { 396 397 // pull out the mechanism token 398 byte[] accept_token = targToken.getResponseToken(); 399 if (accept_token == null) { 400 if (!isMechContextEstablished()) { 401 // return with failure 402 throw new GSSException(errorCode, -1, 403 "mechanism token from server is null"); 404 } 405 } else { 406 mechToken = GSS_initSecContext(accept_token); 407 } 408 // verify MIC 409 if (!GSSUtil.useMSInterop()) { 410 byte[] micToken = targToken.getMechListMIC(); 411 if (!verifyMechListMIC(DER_mechTypes, micToken)) { 412 throw new GSSException(errorCode, -1, 413 "verification of MIC on MechList Failed!"); 414 } 415 } 416 if (isMechContextEstablished()) { 417 state = STATE_DONE; 418 retVal = mechToken; 419 if (DEBUG) { 420 System.out.println("SPNEGO Negotiated Mechanism = " 421 + internal_mech + " " + 422 GSSUtil.getMechStr(internal_mech)); 423 } 424 } else { 425 // generate SPNEGO token 426 initToken = new NegTokenInit(null, null, 427 mechToken, null); 428 if (DEBUG) { 429 System.out.println("SpNegoContext.initSecContext:" + 430 " continue sending token of type = " + 431 SpNegoToken.getTokenName(initToken.getType())); 432 } 433 // get the encoded token 434 retVal = initToken.getEncoded(); 435 } 436 } 437 438 } else { 439 // XXX Use logging API 440 if (DEBUG) { 441 System.out.println(state); 442 } 443 } 444 if (DEBUG) { 445 if (retVal != null) { 446 System.out.println("SNegoContext.initSecContext: " + 447 "sending token = " + SpNegoToken.getHexBytes(retVal)); 448 } 449 } 450 } catch (GSSException e) { 451 GSSException gssException = 452 new GSSException(errorCode, -1, e.getMessage()); 453 gssException.initCause(e); 454 throw gssException; 455 } catch (IOException e) { 456 GSSException gssException = 457 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 458 gssException.initCause(e); 459 throw gssException; 460 } 461 462 return retVal; 463 } 464 465 466 /** 467 * Acceptor's context establishment call. This method may be 468 * required to be called several times. A CONTINUE_NEEDED return 469 * call indicates that more calls are needed after the next token 470 * is received from the peer. 471 * 472 * @param is contains the token received from the peer. 473 * @return any token required to be sent to the peer 474 * It is responsibility of the caller to send the token 475 * to its peer for processing. 476 * @exception GSSException 477 */ 478 public final byte[] acceptSecContext(InputStream is, int mechTokenSize) 479 throws GSSException { 480 481 byte[] retVal = null; 482 SpNegoToken.NegoResult negoResult; 483 boolean valid = true; 484 485 if (DEBUG) { 486 System.out.println("Entered SpNegoContext.acceptSecContext with " + 487 "state=" + printState(state)); 488 } 489 490 if (isInitiator()) { 491 throw new GSSException(GSSException.FAILURE, -1, 492 "acceptSecContext on an initiator " + 493 "GSSContext"); 494 } 495 try { 496 if (state == STATE_NEW) { 497 state = STATE_IN_PROCESS; 498 499 // read data 500 byte[] token = new byte[is.available()]; 501 SpNegoToken.readFully(is, token); 502 if (DEBUG) { 503 System.out.println("SpNegoContext.acceptSecContext: " + 504 "receiving token = " + 505 SpNegoToken.getHexBytes(token)); 506 } 507 508 // read the SPNEGO token 509 // token will be validated when parsing 510 NegTokenInit initToken = new NegTokenInit(token); 511 512 if (DEBUG) { 513 System.out.println("SpNegoContext.acceptSecContext: " + 514 "received token of type = " + 515 SpNegoToken.getTokenName(initToken.getType())); 516 } 517 518 Oid[] mechList = initToken.getMechTypeList(); 519 DER_mechTypes = initToken.getMechTypes(); 520 if (DER_mechTypes == null) { 521 valid = false; 522 } 523 524 /* 525 * Select the best match between the list of mechs 526 * that the initiator requested and the list that 527 * the acceptor will support. 528 */ 529 Oid[] supported_mechSet = getAvailableMechs(); 530 Oid mech_wanted = 531 negotiate_mech_type(supported_mechSet, mechList); 532 if (mech_wanted == null) { 533 valid = false; 534 } 535 // save the desired mechanism 536 internal_mech = mech_wanted; 537 538 // get the token for mechanism 539 byte[] accept_token; 540 541 if (mechList[0].equals(mech_wanted) || 542 (GSSUtil.isKerberosMech(mechList[0]) && 543 GSSUtil.isKerberosMech(mech_wanted))) { 544 // get the mechanism token 545 if (DEBUG && !mech_wanted.equals(mechList[0])) { 546 System.out.println("SpNegoContext.acceptSecContext: " + 547 "negotiated mech adjusted to " + mechList[0]); 548 } 549 byte[] mechToken = initToken.getMechToken(); 550 if (mechToken == null) { 551 throw new GSSException(GSSException.FAILURE, -1, 552 "mechToken is missing"); 553 } 554 accept_token = GSS_acceptSecContext(mechToken); 555 mech_wanted = mechList[0]; 556 } else { 557 accept_token = null; 558 } 559 560 // verify MIC 561 if (!GSSUtil.useMSInterop() && valid) { 562 valid = verifyMechListMIC(DER_mechTypes, 563 initToken.getMechListMIC()); 564 } 565 566 // determine negotiated result status 567 if (valid) { 568 if (isMechContextEstablished()) { 569 negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; 570 state = STATE_DONE; 571 // now set the context flags for acceptor 572 setContextFlags(); 573 // print the negotiated mech info 574 if (DEBUG) { 575 System.out.println("SPNEGO Negotiated Mechanism = " 576 + internal_mech + " " + 577 GSSUtil.getMechStr(internal_mech)); 578 } 579 } else { 580 negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; 581 state = STATE_IN_PROCESS; 582 } 583 } else { 584 negoResult = SpNegoToken.NegoResult.REJECT; 585 state = STATE_DONE; 586 } 587 588 if (DEBUG) { 589 System.out.println("SpNegoContext.acceptSecContext: " + 590 "mechanism wanted = " + mech_wanted); 591 System.out.println("SpNegoContext.acceptSecContext: " + 592 "negotiated result = " + negoResult); 593 } 594 595 // generate SPNEGO token 596 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(), 597 mech_wanted, accept_token, null); 598 if (DEBUG) { 599 System.out.println("SpNegoContext.acceptSecContext: " + 600 "sending token of type = " + 601 SpNegoToken.getTokenName(targToken.getType())); 602 } 603 // get the encoded token 604 retVal = targToken.getEncoded(); 605 606 } else if (state == STATE_IN_PROCESS) { 607 // read data 608 byte[] token = new byte[is.available()]; 609 SpNegoToken.readFully(is, token); 610 if (DEBUG) { 611 System.out.println("SpNegoContext.acceptSecContext: " + 612 "receiving token = " + 613 SpNegoToken.getHexBytes(token)); 614 } 615 616 // read the SPNEGO token 617 // token will be validated when parsing 618 NegTokenTarg inputToken = new NegTokenTarg(token); 619 620 if (DEBUG) { 621 System.out.println("SpNegoContext.acceptSecContext: " + 622 "received token of type = " + 623 SpNegoToken.getTokenName(inputToken.getType())); 624 } 625 626 // read the token 627 byte[] client_token = inputToken.getResponseToken(); 628 byte[] accept_token = GSS_acceptSecContext(client_token); 629 if (accept_token == null) { 630 valid = false; 631 } 632 633 // determine negotiated result status 634 if (valid) { 635 if (isMechContextEstablished()) { 636 negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; 637 state = STATE_DONE; 638 } else { 639 negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; 640 state = STATE_IN_PROCESS; 641 } 642 } else { 643 negoResult = SpNegoToken.NegoResult.REJECT; 644 state = STATE_DONE; 645 } 646 647 // generate SPNEGO token 648 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(), 649 null, accept_token, null); 650 if (DEBUG) { 651 System.out.println("SpNegoContext.acceptSecContext: " + 652 "sending token of type = " + 653 SpNegoToken.getTokenName(targToken.getType())); 654 } 655 // get the encoded token 656 retVal = targToken.getEncoded(); 657 658 } else { 659 // XXX Use logging API 660 if (DEBUG) { 661 System.out.println("AcceptSecContext: state = " + state); 662 } 663 } 664 if (DEBUG) { 665 System.out.println("SpNegoContext.acceptSecContext: " + 666 "sending token = " + SpNegoToken.getHexBytes(retVal)); 667 } 668 } catch (IOException e) { 669 GSSException gssException = 670 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 671 gssException.initCause(e); 672 throw gssException; 673 } 674 675 if (state == STATE_DONE) { 676 // now set the context flags for acceptor 677 setContextFlags(); 678 } 679 return retVal; 680 } 681 682 /** 683 * obtain the available mechanisms 684 */ 685 private Oid[] getAvailableMechs() { 686 if (myCred != null) { 687 Oid[] mechs = new Oid[1]; 688 mechs[0] = myCred.getInternalMech(); 689 return mechs; 690 } else { 691 return factory.availableMechs; 692 } 693 } 694 695 /** 696 * get ther DER encoded MechList 697 */ 698 private byte[] getEncodedMechs(Oid[] mechSet) 699 throws IOException, GSSException { 700 701 DerOutputStream mech = new DerOutputStream(); 702 for (int i = 0; i < mechSet.length; i++) { 703 byte[] mechType = mechSet[i].getDER(); 704 mech.write(mechType); 705 } 706 // insert in SEQUENCE 707 DerOutputStream mechTypeList = new DerOutputStream(); 708 mechTypeList.write(DerValue.tag_Sequence, mech); 709 byte[] encoded = mechTypeList.toByteArray(); 710 return encoded; 711 } 712 713 /** 714 * get the context flags 715 */ 716 private BitArray getContextFlags() { 717 BitArray out = new BitArray(7); 718 719 if (getCredDelegState()) out.set(0, true); 720 if (getMutualAuthState()) out.set(1, true); 721 if (getReplayDetState()) out.set(2, true); 722 if (getSequenceDetState()) out.set(3, true); 723 if (getConfState()) out.set(5, true); 724 if (getIntegState()) out.set(6, true); 725 726 return out; 727 } 728 729 // Only called on acceptor side. On the initiator side, most flags 730 // are already set at request. For those that might get chanegd, 731 // state from mech below is used. 732 private void setContextFlags() { 733 734 if (mechContext != null) { 735 // default for cred delegation is false 736 if (mechContext.getCredDelegState()) { 737 credDelegState = true; 738 } 739 // default for the following are true 740 if (!mechContext.getMutualAuthState()) { 741 mutualAuthState = false; 742 } 743 if (!mechContext.getReplayDetState()) { 744 replayDetState = false; 745 } 746 if (!mechContext.getSequenceDetState()) { 747 sequenceDetState = false; 748 } 749 if (!mechContext.getIntegState()) { 750 integState = false; 751 } 752 if (!mechContext.getConfState()) { 753 confState = false; 754 } 755 } 756 } 757 758 /** 759 * generate MIC on mechList. Not used at the moment. 760 */ 761 /*private byte[] generateMechListMIC(byte[] mechTypes) 762 throws GSSException { 763 764 // sanity check the required input 765 if (mechTypes == null) { 766 if (DEBUG) { 767 System.out.println("SpNegoContext: no MIC token included"); 768 } 769 return null; 770 } 771 772 // check if mechanism supports integrity 773 if (!mechContext.getIntegState()) { 774 if (DEBUG) { 775 System.out.println("SpNegoContext: no MIC token included" + 776 " - mechanism does not support integrity"); 777 } 778 return null; 779 } 780 781 // compute MIC on DER encoded mechanism list 782 byte[] mic = null; 783 try { 784 MessageProp prop = new MessageProp(0, true); 785 mic = getMIC(mechTypes, 0, mechTypes.length, prop); 786 if (DEBUG) { 787 System.out.println("SpNegoContext: getMIC = " + 788 SpNegoToken.getHexBytes(mic)); 789 } 790 } catch (GSSException e) { 791 mic = null; 792 if (DEBUG) { 793 System.out.println("SpNegoContext: no MIC token included" + 794 " - getMIC failed : " + e.getMessage()); 795 } 796 } 797 return mic; 798 }*/ 799 800 /** 801 * verify MIC on MechList 802 */ 803 private boolean verifyMechListMIC(byte[] mechTypes, byte[] token) 804 throws GSSException { 805 806 // sanity check the input 807 if (token == null) { 808 if (DEBUG) { 809 System.out.println("SpNegoContext: no MIC token validation"); 810 } 811 return true; 812 } 813 814 // check if mechanism supports integrity 815 if (!mechContext.getIntegState()) { 816 if (DEBUG) { 817 System.out.println("SpNegoContext: no MIC token validation" + 818 " - mechanism does not support integrity"); 819 } 820 return true; 821 } 822 823 // now verify the token 824 boolean valid = false; 825 try { 826 MessageProp prop = new MessageProp(0, true); 827 verifyMIC(token, 0, token.length, mechTypes, 828 0, mechTypes.length, prop); 829 valid = true; 830 } catch (GSSException e) { 831 valid = false; 832 if (DEBUG) { 833 System.out.println("SpNegoContext: MIC validation failed! " + 834 e.getMessage()); 835 } 836 } 837 return valid; 838 } 839 840 /** 841 * call gss_init_sec_context for the corresponding underlying mechanism 842 */ 843 private byte[] GSS_initSecContext(byte[] token) throws GSSException { 844 byte[] tok = null; 845 846 if (mechContext == null) { 847 // initialize mech context 848 GSSName serverName = 849 factory.manager.createName(peerName.toString(), 850 peerName.getStringNameType(), internal_mech); 851 GSSCredential cred = null; 852 if (myCred != null) { 853 // create context with provided credential 854 cred = new GSSCredentialImpl(factory.manager, 855 myCred.getInternalCred()); 856 } 857 mechContext = 858 factory.manager.createContext(serverName, 859 internal_mech, cred, GSSContext.DEFAULT_LIFETIME); 860 mechContext.requestConf(confState); 861 mechContext.requestInteg(integState); 862 mechContext.requestCredDeleg(credDelegState); 863 mechContext.requestMutualAuth(mutualAuthState); 864 mechContext.requestReplayDet(replayDetState); 865 mechContext.requestSequenceDet(sequenceDetState); 866 if (mechContext instanceof GSSContextImpl) { 867 ((GSSContextImpl)mechContext).requestDelegPolicy( 868 delegPolicyState); 869 } 870 } 871 872 // pass token 873 if (token != null) { 874 tok = token; 875 } else { 876 tok = new byte[0]; 877 } 878 879 // pass token to mechanism initSecContext 880 byte[] init_token = mechContext.initSecContext(tok, 0, tok.length); 881 882 return init_token; 883 } 884 885 /** 886 * call gss_accept_sec_context for the corresponding underlying mechanism 887 */ 888 private byte[] GSS_acceptSecContext(byte[] token) throws GSSException { 889 890 if (mechContext == null) { 891 // initialize mech context 892 GSSCredential cred = null; 893 if (myCred != null) { 894 // create context with provided credential 895 cred = new GSSCredentialImpl(factory.manager, 896 myCred.getInternalCred()); 897 } 898 mechContext = factory.manager.createContext(cred); 899 } 900 901 // pass token to mechanism acceptSecContext 902 byte[] accept_token = 903 mechContext.acceptSecContext(token, 0, token.length); 904 905 return accept_token; 906 } 907 908 /** 909 * This routine compares the recieved mechset to the mechset that 910 * this server can support. It looks sequentially through the mechset 911 * and the first one that matches what the server can support is 912 * chosen as the negotiated mechanism. If one is found, negResult 913 * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult 914 * is set to REJECT. 915 */ 916 private static Oid negotiate_mech_type(Oid[] supported_mechSet, 917 Oid[] mechSet) { 918 for (int i = 0; i < supported_mechSet.length; i++) { 919 for (int j = 0; j < mechSet.length; j++) { 920 if (mechSet[j].equals(supported_mechSet[i])) { 921 if (DEBUG) { 922 System.out.println("SpNegoContext: " + 923 "negotiated mechanism = " + mechSet[j]); 924 } 925 return (mechSet[j]); 926 } 927 } 928 } 929 return null; 930 } 931 932 public final boolean isEstablished() { 933 return (state == STATE_DONE); 934 } 935 936 public final boolean isMechContextEstablished() { 937 if (mechContext != null) { 938 return mechContext.isEstablished(); 939 } else { 940 if (DEBUG) { 941 System.out.println("The underlying mechanism context has " + 942 "not been initialized"); 943 } 944 return false; 945 } 946 } 947 948 public final byte [] export() throws GSSException { 949 throw new GSSException(GSSException.UNAVAILABLE, -1, 950 "GSS Export Context not available"); 951 } 952 953 /** 954 * Sets the channel bindings to be used during context 955 * establishment. 956 */ 957 public final void setChannelBinding(ChannelBinding channelBinding) 958 throws GSSException { 959 this.channelBinding = channelBinding; 960 } 961 962 final ChannelBinding getChannelBinding() { 963 return channelBinding; 964 } 965 966 /* 967 * Anonymity is a little different in that after an application 968 * requests anonymity it will want to know whether the mechanism 969 * can support it or not, prior to sending any tokens across for 970 * context establishment. Since this is from the initiator's 971 * perspective, it essentially requests that the initiator be 972 * anonymous. 973 */ 974 public final void requestAnonymity(boolean value) throws GSSException { 975 // Ignore silently. Application will check back with 976 // getAnonymityState. 977 } 978 979 // RFC 2853 actually calls for this to be called after context 980 // establishment to get the right answer, but that is 981 // incorrect. The application may not want to send over any 982 // tokens if anonymity is not available. 983 public final boolean getAnonymityState() { 984 return false; 985 } 986 987 /** 988 * Requests the desired lifetime. Can only be used on the context 989 * initiator's side. 990 */ 991 public void requestLifetime(int lifetime) throws GSSException { 992 if (state == STATE_NEW && isInitiator()) 993 this.lifetime = lifetime; 994 } 995 996 /** 997 * The lifetime remaining for this context. 998 */ 999 public final int getLifetime() { 1000 if (mechContext != null) { 1001 return mechContext.getLifetime(); 1002 } else { 1003 return GSSContext.INDEFINITE_LIFETIME; 1004 } 1005 } 1006 1007 public final boolean isTransferable() throws GSSException { 1008 return false; 1009 } 1010 1011 /** 1012 * Requests that sequence checking be done on the GSS wrap and MIC 1013 * tokens. 1014 */ 1015 public final void requestSequenceDet(boolean value) throws GSSException { 1016 if (state == STATE_NEW && isInitiator()) 1017 sequenceDetState = value; 1018 } 1019 1020 /** 1021 * Is sequence checking enabled on the GSS Wrap and MIC tokens? 1022 * We enable sequence checking if replay detection is enabled. 1023 */ 1024 public final boolean getSequenceDetState() { 1025 return sequenceDetState || replayDetState; 1026 } 1027 1028 /** 1029 * Requests that replay detection be done on the GSS wrap and MIC 1030 * tokens. 1031 */ 1032 public final void requestReplayDet(boolean value) throws GSSException { 1033 if (state == STATE_NEW && isInitiator()) 1034 replayDetState = value; 1035 } 1036 1037 /** 1038 * Is replay detection enabled on the GSS wrap and MIC tokens? 1039 * We enable replay detection if sequence checking is enabled. 1040 */ 1041 public final boolean getReplayDetState() { 1042 return replayDetState || sequenceDetState; 1043 } 1044 1045 public final GSSNameSpi getTargName() throws GSSException { 1046 // fill-in the GSSName 1047 // get the peer name for the mechanism 1048 if (mechContext != null) { 1049 GSSNameImpl targName = (GSSNameImpl)mechContext.getTargName(); 1050 peerName = targName.getElement(internal_mech); 1051 return peerName; 1052 } else { 1053 if (DEBUG) { 1054 System.out.println("The underlying mechanism context has " + 1055 "not been initialized"); 1056 } 1057 return null; 1058 } 1059 } 1060 1061 public final GSSNameSpi getSrcName() throws GSSException { 1062 // fill-in the GSSName 1063 // get the src name for the mechanism 1064 if (mechContext != null) { 1065 GSSNameImpl srcName = (GSSNameImpl)mechContext.getSrcName(); 1066 myName = srcName.getElement(internal_mech); 1067 return myName; 1068 } else { 1069 if (DEBUG) { 1070 System.out.println("The underlying mechanism context has " + 1071 "not been initialized"); 1072 } 1073 return null; 1074 } 1075 } 1076 1077 /** 1078 * Returns the delegated credential for the context. This 1079 * is an optional feature of contexts which not all 1080 * mechanisms will support. A context can be requested to 1081 * support credential delegation by using the <b>CRED_DELEG</b>. 1082 * This is only valid on the acceptor side of the context. 1083 * @return GSSCredentialSpi object for the delegated credential 1084 * @exception GSSException 1085 * @see GSSContext#getCredDelegState 1086 */ 1087 public final GSSCredentialSpi getDelegCred() throws GSSException { 1088 if (state != STATE_IN_PROCESS && state != STATE_DONE) 1089 throw new GSSException(GSSException.NO_CONTEXT); 1090 if (mechContext != null) { 1091 GSSCredentialImpl delegCred = 1092 (GSSCredentialImpl)mechContext.getDelegCred(); 1093 if (delegCred == null) { 1094 return null; 1095 } 1096 // determine delegated cred element usage 1097 boolean initiate = false; 1098 if (delegCred.getUsage() == GSSCredential.INITIATE_ONLY) { 1099 initiate = true; 1100 } 1101 GSSCredentialSpi mechCred = 1102 delegCred.getElement(internal_mech, initiate); 1103 SpNegoCredElement cred = new SpNegoCredElement(mechCred); 1104 return cred.getInternalCred(); 1105 } else { 1106 throw new GSSException(GSSException.NO_CONTEXT, -1, 1107 "getDelegCred called in invalid state!"); 1108 } 1109 } 1110 1111 public final int getWrapSizeLimit(int qop, boolean confReq, 1112 int maxTokSize) throws GSSException { 1113 if (mechContext != null) { 1114 return mechContext.getWrapSizeLimit(qop, confReq, maxTokSize); 1115 } else { 1116 throw new GSSException(GSSException.NO_CONTEXT, -1, 1117 "getWrapSizeLimit called in invalid state!"); 1118 } 1119 } 1120 1121 public final byte[] wrap(byte inBuf[], int offset, int len, 1122 MessageProp msgProp) throws GSSException { 1123 if (mechContext != null) { 1124 return mechContext.wrap(inBuf, offset, len, msgProp); 1125 } else { 1126 throw new GSSException(GSSException.NO_CONTEXT, -1, 1127 "Wrap called in invalid state!"); 1128 } 1129 } 1130 1131 public final void wrap(InputStream is, OutputStream os, 1132 MessageProp msgProp) throws GSSException { 1133 if (mechContext != null) { 1134 mechContext.wrap(is, os, msgProp); 1135 } else { 1136 throw new GSSException(GSSException.NO_CONTEXT, -1, 1137 "Wrap called in invalid state!"); 1138 } 1139 } 1140 1141 public final byte[] unwrap(byte inBuf[], int offset, int len, 1142 MessageProp msgProp) 1143 throws GSSException { 1144 if (mechContext != null) { 1145 return mechContext.unwrap(inBuf, offset, len, msgProp); 1146 } else { 1147 throw new GSSException(GSSException.NO_CONTEXT, -1, 1148 "UnWrap called in invalid state!"); 1149 } 1150 } 1151 1152 public final void unwrap(InputStream is, OutputStream os, 1153 MessageProp msgProp) throws GSSException { 1154 if (mechContext != null) { 1155 mechContext.unwrap(is, os, msgProp); 1156 } else { 1157 throw new GSSException(GSSException.NO_CONTEXT, -1, 1158 "UnWrap called in invalid state!"); 1159 } 1160 } 1161 1162 public final byte[] getMIC(byte []inMsg, int offset, int len, 1163 MessageProp msgProp) 1164 throws GSSException { 1165 if (mechContext != null) { 1166 return mechContext.getMIC(inMsg, offset, len, msgProp); 1167 } else { 1168 throw new GSSException(GSSException.NO_CONTEXT, -1, 1169 "getMIC called in invalid state!"); 1170 } 1171 } 1172 1173 public final void getMIC(InputStream is, OutputStream os, 1174 MessageProp msgProp) throws GSSException { 1175 if (mechContext != null) { 1176 mechContext.getMIC(is, os, msgProp); 1177 } else { 1178 throw new GSSException(GSSException.NO_CONTEXT, -1, 1179 "getMIC called in invalid state!"); 1180 } 1181 } 1182 1183 public final void verifyMIC(byte []inTok, int tokOffset, int tokLen, 1184 byte[] inMsg, int msgOffset, int msgLen, 1185 MessageProp msgProp) 1186 throws GSSException { 1187 if (mechContext != null) { 1188 mechContext.verifyMIC(inTok, tokOffset, tokLen, inMsg, msgOffset, 1189 msgLen, msgProp); 1190 } else { 1191 throw new GSSException(GSSException.NO_CONTEXT, -1, 1192 "verifyMIC called in invalid state!"); 1193 } 1194 } 1195 1196 public final void verifyMIC(InputStream is, InputStream msgStr, 1197 MessageProp msgProp) throws GSSException { 1198 if (mechContext != null) { 1199 mechContext.verifyMIC(is, msgStr, msgProp); 1200 } else { 1201 throw new GSSException(GSSException.NO_CONTEXT, -1, 1202 "verifyMIC called in invalid state!"); 1203 } 1204 } 1205 1206 private static String printState(int state) { 1207 switch (state) { 1208 case STATE_NEW: 1209 return ("STATE_NEW"); 1210 case STATE_IN_PROCESS: 1211 return ("STATE_IN_PROCESS"); 1212 case STATE_DONE: 1213 return ("STATE_DONE"); 1214 case STATE_DELETED: 1215 return ("STATE_DELETED"); 1216 default: 1217 return ("Unknown state " + state); 1218 } 1219 } 1220 1221 /** 1222 * Retrieve attribute of the context for {@code type}. 1223 */ 1224 public Object inquireSecContext(String type) 1225 throws GSSException { 1226 if (mechContext == null) { 1227 throw new GSSException(GSSException.NO_CONTEXT, -1, 1228 "Underlying mech not established."); 1229 } 1230 if (mechContext instanceof GSSContextImpl) { 1231 return ((GSSContextImpl)mechContext).inquireSecContext(type); 1232 } else { 1233 throw new GSSException(GSSException.BAD_MECH, -1, 1234 "inquireSecContext not supported by underlying mech."); 1235 } 1236 } 1237 } 1238