Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java
41161 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.jgss.krb5;2627import org.ietf.jgss.*;28import sun.security.jgss.spi.*;29import sun.security.krb5.PrincipalName;30import sun.security.krb5.Realm;31import sun.security.krb5.KrbException;3233import javax.security.auth.kerberos.ServicePermission;34import java.net.InetAddress;35import java.net.UnknownHostException;36import java.security.Provider;37import java.util.Locale;3839import static java.nio.charset.StandardCharsets.UTF_8;4041/**42* Implements the GSSNameSpi for the krb5 mechanism.43*44* @author Mayank Upadhyay45*/46public class Krb5NameElement47implements GSSNameSpi {4849private PrincipalName krb5PrincipalName;5051private String gssNameStr = null;52private Oid gssNameType = null;5354private Krb5NameElement(PrincipalName principalName,55String gssNameStr,56Oid gssNameType) {57this.krb5PrincipalName = principalName;58this.gssNameStr = gssNameStr;59this.gssNameType = gssNameType;60}6162/**63* Instantiates a new Krb5NameElement object. Internally it stores the64* information provided by the input parameters so that they may later65* be used for output when a printable representaion of this name is66* needed in GSS-API format rather than in Kerberos format.67*68*/69static Krb5NameElement getInstance(String gssNameStr, Oid gssNameType)70throws GSSException {7172/*73* A null gssNameType implies that the mechanism default74* Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL be used.75*/76if (gssNameType == null)77gssNameType = Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL;78else79if (!gssNameType.equals(GSSName.NT_USER_NAME) &&80!gssNameType.equals(GSSName.NT_HOSTBASED_SERVICE) &&81!gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL) &&82!gssNameType.equals(GSSName.NT_EXPORT_NAME))83throw new GSSException(GSSException.BAD_NAMETYPE, -1,84gssNameType.toString()85+" is an unsupported nametype");8687PrincipalName principalName;88try {8990if (gssNameType.equals(GSSName.NT_EXPORT_NAME) ||91gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)) {92principalName = new PrincipalName(gssNameStr,93PrincipalName.KRB_NT_PRINCIPAL);94} else {9596String[] components = getComponents(gssNameStr);9798/*99* We have forms of GSS name strings that can come in:100*101* 1. names of the form "foo" with just one102* component. (This might include a "@" but only in escaped103* form like "\@")104* 2. names of the form "foo@bar" with two components105*106* The nametypes that are accepted are NT_USER_NAME, and107* NT_HOSTBASED_SERVICE.108*/109110if (gssNameType.equals(GSSName.NT_USER_NAME))111principalName = new PrincipalName(gssNameStr,112PrincipalName.KRB_NT_PRINCIPAL);113else {114String hostName = null;115String service = components[0];116if (components.length >= 2)117hostName = components[1];118119String principal = getHostBasedInstance(service, hostName);120principalName = new PrincipalName(principal,121PrincipalName.KRB_NT_SRV_HST);122}123}124125} catch (KrbException e) {126throw new GSSException(GSSException.BAD_NAME, -1, e.getMessage());127}128129if (principalName.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {130@SuppressWarnings("removal")131SecurityManager sm = System.getSecurityManager();132if (sm != null) {133try {134sm.checkPermission(new ServicePermission(135"@" + principalName.getRealmAsString(), "-"));136} catch (SecurityException se) {137// Do not chain the actual exception to hide info138throw new GSSException(GSSException.FAILURE);139}140}141}142return new Krb5NameElement(principalName, gssNameStr, gssNameType);143}144145public static Krb5NameElement getInstance(PrincipalName principalName) {146return new Krb5NameElement(principalName,147principalName.getName(),148Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);149}150151private static String[] getComponents(String gssNameStr)152throws GSSException {153154String[] retVal;155156// XXX Perhaps provide this parsing code in PrincipalName157158// Look for @ as in service@host159// Assumes host name will not have an escaped '@'160int separatorPos = gssNameStr.lastIndexOf('@', gssNameStr.length());161162// Not really a separator if it is escaped. Then this is just part163// of the principal name or service name164if ((separatorPos > 0) &&165(gssNameStr.charAt(separatorPos-1) == '\\')) {166// Is the `\` character escaped itself?167if ((separatorPos - 2 < 0) ||168(gssNameStr.charAt(separatorPos-2) != '\\'))169separatorPos = -1;170}171172if (separatorPos > 0) {173String serviceName = gssNameStr.substring(0, separatorPos);174String hostName = gssNameStr.substring(separatorPos+1);175retVal = new String[] { serviceName, hostName};176} else {177retVal = new String[] {gssNameStr};178}179180return retVal;181182}183184private static String getHostBasedInstance(String serviceName,185String hostName)186throws GSSException {187StringBuffer temp = new StringBuffer(serviceName);188189try {190// A lack of "@" defaults to the service being on the local191// host as per RFC 2743192// XXX Move this part into JGSS framework193if (hostName == null)194hostName = InetAddress.getLocalHost().getHostName();195196} catch (UnknownHostException e) {197// use hostname as it is198}199hostName = hostName.toLowerCase(Locale.ENGLISH);200201temp = temp.append('/').append(hostName);202return temp.toString();203}204205public final PrincipalName getKrb5PrincipalName() {206return krb5PrincipalName;207}208209/**210* Equal method for the GSSNameSpi objects.211* If either name denotes an anonymous principal, the call should212* return false.213*214* @param other to be compared with215* @return true if they both refer to the same entity, else false216* @exception GSSException with major codes of BAD_NAMETYPE,217* BAD_NAME, FAILURE218*/219public boolean equals(GSSNameSpi other) throws GSSException {220221if (other == this)222return true;223224if (other instanceof Krb5NameElement) {225Krb5NameElement that = (Krb5NameElement) other;226return (this.krb5PrincipalName.getName().equals(227that.krb5PrincipalName.getName()));228}229return false;230}231232/**233* Compares this <code>GSSNameSpi</code> object to another Object234* that might be a <code>GSSNameSpi</code>. The behaviour is exactly235* the same as in {@link #equals(GSSNameSpi) equals} except that236* no GSSException is thrown; instead, false will be returned in the237* situation where an error occurs.238*239* @param another the object to be compared to240* @return true if they both refer to the same entity, else false241* @see #equals(GSSNameSpi)242*/243public boolean equals(Object another) {244if (this == another) {245return true;246}247248try {249if (another instanceof Krb5NameElement)250return equals((Krb5NameElement) another);251} catch (GSSException e) {252// ignore exception253}254return false;255}256257/**258* Returns a hashcode value for this GSSNameSpi.259*260* @return a hashCode value261*/262public int hashCode() {263return 37 * 17 + krb5PrincipalName.getName().hashCode();264}265266267/**268* Returns the principal name in the form user@REALM or269* host/service@REALM but with the following constraints that are270* imposed by RFC 1964:271* <pre>272* (1) all occurrences of the characters `@`, `/`, and `\` within273* principal components or realm names shall be quoted with an274* immediately-preceding `\`.275*276* (2) all occurrences of the null, backspace, tab, or newline277* characters within principal components or realm names will be278* represented, respectively, with `\0`, `\b`, `\t`, or `\n`.279*280* (3) the `\` quoting character shall not be emitted within an281* exported name except to accommodate cases (1) and (2).282* </pre>283*/284public byte[] export() throws GSSException {285// XXX Apply the above constraints.286return krb5PrincipalName.getName().getBytes(UTF_8);287}288289/**290* Get the mechanism type that this NameElement corresponds to.291*292* @return the Oid of the mechanism type293*/294public Oid getMechanism() {295return (Krb5MechFactory.GSS_KRB5_MECH_OID);296}297298/**299* Returns a string representation for this name. The printed300* name type can be obtained by calling getStringNameType().301*302* @return string form of this name303* @see #getStringNameType()304* @overrides Object#toString305*/306public String toString() {307return (gssNameStr);308// For testing: return (super.toString());309}310311/**312* Returns the name type oid.313*/314public Oid getGSSNameType() {315return (gssNameType);316}317318/**319* Returns the oid describing the format of the printable name.320*321* @return the Oid for the format of the printed name322*/323public Oid getStringNameType() {324// XXX For NT_EXPORT_NAME return a different name type. Infact,325// don't even store NT_EXPORT_NAME in the cons.326return (gssNameType);327}328329/**330* Indicates if this name object represents an Anonymous name.331*/332public boolean isAnonymousName() {333return (gssNameType.equals(GSSName.NT_ANONYMOUS));334}335336public Provider getProvider() {337return Krb5MechFactory.PROVIDER;338}339340}341342343