jdk/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2000-2006 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.security.jgss.krb5;
+
+import org.ietf.jgss.*;
+import sun.security.jgss.spi.*;
+import javax.security.auth.kerberos.*;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.ServiceName;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.Provider;
+
+/**
+ * Implements the GSSNameSpi for the krb5 mechanism.
+ *
+ * @author Mayank Upadhyay
+ */
+public class Krb5NameElement
+    implements GSSNameSpi {
+
+    private PrincipalName krb5PrincipalName;
+
+    private String gssNameStr = null;
+    private Oid gssNameType = null;
+
+    // XXX Move this concept into PrincipalName's asn1Encode() sometime
+    private static String CHAR_ENCODING = "UTF-8";
+
+    private Krb5NameElement(PrincipalName principalName,
+                            String gssNameStr,
+                            Oid gssNameType) {
+        this.krb5PrincipalName = principalName;
+        this.gssNameStr = gssNameStr;
+        this.gssNameType = gssNameType;
+    }
+
+    /**
+     * Instantiates a new Krb5NameElement object. Internally it stores the
+     * information provided by the input parameters so that they may later
+     * be used for output when a printable representaion of this name is
+     * needed in GSS-API format rather than in Kerberos format.
+     *
+     */
+    static Krb5NameElement getInstance(String gssNameStr, Oid gssNameType)
+        throws GSSException {
+
+        /*
+         * A null gssNameType implies that the mechanism default
+         * Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL be used.
+         */
+        if (gssNameType == null)
+            gssNameType = Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL;
+        else
+            if (!gssNameType.equals(GSSName.NT_USER_NAME) &&
+                !gssNameType.equals(GSSName.NT_HOSTBASED_SERVICE) &&
+                !gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL) &&
+                !gssNameType.equals(GSSName.NT_EXPORT_NAME))
+                throw new GSSException(GSSException.BAD_NAMETYPE, -1,
+                                       gssNameType.toString()
+                                       +" is an unsupported nametype");
+
+        PrincipalName principalName;
+        try {
+
+            if (gssNameType.equals(GSSName.NT_EXPORT_NAME) ||
+                gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)) {
+                principalName = new PrincipalName(gssNameStr,
+                                  PrincipalName.KRB_NT_PRINCIPAL);
+            } else {
+
+                String[] components = getComponents(gssNameStr);
+
+                /*
+                 * We have forms of GSS name strings that can come in:
+                 *
+                 * 1. names of the form "foo" with just one
+                 * component. (This might include a "@" but only in escaped
+                 * form like "\@")
+                 * 2. names of the form "foo@bar" with two components
+                 *
+                 * The nametypes that are accepted are NT_USER_NAME, and
+                 * NT_HOSTBASED_SERVICE.
+                 */
+
+                if (gssNameType.equals(GSSName.NT_USER_NAME))
+                    principalName = new PrincipalName(gssNameStr,
+                                    PrincipalName.KRB_NT_PRINCIPAL);
+                else {
+                    String hostName = null;
+                    String service = components[0];
+                    if (components.length >= 2)
+                        hostName = components[1];
+
+                    String principal = getHostBasedInstance(service, hostName);
+                    principalName = new ServiceName(principal,
+                                            PrincipalName.KRB_NT_SRV_HST);
+                }
+            }
+
+        } catch (KrbException e) {
+            throw new GSSException(GSSException.BAD_NAME, -1, e.getMessage());
+        }
+
+        return new Krb5NameElement(principalName, gssNameStr, gssNameType);
+    }
+
+    static Krb5NameElement getInstance(PrincipalName principalName) {
+        return new Krb5NameElement(principalName,
+                                   principalName.getName(),
+                                   Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
+    }
+
+    private static String[] getComponents(String gssNameStr)
+        throws GSSException {
+
+        String[] retVal;
+
+        // XXX Perhaps provide this parsing code in PrincipalName
+
+        // Look for @ as in service@host
+        // Assumes host name will not have an escaped '@'
+        int separatorPos = gssNameStr.lastIndexOf('@', gssNameStr.length());
+
+        // Not really a separator if it is escaped. Then this is just part
+        // of the principal name or service name
+        if ((separatorPos > 0) &&
+                (gssNameStr.charAt(separatorPos-1) == '\\')) {
+            // Is the `\` character escaped itself?
+            if ((separatorPos - 2 < 0) ||
+                (gssNameStr.charAt(separatorPos-2) != '\\'))
+                separatorPos = -1;
+        }
+
+        if (separatorPos > 0) {
+            String serviceName = gssNameStr.substring(0, separatorPos);
+            String hostName = gssNameStr.substring(separatorPos+1);
+            retVal = new String[] { serviceName, hostName};
+        } else {
+            retVal = new String[] {gssNameStr};
+        }
+
+        return retVal;
+
+    }
+
+    private static String getHostBasedInstance(String serviceName,
+                                               String hostName)
+        throws GSSException {
+            StringBuffer temp = new StringBuffer(serviceName);
+
+            try {
+                // A lack of "@" defaults to the service being on the local
+                // host as per RFC 2743
+                // XXX Move this part into JGSS framework
+                if (hostName == null)
+                    hostName = InetAddress.getLocalHost().getHostName();
+
+            } catch (UnknownHostException e) {
+                // use hostname as it is
+            }
+            hostName = hostName.toLowerCase();
+
+            temp = temp.append('/').append(hostName);
+            return temp.toString();
+    }
+
+    public final PrincipalName getKrb5PrincipalName() {
+        return krb5PrincipalName;
+    }
+
+    /**
+     * Equal method for the GSSNameSpi objects.
+     * If either name denotes an anonymous principal, the call should
+     * return false.
+     *
+     * @param name to be compared with
+     * @returns true if they both refer to the same entity, else false
+     * @exception GSSException with major codes of BAD_NAMETYPE,
+     *  BAD_NAME, FAILURE
+     */
+    public boolean equals(GSSNameSpi other) throws GSSException {
+
+        if (other == this)
+            return true;
+
+        if (other instanceof Krb5NameElement) {
+                Krb5NameElement that = (Krb5NameElement) other;
+                return (this.krb5PrincipalName.getName().equals(
+                            that.krb5PrincipalName.getName()));
+        }
+        return false;
+    }
+
+    /**
+     * Compares this <code>GSSNameSpi</code> object to another Object
+     * that might be a <code>GSSNameSpi</code>. The behaviour is exactly
+     * the same as in {@link #equals(GSSNameSpi) equals} except that
+     * no GSSException is thrown; instead, false will be returned in the
+     * situation where an error occurs.
+     *
+     * @param another the object to be compared to
+     * @returns true if they both refer to the same entity, else false
+     * @see #equals(GSSNameSpi)
+     */
+    public boolean equals(Object another) {
+        if (this == another) {
+            return true;
+        }
+
+        try {
+            if (another instanceof Krb5NameElement)
+                 return equals((Krb5NameElement) another);
+        } catch (GSSException e) {
+            // ignore exception
+        }
+        return false;
+    }
+
+    /**
+     * Returns a hashcode value for this GSSNameSpi.
+     *
+     * @return a hashCode value
+     */
+    public int hashCode() {
+        return 37 * 17 + krb5PrincipalName.getName().hashCode();
+    }
+
+
+    /**
+     * Returns the principal name in the form user@REALM or
+     * host/service@REALM but with the following contraints that are
+     * imposed by RFC 1964:
+     * <pre>
+     *  (1) all occurrences of the characters `@`,  `/`, and `\` within
+     *   principal components or realm names shall be quoted with an
+     *   immediately-preceding `\`.
+     *
+     *   (2) all occurrences of the null, backspace, tab, or newline
+     *   characters within principal components or realm names will be
+     *   represented, respectively, with `\0`, `\b`, `\t`, or `\n`.
+     *
+     *   (3) the `\` quoting character shall not be emitted within an
+     *   exported name except to accomodate cases (1) and (2).
+     * </pre>
+     */
+    public byte[] export() throws GSSException {
+        // XXX Apply the above constraints.
+        byte[] retVal = null;
+        try {
+            retVal = krb5PrincipalName.getName().getBytes(CHAR_ENCODING);
+        } catch (UnsupportedEncodingException e) {
+            // Can't happen
+        }
+        return retVal;
+    }
+
+    /**
+     * Get the mechanism type that this NameElement corresponds to.
+     *
+     * @return the Oid of the mechanism type
+     */
+    public Oid getMechanism() {
+        return (Krb5MechFactory.GSS_KRB5_MECH_OID);
+    }
+
+    /**
+     * Returns a string representation for this name. The printed
+     * name type can be obtained by calling getStringNameType().
+     *
+     * @return string form of this name
+     * @see #getStringNameType()
+     * @overrides Object#toString
+     */
+    public String toString() {
+        return (gssNameStr);
+        // For testing: return (super.toString());
+    }
+
+    /**
+     * Returns the name type oid.
+     */
+    public Oid getGSSNameType() {
+        return (gssNameType);
+    }
+
+    /**
+     * Returns the oid describing the format of the printable name.
+     *
+     * @return the Oid for the format of the printed name
+     */
+    public Oid getStringNameType() {
+        // XXX For NT_EXPORT_NAME return a different name type. Infact,
+        // don't even store NT_EXPORT_NAME in the cons.
+        return (gssNameType);
+    }
+
+    /**
+     * Indicates if this name object represents an Anonymous name.
+     */
+    public boolean isAnonymousName() {
+        return (gssNameType.equals(GSSName.NT_ANONYMOUS));
+    }
+
+    public Provider getProvider() {
+        return Krb5MechFactory.PROVIDER;
+    }
+
+}