/* * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.jgss; import org.ietf.jgss.*; import sun.security.jgss.spi.*; import java.util.*; import com.sun.security.jgss.*; import sun.security.jgss.spnego.SpNegoCredElement; public class GSSCredentialImpl implements ExtendedGSSCredential { private GSSManagerImpl gssManager = null; private boolean destroyed = false; /* * We store all elements in a hashtable, using as the * key. This makes it easy to locate the specific kind of credential we * need. The implementation needs to be optimized for the case where * there is just one element (tempCred). */ private Hashtable hashtable = null; // XXX Optimization for single mech usage private GSSCredentialSpi tempCred = null; GSSCredentialImpl(GSSManagerImpl gssManager, int usage) throws GSSException { this(gssManager, null, GSSCredential.DEFAULT_LIFETIME, (Oid[]) null, usage); } GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, int lifetime, Oid mech, int usage) throws GSSException { if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; init(gssManager); add(name, lifetime, lifetime, mech, usage); } GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, int lifetime, Oid mechs[], int usage) throws GSSException { init(gssManager); boolean defaultList = false; if (mechs == null) { mechs = gssManager.getMechs(); defaultList = true; } for (int i = 0; i < mechs.length; i++) { try { add(name, lifetime, lifetime, mechs[i], usage); } catch (GSSException e) { if (defaultList) { // Try the next mechanism GSSUtil.debug("Ignore " + e + " while acquring cred for " + mechs[i]); //e.printStackTrace(); } else throw e; // else try the next mechanism } } if ((hashtable.size() == 0) || (usage != getUsage())) throw new GSSException(GSSException.NO_CRED); } // Wrap a mech cred into a GSS cred public GSSCredentialImpl(GSSManagerImpl gssManager, GSSCredentialSpi mechElement) throws GSSException { init(gssManager); int usage = GSSCredential.ACCEPT_ONLY; if (mechElement.isInitiatorCredential()) { if (mechElement.isAcceptorCredential()) { usage = GSSCredential.INITIATE_AND_ACCEPT; } else { usage = GSSCredential.INITIATE_ONLY; } } SearchKey key = new SearchKey(mechElement.getMechanism(), usage); tempCred = mechElement; hashtable.put(key, tempCred); // More mechs that can use this cred, say, SPNEGO if (!GSSUtil.isSpNegoMech(mechElement.getMechanism())) { key = new SearchKey(GSSUtil.GSS_SPNEGO_MECH_OID, usage); hashtable.put(key, new SpNegoCredElement(mechElement)); } } void init(GSSManagerImpl gssManager) { this.gssManager = gssManager; hashtable = new Hashtable( gssManager.getMechs().length); } public void dispose() throws GSSException { if (!destroyed) { GSSCredentialSpi element; Enumeration values = hashtable.elements(); while (values.hasMoreElements()) { element = values.nextElement(); element.dispose(); } destroyed = true; } } public GSSCredential impersonate(GSSName name) throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } Oid mech = tempCred.getMechanism(); GSSNameSpi nameElement = (name == null ? null : ((GSSNameImpl)name).getElement(mech)); GSSCredentialSpi cred = tempCred.impersonate(nameElement); return (cred == null ? null : new GSSCredentialImpl(gssManager, cred)); } public GSSName getName() throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } return GSSNameImpl.wrapElement(gssManager, tempCred.getName()); } public GSSName getName(Oid mech) throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } SearchKey key = null; GSSCredentialSpi element = null; if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); element = hashtable.get(key); if (element == null) { key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); element = hashtable.get(key); } if (element == null) { key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); element = hashtable.get(key); } if (element == null) { throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); } return GSSNameImpl.wrapElement(gssManager, element.getName()); } /** * Returns the remaining lifetime of this credential. The remaining * lifetime is defined as the minimum lifetime, either for initiate or * for accept, across all elements contained in it. Not terribly * useful, but required by GSS-API. */ public int getRemainingLifetime() throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } SearchKey tempKey; GSSCredentialSpi tempCred; int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0; int min = INDEFINITE_LIFETIME; for (Enumeration e = hashtable.keys(); e.hasMoreElements(); ) { tempKey = e.nextElement(); tempCred = hashtable.get(tempKey); if (tempKey.getUsage() == INITIATE_ONLY) tempLife = tempCred.getInitLifetime(); else if (tempKey.getUsage() == ACCEPT_ONLY) tempLife = tempCred.getAcceptLifetime(); else { tempInitLife = tempCred.getInitLifetime(); tempAcceptLife = tempCred.getAcceptLifetime(); tempLife = (tempInitLife < tempAcceptLife ? tempInitLife: tempAcceptLife); } if (min > tempLife) min = tempLife; } return min; } public int getRemainingInitLifetime(Oid mech) throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } GSSCredentialSpi element = null; SearchKey key = null; boolean found = false; int max = 0; if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); element = hashtable.get(key); if (element != null) { found = true; if (max < element.getInitLifetime()) max = element.getInitLifetime(); } key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); element = hashtable.get(key); if (element != null) { found = true; if (max < element.getInitLifetime()) max = element.getInitLifetime(); } if (!found) { throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); } return max; } public int getRemainingAcceptLifetime(Oid mech) throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } GSSCredentialSpi element = null; SearchKey key = null; boolean found = false; int max = 0; if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); element = hashtable.get(key); if (element != null) { found = true; if (max < element.getAcceptLifetime()) max = element.getAcceptLifetime(); } key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); element = hashtable.get(key); if (element != null) { found = true; if (max < element.getAcceptLifetime()) max = element.getAcceptLifetime(); } if (!found) { throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); } return max; } /** * Returns the usage mode for this credential. Returns * INITIATE_AND_ACCEPT if any one element contained in it supports * INITIATE_AND_ACCEPT or if two different elements exist where one * support INITIATE_ONLY and the other supports ACCEPT_ONLY. */ public int getUsage() throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } SearchKey tempKey; boolean initiate = false; boolean accept = false; for (Enumeration e = hashtable.keys(); e.hasMoreElements(); ) { tempKey = e.nextElement(); if (tempKey.getUsage() == INITIATE_ONLY) initiate = true; else if (tempKey.getUsage() == ACCEPT_ONLY) accept = true; else return INITIATE_AND_ACCEPT; } if (initiate) { if (accept) return INITIATE_AND_ACCEPT; else return INITIATE_ONLY; } else return ACCEPT_ONLY; } public int getUsage(Oid mech) throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } GSSCredentialSpi element = null; SearchKey key = null; boolean initiate = false; boolean accept = false; if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); element = hashtable.get(key); if (element != null) { initiate = true; } key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); element = hashtable.get(key); if (element != null) { accept = true; } key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); element = hashtable.get(key); if (element != null) { initiate = true; accept = true; } if (initiate && accept) return GSSCredential.INITIATE_AND_ACCEPT; else if (initiate) return GSSCredential.INITIATE_ONLY; else if (accept) return GSSCredential.ACCEPT_ONLY; else { throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); } } public Oid[] getMechs() throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } Vector result = new Vector(hashtable.size()); for (Enumeration e = hashtable.keys(); e.hasMoreElements(); ) { SearchKey tempKey = e.nextElement(); result.addElement(tempKey.getMech()); } return result.toArray(new Oid[0]); } public void add(GSSName name, int initLifetime, int acceptLifetime, Oid mech, int usage) throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; SearchKey key = new SearchKey(mech, usage); if (hashtable.containsKey(key)) { throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT, "Duplicate element found: " + getElementStr(mech, usage)); } // XXX If not instance of GSSNameImpl then throw exception // Application mixing GSS implementations GSSNameSpi nameElement = (name == null ? null : ((GSSNameImpl)name).getElement(mech)); tempCred = gssManager.getCredentialElement(nameElement, initLifetime, acceptLifetime, mech, usage); /* * Not all mechanisms support the concept of one credential element * that can be used for both initiating and accepting a context. In * the event that an application requests usage INITIATE_AND_ACCEPT * for a credential from such a mechanism, the GSS framework will * need to obtain two different credential elements from the * mechanism, one that will have usage INITIATE_ONLY and another * that will have usage ACCEPT_ONLY. The mechanism will help the * GSS-API realize this by returning a credential element with * usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another * call to getCredentialElement, this time with the other usage * mode. */ if (tempCred != null) { if (usage == GSSCredential.INITIATE_AND_ACCEPT && (!tempCred.isAcceptorCredential() || !tempCred.isInitiatorCredential())) { int currentUsage; int desiredUsage; if (!tempCred.isInitiatorCredential()) { currentUsage = GSSCredential.ACCEPT_ONLY; desiredUsage = GSSCredential.INITIATE_ONLY; } else { currentUsage = GSSCredential.INITIATE_ONLY; desiredUsage = GSSCredential.ACCEPT_ONLY; } key = new SearchKey(mech, currentUsage); hashtable.put(key, tempCred); tempCred = gssManager.getCredentialElement(nameElement, initLifetime, acceptLifetime, mech, desiredUsage); key = new SearchKey(mech, desiredUsage); hashtable.put(key, tempCred); } else { hashtable.put(key, tempCred); } } } public boolean equals(Object another) { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } if (this == another) { return true; } if (!(another instanceof GSSCredentialImpl)) { return false; } // NOTE: The specification does not define the criteria to compare // credentials. /* * XXX * The RFC says: "Tests if this GSSCredential refers to the same * entity as the supplied object. The two credentials must be * acquired over the same mechanisms and must refer to the same * principal. Returns "true" if the two GSSCredentials refer to * the same entity; "false" otherwise." * * Well, when do two credentials refer to the same principal? Do * they need to have one GSSName in common for the different * GSSName's that the credential elements return? Or do all * GSSName's have to be in common when the names are exported with * their respective mechanisms for the credential elements? */ return false; } /** * Returns a hashcode value for this GSSCredential. * * @return a hashCode value */ public int hashCode() { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } // NOTE: The specification does not define the criteria to compare // credentials. /* * XXX * Decide on a criteria for equals first then do this. */ return 1; } /** * Returns the specified mechanism's credential-element. * * @param mechOid - the oid for mechanism to retrieve * @param throwExcep - boolean indicating if the function is * to throw exception or return null when element is not * found. * @return mechanism credential object * @exception GSSException of invalid mechanism */ public GSSCredentialSpi getElement(Oid mechOid, boolean initiate) throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } SearchKey key; GSSCredentialSpi element; if (mechOid == null) { /* * First see if the default mechanism satisfies the * desired usage. */ mechOid = ProviderList.DEFAULT_MECH_OID; key = new SearchKey(mechOid, initiate? INITIATE_ONLY : ACCEPT_ONLY); element = hashtable.get(key); if (element == null) { key = new SearchKey(mechOid, INITIATE_AND_ACCEPT); element = hashtable.get(key); if (element == null) { /* * Now just return any element that satisfies the * desired usage. */ Object[] elements = hashtable.entrySet().toArray(); for (int i = 0; i < elements.length; i++) { element = (GSSCredentialSpi) ((Map.Entry)elements[i]).getValue(); if (element.isInitiatorCredential() == initiate) break; } // for loop } } } else { if (initiate) key = new SearchKey(mechOid, INITIATE_ONLY); else key = new SearchKey(mechOid, ACCEPT_ONLY); element = hashtable.get(key); if (element == null) { key = new SearchKey(mechOid, INITIATE_AND_ACCEPT); element = hashtable.get(key); } } if (element == null) throw new GSSExceptionImpl(GSSException.NO_CRED, "No credential found for: " + getElementStr(mechOid, initiate? INITIATE_ONLY : ACCEPT_ONLY)); return element; } Set getElements() { HashSet retVal = new HashSet(hashtable.size()); Enumeration values = hashtable.elements(); while (values.hasMoreElements()) { GSSCredentialSpi o = values.nextElement(); retVal.add(o); } return retVal; } private static String getElementStr(Oid mechOid, int usage) { String displayString = mechOid.toString(); if (usage == GSSCredential.INITIATE_ONLY) { displayString = displayString.concat(" usage: Initiate"); } else if (usage == GSSCredential.ACCEPT_ONLY) { displayString = displayString.concat(" usage: Accept"); } else { displayString = displayString.concat(" usage: Initiate and Accept"); } return displayString; } public String toString() { if (destroyed) { throw new IllegalStateException("This credential is " + "no longer valid"); } GSSCredentialSpi element = null; StringBuffer buffer = new StringBuffer("[GSSCredential: "); Object[] elements = hashtable.entrySet().toArray(); for (int i = 0; i < elements.length; i++) { try { buffer.append('\n'); element = (GSSCredentialSpi) ((Map.Entry)elements[i]).getValue(); buffer.append(element.getName()); buffer.append(' '); buffer.append(element.getMechanism()); buffer.append(element.isInitiatorCredential() ? " Initiate" : ""); buffer.append(element.isAcceptorCredential() ? " Accept" : ""); buffer.append(" ["); buffer.append(element.toString()); buffer.append(']'); } catch (GSSException e) { // skip to next element } } buffer.append(']'); return buffer.toString(); } static class SearchKey { private Oid mechOid = null; private int usage = GSSCredential.INITIATE_AND_ACCEPT; public SearchKey(Oid mechOid, int usage) { this.mechOid = mechOid; this.usage = usage; } public Oid getMech() { return mechOid; } public int getUsage() { return usage; } public boolean equals(Object other) { if (! (other instanceof SearchKey)) return false; SearchKey that = (SearchKey) other; return ((this.mechOid.equals(that.mechOid)) && (this.usage == that.usage)); } public int hashCode() { return mechOid.hashCode(); } } }