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 // get the mechanism token 543 byte[] mechToken = initToken.getMechToken(); 544 if (mechToken == null) { 545 throw new GSSException(GSSException.FAILURE, -1, 546 "mechToken is missing"); 547 } 548 accept_token = GSS_acceptSecContext(mechToken); 549 } else { 550 accept_token = null; 551 } 552 553 // verify MIC 554 if (!GSSUtil.useMSInterop() && valid) { 555 valid = verifyMechListMIC(DER_mechTypes, 556 initToken.getMechListMIC()); 557 } 558 559 // determine negotiated result status 560 if (valid) { 561 if (isMechContextEstablished()) { 562 negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; 563 state = STATE_DONE; 564 // now set the context flags for acceptor 565 setContextFlags(); 566 // print the negotiated mech info 567 if (DEBUG) { 568 System.out.println("SPNEGO Negotiated Mechanism = " 569 + internal_mech + " " + 570 GSSUtil.getMechStr(internal_mech)); 571 } 572 } else { 573 negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; 574 state = STATE_IN_PROCESS; 575 } 576 } else { 577 negoResult = SpNegoToken.NegoResult.REJECT; 578 state = STATE_DONE; 579 } 580 581 if (DEBUG) { 582 System.out.println("SpNegoContext.acceptSecContext: " + 583 "mechanism wanted = " + mech_wanted); 584 System.out.println("SpNegoContext.acceptSecContext: " + 585 "negotiated result = " + negoResult); 586 } 587 588 // generate SPNEGO token 589 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(), 590 mech_wanted, accept_token, null); 591 if (DEBUG) { 592 System.out.println("SpNegoContext.acceptSecContext: " + 593 "sending token of type = " + 594 SpNegoToken.getTokenName(targToken.getType())); 595 } 596 // get the encoded token 597 retVal = targToken.getEncoded(); 598 599 } else if (state == STATE_IN_PROCESS) { 600 // read data 601 byte[] token = new byte[is.available()]; 602 SpNegoToken.readFully(is, token); 603 if (DEBUG) { 604 System.out.println("SpNegoContext.acceptSecContext: " + 605 "receiving token = " + 606 SpNegoToken.getHexBytes(token)); 607 } 608 609 // read the SPNEGO token 610 // token will be validated when parsing 611 NegTokenTarg inputToken = new NegTokenTarg(token); 612 613 if (DEBUG) { 614 System.out.println("SpNegoContext.acceptSecContext: " + 615 "received token of type = " + 616 SpNegoToken.getTokenName(inputToken.getType())); 617 } 618 619 // read the token 620 byte[] client_token = inputToken.getResponseToken(); 621 byte[] accept_token = GSS_acceptSecContext(client_token); 622 if (accept_token == null) { 623 valid = false; 624 } 625 626 // determine negotiated result status 627 if (valid) { 628 if (isMechContextEstablished()) { 629 negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; 630 state = STATE_DONE; 631 } else { 632 negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; 633 state = STATE_IN_PROCESS; 634 } 635 } else { 636 negoResult = SpNegoToken.NegoResult.REJECT; 637 state = STATE_DONE; 638 } 639 640 // generate SPNEGO token 641 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(), 642 null, accept_token, null); 643 if (DEBUG) { 644 System.out.println("SpNegoContext.acceptSecContext: " + 645 "sending token of type = " + 646 SpNegoToken.getTokenName(targToken.getType())); 647 } 648 // get the encoded token 649 retVal = targToken.getEncoded(); 650 651 } else { 652 // XXX Use logging API 653 if (DEBUG) { 654 System.out.println("AcceptSecContext: state = " + state); 655 } 656 } 657 if (DEBUG) { 658 System.out.println("SpNegoContext.acceptSecContext: " + 659 "sending token = " + SpNegoToken.getHexBytes(retVal)); 660 } 661 } catch (IOException e) { 662 GSSException gssException = 663 new GSSException(GSSException.FAILURE, -1, e.getMessage()); 664 gssException.initCause(e); 665 throw gssException; 666 } 667 668 if (state == STATE_DONE) { 669 // now set the context flags for acceptor 670 setContextFlags(); 671 } 672 return retVal; 673 } 674 675 /** 676 * obtain the available mechanisms 677 */ 678 private Oid[] getAvailableMechs() { 679 if (myCred != null) { 680 Oid[] mechs = new Oid[1]; 681 mechs[0] = myCred.getInternalMech(); 682 return mechs; 683 } else { 684 return factory.availableMechs; 685 } 686 } 687 688 /** 689 * get ther DER encoded MechList 690 */ 691 private byte[] getEncodedMechs(Oid[] mechSet) 692 throws IOException, GSSException { 693 694 DerOutputStream mech = new DerOutputStream(); 695 for (int i = 0; i < mechSet.length; i++) { 696 byte[] mechType = mechSet[i].getDER(); 697 mech.write(mechType); 698 } 699 // insert in SEQUENCE 700 DerOutputStream mechTypeList = new DerOutputStream(); 701 mechTypeList.write(DerValue.tag_Sequence, mech); 702 byte[] encoded = mechTypeList.toByteArray(); 703 return encoded; 704 } 705 706 /** 707 * get the context flags 708 */ 709 private BitArray getContextFlags() { 710 BitArray out = new BitArray(7); 711 712 if (getCredDelegState()) out.set(0, true); 713 if (getMutualAuthState()) out.set(1, true); 714 if (getReplayDetState()) out.set(2, true); 715 if (getSequenceDetState()) out.set(3, true); 716 if (getConfState()) out.set(5, true); 717 if (getIntegState()) out.set(6, true); 718 719 return out; 720 } 721 722 // Only called on acceptor side. On the initiator side, most flags 723 // are already set at request. For those that might get chanegd, 724 // state from mech below is used. 725 private void setContextFlags() { 726 727 if (mechContext != null) { 728 // default for cred delegation is false 729 if (mechContext.getCredDelegState()) { 730 credDelegState = true; 731 } 732 // default for the following are true 733 if (!mechContext.getMutualAuthState()) { 734 mutualAuthState = false; 735 } 736 if (!mechContext.getReplayDetState()) { 737 replayDetState = false; 738 } 739 if (!mechContext.getSequenceDetState()) { 740 sequenceDetState = false; 741 } 742 if (!mechContext.getIntegState()) { 743 integState = false; 744 } 745 if (!mechContext.getConfState()) { 746 confState = false; 747 } 748 } 749 } 750 751 /** 752 * generate MIC on mechList. Not used at the moment. 753 */ 754 /*private byte[] generateMechListMIC(byte[] mechTypes) 755 throws GSSException { 756 757 // sanity check the required input 758 if (mechTypes == null) { 759 if (DEBUG) { 760 System.out.println("SpNegoContext: no MIC token included"); 761 } 762 return null; 763 } 764 765 // check if mechanism supports integrity 766 if (!mechContext.getIntegState()) { 767 if (DEBUG) { 768 System.out.println("SpNegoContext: no MIC token included" + 769 " - mechanism does not support integrity"); 770 } 771 return null; 772 } 773 774 // compute MIC on DER encoded mechanism list 775 byte[] mic = null; 776 try { 777 MessageProp prop = new MessageProp(0, true); 778 mic = getMIC(mechTypes, 0, mechTypes.length, prop); 779 if (DEBUG) { 780 System.out.println("SpNegoContext: getMIC = " + 781 SpNegoToken.getHexBytes(mic)); 782 } 783 } catch (GSSException e) { 784 mic = null; 785 if (DEBUG) { 786 System.out.println("SpNegoContext: no MIC token included" + 787 " - getMIC failed : " + e.getMessage()); 788 } 789 } 790 return mic; 791 }*/ 792 793 /** 794 * verify MIC on MechList 795 */ 796 private boolean verifyMechListMIC(byte[] mechTypes, byte[] token) 797 throws GSSException { 798 799 // sanity check the input 800 if (token == null) { 801 if (DEBUG) { 802 System.out.println("SpNegoContext: no MIC token validation"); 803 } 804 return true; 805 } 806 807 // check if mechanism supports integrity 808 if (!mechContext.getIntegState()) { 809 if (DEBUG) { 810 System.out.println("SpNegoContext: no MIC token validation" + 811 " - mechanism does not support integrity"); 812 } 813 return true; 814 } 815 816 // now verify the token 817 boolean valid = false; 818 try { 819 MessageProp prop = new MessageProp(0, true); 820 verifyMIC(token, 0, token.length, mechTypes, 821 0, mechTypes.length, prop); 822 valid = true; 823 } catch (GSSException e) { 824 valid = false; 825 if (DEBUG) { 826 System.out.println("SpNegoContext: MIC validation failed! " + 827 e.getMessage()); 828 } 829 } 830 return valid; 831 } 832 833 /** 834 * call gss_init_sec_context for the corresponding underlying mechanism 835 */ 836 private byte[] GSS_initSecContext(byte[] token) throws GSSException { 837 byte[] tok = null; 838 839 if (mechContext == null) { 840 // initialize mech context 841 GSSName serverName = 842 factory.manager.createName(peerName.toString(), 843 peerName.getStringNameType(), internal_mech); 844 GSSCredential cred = null; 845 if (myCred != null) { 846 // create context with provided credential 847 cred = new GSSCredentialImpl(factory.manager, 848 myCred.getInternalCred()); 849 } 850 mechContext = 851 factory.manager.createContext(serverName, 852 internal_mech, cred, GSSContext.DEFAULT_LIFETIME); 853 mechContext.requestConf(confState); 854 mechContext.requestInteg(integState); 855 mechContext.requestCredDeleg(credDelegState); 856 mechContext.requestMutualAuth(mutualAuthState); 857 mechContext.requestReplayDet(replayDetState); 858 mechContext.requestSequenceDet(sequenceDetState); 859 if (mechContext instanceof GSSContextImpl) { 860 ((GSSContextImpl)mechContext).requestDelegPolicy( 861 delegPolicyState); 862 } 863 } 864 865 // pass token 866 if (token != null) { 867 tok = token; 868 } else { 869 tok = new byte[0]; 870 } 871 872 // pass token to mechanism initSecContext 873 byte[] init_token = mechContext.initSecContext(tok, 0, tok.length); 874 875 return init_token; 876 } 877 878 /** 879 * call gss_accept_sec_context for the corresponding underlying mechanism 880 */ 881 private byte[] GSS_acceptSecContext(byte[] token) throws GSSException { 882 883 if (mechContext == null) { 884 // initialize mech context 885 GSSCredential cred = null; 886 if (myCred != null) { 887 // create context with provided credential 888 cred = new GSSCredentialImpl(factory.manager, 889 myCred.getInternalCred()); 890 } 891 mechContext = factory.manager.createContext(cred); 892 } 893 894 // pass token to mechanism acceptSecContext 895 byte[] accept_token = 896 mechContext.acceptSecContext(token, 0, token.length); 897 898 return accept_token; 899 } 900 901 /** 902 * This routine compares the recieved mechset to the mechset that 903 * this server can support. It looks sequentially through the mechset 904 * and the first one that matches what the server can support is 905 * chosen as the negotiated mechanism. If one is found, negResult 906 * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult 907 * is set to REJECT. 908 */ 909 private static Oid negotiate_mech_type(Oid[] supported_mechSet, 910 Oid[] mechSet) { 911 for (int i = 0; i < supported_mechSet.length; i++) { 912 for (int j = 0; j < mechSet.length; j++) { 913 if (mechSet[j].equals(supported_mechSet[i])) { 914 if (DEBUG) { 915 System.out.println("SpNegoContext: " + 916 "negotiated mechanism = " + mechSet[j]); 917 } 918 return (mechSet[j]); 919 } 920 } 921 } 922 return null; 923 } 924 925 public final boolean isEstablished() { 926 return (state == STATE_DONE); 927 } 928 929 public final boolean isMechContextEstablished() { 930 if (mechContext != null) { 931 return mechContext.isEstablished(); 932 } else { 933 if (DEBUG) { 934 System.out.println("The underlying mechanism context has " + 935 "not been initialized"); 936 } 937 return false; 938 } 939 } 940 941 public final byte [] export() throws GSSException { 942 throw new GSSException(GSSException.UNAVAILABLE, -1, 943 "GSS Export Context not available"); 944 } 945 946 /** 947 * Sets the channel bindings to be used during context 948 * establishment. 949 */ 950 public final void setChannelBinding(ChannelBinding channelBinding) 951 throws GSSException { 952 this.channelBinding = channelBinding; 953 } 954 955 final ChannelBinding getChannelBinding() { 956 return channelBinding; 957 } 958 959 /* 960 * Anonymity is a little different in that after an application 961 * requests anonymity it will want to know whether the mechanism 962 * can support it or not, prior to sending any tokens across for 963 * context establishment. Since this is from the initiator's 964 * perspective, it essentially requests that the initiator be 965 * anonymous. 966 */ 967 public final void requestAnonymity(boolean value) throws GSSException { 968 // Ignore silently. Application will check back with 969 // getAnonymityState. 970 } 971 972 // RFC 2853 actually calls for this to be called after context 973 // establishment to get the right answer, but that is 974 // incorrect. The application may not want to send over any 975 // tokens if anonymity is not available. 976 public final boolean getAnonymityState() { 977 return false; 978 } 979 980 /** 981 * Requests the desired lifetime. Can only be used on the context 982 * initiator's side. 983 */ 984 public void requestLifetime(int lifetime) throws GSSException { 985 if (state == STATE_NEW && isInitiator()) 986 this.lifetime = lifetime; 987 } 988 989 /** 990 * The lifetime remaining for this context. 991 */ 992 public final int getLifetime() { 993 if (mechContext != null) { 994 return mechContext.getLifetime(); 995 } else { 996 return GSSContext.INDEFINITE_LIFETIME; 997 } 998 } 999 1000 public final boolean isTransferable() throws GSSException { 1001 return false; 1002 } 1003 1004 /** 1005 * Requests that sequence checking be done on the GSS wrap and MIC 1006 * tokens. 1007 */ 1008 public final void requestSequenceDet(boolean value) throws GSSException { 1009 if (state == STATE_NEW && isInitiator()) 1010 sequenceDetState = value; 1011 } 1012 1013 /** 1014 * Is sequence checking enabled on the GSS Wrap and MIC tokens? 1015 * We enable sequence checking if replay detection is enabled. 1016 */ 1017 public final boolean getSequenceDetState() { 1018 return sequenceDetState || replayDetState; 1019 } 1020 1021 /** 1022 * Requests that replay detection be done on the GSS wrap and MIC 1023 * tokens. 1024 */ 1025 public final void requestReplayDet(boolean value) throws GSSException { 1026 if (state == STATE_NEW && isInitiator()) 1027 replayDetState = value; 1028 } 1029 1030 /** 1031 * Is replay detection enabled on the GSS wrap and MIC tokens? 1032 * We enable replay detection if sequence checking is enabled. 1033 */ 1034 public final boolean getReplayDetState() { 1035 return replayDetState || sequenceDetState; 1036 } 1037 1038 public final GSSNameSpi getTargName() throws GSSException { 1039 // fill-in the GSSName 1040 // get the peer name for the mechanism 1041 if (mechContext != null) { 1042 GSSNameImpl targName = (GSSNameImpl)mechContext.getTargName(); 1043 peerName = targName.getElement(internal_mech); 1044 return peerName; 1045 } else { 1046 if (DEBUG) { 1047 System.out.println("The underlying mechanism context has " + 1048 "not been initialized"); 1049 } 1050 return null; 1051 } 1052 } 1053 1054 public final GSSNameSpi getSrcName() throws GSSException { 1055 // fill-in the GSSName 1056 // get the src name for the mechanism 1057 if (mechContext != null) { 1058 GSSNameImpl srcName = (GSSNameImpl)mechContext.getSrcName(); 1059 myName = srcName.getElement(internal_mech); 1060 return myName; 1061 } else { 1062 if (DEBUG) { 1063 System.out.println("The underlying mechanism context has " + 1064 "not been initialized"); 1065 } 1066 return null; 1067 } 1068 } 1069 1070 /** 1071 * Returns the delegated credential for the context. This 1072 * is an optional feature of contexts which not all 1073 * mechanisms will support. A context can be requested to 1074 * support credential delegation by using the <b>CRED_DELEG</b>. 1075 * This is only valid on the acceptor side of the context. 1076 * @return GSSCredentialSpi object for the delegated credential 1077 * @exception GSSException 1078 * @see GSSContext#getCredDelegState 1079 */ 1080 public final GSSCredentialSpi getDelegCred() throws GSSException { 1081 if (state != STATE_IN_PROCESS && state != STATE_DONE) 1082 throw new GSSException(GSSException.NO_CONTEXT); 1083 if (mechContext != null) { 1084 GSSCredentialImpl delegCred = 1085 (GSSCredentialImpl)mechContext.getDelegCred(); 1086 if (delegCred == null) { 1087 return null; 1088 } 1089 // determine delegated cred element usage 1090 boolean initiate = false; 1091 if (delegCred.getUsage() == GSSCredential.INITIATE_ONLY) { 1092 initiate = true; 1093 } 1094 GSSCredentialSpi mechCred = 1095 delegCred.getElement(internal_mech, initiate); 1096 SpNegoCredElement cred = new SpNegoCredElement(mechCred); 1097 return cred.getInternalCred(); 1098 } else { 1099 throw new GSSException(GSSException.NO_CONTEXT, -1, 1100 "getDelegCred called in invalid state!"); 1101 } 1102 } 1103 1104 public final int getWrapSizeLimit(int qop, boolean confReq, 1105 int maxTokSize) throws GSSException { 1106 if (mechContext != null) { 1107 return mechContext.getWrapSizeLimit(qop, confReq, maxTokSize); 1108 } else { 1109 throw new GSSException(GSSException.NO_CONTEXT, -1, 1110 "getWrapSizeLimit called in invalid state!"); 1111 } 1112 } 1113 1114 public final byte[] wrap(byte inBuf[], int offset, int len, 1115 MessageProp msgProp) throws GSSException { 1116 if (mechContext != null) { 1117 return mechContext.wrap(inBuf, offset, len, msgProp); 1118 } else { 1119 throw new GSSException(GSSException.NO_CONTEXT, -1, 1120 "Wrap called in invalid state!"); 1121 } 1122 } 1123 1124 public final void wrap(InputStream is, OutputStream os, 1125 MessageProp msgProp) throws GSSException { 1126 if (mechContext != null) { 1127 mechContext.wrap(is, os, msgProp); 1128 } else { 1129 throw new GSSException(GSSException.NO_CONTEXT, -1, 1130 "Wrap called in invalid state!"); 1131 } 1132 } 1133 1134 public final byte[] unwrap(byte inBuf[], int offset, int len, 1135 MessageProp msgProp) 1136 throws GSSException { 1137 if (mechContext != null) { 1138 return mechContext.unwrap(inBuf, offset, len, msgProp); 1139 } else { 1140 throw new GSSException(GSSException.NO_CONTEXT, -1, 1141 "UnWrap called in invalid state!"); 1142 } 1143 } 1144 1145 public final void unwrap(InputStream is, OutputStream os, 1146 MessageProp msgProp) throws GSSException { 1147 if (mechContext != null) { 1148 mechContext.unwrap(is, os, msgProp); 1149 } else { 1150 throw new GSSException(GSSException.NO_CONTEXT, -1, 1151 "UnWrap called in invalid state!"); 1152 } 1153 } 1154 1155 public final byte[] getMIC(byte []inMsg, int offset, int len, 1156 MessageProp msgProp) 1157 throws GSSException { 1158 if (mechContext != null) { 1159 return mechContext.getMIC(inMsg, offset, len, msgProp); 1160 } else { 1161 throw new GSSException(GSSException.NO_CONTEXT, -1, 1162 "getMIC called in invalid state!"); 1163 } 1164 } 1165 1166 public final void getMIC(InputStream is, OutputStream os, 1167 MessageProp msgProp) throws GSSException { 1168 if (mechContext != null) { 1169 mechContext.getMIC(is, os, msgProp); 1170 } else { 1171 throw new GSSException(GSSException.NO_CONTEXT, -1, 1172 "getMIC called in invalid state!"); 1173 } 1174 } 1175 1176 public final void verifyMIC(byte []inTok, int tokOffset, int tokLen, 1177 byte[] inMsg, int msgOffset, int msgLen, 1178 MessageProp msgProp) 1179 throws GSSException { 1180 if (mechContext != null) { 1181 mechContext.verifyMIC(inTok, tokOffset, tokLen, inMsg, msgOffset, 1182 msgLen, msgProp); 1183 } else { 1184 throw new GSSException(GSSException.NO_CONTEXT, -1, 1185 "verifyMIC called in invalid state!"); 1186 } 1187 } 1188 1189 public final void verifyMIC(InputStream is, InputStream msgStr, 1190 MessageProp msgProp) throws GSSException { 1191 if (mechContext != null) { 1192 mechContext.verifyMIC(is, msgStr, msgProp); 1193 } else { 1194 throw new GSSException(GSSException.NO_CONTEXT, -1, 1195 "verifyMIC called in invalid state!"); 1196 } 1197 } 1198 1199 private static String printState(int state) { 1200 switch (state) { 1201 case STATE_NEW: 1202 return ("STATE_NEW"); 1203 case STATE_IN_PROCESS: 1204 return ("STATE_IN_PROCESS"); 1205 case STATE_DONE: 1206 return ("STATE_DONE"); 1207 case STATE_DELETED: 1208 return ("STATE_DELETED"); 1209 default: 1210 return ("Unknown state " + state); 1211 } 1212 } 1213 1214 /** 1215 * Retrieve attribute of the context for {@code type}. 1216 */ 1217 public Object inquireSecContext(String type) 1218 throws GSSException { 1219 if (mechContext == null) { 1220 throw new GSSException(GSSException.NO_CONTEXT, -1, 1221 "Underlying mech not established."); 1222 } 1223 if (mechContext instanceof GSSContextImpl) { 1224 return ((GSSContextImpl)mechContext).inquireSecContext(type); 1225 } else { 1226 throw new GSSException(GSSException.BAD_MECH, -1, 1227 "inquireSecContext not supported by underlying mech."); 1228 } 1229 } 1230 } 1231