0N/A/*
2362N/A * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.jgss;
0N/A
0N/Aimport org.ietf.jgss.*;
0N/Aimport sun.security.jgss.spi.*;
0N/Aimport java.util.Set;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.HashSet;
0N/Aimport java.util.Arrays;
0N/Aimport java.io.IOException;
0N/Aimport java.io.UnsupportedEncodingException;
0N/Aimport sun.security.util.ObjectIdentifier;
0N/Aimport sun.security.util.DerInputStream;
0N/Aimport sun.security.util.DerOutputStream;
0N/A
0N/A/**
0N/A * This is the implementation class for GSSName. Conceptually the
0N/A * GSSName is a container with mechanism specific name elements. Each
0N/A * name element is a representation of how that particular mechanism
0N/A * would canonicalize this principal.
0N/A *
0N/A * Generally a GSSName is created by an application when it supplies
0N/A * a sequence of bytes and a nametype that helps each mechanism
0N/A * decide how to interpret those bytes.
0N/A *
0N/A * It is not necessary to create name elements for each available
0N/A * mechanism at the time the application creates the GSSName. This
0N/A * implementation does this lazily, as and when name elements for
0N/A * mechanisms are required to be handed out. (Generally, other GSS
0N/A * classes like GSSContext and GSSCredential request specific
0N/A * elements depending on the mechanisms that they are dealing with.)
0N/A * Assume that getting a mechanism to parse the applciation specified
0N/A * bytes is an expensive call.
0N/A *
0N/A * When a GSSName is canonicalized wrt some mechanism, it is supposed
0N/A * to discard all elements of other mechanisms and retain only the
0N/A * element for this mechanism. In GSS terminology this is called a
0N/A * Mechanism Name or MN. This implementation tries to retain the
0N/A * application provided bytes and name type just in case the MN is
0N/A * asked to produce an element for a mechanism that is different.
0N/A *
0N/A * When a GSSName is to be exported, the name element for the desired
0N/A * mechanism is converted to a byte representation and written
0N/A * out. It might happen that a name element for that mechanism cannot
0N/A * be obtained. This happens when the mechanism is just not supported
0N/A * in this GSS-API or when the mechanism is supported but bytes
0N/A * corresponding to the nametypes that it understands are not
0N/A * available in this GSSName.
0N/A *
0N/A * This class is safe for sharing. Each retrieval of a name element
0N/A * from getElement() might potentially add a new element to the
0N/A * hashmap of elements, but getElement() is synchronized.
0N/A *
0N/A * @author Mayank Upadhyay
0N/A * @since 1.4
0N/A */
0N/A
0N/Apublic class GSSNameImpl implements GSSName {
0N/A
2036N/A /**
2036N/A * The old Oid used in RFC 2853. Now supported as
2036N/A * input parameters in:
2036N/A *
2036N/A * 1. The four overloaded GSSManager.createName(*) methods
2036N/A * 2. GSSManager.getMechsForName(Oid)
2036N/A *
2036N/A * Note that even if a GSSName is created with this old Oid,
2036N/A * its internal name type and getStringNameType() output are
2036N/A * always the new value.
2036N/A */
2036N/A final static Oid oldHostbasedServiceName;
2036N/A
2036N/A static {
2036N/A Oid tmp = null;
2036N/A try {
2036N/A tmp = new Oid("1.3.6.1.5.6.2");
2036N/A } catch (Exception e) {
2036N/A // should never happen
2036N/A }
2036N/A oldHostbasedServiceName = tmp;
2036N/A }
2036N/A
0N/A private GSSManagerImpl gssManager = null;
0N/A
0N/A /*
0N/A * Store whatever the application passed in. We will use this to
0N/A * get individual mechanisms to create name elements as and when
0N/A * needed.
0N/A * Store both the String and the byte[]. Leave I18N to the
0N/A * mechanism by allowing it to extract bytes from the String!
0N/A */
0N/A
0N/A private String appNameStr = null;
0N/A private byte[] appNameBytes = null;
0N/A private Oid appNameType = null;
0N/A
0N/A /*
0N/A * When we figure out what the printable name would be, we store
0N/A * both the name and its type.
0N/A */
0N/A
0N/A private String printableName = null;
0N/A private Oid printableNameType = null;
0N/A
0N/A private HashMap<Oid, GSSNameSpi> elements = null;
0N/A private GSSNameSpi mechElement = null;
0N/A
0N/A static GSSNameImpl wrapElement(GSSManagerImpl gssManager,
0N/A GSSNameSpi mechElement) throws GSSException {
0N/A return (mechElement == null ?
0N/A null : new GSSNameImpl(gssManager, mechElement));
0N/A }
0N/A
0N/A GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {
0N/A this.gssManager = gssManager;
0N/A appNameStr = printableName = mechElement.toString();
0N/A appNameType = printableNameType = mechElement.getStringNameType();
0N/A this.mechElement = mechElement;
0N/A elements = new HashMap<Oid, GSSNameSpi>(1);
0N/A elements.put(mechElement.getMechanism(), this.mechElement);
0N/A }
0N/A
0N/A GSSNameImpl(GSSManagerImpl gssManager,
0N/A Object appName,
0N/A Oid appNameType)
0N/A throws GSSException {
0N/A this(gssManager, appName, appNameType, null);
0N/A }
0N/A
0N/A GSSNameImpl(GSSManagerImpl gssManager,
0N/A Object appName,
0N/A Oid appNameType,
0N/A Oid mech)
0N/A throws GSSException {
0N/A
2036N/A if (oldHostbasedServiceName.equals(appNameType)) {
2036N/A appNameType = GSSName.NT_HOSTBASED_SERVICE;
2036N/A }
0N/A if (appName == null)
0N/A throw new GSSExceptionImpl(GSSException.BAD_NAME,
0N/A "Cannot import null name");
0N/A if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
0N/A if (NT_EXPORT_NAME.equals(appNameType)) {
0N/A importName(gssManager, appName);
0N/A } else {
0N/A init(gssManager, appName, appNameType, mech);
0N/A }
0N/A }
0N/A
0N/A private void init(GSSManagerImpl gssManager,
0N/A Object appName, Oid appNameType,
0N/A Oid mech)
0N/A throws GSSException {
0N/A
0N/A this.gssManager = gssManager;
0N/A this.elements =
0N/A new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);
0N/A
0N/A if (appName instanceof String) {
0N/A this.appNameStr = (String) appName;
0N/A /*
0N/A * If appNameType is null, then the nametype for this printable
0N/A * string is determined only by interrogating the
0N/A * mechanism. Thus, defer the setting of printableName and
0N/A * printableNameType till later.
0N/A */
0N/A if (appNameType != null) {
0N/A printableName = appNameStr;
0N/A printableNameType = appNameType;
0N/A }
0N/A } else {
0N/A this.appNameBytes = (byte[]) appName;
0N/A }
0N/A
0N/A this.appNameType = appNameType;
0N/A
0N/A mechElement = getElement(mech);
0N/A
0N/A /*
0N/A * printableName will be null if appName was in a byte[] or if
0N/A * appName was in a String but appNameType was null.
0N/A */
0N/A if (printableName == null) {
0N/A printableName = mechElement.toString();
0N/A printableNameType = mechElement.getStringNameType();
0N/A }
0N/A
0N/A /*
0N/A * At this point the GSSNameImpl has the following set:
0N/A * appNameStr or appNameBytes
0N/A * appNameType (could be null)
0N/A * printableName
0N/A * printableNameType
0N/A * mechElement (which also exists in the hashmap of elements)
0N/A */
0N/A }
0N/A
0N/A private void importName(GSSManagerImpl gssManager,
0N/A Object appName)
0N/A throws GSSException {
0N/A
0N/A int pos = 0;
0N/A byte[] bytes = null;
0N/A
0N/A if (appName instanceof String) {
0N/A try {
0N/A bytes = ((String) appName).getBytes("UTF-8");
0N/A } catch (UnsupportedEncodingException e) {
0N/A // Won't happen
0N/A }
0N/A } else
0N/A bytes = (byte[]) appName;
0N/A
0N/A if ((bytes[pos++] != 0x04) ||
0N/A (bytes[pos++] != 0x01))
0N/A throw new GSSExceptionImpl(GSSException.BAD_NAME,
0N/A "Exported name token id is corrupted!");
0N/A
0N/A int oidLen = (((0xFF & bytes[pos++]) << 8) |
0N/A (0xFF & bytes[pos++]));
0N/A ObjectIdentifier temp = null;
0N/A try {
0N/A DerInputStream din = new DerInputStream(bytes, pos,
0N/A oidLen);
0N/A temp = new ObjectIdentifier(din);
0N/A } catch (IOException e) {
0N/A throw new GSSExceptionImpl(GSSException.BAD_NAME,
0N/A "Exported name Object identifier is corrupted!");
0N/A }
0N/A Oid oid = new Oid(temp.toString());
0N/A pos += oidLen;
0N/A int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |
0N/A ((0xFF & bytes[pos++]) << 16) |
0N/A ((0xFF & bytes[pos++]) << 8) |
0N/A (0xFF & bytes[pos++]));
0N/A byte[] mechPortion = new byte[mechPortionLen];
0N/A System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);
0N/A
0N/A init(gssManager, mechPortion, NT_EXPORT_NAME, oid);
0N/A }
0N/A
0N/A public GSSName canonicalize(Oid mech) throws GSSException {
0N/A if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
0N/A
0N/A return wrapElement(gssManager, getElement(mech));
0N/A }
0N/A
0N/A /**
0N/A * This method may return false negatives. But if it says two
0N/A * names are equals, then there is some mechanism that
0N/A * authenticates them as the same principal.
0N/A */
0N/A public boolean equals(GSSName other) throws GSSException {
0N/A
0N/A if (this.isAnonymous() || other.isAnonymous())
0N/A return false;
0N/A
0N/A if (other == this)
0N/A return true;
0N/A
0N/A if (! (other instanceof GSSNameImpl))
0N/A return equals(gssManager.createName(other.toString(),
0N/A other.getStringNameType()));
0N/A
0N/A /*
0N/A * XXX Do a comparison of the appNameStr/appNameBytes if
0N/A * available. If that fails, then proceed with this test.
0N/A */
0N/A
0N/A GSSNameImpl that = (GSSNameImpl) other;
0N/A
0N/A GSSNameSpi myElement = this.mechElement;
0N/A GSSNameSpi element = that.mechElement;
0N/A
0N/A /*
0N/A * XXX If they are not of the same mechanism type, convert both to
0N/A * Kerberos since it is guaranteed to be present.
0N/A */
0N/A if ((myElement == null) && (element != null)) {
0N/A myElement = this.getElement(element.getMechanism());
0N/A } else if ((myElement != null) && (element == null)) {
0N/A element = that.getElement(myElement.getMechanism());
0N/A }
0N/A
0N/A if (myElement != null && element != null) {
0N/A return myElement.equals(element);
0N/A }
0N/A
0N/A if ((this.appNameType != null) &&
0N/A (that.appNameType != null)) {
0N/A if (!this.appNameType.equals(that.appNameType)) {
0N/A return false;
0N/A }
0N/A byte[] myBytes = null;
0N/A byte[] bytes = null;
0N/A try {
0N/A myBytes =
0N/A (this.appNameStr != null ?
0N/A this.appNameStr.getBytes("UTF-8") :
0N/A this.appNameBytes);
0N/A bytes =
0N/A (that.appNameStr != null ?
0N/A that.appNameStr.getBytes("UTF-8") :
0N/A that.appNameBytes);
0N/A } catch (UnsupportedEncodingException e) {
0N/A // Won't happen
0N/A }
0N/A
0N/A return Arrays.equals(myBytes, bytes);
0N/A }
0N/A
0N/A return false;
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Returns a hashcode value for this GSSName.
0N/A *
0N/A * @return a hashCode value
0N/A */
0N/A public int hashCode() {
0N/A /*
0N/A * XXX
0N/A * In order to get this to work reliably and properly(!), obtain a
0N/A * Kerberos name element for the name and then call hashCode on its
0N/A * string representation. But this cannot be done if the nametype
0N/A * is not one of those supported by the Kerberos provider and hence
0N/A * this name cannot be imported by Kerberos. In that case return a
0N/A * constant value!
0N/A */
0N/A
0N/A return 1;
0N/A }
0N/A
0N/A public boolean equals(Object another) {
0N/A
0N/A try {
0N/A // XXX This can lead to an infinite loop. Extract info
0N/A // and create a GSSNameImpl with it.
0N/A
0N/A if (another instanceof GSSName)
0N/A return equals((GSSName) another);
0N/A } catch (GSSException e) {
0N/A // Squelch it and return false
0N/A }
0N/A
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Returns a flat name representation for this object. The name
0N/A * format is defined in RFC 2743:
0N/A *<pre>
0N/A * Length Name Description
0N/A * 2 TOK_ID Token Identifier
0N/A * For exported name objects, this
0N/A * must be hex 04 01.
0N/A * 2 MECH_OID_LEN Length of the Mechanism OID
0N/A * MECH_OID_LEN MECH_OID Mechanism OID, in DER
0N/A * 4 NAME_LEN Length of name
0N/A * NAME_LEN NAME Exported name; format defined in
0N/A * applicable mechanism draft.
0N/A *</pre>
0N/A *
0N/A * Note that it is not required to canonicalize a name before
0N/A * calling export(). i.e., the name need not be an MN. If it is
0N/A * not an MN, an implementation defined algorithm can be used for
0N/A * choosing the mechanism which should export this name.
0N/A *
0N/A * @return the flat name representation for this object
0N/A * @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
0N/A * BAD_NAME, FAILURE.
0N/A */
0N/A public byte[] export() throws GSSException {
0N/A
0N/A if (mechElement == null) {
0N/A /* Use default mech */
0N/A mechElement = getElement(ProviderList.DEFAULT_MECH_OID);
0N/A }
0N/A
0N/A byte[] mechPortion = mechElement.export();
0N/A byte[] oidBytes = null;
0N/A ObjectIdentifier oid = null;
0N/A
0N/A try {
0N/A oid = new ObjectIdentifier
0N/A (mechElement.getMechanism().toString());
0N/A } catch (IOException e) {
0N/A throw new GSSExceptionImpl(GSSException.FAILURE,
0N/A "Invalid OID String ");
0N/A }
0N/A DerOutputStream dout = new DerOutputStream();
0N/A try {
0N/A dout.putOID(oid);
0N/A } catch (IOException e) {
0N/A throw new GSSExceptionImpl(GSSException.FAILURE,
0N/A "Could not ASN.1 Encode "
0N/A + oid.toString());
0N/A }
0N/A oidBytes = dout.toByteArray();
0N/A
0N/A byte[] retVal = new byte[2
0N/A + 2 + oidBytes.length
0N/A + 4 + mechPortion.length];
0N/A int pos = 0;
0N/A retVal[pos++] = 0x04;
0N/A retVal[pos++] = 0x01;
0N/A retVal[pos++] = (byte) (oidBytes.length>>>8);
0N/A retVal[pos++] = (byte) oidBytes.length;
0N/A System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);
0N/A pos += oidBytes.length;
0N/A retVal[pos++] = (byte) (mechPortion.length>>>24);
0N/A retVal[pos++] = (byte) (mechPortion.length>>>16);
0N/A retVal[pos++] = (byte) (mechPortion.length>>>8);
0N/A retVal[pos++] = (byte) mechPortion.length;
0N/A System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);
0N/A return retVal;
0N/A }
0N/A
0N/A public String toString() {
0N/A return printableName;
0N/A
0N/A }
0N/A
0N/A public Oid getStringNameType() throws GSSException {
0N/A return printableNameType;
0N/A }
0N/A
0N/A public boolean isAnonymous() {
0N/A if (printableNameType == null) {
0N/A return false;
0N/A } else {
0N/A return GSSName.NT_ANONYMOUS.equals(printableNameType);
0N/A }
0N/A }
0N/A
0N/A public boolean isMN() {
0N/A return true; // Since always canonicalized for some mech
0N/A }
0N/A
0N/A public synchronized GSSNameSpi getElement(Oid mechOid)
0N/A throws GSSException {
0N/A
0N/A GSSNameSpi retVal = elements.get(mechOid);
0N/A
0N/A if (retVal == null) {
0N/A if (appNameStr != null) {
0N/A retVal = gssManager.getNameElement
0N/A (appNameStr, appNameType, mechOid);
0N/A } else {
0N/A retVal = gssManager.getNameElement
0N/A (appNameBytes, appNameType, mechOid);
0N/A }
0N/A elements.put(mechOid, retVal);
0N/A }
0N/A return retVal;
0N/A }
0N/A
0N/A Set<GSSNameSpi> getElements() {
0N/A return new HashSet<GSSNameSpi>(elements.values());
0N/A }
0N/A
0N/A private static String getNameTypeStr(Oid nameTypeOid) {
0N/A
0N/A if (nameTypeOid == null)
0N/A return "(NT is null)";
0N/A
0N/A if (nameTypeOid.equals(NT_USER_NAME))
0N/A return "NT_USER_NAME";
0N/A if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))
0N/A return "NT_HOSTBASED_SERVICE";
0N/A if (nameTypeOid.equals(NT_EXPORT_NAME))
0N/A return "NT_EXPORT_NAME";
0N/A if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
0N/A return "NT_GSS_KRB5_PRINCIPAL";
0N/A else
0N/A return "Unknown";
0N/A }
0N/A}