Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java
41159 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.jgss;2627import org.ietf.jgss.*;28import sun.security.jgss.spi.*;2930import java.util.*;31import sun.security.jgss.spnego.SpNegoCredElement;3233public class GSSCredentialImpl implements GSSCredential {3435private GSSManagerImpl gssManager = null;36private boolean destroyed = false;3738/*39* We store all elements in a hashtable, using <oid, usage> as the40* key. This makes it easy to locate the specific kind of credential we41* need. The implementation needs to be optimized for the case where42* there is just one element (tempCred).43*/44private Hashtable<SearchKey, GSSCredentialSpi> hashtable = null;4546// XXX Optimization for single mech usage47private GSSCredentialSpi tempCred = null;4849public GSSCredentialImpl() {50// Useless51}5253// Used by new ExtendedGSSCredential.ExtendedGSSCredentialImpl(cred)54protected GSSCredentialImpl(GSSCredentialImpl src) {55this.gssManager = src.gssManager;56this.destroyed = src.destroyed;57this.hashtable = src.hashtable;58this.tempCred = src.tempCred;59}6061GSSCredentialImpl(GSSManagerImpl gssManager, int usage)62throws GSSException {63this(gssManager, null, GSSCredential.DEFAULT_LIFETIME,64(Oid[]) null, usage);65}6667GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,68int lifetime, Oid mech, int usage)69throws GSSException {70if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;7172init(gssManager);73add(name, lifetime, lifetime, mech, usage);74}7576GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,77int lifetime, Oid[] mechs, int usage)78throws GSSException {79init(gssManager);80boolean defaultList = false;81if (mechs == null) {82mechs = gssManager.getMechs();83defaultList = true;84}8586for (int i = 0; i < mechs.length; i++) {87try {88add(name, lifetime, lifetime, mechs[i], usage);89} catch (GSSException e) {90if (defaultList) {91// Try the next mechanism92GSSUtil.debug("Ignore " + e + " while acquring cred for "93+ mechs[i]);94//e.printStackTrace();95} else throw e; // else try the next mechanism96}97}98if ((hashtable.size() == 0) || (usage != getUsage()))99throw new GSSException(GSSException.NO_CRED);100}101102// Wrap a mech cred into a GSS cred103public GSSCredentialImpl(GSSManagerImpl gssManager,104GSSCredentialSpi mechElement) throws GSSException {105106init(gssManager);107int usage = GSSCredential.ACCEPT_ONLY;108if (mechElement.isInitiatorCredential()) {109if (mechElement.isAcceptorCredential()) {110usage = GSSCredential.INITIATE_AND_ACCEPT;111} else {112usage = GSSCredential.INITIATE_ONLY;113}114}115SearchKey key = new SearchKey(mechElement.getMechanism(),116usage);117tempCred = mechElement;118hashtable.put(key, tempCred);119// More mechs that can use this cred, say, SPNEGO120if (!GSSUtil.isSpNegoMech(mechElement.getMechanism())) {121key = new SearchKey(GSSUtil.GSS_SPNEGO_MECH_OID, usage);122hashtable.put(key, new SpNegoCredElement(mechElement));123}124}125126void init(GSSManagerImpl gssManager) {127this.gssManager = gssManager;128hashtable = new Hashtable<SearchKey, GSSCredentialSpi>(129gssManager.getMechs().length);130}131132public void dispose() throws GSSException {133if (!destroyed) {134GSSCredentialSpi element;135Enumeration<GSSCredentialSpi> values = hashtable.elements();136while (values.hasMoreElements()) {137element = values.nextElement();138element.dispose();139}140destroyed = true;141}142}143144public GSSCredential impersonate(GSSName name) throws GSSException {145if (destroyed) {146throw new IllegalStateException("This credential is " +147"no longer valid");148}149Oid mech = tempCred.getMechanism();150GSSNameSpi nameElement = (name == null ? null :151((GSSNameImpl)name).getElement(mech));152GSSCredentialSpi cred = tempCred.impersonate(nameElement);153return (cred == null ?154null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, cred)));155}156157public GSSName getName() throws GSSException {158if (destroyed) {159throw new IllegalStateException("This credential is " +160"no longer valid");161}162return GSSNameImpl.wrapElement(gssManager, tempCred.getName());163}164165public GSSName getName(Oid mech) throws GSSException {166167if (destroyed) {168throw new IllegalStateException("This credential is " +169"no longer valid");170}171172SearchKey key = null;173GSSCredentialSpi element = null;174175if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;176177key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);178element = hashtable.get(key);179180if (element == null) {181key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);182element = hashtable.get(key);183}184185if (element == null) {186key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);187element = hashtable.get(key);188}189190if (element == null) {191throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);192}193194return GSSNameImpl.wrapElement(gssManager, element.getName());195196}197198/**199* Returns the remaining lifetime of this credential. The remaining200* lifetime is defined as the minimum lifetime, either for initiate or201* for accept, across all elements contained in it. Not terribly202* useful, but required by GSS-API.203*/204public int getRemainingLifetime() throws GSSException {205206if (destroyed) {207throw new IllegalStateException("This credential is " +208"no longer valid");209}210211SearchKey tempKey;212GSSCredentialSpi tempCred;213int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0;214int min = INDEFINITE_LIFETIME;215216for (Enumeration<SearchKey> e = hashtable.keys();217e.hasMoreElements(); ) {218tempKey = e.nextElement();219tempCred = hashtable.get(tempKey);220if (tempKey.getUsage() == INITIATE_ONLY)221tempLife = tempCred.getInitLifetime();222else if (tempKey.getUsage() == ACCEPT_ONLY)223tempLife = tempCred.getAcceptLifetime();224else {225tempInitLife = tempCred.getInitLifetime();226tempAcceptLife = tempCred.getAcceptLifetime();227tempLife = (tempInitLife < tempAcceptLife ?228tempInitLife:229tempAcceptLife);230}231if (min > tempLife)232min = tempLife;233}234235return min;236}237238public int getRemainingInitLifetime(Oid mech) throws GSSException {239240if (destroyed) {241throw new IllegalStateException("This credential is " +242"no longer valid");243}244245GSSCredentialSpi element = null;246SearchKey key = null;247boolean found = false;248int max = 0;249250if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;251252key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);253element = hashtable.get(key);254255if (element != null) {256found = true;257if (max < element.getInitLifetime())258max = element.getInitLifetime();259}260261key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);262element = hashtable.get(key);263264if (element != null) {265found = true;266if (max < element.getInitLifetime())267max = element.getInitLifetime();268}269270if (!found) {271throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);272}273274return max;275276}277278public int getRemainingAcceptLifetime(Oid mech) throws GSSException {279280if (destroyed) {281throw new IllegalStateException("This credential is " +282"no longer valid");283}284285GSSCredentialSpi element = null;286SearchKey key = null;287boolean found = false;288int max = 0;289290if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;291292key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);293element = hashtable.get(key);294295if (element != null) {296found = true;297if (max < element.getAcceptLifetime())298max = element.getAcceptLifetime();299}300301key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);302element = hashtable.get(key);303304if (element != null) {305found = true;306if (max < element.getAcceptLifetime())307max = element.getAcceptLifetime();308}309310if (!found) {311throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);312}313314return max;315316}317318/**319* Returns the usage mode for this credential. Returns320* INITIATE_AND_ACCEPT if any one element contained in it supports321* INITIATE_AND_ACCEPT or if two different elements exist where one322* support INITIATE_ONLY and the other supports ACCEPT_ONLY.323*/324public int getUsage() throws GSSException {325326if (destroyed) {327throw new IllegalStateException("This credential is " +328"no longer valid");329}330331SearchKey tempKey;332boolean initiate = false;333boolean accept = false;334335for (Enumeration<SearchKey> e = hashtable.keys();336e.hasMoreElements(); ) {337tempKey = e.nextElement();338if (tempKey.getUsage() == INITIATE_ONLY)339initiate = true;340else if (tempKey.getUsage() == ACCEPT_ONLY)341accept = true;342else343return INITIATE_AND_ACCEPT;344}345if (initiate) {346if (accept)347return INITIATE_AND_ACCEPT;348else349return INITIATE_ONLY;350} else351return ACCEPT_ONLY;352}353354public int getUsage(Oid mech) throws GSSException {355356if (destroyed) {357throw new IllegalStateException("This credential is " +358"no longer valid");359}360361GSSCredentialSpi element = null;362SearchKey key = null;363boolean initiate = false;364boolean accept = false;365366if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;367368key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);369element = hashtable.get(key);370371if (element != null) {372initiate = true;373}374375key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);376element = hashtable.get(key);377378if (element != null) {379accept = true;380}381382key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);383element = hashtable.get(key);384385if (element != null) {386initiate = true;387accept = true;388}389390if (initiate && accept)391return GSSCredential.INITIATE_AND_ACCEPT;392else if (initiate)393return GSSCredential.INITIATE_ONLY;394else if (accept)395return GSSCredential.ACCEPT_ONLY;396else {397throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);398}399}400401public Oid[] getMechs() throws GSSException {402403if (destroyed) {404throw new IllegalStateException("This credential is " +405"no longer valid");406}407Vector<Oid> result = new Vector<Oid>(hashtable.size());408409for (Enumeration<SearchKey> e = hashtable.keys();410e.hasMoreElements(); ) {411SearchKey tempKey = e.nextElement();412result.addElement(tempKey.getMech());413}414return result.toArray(new Oid[0]);415}416417public void add(GSSName name, int initLifetime, int acceptLifetime,418Oid mech, int usage) throws GSSException {419420if (destroyed) {421throw new IllegalStateException("This credential is " +422"no longer valid");423}424if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;425426SearchKey key = new SearchKey(mech, usage);427if (hashtable.containsKey(key)) {428throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT,429"Duplicate element found: " +430getElementStr(mech, usage));431}432433// XXX If not instance of GSSNameImpl then throw exception434// Application mixing GSS implementations435GSSNameSpi nameElement = (name == null ? null :436((GSSNameImpl)name).getElement(mech));437438tempCred = gssManager.getCredentialElement(nameElement,439initLifetime,440acceptLifetime,441mech,442usage);443/*444* Not all mechanisms support the concept of one credential element445* that can be used for both initiating and accepting a context. In446* the event that an application requests usage INITIATE_AND_ACCEPT447* for a credential from such a mechanism, the GSS framework will448* need to obtain two different credential elements from the449* mechanism, one that will have usage INITIATE_ONLY and another450* that will have usage ACCEPT_ONLY. The mechanism will help the451* GSS-API realize this by returning a credential element with452* usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another453* call to getCredentialElement, this time with the other usage454* mode.455*/456457if (tempCred != null) {458if (usage == GSSCredential.INITIATE_AND_ACCEPT &&459(!tempCred.isAcceptorCredential() ||460!tempCred.isInitiatorCredential())) {461462int currentUsage;463int desiredUsage;464465if (!tempCred.isInitiatorCredential()) {466currentUsage = GSSCredential.ACCEPT_ONLY;467desiredUsage = GSSCredential.INITIATE_ONLY;468} else {469currentUsage = GSSCredential.INITIATE_ONLY;470desiredUsage = GSSCredential.ACCEPT_ONLY;471}472473key = new SearchKey(mech, currentUsage);474hashtable.put(key, tempCred);475476tempCred = gssManager.getCredentialElement(nameElement,477initLifetime,478acceptLifetime,479mech,480desiredUsage);481482key = new SearchKey(mech, desiredUsage);483hashtable.put(key, tempCred);484} else {485hashtable.put(key, tempCred);486}487}488}489490public boolean equals(Object another) {491492if (destroyed) {493throw new IllegalStateException("This credential is " +494"no longer valid");495}496497if (this == another) {498return true;499}500501if (!(another instanceof GSSCredentialImpl)) {502return false;503}504505// NOTE: The specification does not define the criteria to compare506// credentials.507/*508* XXX509* The RFC says: "Tests if this GSSCredential refers to the same510* entity as the supplied object. The two credentials must be511* acquired over the same mechanisms and must refer to the same512* principal. Returns "true" if the two GSSCredentials refer to513* the same entity; "false" otherwise."514*515* Well, when do two credentials refer to the same principal? Do516* they need to have one GSSName in common for the different517* GSSName's that the credential elements return? Or do all518* GSSName's have to be in common when the names are exported with519* their respective mechanisms for the credential elements?520*/521return false;522523}524525/**526* Returns a hashcode value for this GSSCredential.527*528* @return a hashCode value529*/530public int hashCode() {531532if (destroyed) {533throw new IllegalStateException("This credential is " +534"no longer valid");535}536537// NOTE: The specification does not define the criteria to compare538// credentials.539/*540* XXX541* Decide on a criteria for equals first then do this.542*/543return 1;544}545546/**547* Returns the specified mechanism's credential-element.548*549* @param mechOid the oid for mechanism to retrieve550* @param initiate boolean indicating if the function is551* to throw exception or return null when element is not552* found.553* @return mechanism credential object554* @exception GSSException of invalid mechanism555*/556public GSSCredentialSpi getElement(Oid mechOid, boolean initiate)557throws GSSException {558559if (destroyed) {560throw new IllegalStateException("This credential is " +561"no longer valid");562}563564SearchKey key;565GSSCredentialSpi element;566567if (mechOid == null) {568/*569* First see if the default mechanism satisfies the570* desired usage.571*/572mechOid = ProviderList.DEFAULT_MECH_OID;573key = new SearchKey(mechOid,574initiate? INITIATE_ONLY : ACCEPT_ONLY);575element = hashtable.get(key);576if (element == null) {577key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);578element = hashtable.get(key);579if (element == null) {580/*581* Now just return any element that satisfies the582* desired usage.583*/584Object[] elements = hashtable.entrySet().toArray();585for (int i = 0; i < elements.length; i++) {586element = (GSSCredentialSpi)587((Map.Entry)elements[i]).getValue();588if (element.isInitiatorCredential() == initiate)589break;590} // for loop591}592}593} else {594595if (initiate)596key = new SearchKey(mechOid, INITIATE_ONLY);597else598key = new SearchKey(mechOid, ACCEPT_ONLY);599600element = hashtable.get(key);601602if (element == null) {603key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);604element = hashtable.get(key);605}606}607608if (element == null)609throw new GSSExceptionImpl(GSSException.NO_CRED,610"No credential found for: " +611getElementStr(mechOid,612initiate? INITIATE_ONLY : ACCEPT_ONLY));613return element;614}615616Set<GSSCredentialSpi> getElements() {617HashSet<GSSCredentialSpi> retVal =618new HashSet<GSSCredentialSpi>(hashtable.size());619Enumeration<GSSCredentialSpi> values = hashtable.elements();620while (values.hasMoreElements()) {621GSSCredentialSpi o = values.nextElement();622retVal.add(o);623}624return retVal;625}626627private static String getElementStr(Oid mechOid, int usage) {628String displayString = mechOid.toString();629if (usage == GSSCredential.INITIATE_ONLY) {630displayString =631displayString.concat(" usage: Initiate");632} else if (usage == GSSCredential.ACCEPT_ONLY) {633displayString =634displayString.concat(" usage: Accept");635} else {636displayString =637displayString.concat(" usage: Initiate and Accept");638}639return displayString;640}641642public String toString() {643644if (destroyed) {645throw new IllegalStateException("This credential is " +646"no longer valid");647}648649GSSCredentialSpi element = null;650StringBuilder sb = new StringBuilder("[GSSCredential: ");651Object[] elements = hashtable.entrySet().toArray();652for (int i = 0; i < elements.length; i++) {653try {654sb.append('\n');655element = (GSSCredentialSpi)656((Map.Entry)elements[i]).getValue();657sb.append(element.getName());658sb.append(' ');659sb.append(element.getMechanism());660sb.append(element.isInitiatorCredential() ?661" Initiate" : "");662sb.append(element.isAcceptorCredential() ?663" Accept" : "");664sb.append(" [");665sb.append(element.getClass());666sb.append(']');667} catch (GSSException e) {668// skip to next element669}670}671sb.append(']');672return sb.toString();673}674675static class SearchKey {676private Oid mechOid = null;677private int usage = GSSCredential.INITIATE_AND_ACCEPT;678public SearchKey(Oid mechOid, int usage) {679680this.mechOid = mechOid;681this.usage = usage;682}683public Oid getMech() {684return mechOid;685}686public int getUsage() {687return usage;688}689public boolean equals(Object other) {690if (! (other instanceof SearchKey))691return false;692SearchKey that = (SearchKey) other;693return ((this.mechOid.equals(that.mechOid)) &&694(this.usage == that.usage));695}696public int hashCode() {697return mechOid.hashCode();698}699}700701}702703704