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