Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/jgss/GSSNameImpl.java
38830 views
/*1* Copyright (c) 2000, 2010, 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.*;29import java.util.Set;30import java.util.HashMap;31import java.util.HashSet;32import java.util.Arrays;33import java.io.IOException;34import java.io.UnsupportedEncodingException;35import sun.security.util.ObjectIdentifier;36import sun.security.util.DerInputStream;37import sun.security.util.DerOutputStream;3839/**40* This is the implementation class for GSSName. Conceptually the41* GSSName is a container with mechanism specific name elements. Each42* name element is a representation of how that particular mechanism43* would canonicalize this principal.44*45* Generally a GSSName is created by an application when it supplies46* a sequence of bytes and a nametype that helps each mechanism47* decide how to interpret those bytes.48*49* It is not necessary to create name elements for each available50* mechanism at the time the application creates the GSSName. This51* implementation does this lazily, as and when name elements for52* mechanisms are required to be handed out. (Generally, other GSS53* classes like GSSContext and GSSCredential request specific54* elements depending on the mechanisms that they are dealing with.)55* Assume that getting a mechanism to parse the applciation specified56* bytes is an expensive call.57*58* When a GSSName is canonicalized wrt some mechanism, it is supposed59* to discard all elements of other mechanisms and retain only the60* element for this mechanism. In GSS terminology this is called a61* Mechanism Name or MN. This implementation tries to retain the62* application provided bytes and name type just in case the MN is63* asked to produce an element for a mechanism that is different.64*65* When a GSSName is to be exported, the name element for the desired66* mechanism is converted to a byte representation and written67* out. It might happen that a name element for that mechanism cannot68* be obtained. This happens when the mechanism is just not supported69* in this GSS-API or when the mechanism is supported but bytes70* corresponding to the nametypes that it understands are not71* available in this GSSName.72*73* This class is safe for sharing. Each retrieval of a name element74* from getElement() might potentially add a new element to the75* hashmap of elements, but getElement() is synchronized.76*77* @author Mayank Upadhyay78* @since 1.479*/8081public class GSSNameImpl implements GSSName {8283/**84* The old Oid used in RFC 2853. Now supported as85* input parameters in:86*87* 1. The four overloaded GSSManager.createName(*) methods88* 2. GSSManager.getMechsForName(Oid)89*90* Note that even if a GSSName is created with this old Oid,91* its internal name type and getStringNameType() output are92* always the new value.93*/94final static Oid oldHostbasedServiceName;9596static {97Oid tmp = null;98try {99tmp = new Oid("1.3.6.1.5.6.2");100} catch (Exception e) {101// should never happen102}103oldHostbasedServiceName = tmp;104}105106private GSSManagerImpl gssManager = null;107108/*109* Store whatever the application passed in. We will use this to110* get individual mechanisms to create name elements as and when111* needed.112* Store both the String and the byte[]. Leave I18N to the113* mechanism by allowing it to extract bytes from the String!114*/115116private String appNameStr = null;117private byte[] appNameBytes = null;118private Oid appNameType = null;119120/*121* When we figure out what the printable name would be, we store122* both the name and its type.123*/124125private String printableName = null;126private Oid printableNameType = null;127128private HashMap<Oid, GSSNameSpi> elements = null;129private GSSNameSpi mechElement = null;130131static GSSNameImpl wrapElement(GSSManagerImpl gssManager,132GSSNameSpi mechElement) throws GSSException {133return (mechElement == null ?134null : new GSSNameImpl(gssManager, mechElement));135}136137GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {138this.gssManager = gssManager;139appNameStr = printableName = mechElement.toString();140appNameType = printableNameType = mechElement.getStringNameType();141this.mechElement = mechElement;142elements = new HashMap<Oid, GSSNameSpi>(1);143elements.put(mechElement.getMechanism(), this.mechElement);144}145146GSSNameImpl(GSSManagerImpl gssManager,147Object appName,148Oid appNameType)149throws GSSException {150this(gssManager, appName, appNameType, null);151}152153GSSNameImpl(GSSManagerImpl gssManager,154Object appName,155Oid appNameType,156Oid mech)157throws GSSException {158159if (oldHostbasedServiceName.equals(appNameType)) {160appNameType = GSSName.NT_HOSTBASED_SERVICE;161}162if (appName == null)163throw new GSSExceptionImpl(GSSException.BAD_NAME,164"Cannot import null name");165if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;166if (NT_EXPORT_NAME.equals(appNameType)) {167importName(gssManager, appName);168} else {169init(gssManager, appName, appNameType, mech);170}171}172173private void init(GSSManagerImpl gssManager,174Object appName, Oid appNameType,175Oid mech)176throws GSSException {177178this.gssManager = gssManager;179this.elements =180new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);181182if (appName instanceof String) {183this.appNameStr = (String) appName;184/*185* If appNameType is null, then the nametype for this printable186* string is determined only by interrogating the187* mechanism. Thus, defer the setting of printableName and188* printableNameType till later.189*/190if (appNameType != null) {191printableName = appNameStr;192printableNameType = appNameType;193}194} else {195this.appNameBytes = (byte[]) appName;196}197198this.appNameType = appNameType;199200mechElement = getElement(mech);201202/*203* printableName will be null if appName was in a byte[] or if204* appName was in a String but appNameType was null.205*/206if (printableName == null) {207printableName = mechElement.toString();208printableNameType = mechElement.getStringNameType();209}210211/*212* At this point the GSSNameImpl has the following set:213* appNameStr or appNameBytes214* appNameType (could be null)215* printableName216* printableNameType217* mechElement (which also exists in the hashmap of elements)218*/219}220221private void importName(GSSManagerImpl gssManager,222Object appName)223throws GSSException {224225int pos = 0;226byte[] bytes = null;227228if (appName instanceof String) {229try {230bytes = ((String) appName).getBytes("UTF-8");231} catch (UnsupportedEncodingException e) {232// Won't happen233}234} else235bytes = (byte[]) appName;236237if ((bytes[pos++] != 0x04) ||238(bytes[pos++] != 0x01))239throw new GSSExceptionImpl(GSSException.BAD_NAME,240"Exported name token id is corrupted!");241242int oidLen = (((0xFF & bytes[pos++]) << 8) |243(0xFF & bytes[pos++]));244ObjectIdentifier temp = null;245try {246DerInputStream din = new DerInputStream(bytes, pos,247oidLen);248temp = new ObjectIdentifier(din);249} catch (IOException e) {250throw new GSSExceptionImpl(GSSException.BAD_NAME,251"Exported name Object identifier is corrupted!");252}253Oid oid = new Oid(temp.toString());254pos += oidLen;255int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |256((0xFF & bytes[pos++]) << 16) |257((0xFF & bytes[pos++]) << 8) |258(0xFF & bytes[pos++]));259if (mechPortionLen < 0 || pos > bytes.length - mechPortionLen) {260throw new GSSExceptionImpl(GSSException.BAD_NAME,261"Exported name mech name is corrupted!");262}263byte[] mechPortion = new byte[mechPortionLen];264System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);265266init(gssManager, mechPortion, NT_EXPORT_NAME, oid);267}268269public GSSName canonicalize(Oid mech) throws GSSException {270if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;271272return wrapElement(gssManager, getElement(mech));273}274275/**276* This method may return false negatives. But if it says two277* names are equals, then there is some mechanism that278* authenticates them as the same principal.279*/280public boolean equals(GSSName other) throws GSSException {281282if (this.isAnonymous() || other.isAnonymous())283return false;284285if (other == this)286return true;287288if (! (other instanceof GSSNameImpl))289return equals(gssManager.createName(other.toString(),290other.getStringNameType()));291292/*293* XXX Do a comparison of the appNameStr/appNameBytes if294* available. If that fails, then proceed with this test.295*/296297GSSNameImpl that = (GSSNameImpl) other;298299GSSNameSpi myElement = this.mechElement;300GSSNameSpi element = that.mechElement;301302/*303* XXX If they are not of the same mechanism type, convert both to304* Kerberos since it is guaranteed to be present.305*/306if ((myElement == null) && (element != null)) {307myElement = this.getElement(element.getMechanism());308} else if ((myElement != null) && (element == null)) {309element = that.getElement(myElement.getMechanism());310}311312if (myElement != null && element != null) {313return myElement.equals(element);314}315316if ((this.appNameType != null) &&317(that.appNameType != null)) {318if (!this.appNameType.equals(that.appNameType)) {319return false;320}321byte[] myBytes = null;322byte[] bytes = null;323try {324myBytes =325(this.appNameStr != null ?326this.appNameStr.getBytes("UTF-8") :327this.appNameBytes);328bytes =329(that.appNameStr != null ?330that.appNameStr.getBytes("UTF-8") :331that.appNameBytes);332} catch (UnsupportedEncodingException e) {333// Won't happen334}335336return Arrays.equals(myBytes, bytes);337}338339return false;340341}342343/**344* Returns a hashcode value for this GSSName.345*346* @return a hashCode value347*/348public int hashCode() {349/*350* XXX351* In order to get this to work reliably and properly(!), obtain a352* Kerberos name element for the name and then call hashCode on its353* string representation. But this cannot be done if the nametype354* is not one of those supported by the Kerberos provider and hence355* this name cannot be imported by Kerberos. In that case return a356* constant value!357*/358359return 1;360}361362public boolean equals(Object another) {363364try {365// XXX This can lead to an infinite loop. Extract info366// and create a GSSNameImpl with it.367368if (another instanceof GSSName)369return equals((GSSName) another);370} catch (GSSException e) {371// Squelch it and return false372}373374return false;375}376377/**378* Returns a flat name representation for this object. The name379* format is defined in RFC 2743:380*<pre>381* Length Name Description382* 2 TOK_ID Token Identifier383* For exported name objects, this384* must be hex 04 01.385* 2 MECH_OID_LEN Length of the Mechanism OID386* MECH_OID_LEN MECH_OID Mechanism OID, in DER387* 4 NAME_LEN Length of name388* NAME_LEN NAME Exported name; format defined in389* applicable mechanism draft.390*</pre>391*392* Note that it is not required to canonicalize a name before393* calling export(). i.e., the name need not be an MN. If it is394* not an MN, an implementation defined algorithm can be used for395* choosing the mechanism which should export this name.396*397* @return the flat name representation for this object398* @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,399* BAD_NAME, FAILURE.400*/401public byte[] export() throws GSSException {402403if (mechElement == null) {404/* Use default mech */405mechElement = getElement(ProviderList.DEFAULT_MECH_OID);406}407408byte[] mechPortion = mechElement.export();409byte[] oidBytes = null;410ObjectIdentifier oid = null;411412try {413oid = new ObjectIdentifier414(mechElement.getMechanism().toString());415} catch (IOException e) {416throw new GSSExceptionImpl(GSSException.FAILURE,417"Invalid OID String ");418}419DerOutputStream dout = new DerOutputStream();420try {421dout.putOID(oid);422} catch (IOException e) {423throw new GSSExceptionImpl(GSSException.FAILURE,424"Could not ASN.1 Encode "425+ oid.toString());426}427oidBytes = dout.toByteArray();428429byte[] retVal = new byte[2430+ 2 + oidBytes.length431+ 4 + mechPortion.length];432int pos = 0;433retVal[pos++] = 0x04;434retVal[pos++] = 0x01;435retVal[pos++] = (byte) (oidBytes.length>>>8);436retVal[pos++] = (byte) oidBytes.length;437System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);438pos += oidBytes.length;439retVal[pos++] = (byte) (mechPortion.length>>>24);440retVal[pos++] = (byte) (mechPortion.length>>>16);441retVal[pos++] = (byte) (mechPortion.length>>>8);442retVal[pos++] = (byte) mechPortion.length;443System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);444return retVal;445}446447public String toString() {448return printableName;449450}451452public Oid getStringNameType() throws GSSException {453return printableNameType;454}455456public boolean isAnonymous() {457if (printableNameType == null) {458return false;459} else {460return GSSName.NT_ANONYMOUS.equals(printableNameType);461}462}463464public boolean isMN() {465return true; // Since always canonicalized for some mech466}467468public synchronized GSSNameSpi getElement(Oid mechOid)469throws GSSException {470471GSSNameSpi retVal = elements.get(mechOid);472473if (retVal == null) {474if (appNameStr != null) {475retVal = gssManager.getNameElement476(appNameStr, appNameType, mechOid);477} else {478retVal = gssManager.getNameElement479(appNameBytes, appNameType, mechOid);480}481elements.put(mechOid, retVal);482}483return retVal;484}485486Set<GSSNameSpi> getElements() {487return new HashSet<GSSNameSpi>(elements.values());488}489490private static String getNameTypeStr(Oid nameTypeOid) {491492if (nameTypeOid == null)493return "(NT is null)";494495if (nameTypeOid.equals(NT_USER_NAME))496return "NT_USER_NAME";497if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))498return "NT_HOSTBASED_SERVICE";499if (nameTypeOid.equals(NT_EXPORT_NAME))500return "NT_EXPORT_NAME";501if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))502return "NT_GSS_KRB5_PRINCIPAL";503else504return "Unknown";505}506}507508509