1 /* 2 * Copyright (c) 2000, 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; 27 28 import org.ietf.jgss.*; 29 import sun.security.jgss.spi.*; 30 31 import java.util.*; 32 import sun.security.jgss.spnego.SpNegoCredElement; 33 34 public class GSSCredentialImpl implements GSSCredential { 35 36 private GSSManagerImpl gssManager = null; 37 private boolean destroyed = false; 38 39 /* 40 * We store all elements in a hashtable, using <oid, usage> as the 41 * key. This makes it easy to locate the specific kind of credential we 42 * need. The implementation needs to be optimized for the case where 43 * there is just one element (tempCred). 44 */ 45 private Hashtable<SearchKey, GSSCredentialSpi> hashtable = null; 46 47 // XXX Optimization for single mech usage 48 private GSSCredentialSpi tempCred = null; 49 50 public GSSCredentialImpl() { 51 // Useless 52 } 53 54 // Used by new ExtendedGSSCredential.ExtendedGSSCredentialImpl(cred) 55 protected GSSCredentialImpl(GSSCredentialImpl src) { 56 this.gssManager = src.gssManager; 57 this.destroyed = src.destroyed; 58 this.hashtable = src.hashtable; 59 this.tempCred = src.tempCred; 60 } 61 62 GSSCredentialImpl(GSSManagerImpl gssManager, int usage) 63 throws GSSException { 64 this(gssManager, null, GSSCredential.DEFAULT_LIFETIME, 65 (Oid[]) null, usage); 66 } 67 68 GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, 69 int lifetime, Oid mech, int usage) 70 throws GSSException { 71 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 72 73 init(gssManager); 74 add(name, lifetime, lifetime, mech, usage); 75 } 76 77 GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, 78 int lifetime, Oid[] mechs, int usage) 79 throws GSSException { 80 init(gssManager); 81 boolean defaultList = false; 82 if (mechs == null) { 83 mechs = gssManager.getMechs(); 84 defaultList = true; 85 } 86 87 for (int i = 0; i < mechs.length; i++) { 88 try { 89 add(name, lifetime, lifetime, mechs[i], usage); 90 } catch (GSSException e) { 91 if (defaultList) { 92 // Try the next mechanism 93 GSSUtil.debug("Ignore " + e + " while acquring cred for " 94 + mechs[i]); 95 //e.printStackTrace(); 96 } else throw e; // else try the next mechanism 97 } 98 } 99 if ((hashtable.size() == 0) || (usage != getUsage())) 100 throw new GSSException(GSSException.NO_CRED); 101 } 102 103 // Wrap a mech cred into a GSS cred 104 public GSSCredentialImpl(GSSManagerImpl gssManager, 105 GSSCredentialSpi mechElement) throws GSSException { 106 107 init(gssManager); 108 int usage = GSSCredential.ACCEPT_ONLY; 109 if (mechElement.isInitiatorCredential()) { 110 if (mechElement.isAcceptorCredential()) { 111 usage = GSSCredential.INITIATE_AND_ACCEPT; 112 } else { 113 usage = GSSCredential.INITIATE_ONLY; 114 } 115 } 116 SearchKey key = new SearchKey(mechElement.getMechanism(), 117 usage); 118 tempCred = mechElement; 119 hashtable.put(key, tempCred); 120 // More mechs that can use this cred, say, SPNEGO 121 if (!GSSUtil.isSpNegoMech(mechElement.getMechanism())) { 122 key = new SearchKey(GSSUtil.GSS_SPNEGO_MECH_OID, usage); 123 hashtable.put(key, new SpNegoCredElement(mechElement)); 124 } 125 } 126 127 void init(GSSManagerImpl gssManager) { 128 this.gssManager = gssManager; 129 hashtable = new Hashtable<SearchKey, GSSCredentialSpi>( 130 gssManager.getMechs().length); 131 } 132 133 public void dispose() throws GSSException { 134 if (!destroyed) { 135 GSSCredentialSpi element; 136 Enumeration<GSSCredentialSpi> values = hashtable.elements(); 137 while (values.hasMoreElements()) { 138 element = values.nextElement(); 139 element.dispose(); 140 } 141 destroyed = true; 142 } 143 } 144 145 public GSSCredential impersonate(GSSName name) throws GSSException { 146 if (destroyed) { 147 throw new IllegalStateException("This credential is " + 148 "no longer valid"); 149 } 150 Oid mech = tempCred.getMechanism(); 151 GSSNameSpi nameElement = (name == null ? null : 152 ((GSSNameImpl)name).getElement(mech)); 153 GSSCredentialSpi cred = tempCred.impersonate(nameElement); 154 return (cred == null ? 155 null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, cred))); 156 } 157 158 public GSSName getName() throws GSSException { 159 if (destroyed) { 160 throw new IllegalStateException("This credential is " + 161 "no longer valid"); 162 } 163 return GSSNameImpl.wrapElement(gssManager, tempCred.getName()); 164 } 165 166 public GSSName getName(Oid mech) throws GSSException { 167 168 if (destroyed) { 169 throw new IllegalStateException("This credential is " + 170 "no longer valid"); 171 } 172 173 SearchKey key = null; 174 GSSCredentialSpi element = null; 175 176 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 177 178 key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); 179 element = hashtable.get(key); 180 181 if (element == null) { 182 key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); 183 element = hashtable.get(key); 184 } 185 186 if (element == null) { 187 key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); 188 element = hashtable.get(key); 189 } 190 191 if (element == null) { 192 throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); 193 } 194 195 return GSSNameImpl.wrapElement(gssManager, element.getName()); 196 197 } 198 199 /** 200 * Returns the remaining lifetime of this credential. The remaining 201 * lifetime is defined as the minimum lifetime, either for initiate or 202 * for accept, across all elements contained in it. Not terribly 203 * useful, but required by GSS-API. 204 */ 205 public int getRemainingLifetime() throws GSSException { 206 207 if (destroyed) { 208 throw new IllegalStateException("This credential is " + 209 "no longer valid"); 210 } 211 212 SearchKey tempKey; 213 GSSCredentialSpi tempCred; 214 int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0; 215 int min = INDEFINITE_LIFETIME; 216 217 for (Enumeration<SearchKey> e = hashtable.keys(); 218 e.hasMoreElements(); ) { 219 tempKey = e.nextElement(); 220 tempCred = hashtable.get(tempKey); 221 if (tempKey.getUsage() == INITIATE_ONLY) 222 tempLife = tempCred.getInitLifetime(); 223 else if (tempKey.getUsage() == ACCEPT_ONLY) 224 tempLife = tempCred.getAcceptLifetime(); 225 else { 226 tempInitLife = tempCred.getInitLifetime(); 227 tempAcceptLife = tempCred.getAcceptLifetime(); 228 tempLife = (tempInitLife < tempAcceptLife ? 229 tempInitLife: 230 tempAcceptLife); 231 } 232 if (min > tempLife) 233 min = tempLife; 234 } 235 236 return min; 237 } 238 239 public int getRemainingInitLifetime(Oid mech) throws GSSException { 240 241 if (destroyed) { 242 throw new IllegalStateException("This credential is " + 243 "no longer valid"); 244 } 245 246 GSSCredentialSpi element = null; 247 SearchKey key = null; 248 boolean found = false; 249 int max = 0; 250 251 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 252 253 key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); 254 element = hashtable.get(key); 255 256 if (element != null) { 257 found = true; 258 if (max < element.getInitLifetime()) 259 max = element.getInitLifetime(); 260 } 261 262 key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); 263 element = hashtable.get(key); 264 265 if (element != null) { 266 found = true; 267 if (max < element.getInitLifetime()) 268 max = element.getInitLifetime(); 269 } 270 271 if (!found) { 272 throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); 273 } 274 275 return max; 276 277 } 278 279 public int getRemainingAcceptLifetime(Oid mech) throws GSSException { 280 281 if (destroyed) { 282 throw new IllegalStateException("This credential is " + 283 "no longer valid"); 284 } 285 286 GSSCredentialSpi element = null; 287 SearchKey key = null; 288 boolean found = false; 289 int max = 0; 290 291 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 292 293 key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); 294 element = hashtable.get(key); 295 296 if (element != null) { 297 found = true; 298 if (max < element.getAcceptLifetime()) 299 max = element.getAcceptLifetime(); 300 } 301 302 key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); 303 element = hashtable.get(key); 304 305 if (element != null) { 306 found = true; 307 if (max < element.getAcceptLifetime()) 308 max = element.getAcceptLifetime(); 309 } 310 311 if (!found) { 312 throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); 313 } 314 315 return max; 316 317 } 318 319 /** 320 * Returns the usage mode for this credential. Returns 321 * INITIATE_AND_ACCEPT if any one element contained in it supports 322 * INITIATE_AND_ACCEPT or if two different elements exist where one 323 * support INITIATE_ONLY and the other supports ACCEPT_ONLY. 324 */ 325 public int getUsage() throws GSSException { 326 327 if (destroyed) { 328 throw new IllegalStateException("This credential is " + 329 "no longer valid"); 330 } 331 332 SearchKey tempKey; 333 boolean initiate = false; 334 boolean accept = false; 335 336 for (Enumeration<SearchKey> e = hashtable.keys(); 337 e.hasMoreElements(); ) { 338 tempKey = e.nextElement(); 339 if (tempKey.getUsage() == INITIATE_ONLY) 340 initiate = true; 341 else if (tempKey.getUsage() == ACCEPT_ONLY) 342 accept = true; 343 else 344 return INITIATE_AND_ACCEPT; 345 } 346 if (initiate) { 347 if (accept) 348 return INITIATE_AND_ACCEPT; 349 else 350 return INITIATE_ONLY; 351 } else 352 return ACCEPT_ONLY; 353 } 354 355 public int getUsage(Oid mech) throws GSSException { 356 357 if (destroyed) { 358 throw new IllegalStateException("This credential is " + 359 "no longer valid"); 360 } 361 362 GSSCredentialSpi element = null; 363 SearchKey key = null; 364 boolean initiate = false; 365 boolean accept = false; 366 367 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 368 369 key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); 370 element = hashtable.get(key); 371 372 if (element != null) { 373 initiate = true; 374 } 375 376 key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); 377 element = hashtable.get(key); 378 379 if (element != null) { 380 accept = true; 381 } 382 383 key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); 384 element = hashtable.get(key); 385 386 if (element != null) { 387 initiate = true; 388 accept = true; 389 } 390 391 if (initiate && accept) 392 return GSSCredential.INITIATE_AND_ACCEPT; 393 else if (initiate) 394 return GSSCredential.INITIATE_ONLY; 395 else if (accept) 396 return GSSCredential.ACCEPT_ONLY; 397 else { 398 throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); 399 } 400 } 401 402 public Oid[] getMechs() throws GSSException { 403 404 if (destroyed) { 405 throw new IllegalStateException("This credential is " + 406 "no longer valid"); 407 } 408 Vector<Oid> result = new Vector<Oid>(hashtable.size()); 409 410 for (Enumeration<SearchKey> e = hashtable.keys(); 411 e.hasMoreElements(); ) { 412 SearchKey tempKey = e.nextElement(); 413 result.addElement(tempKey.getMech()); 414 } 415 return result.toArray(new Oid[0]); 416 } 417 418 public void add(GSSName name, int initLifetime, int acceptLifetime, 419 Oid mech, int usage) throws GSSException { 420 421 if (destroyed) { 422 throw new IllegalStateException("This credential is " + 423 "no longer valid"); 424 } 425 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; 426 427 SearchKey key = new SearchKey(mech, usage); 428 if (hashtable.containsKey(key)) { 429 throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT, 430 "Duplicate element found: " + 431 getElementStr(mech, usage)); 432 } 433 434 // XXX If not instance of GSSNameImpl then throw exception 435 // Application mixing GSS implementations 436 GSSNameSpi nameElement = (name == null ? null : 437 ((GSSNameImpl)name).getElement(mech)); 438 439 tempCred = gssManager.getCredentialElement(nameElement, 440 initLifetime, 441 acceptLifetime, 442 mech, 443 usage); 444 /* 445 * Not all mechanisms support the concept of one credential element 446 * that can be used for both initiating and accepting a context. In 447 * the event that an application requests usage INITIATE_AND_ACCEPT 448 * for a credential from such a mechanism, the GSS framework will 449 * need to obtain two different credential elements from the 450 * mechanism, one that will have usage INITIATE_ONLY and another 451 * that will have usage ACCEPT_ONLY. The mechanism will help the 452 * GSS-API realize this by returning a credential element with 453 * usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another 454 * call to getCredentialElement, this time with the other usage 455 * mode. 456 */ 457 458 if (tempCred != null) { 459 if (usage == GSSCredential.INITIATE_AND_ACCEPT && 460 (!tempCred.isAcceptorCredential() || 461 !tempCred.isInitiatorCredential())) { 462 463 int currentUsage; 464 int desiredUsage; 465 466 if (!tempCred.isInitiatorCredential()) { 467 currentUsage = GSSCredential.ACCEPT_ONLY; 468 desiredUsage = GSSCredential.INITIATE_ONLY; 469 } else { 470 currentUsage = GSSCredential.INITIATE_ONLY; 471 desiredUsage = GSSCredential.ACCEPT_ONLY; 472 } 473 474 key = new SearchKey(mech, currentUsage); 475 hashtable.put(key, tempCred); 476 477 tempCred = gssManager.getCredentialElement(nameElement, 478 initLifetime, 479 acceptLifetime, 480 mech, 481 desiredUsage); 482 483 key = new SearchKey(mech, desiredUsage); 484 hashtable.put(key, tempCred); 485 } else { 486 hashtable.put(key, tempCred); 487 } 488 } 489 } 490 491 public boolean equals(Object another) { 492 493 if (destroyed) { 494 throw new IllegalStateException("This credential is " + 495 "no longer valid"); 496 } 497 498 if (this == another) { 499 return true; 500 } 501 502 if (!(another instanceof GSSCredentialImpl)) { 503 return false; 504 } 505 506 // NOTE: The specification does not define the criteria to compare 507 // credentials. 508 /* 509 * XXX 510 * The RFC says: "Tests if this GSSCredential refers to the same 511 * entity as the supplied object. The two credentials must be 512 * acquired over the same mechanisms and must refer to the same 513 * principal. Returns "true" if the two GSSCredentials refer to 514 * the same entity; "false" otherwise." 515 * 516 * Well, when do two credentials refer to the same principal? Do 517 * they need to have one GSSName in common for the different 518 * GSSName's that the credential elements return? Or do all 519 * GSSName's have to be in common when the names are exported with 520 * their respective mechanisms for the credential elements? 521 */ 522 return false; 523 524 } 525 526 /** 527 * Returns a hashcode value for this GSSCredential. 528 * 529 * @return a hashCode value 530 */ 531 public int hashCode() { 532 533 if (destroyed) { 534 throw new IllegalStateException("This credential is " + 535 "no longer valid"); 536 } 537 538 // NOTE: The specification does not define the criteria to compare 539 // credentials. 540 /* 541 * XXX 542 * Decide on a criteria for equals first then do this. 543 */ 544 return 1; 545 } 546 547 /** 548 * Returns the specified mechanism's credential-element. 549 * 550 * @param mechOid - the oid for mechanism to retrieve 551 * @param throwExcep - boolean indicating if the function is 552 * to throw exception or return null when element is not 553 * found. 554 * @return mechanism credential object 555 * @exception GSSException of invalid mechanism 556 */ 557 public GSSCredentialSpi getElement(Oid mechOid, boolean initiate) 558 throws GSSException { 559 560 if (destroyed) { 561 throw new IllegalStateException("This credential is " + 562 "no longer valid"); 563 } 564 565 SearchKey key; 566 GSSCredentialSpi element; 567 568 if (mechOid == null) { 569 /* 570 * First see if the default mechanism satisfies the 571 * desired usage. 572 */ 573 mechOid = ProviderList.DEFAULT_MECH_OID; 574 key = new SearchKey(mechOid, 575 initiate? INITIATE_ONLY : ACCEPT_ONLY); 576 element = hashtable.get(key); 577 if (element == null) { 578 key = new SearchKey(mechOid, INITIATE_AND_ACCEPT); 579 element = hashtable.get(key); 580 if (element == null) { 581 /* 582 * Now just return any element that satisfies the 583 * desired usage. 584 */ 585 Object[] elements = hashtable.entrySet().toArray(); 586 for (int i = 0; i < elements.length; i++) { 587 element = (GSSCredentialSpi) 588 ((Map.Entry)elements[i]).getValue(); 589 if (element.isInitiatorCredential() == initiate) 590 break; 591 } // for loop 592 } 593 } 594 } else { 595 596 if (initiate) 597 key = new SearchKey(mechOid, INITIATE_ONLY); 598 else 599 key = new SearchKey(mechOid, ACCEPT_ONLY); 600 601 element = hashtable.get(key); 602 603 if (element == null) { 604 key = new SearchKey(mechOid, INITIATE_AND_ACCEPT); 605 element = hashtable.get(key); 606 } 607 } 608 609 if (element == null) 610 throw new GSSExceptionImpl(GSSException.NO_CRED, 611 "No credential found for: " + 612 getElementStr(mechOid, 613 initiate? INITIATE_ONLY : ACCEPT_ONLY)); 614 return element; 615 } 616 617 Set<GSSCredentialSpi> getElements() { 618 HashSet<GSSCredentialSpi> retVal = 619 new HashSet<GSSCredentialSpi>(hashtable.size()); 620 Enumeration<GSSCredentialSpi> values = hashtable.elements(); 621 while (values.hasMoreElements()) { 622 GSSCredentialSpi o = values.nextElement(); 623 retVal.add(o); 624 } 625 return retVal; 626 } 627 628 private static String getElementStr(Oid mechOid, int usage) { 629 String displayString = mechOid.toString(); 630 if (usage == GSSCredential.INITIATE_ONLY) { 631 displayString = 632 displayString.concat(" usage: Initiate"); 633 } else if (usage == GSSCredential.ACCEPT_ONLY) { 634 displayString = 635 displayString.concat(" usage: Accept"); 636 } else { 637 displayString = 638 displayString.concat(" usage: Initiate and Accept"); 639 } 640 return displayString; 641 } 642 643 public String toString() { 644 645 if (destroyed) { 646 throw new IllegalStateException("This credential is " + 647 "no longer valid"); 648 } 649 650 GSSCredentialSpi element = null; 651 StringBuilder sb = new StringBuilder("[GSSCredential: "); 652 Object[] elements = hashtable.entrySet().toArray(); 653 for (int i = 0; i < elements.length; i++) { 654 try { 655 sb.append('\n'); 656 element = (GSSCredentialSpi) 657 ((Map.Entry)elements[i]).getValue(); 658 sb.append(element.getName()); 659 sb.append(' '); 660 sb.append(element.getMechanism()); 661 sb.append(element.isInitiatorCredential() ? 662 " Initiate" : ""); 663 sb.append(element.isAcceptorCredential() ? 664 " Accept" : ""); 665 sb.append(" ["); 666 sb.append(element.getClass()); 667 sb.append(']'); 668 } catch (GSSException e) { 669 // skip to next element 670 } 671 } 672 sb.append(']'); 673 return sb.toString(); 674 } 675 676 static class SearchKey { 677 private Oid mechOid = null; 678 private int usage = GSSCredential.INITIATE_AND_ACCEPT; 679 public SearchKey(Oid mechOid, int usage) { 680 681 this.mechOid = mechOid; 682 this.usage = usage; 683 } 684 public Oid getMech() { 685 return mechOid; 686 } 687 public int getUsage() { 688 return usage; 689 } 690 public boolean equals(Object other) { 691 if (! (other instanceof SearchKey)) 692 return false; 693 SearchKey that = (SearchKey) other; 694 return ((this.mechOid.equals(that.mechOid)) && 695 (this.usage == that.usage)); 696 } 697 public int hashCode() { 698 return mechOid.hashCode(); 699 } 700 } 701 702 }