1/* 2 * Copyright (c) 2000, 2015, 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 26package sun.security.jgss; 27 28import org.ietf.jgss.*; 29import sun.security.jgss.spi.*; 30import sun.security.util.ObjectIdentifier; 31import java.io.InputStream; 32import java.io.OutputStream; 33import java.io.ByteArrayInputStream; 34import java.io.ByteArrayOutputStream; 35import java.io.IOException; 36import java.lang.reflect.Field; 37import java.lang.reflect.Modifier; 38 39/** 40 * This class represents the JGSS security context and its associated 41 * operations. JGSS security contexts are established between 42 * peers using locally established credentials. Multiple contexts 43 * may exist simultaneously between a pair of peers, using the same 44 * or different set of credentials. The JGSS is independent of 45 * the underlying transport protocols and depends on its callers to 46 * transport the tokens between peers. 47 * <p> 48 * The context object can be thought of as having 3 implicit states: 49 * before it is established, during its context establishment, and 50 * after a fully established context exists. 51 * <p> 52 * Before the context establishment phase is initiated, the context 53 * initiator may request specific characteristics desired of the 54 * established context. These can be set using the set methods. After the 55 * context is established, the caller can check the actual characteristic 56 * and services offered by the context using the query methods. 57 * <p> 58 * The context establishment phase begins with the first call to the 59 * initSecContext method by the context initiator. During this phase the 60 * initSecContext and acceptSecContext methods will produce GSS-API 61 * authentication tokens which the calling application needs to send to its 62 * peer. The initSecContext and acceptSecContext methods may 63 * return a CONTINUE_NEEDED code which indicates that a token is needed 64 * from its peer in order to continue the context establishment phase. A 65 * return code of COMPLETE signals that the local end of the context is 66 * established. This may still require that a token be sent to the peer, 67 * depending if one is produced by GSS-API. The isEstablished method can 68 * also be used to determine if the local end of the context has been 69 * fully established. During the context establishment phase, the 70 * isProtReady method may be called to determine if the context can be 71 * used for the per-message operations. This allows implementation to 72 * use per-message operations on contexts which aren't fully established. 73 * <p> 74 * After the context has been established or the isProtReady method 75 * returns "true", the query routines can be invoked to determine the actual 76 * characteristics and services of the established context. The 77 * application can also start using the per-message methods of wrap and 78 * getMIC to obtain cryptographic operations on application supplied data. 79 * <p> 80 * When the context is no longer needed, the application should call 81 * dispose to release any system resources the context may be using. 82 * <DL><DT><B>RFC 2078</b> 83 * <DD>This class corresponds to the context level calls together with 84 * the per message calls of RFC 2078. The gss_init_sec_context and 85 * gss_accept_sec_context calls have been made simpler by only taking 86 * required parameters. The context can have its properties set before 87 * the first call to initSecContext. The supplementary status codes for the 88 * per-message operations are returned in an instance of the MessageProp 89 * class, which is used as an argument in these calls.</dl> 90 */ 91public class GSSContextImpl implements GSSContext { 92 93 private GSSManagerImpl gssManager; 94 private boolean initiator; 95 96 // private flags for the context state 97 private static final int PRE_INIT = 1; 98 private static final int IN_PROGRESS = 2; 99 private static final int READY = 3; 100 private static final int DELETED = 4; 101 102 // instance variables 103 private int currentState = PRE_INIT; 104 105 private GSSContextSpi mechCtxt = null; 106 private Oid mechOid = null; 107 private ObjectIdentifier objId = null; 108 109 private GSSCredentialImpl myCred = null; 110 111 private GSSNameImpl srcName = null; 112 private GSSNameImpl targName = null; 113 114 private int reqLifetime = INDEFINITE_LIFETIME; 115 private ChannelBinding channelBindings = null; 116 117 private boolean reqConfState = true; 118 private boolean reqIntegState = true; 119 private boolean reqMutualAuthState = true; 120 private boolean reqReplayDetState = true; 121 private boolean reqSequenceDetState = true; 122 private boolean reqCredDelegState = false; 123 private boolean reqAnonState = false; 124 private boolean reqDelegPolicyState = false; 125 126 public GSSContextImpl() { 127 // Useless 128 } 129 130 // Used by new ExtendedGSSContext.ExtendedGSSContextImpl(ctxt) 131 protected GSSContextImpl(GSSContextImpl src) { 132 for (Field f: GSSContextImpl.class.getDeclaredFields()) { 133 if (!Modifier.isStatic(f.getModifiers())) { 134 try { 135 f.set(this, f.get(src)); 136 } catch (Exception e) { 137 throw new RuntimeException(e); 138 } 139 } 140 } 141 } 142 /** 143 * Creates a GSSContextImp on the context initiator's side. 144 */ 145 public GSSContextImpl(GSSManagerImpl gssManager, GSSName peer, Oid mech, 146 GSSCredential myCred, int lifetime) 147 throws GSSException { 148 if ((peer == null) || !(peer instanceof GSSNameImpl)) { 149 throw new GSSException(GSSException.BAD_NAME); 150 } 151 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 152 153 this.gssManager = gssManager; 154 this.myCred = (GSSCredentialImpl) myCred; // XXX Check first 155 reqLifetime = lifetime; 156 targName = (GSSNameImpl)peer; 157 this.mechOid = mech; 158 initiator = true; 159 } 160 161 /** 162 * Creates a GSSContextImpl on the context acceptor's side. 163 */ 164 public GSSContextImpl(GSSManagerImpl gssManager, GSSCredential myCred) 165 throws GSSException { 166 this.gssManager = gssManager; 167 this.myCred = (GSSCredentialImpl) myCred; // XXX Check first 168 initiator = false; 169 } 170 171 /** 172 * Creates a GSSContextImpl out of a previously exported 173 * GSSContext. 174 * 175 * @see #isTransferable 176 */ 177 public GSSContextImpl(GSSManagerImpl gssManager, byte[] interProcessToken) 178 throws GSSException { 179 this.gssManager = gssManager; 180 mechCtxt = gssManager.getMechanismContext(interProcessToken); 181 initiator = mechCtxt.isInitiator(); 182 this.mechOid = mechCtxt.getMech(); 183 } 184 185 public byte[] initSecContext(byte inputBuf[], int offset, int len) 186 throws GSSException { 187 /* 188 * Size of ByteArrayOutputStream will double each time that extra 189 * bytes are to be written. Usually, without delegation, a GSS 190 * initial token containing the Kerberos AP-REQ is between 400 and 191 * 600 bytes. 192 */ 193 ByteArrayOutputStream bos = new ByteArrayOutputStream(600); 194 ByteArrayInputStream bin = 195 new ByteArrayInputStream(inputBuf, offset, len); 196 int size = initSecContext(bin, bos); 197 return (size == 0? null : bos.toByteArray()); 198 } 199 200 public int initSecContext(InputStream inStream, 201 OutputStream outStream) throws GSSException { 202 203 if (mechCtxt != null && currentState != IN_PROGRESS) { 204 throw new GSSExceptionImpl(GSSException.FAILURE, 205 "Illegal call to initSecContext"); 206 } 207 208 GSSHeader gssHeader = null; 209 int inTokenLen = -1; 210 GSSCredentialSpi credElement = null; 211 boolean firstToken = false; 212 213 try { 214 if (mechCtxt == null) { 215 if (myCred != null) { 216 try { 217 credElement = myCred.getElement(mechOid, true); 218 } catch (GSSException ge) { 219 if (GSSUtil.isSpNegoMech(mechOid) && 220 ge.getMajor() == GSSException.NO_CRED) { 221 credElement = myCred.getElement 222 (myCred.getMechs()[0], true); 223 } else { 224 throw ge; 225 } 226 } 227 } 228 GSSNameSpi nameElement = targName.getElement(mechOid); 229 mechCtxt = gssManager.getMechanismContext(nameElement, 230 credElement, 231 reqLifetime, 232 mechOid); 233 mechCtxt.requestConf(reqConfState); 234 mechCtxt.requestInteg(reqIntegState); 235 mechCtxt.requestCredDeleg(reqCredDelegState); 236 mechCtxt.requestMutualAuth(reqMutualAuthState); 237 mechCtxt.requestReplayDet(reqReplayDetState); 238 mechCtxt.requestSequenceDet(reqSequenceDetState); 239 mechCtxt.requestAnonymity(reqAnonState); 240 mechCtxt.setChannelBinding(channelBindings); 241 mechCtxt.requestDelegPolicy(reqDelegPolicyState); 242 243 objId = new ObjectIdentifier(mechOid.toString()); 244 245 currentState = IN_PROGRESS; 246 firstToken = true; 247 } else { 248 if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || 249 GSSUtil.isSpNegoMech(mechOid)) { 250 // do not parse GSS header for native provider or SPNEGO 251 // mech 252 } else { 253 // parse GSS header 254 gssHeader = new GSSHeader(inStream); 255 if (!gssHeader.getOid().equals(objId)) 256 throw new GSSExceptionImpl 257 (GSSException.DEFECTIVE_TOKEN, 258 "Mechanism not equal to " + 259 mechOid.toString() + 260 " in initSecContext token"); 261 inTokenLen = gssHeader.getMechTokenLength(); 262 } 263 } 264 265 byte[] obuf = mechCtxt.initSecContext(inStream, inTokenLen); 266 267 int retVal = 0; 268 269 if (obuf != null) { 270 retVal = obuf.length; 271 if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || 272 (!firstToken && GSSUtil.isSpNegoMech(mechOid))) { 273 // do not add GSS header for native provider or SPNEGO 274 // except for the first SPNEGO token 275 } else { 276 // add GSS header 277 gssHeader = new GSSHeader(objId, obuf.length); 278 retVal += gssHeader.encode(outStream); 279 } 280 outStream.write(obuf); 281 } 282 283 if (mechCtxt.isEstablished()) 284 currentState = READY; 285 286 return retVal; 287 288 } catch (IOException e) { 289 throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN, 290 e.getMessage()); 291 } 292 } 293 294 public byte[] acceptSecContext(byte inTok[], int offset, int len) 295 throws GSSException { 296 297 /* 298 * Usually initial GSS token containing a Kerberos AP-REP is less 299 * than 100 bytes. 300 */ 301 ByteArrayOutputStream bos = new ByteArrayOutputStream(100); 302 acceptSecContext(new ByteArrayInputStream(inTok, offset, len), 303 bos); 304 byte[] out = bos.toByteArray(); 305 return (out.length == 0) ? null : out; 306 } 307 308 public void acceptSecContext(InputStream inStream, 309 OutputStream outStream) throws GSSException { 310 311 if (mechCtxt != null && currentState != IN_PROGRESS) { 312 throw new GSSExceptionImpl(GSSException.FAILURE, 313 "Illegal call to acceptSecContext"); 314 } 315 316 GSSHeader gssHeader = null; 317 int inTokenLen = -1; 318 GSSCredentialSpi credElement = null; 319 320 try { 321 if (mechCtxt == null) { 322 // mechOid will be null for an acceptor's context 323 gssHeader = new GSSHeader(inStream); 324 inTokenLen = gssHeader.getMechTokenLength(); 325 326 /* 327 * Convert ObjectIdentifier to Oid 328 */ 329 objId = gssHeader.getOid(); 330 mechOid = new Oid(objId.toString()); 331 // System.out.println("Entered GSSContextImpl.acceptSecContext" 332 // + " with mechanism = " + mechOid); 333 if (myCred != null) { 334 credElement = myCred.getElement(mechOid, false); 335 } 336 337 mechCtxt = gssManager.getMechanismContext(credElement, 338 mechOid); 339 mechCtxt.setChannelBinding(channelBindings); 340 341 currentState = IN_PROGRESS; 342 } else { 343 if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || 344 (GSSUtil.isSpNegoMech(mechOid))) { 345 // do not parse GSS header for native provider and SPNEGO 346 } else { 347 // parse GSS Header 348 gssHeader = new GSSHeader(inStream); 349 if (!gssHeader.getOid().equals(objId)) 350 throw new GSSExceptionImpl 351 (GSSException.DEFECTIVE_TOKEN, 352 "Mechanism not equal to " + 353 mechOid.toString() + 354 " in acceptSecContext token"); 355 inTokenLen = gssHeader.getMechTokenLength(); 356 } 357 } 358 359 byte[] obuf = mechCtxt.acceptSecContext(inStream, inTokenLen); 360 361 if (obuf != null) { 362 int retVal = obuf.length; 363 if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || 364 (GSSUtil.isSpNegoMech(mechOid))) { 365 // do not add GSS header for native provider and SPNEGO 366 } else { 367 // add GSS header 368 gssHeader = new GSSHeader(objId, obuf.length); 369 retVal += gssHeader.encode(outStream); 370 } 371 outStream.write(obuf); 372 } 373 374 if (mechCtxt.isEstablished()) { 375 currentState = READY; 376 } 377 } catch (IOException e) { 378 throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN, 379 e.getMessage()); 380 } 381 } 382 383 public boolean isEstablished() { 384 if (mechCtxt == null) 385 return false; 386 else 387 return (currentState == READY); 388 } 389 390 public int getWrapSizeLimit(int qop, boolean confReq, 391 int maxTokenSize) throws GSSException { 392 if (mechCtxt != null) 393 return mechCtxt.getWrapSizeLimit(qop, confReq, maxTokenSize); 394 else 395 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 396 "No mechanism context yet!"); 397 } 398 399 public byte[] wrap(byte inBuf[], int offset, int len, 400 MessageProp msgProp) throws GSSException { 401 if (mechCtxt != null) 402 return mechCtxt.wrap(inBuf, offset, len, msgProp); 403 else 404 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 405 "No mechanism context yet!"); 406 } 407 408 public void wrap(InputStream inStream, OutputStream outStream, 409 MessageProp msgProp) throws GSSException { 410 if (mechCtxt != null) 411 mechCtxt.wrap(inStream, outStream, msgProp); 412 else 413 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 414 "No mechanism context yet!"); 415 } 416 417 public byte [] unwrap(byte[] inBuf, int offset, int len, 418 MessageProp msgProp) throws GSSException { 419 if (mechCtxt != null) 420 return mechCtxt.unwrap(inBuf, offset, len, msgProp); 421 else 422 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 423 "No mechanism context yet!"); 424 } 425 426 public void unwrap(InputStream inStream, OutputStream outStream, 427 MessageProp msgProp) throws GSSException { 428 if (mechCtxt != null) 429 mechCtxt.unwrap(inStream, outStream, msgProp); 430 else 431 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 432 "No mechanism context yet!"); 433 } 434 435 public byte[] getMIC(byte []inMsg, int offset, int len, 436 MessageProp msgProp) throws GSSException { 437 if (mechCtxt != null) 438 return mechCtxt.getMIC(inMsg, offset, len, msgProp); 439 else 440 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 441 "No mechanism context yet!"); 442 } 443 444 public void getMIC(InputStream inStream, OutputStream outStream, 445 MessageProp msgProp) throws GSSException { 446 if (mechCtxt != null) 447 mechCtxt.getMIC(inStream, outStream, msgProp); 448 else 449 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 450 "No mechanism context yet!"); 451 } 452 453 public void verifyMIC(byte[] inTok, int tokOffset, int tokLen, 454 byte[] inMsg, int msgOffset, int msgLen, 455 MessageProp msgProp) throws GSSException { 456 if (mechCtxt != null) 457 mechCtxt.verifyMIC(inTok, tokOffset, tokLen, 458 inMsg, msgOffset, msgLen, msgProp); 459 else 460 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 461 "No mechanism context yet!"); 462 } 463 464 public void verifyMIC(InputStream tokStream, InputStream msgStream, 465 MessageProp msgProp) throws GSSException { 466 if (mechCtxt != null) 467 mechCtxt.verifyMIC(tokStream, msgStream, msgProp); 468 else 469 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 470 "No mechanism context yet!"); 471 } 472 473 public byte[] export() throws GSSException { 474 // Defaults to null to match old behavior 475 byte[] result = null; 476 // Only allow context export from native provider since JGSS 477 // still has not defined its own interprocess token format 478 if (mechCtxt.isTransferable() && 479 mechCtxt.getProvider().getName().equals("SunNativeGSS")) { 480 result = mechCtxt.export(); 481 } 482 return result; 483 } 484 485 public void requestMutualAuth(boolean state) throws GSSException { 486 if (mechCtxt == null && initiator) 487 reqMutualAuthState = state; 488 } 489 490 public void requestReplayDet(boolean state) throws GSSException { 491 if (mechCtxt == null && initiator) 492 reqReplayDetState = state; 493 } 494 495 public void requestSequenceDet(boolean state) throws GSSException { 496 if (mechCtxt == null && initiator) 497 reqSequenceDetState = state; 498 } 499 500 public void requestCredDeleg(boolean state) throws GSSException { 501 if (mechCtxt == null && initiator) 502 reqCredDelegState = state; 503 } 504 505 public void requestAnonymity(boolean state) throws GSSException { 506 if (mechCtxt == null && initiator) 507 reqAnonState = state; 508 } 509 510 public void requestConf(boolean state) throws GSSException { 511 if (mechCtxt == null && initiator) 512 reqConfState = state; 513 } 514 515 public void requestInteg(boolean state) throws GSSException { 516 if (mechCtxt == null && initiator) 517 reqIntegState = state; 518 } 519 520 public void requestLifetime(int lifetime) throws GSSException { 521 if (mechCtxt == null && initiator) 522 reqLifetime = lifetime; 523 } 524 525 public void setChannelBinding(ChannelBinding channelBindings) 526 throws GSSException { 527 528 if (mechCtxt == null) 529 this.channelBindings = channelBindings; 530 531 } 532 533 public boolean getCredDelegState() { 534 if (mechCtxt != null) 535 return mechCtxt.getCredDelegState(); 536 else 537 return reqCredDelegState; 538 } 539 540 public boolean getMutualAuthState() { 541 if (mechCtxt != null) 542 return mechCtxt.getMutualAuthState(); 543 else 544 return reqMutualAuthState; 545 } 546 547 public boolean getReplayDetState() { 548 if (mechCtxt != null) 549 return mechCtxt.getReplayDetState(); 550 else 551 return reqReplayDetState; 552 } 553 554 public boolean getSequenceDetState() { 555 if (mechCtxt != null) 556 return mechCtxt.getSequenceDetState(); 557 else 558 return reqSequenceDetState; 559 } 560 561 public boolean getAnonymityState() { 562 if (mechCtxt != null) 563 return mechCtxt.getAnonymityState(); 564 else 565 return reqAnonState; 566 } 567 568 public boolean isTransferable() throws GSSException { 569 if (mechCtxt != null) 570 return mechCtxt.isTransferable(); 571 else 572 return false; 573 } 574 575 public boolean isProtReady() { 576 if (mechCtxt != null) 577 return mechCtxt.isProtReady(); 578 else 579 return false; 580 } 581 582 public boolean getConfState() { 583 if (mechCtxt != null) 584 return mechCtxt.getConfState(); 585 else 586 return reqConfState; 587 } 588 589 public boolean getIntegState() { 590 if (mechCtxt != null) 591 return mechCtxt.getIntegState(); 592 else 593 return reqIntegState; 594 } 595 596 public int getLifetime() { 597 if (mechCtxt != null) 598 return mechCtxt.getLifetime(); 599 else 600 return reqLifetime; 601 } 602 603 public GSSName getSrcName() throws GSSException { 604 if (srcName == null) { 605 srcName = GSSNameImpl.wrapElement 606 (gssManager, mechCtxt.getSrcName()); 607 } 608 return srcName; 609 } 610 611 public GSSName getTargName() throws GSSException { 612 if (targName == null) { 613 targName = GSSNameImpl.wrapElement 614 (gssManager, mechCtxt.getTargName()); 615 } 616 return targName; 617 } 618 619 public Oid getMech() throws GSSException { 620 if (mechCtxt != null) { 621 return mechCtxt.getMech(); 622 } 623 return mechOid; 624 } 625 626 public GSSCredential getDelegCred() throws GSSException { 627 628 if (mechCtxt == null) 629 throw new GSSExceptionImpl(GSSException.NO_CONTEXT, 630 "No mechanism context yet!"); 631 GSSCredentialSpi delCredElement = mechCtxt.getDelegCred(); 632 return (delCredElement == null ? 633 null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, delCredElement))); 634 } 635 636 public boolean isInitiator() throws GSSException { 637 return initiator; 638 } 639 640 public void dispose() throws GSSException { 641 currentState = DELETED; 642 if (mechCtxt != null) { 643 mechCtxt.dispose(); 644 mechCtxt = null; 645 } 646 myCred = null; 647 srcName = null; 648 targName = null; 649 } 650 651 // ExtendedGSSContext methods: 652 653 public Object inquireSecContext(String type) throws GSSException { 654 if (mechCtxt == null) { 655 throw new GSSException(GSSException.NO_CONTEXT); 656 } 657 return mechCtxt.inquireSecContext(type); 658 } 659 660 public void requestDelegPolicy(boolean state) throws GSSException { 661 if (mechCtxt == null && initiator) 662 reqDelegPolicyState = state; 663 } 664 665 public boolean getDelegPolicyState() { 666 if (mechCtxt != null) 667 return mechCtxt.getDelegPolicyState(); 668 else 669 return reqDelegPolicyState; 670 } 671} 672