503 lines · 17.4 KB · Java
RawDownload
/* * Copyright (c) 2000, 2010, 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.Set;import java.util.HashMap;import java.util.HashSet;import java.util.Arrays;import java.io.IOException;import java.io.UnsupportedEncodingException;import sun.security.util.ObjectIdentifier;import sun.security.util.DerInputStream;import sun.security.util.DerOutputStream; /** * This is the implementation class for GSSName. Conceptually the * GSSName is a container with mechanism specific name elements. Each * name element is a representation of how that particular mechanism * would canonicalize this principal. * * Generally a GSSName is created by an application when it supplies * a sequence of bytes and a nametype that helps each mechanism * decide how to interpret those bytes. * * It is not necessary to create name elements for each available * mechanism at the time the application creates the GSSName. This * implementation does this lazily, as and when name elements for * mechanisms are required to be handed out. (Generally, other GSS * classes like GSSContext and GSSCredential request specific * elements depending on the mechanisms that they are dealing with.) * Assume that getting a mechanism to parse the applciation specified * bytes is an expensive call. * * When a GSSName is canonicalized wrt some mechanism, it is supposed * to discard all elements of other mechanisms and retain only the * element for this mechanism. In GSS terminology this is called a * Mechanism Name or MN. This implementation tries to retain the * application provided bytes and name type just in case the MN is * asked to produce an element for a mechanism that is different. * * When a GSSName is to be exported, the name element for the desired * mechanism is converted to a byte representation and written * out. It might happen that a name element for that mechanism cannot * be obtained. This happens when the mechanism is just not supported * in this GSS-API or when the mechanism is supported but bytes * corresponding to the nametypes that it understands are not * available in this GSSName. * * This class is safe for sharing. Each retrieval of a name element * from getElement() might potentially add a new element to the * hashmap of elements, but getElement() is synchronized. * * @author Mayank Upadhyay * @since 1.4 */ public class GSSNameImpl implements GSSName {     /**     * The old Oid used in RFC 2853. Now supported as     * input parameters in:     *     * 1. The four overloaded GSSManager.createName(*) methods     * 2. GSSManager.getMechsForName(Oid)     *     * Note that even if a GSSName is created with this old Oid,     * its internal name type and getStringNameType() output are     * always the new value.     */    final static Oid oldHostbasedServiceName;     static {        Oid tmp = null;        try {            tmp = new Oid("1.3.6.1.5.6.2");        } catch (Exception e) {            // should never happen        }        oldHostbasedServiceName = tmp;    }     private GSSManagerImpl gssManager = null;     /*     * Store whatever the application passed in. We will use this to     * get individual mechanisms to create name elements as and when     * needed.     * Store both the String and the byte[]. Leave I18N to the     * mechanism by allowing it to extract bytes from the String!     */     private String appNameStr = null;    private byte[] appNameBytes = null;    private Oid appNameType = null;     /*     * When we figure out what the printable name would be, we store     * both the name and its type.     */     private String printableName = null;    private Oid printableNameType = null;     private HashMap<Oid, GSSNameSpi> elements = null;    private GSSNameSpi mechElement = null;     static GSSNameImpl wrapElement(GSSManagerImpl gssManager,        GSSNameSpi mechElement) throws GSSException {        return (mechElement == null ?            null : new GSSNameImpl(gssManager, mechElement));    }     GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {        this.gssManager = gssManager;        appNameStr = printableName = mechElement.toString();        appNameType = printableNameType = mechElement.getStringNameType();        this.mechElement = mechElement;        elements = new HashMap<Oid, GSSNameSpi>(1);        elements.put(mechElement.getMechanism(), this.mechElement);    }     GSSNameImpl(GSSManagerImpl gssManager,                       Object appName,                       Oid appNameType)        throws GSSException {        this(gssManager, appName, appNameType, null);    }     GSSNameImpl(GSSManagerImpl gssManager,                        Object appName,                        Oid appNameType,                        Oid mech)        throws GSSException {         if (oldHostbasedServiceName.equals(appNameType)) {            appNameType = GSSName.NT_HOSTBASED_SERVICE;        }        if (appName == null)            throw new GSSExceptionImpl(GSSException.BAD_NAME,                                   "Cannot import null name");        if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;        if (NT_EXPORT_NAME.equals(appNameType)) {            importName(gssManager, appName);        } else {            init(gssManager, appName, appNameType, mech);        }    }     private void init(GSSManagerImpl gssManager,                      Object appName, Oid appNameType,                      Oid mech)        throws GSSException {         this.gssManager = gssManager;        this.elements =                new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);         if (appName instanceof String) {            this.appNameStr = (String) appName;            /*             * If appNameType is null, then the nametype for this printable             * string is determined only by interrogating the             * mechanism. Thus, defer the setting of printableName and             * printableNameType till later.             */            if (appNameType != null) {                printableName = appNameStr;                printableNameType = appNameType;            }        } else {            this.appNameBytes = (byte[]) appName;        }         this.appNameType = appNameType;         mechElement = getElement(mech);         /*         * printableName will be null if appName was in a byte[] or if         * appName was in a String but appNameType was null.         */        if (printableName == null) {            printableName = mechElement.toString();            printableNameType = mechElement.getStringNameType();        }         /*         *  At this point the GSSNameImpl has the following set:         *   appNameStr or appNameBytes         *   appNameType (could be null)         *   printableName         *   printableNameType         *   mechElement (which also exists in the hashmap of elements)         */    }     private void importName(GSSManagerImpl gssManager,                            Object appName)        throws GSSException {         int pos = 0;        byte[] bytes = null;         if (appName instanceof String) {            try {                bytes = ((String) appName).getBytes("UTF-8");            } catch (UnsupportedEncodingException e) {                // Won't happen            }        } else            bytes = (byte[]) appName;         if ((bytes[pos++] != 0x04) ||            (bytes[pos++] != 0x01))            throw new GSSExceptionImpl(GSSException.BAD_NAME,                                   "Exported name token id is corrupted!");         int oidLen  = (((0xFF & bytes[pos++]) << 8) |                       (0xFF & bytes[pos++]));        ObjectIdentifier temp = null;        try {            DerInputStream din = new DerInputStream(bytes, pos,                                                    oidLen);            temp = new ObjectIdentifier(din);        } catch (IOException e) {            throw new GSSExceptionImpl(GSSException.BAD_NAME,                       "Exported name Object identifier is corrupted!");        }        Oid oid = new Oid(temp.toString());        pos += oidLen;        int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |                              ((0xFF & bytes[pos++]) << 16) |                              ((0xFF & bytes[pos++]) << 8) |                              (0xFF & bytes[pos++]));        byte[] mechPortion = new byte[mechPortionLen];        System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);         init(gssManager, mechPortion, NT_EXPORT_NAME, oid);    }     public GSSName canonicalize(Oid mech) throws GSSException {        if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;         return wrapElement(gssManager, getElement(mech));    }     /**     * This method may return false negatives. But if it says two     * names are equals, then there is some mechanism that     * authenticates them as the same principal.     */    public boolean equals(GSSName other) throws GSSException {         if (this.isAnonymous() || other.isAnonymous())            return false;         if (other == this)            return true;         if (! (other instanceof GSSNameImpl))            return equals(gssManager.createName(other.toString(),                                                other.getStringNameType()));         /*         * XXX Do a comparison of the appNameStr/appNameBytes if         * available. If that fails, then proceed with this test.         */         GSSNameImpl that = (GSSNameImpl) other;         GSSNameSpi myElement = this.mechElement;        GSSNameSpi element = that.mechElement;         /*         * XXX If they are not of the same mechanism type, convert both to         * Kerberos since it is guaranteed to be present.         */        if ((myElement == null) && (element != null)) {            myElement = this.getElement(element.getMechanism());        } else if ((myElement != null) && (element == null)) {            element = that.getElement(myElement.getMechanism());        }         if (myElement != null && element != null) {            return myElement.equals(element);        }         if ((this.appNameType != null) &&            (that.appNameType != null)) {            if (!this.appNameType.equals(that.appNameType)) {                return false;            }            byte[] myBytes = null;            byte[] bytes = null;            try {                myBytes =                    (this.appNameStr != null ?                     this.appNameStr.getBytes("UTF-8") :                     this.appNameBytes);                bytes =                    (that.appNameStr != null ?                     that.appNameStr.getBytes("UTF-8") :                     that.appNameBytes);            } catch (UnsupportedEncodingException e) {                // Won't happen            }             return Arrays.equals(myBytes, bytes);        }         return false;     }     /**     * Returns a hashcode value for this GSSName.     *     * @return a hashCode value     */    public int hashCode() {        /*         * XXX         * In order to get this to work reliably and properly(!), obtain a         * Kerberos name element for the name and then call hashCode on its         * string representation. But this cannot be done if the nametype         * is not one of those supported by the Kerberos provider and hence         * this name cannot be imported by Kerberos. In that case return a         * constant value!         */         return 1;    }     public boolean equals(Object another) {         try {            // XXX This can lead to an infinite loop. Extract info            // and create a GSSNameImpl with it.             if (another instanceof GSSName)                return equals((GSSName) another);        } catch (GSSException e) {            // Squelch it and return false        }             return false;    }     /**     * Returns a flat name representation for this object. The name     * format is defined in RFC 2743:     *<pre>     * Length           Name          Description     * 2               TOK_ID          Token Identifier     *                                 For exported name objects, this     *                                 must be hex 04 01.     * 2               MECH_OID_LEN    Length of the Mechanism OID     * MECH_OID_LEN    MECH_OID        Mechanism OID, in DER     * 4               NAME_LEN        Length of name     * NAME_LEN        NAME            Exported name; format defined in     *                                 applicable mechanism draft.     *</pre>     *     * Note that it is not required to canonicalize a name before     * calling export(). i.e., the name need not be an MN. If it is     * not an MN, an implementation defined algorithm can be used for     * choosing the mechanism which should export this name.     *     * @return the flat name representation for this object     * @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,     *  BAD_NAME, FAILURE.     */    public byte[] export() throws GSSException {         if (mechElement == null) {            /* Use default mech */            mechElement = getElement(ProviderList.DEFAULT_MECH_OID);        }         byte[] mechPortion = mechElement.export();        byte[] oidBytes = null;        ObjectIdentifier oid = null;         try {            oid = new ObjectIdentifier                (mechElement.getMechanism().toString());        } catch (IOException e) {            throw new GSSExceptionImpl(GSSException.FAILURE,                                       "Invalid OID String ");        }        DerOutputStream dout = new DerOutputStream();        try {            dout.putOID(oid);        } catch (IOException e) {            throw new GSSExceptionImpl(GSSException.FAILURE,                                   "Could not ASN.1 Encode "                                   + oid.toString());        }        oidBytes = dout.toByteArray();         byte[] retVal = new byte[2                                + 2 + oidBytes.length                                + 4 + mechPortion.length];        int pos = 0;        retVal[pos++] = 0x04;        retVal[pos++] = 0x01;        retVal[pos++] = (byte) (oidBytes.length>>>8);        retVal[pos++] = (byte) oidBytes.length;        System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);        pos += oidBytes.length;        retVal[pos++] = (byte) (mechPortion.length>>>24);        retVal[pos++] = (byte) (mechPortion.length>>>16);        retVal[pos++] = (byte) (mechPortion.length>>>8);        retVal[pos++] = (byte)  mechPortion.length;        System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);        return retVal;    }     public String toString() {         return printableName;     }     public Oid getStringNameType() throws GSSException {        return printableNameType;    }     public boolean isAnonymous() {        if (printableNameType == null) {            return false;        } else {            return GSSName.NT_ANONYMOUS.equals(printableNameType);        }    }     public boolean isMN() {        return true; // Since always canonicalized for some mech    }     public synchronized GSSNameSpi getElement(Oid mechOid)        throws GSSException {         GSSNameSpi retVal = elements.get(mechOid);         if (retVal == null) {            if (appNameStr != null) {                retVal = gssManager.getNameElement                    (appNameStr, appNameType, mechOid);            } else {                retVal = gssManager.getNameElement                    (appNameBytes, appNameType, mechOid);            }            elements.put(mechOid, retVal);        }        return retVal;    }     Set<GSSNameSpi> getElements() {        return new HashSet<GSSNameSpi>(elements.values());    }     private static String getNameTypeStr(Oid nameTypeOid) {         if (nameTypeOid == null)            return "(NT is null)";         if (nameTypeOid.equals(NT_USER_NAME))            return "NT_USER_NAME";        if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))            return "NT_HOSTBASED_SERVICE";        if (nameTypeOid.equals(NT_EXPORT_NAME))            return "NT_EXPORT_NAME";        if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))            return "NT_GSS_KRB5_PRINCIPAL";        else

Lines 1–500 of 503.