334 lines · 11.4 KB · Java
RawDownload
/* * Copyright (c) 2000, 2006, 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.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;    } }