Path: blob/master/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java
41159 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*/2425/*26*27* (C) Copyright IBM Corp. 1999 All Rights Reserved.28* Copyright 1997 The Open Group Research Institute. All rights reserved.29*/3031package sun.security.krb5;3233import sun.security.krb5.internal.*;34import sun.security.util.*;35import java.net.*;36import java.util.Vector;37import java.util.Locale;38import java.io.IOException;39import java.math.BigInteger;40import java.util.Arrays;41import sun.security.krb5.internal.ccache.CCacheOutputStream;42import sun.security.krb5.internal.util.KerberosString;434445/**46* Implements the ASN.1 PrincipalName type and its realm in a single class.47* <pre>{@code48* Realm ::= KerberosString49*50* PrincipalName ::= SEQUENCE {51* name-type [0] Int32,52* name-string [1] SEQUENCE OF KerberosString53* }54* }</pre>55* This class is immutable.56* @see Realm57*/58public class PrincipalName implements Cloneable {5960//name types6162/**63* Name type not known64*/65public static final int KRB_NT_UNKNOWN = 0;6667/**68* Just the name of the principal as in DCE, or for users69*/70public static final int KRB_NT_PRINCIPAL = 1;7172/**73* Service and other unique instance (krbtgt)74*/75public static final int KRB_NT_SRV_INST = 2;7677/**78* Service with host name as instance (telnet, rcommands)79*/80public static final int KRB_NT_SRV_HST = 3;8182/**83* Service with host as remaining components84*/85public static final int KRB_NT_SRV_XHST = 4;8687/**88* Unique ID89*/90public static final int KRB_NT_UID = 5;9192/**93* Enterprise name (alias)94*/95public static final int KRB_NT_ENTERPRISE = 10;9697/**98* TGS Name99*/100public static final String TGS_DEFAULT_SRV_NAME = "krbtgt";101public static final int TGS_DEFAULT_NT = KRB_NT_SRV_INST;102103public static final char NAME_COMPONENT_SEPARATOR = '/';104public static final char NAME_REALM_SEPARATOR = '@';105public static final char REALM_COMPONENT_SEPARATOR = '.';106107public static final String NAME_COMPONENT_SEPARATOR_STR = "/";108public static final String NAME_REALM_SEPARATOR_STR = "@";109public static final String REALM_COMPONENT_SEPARATOR_STR = ".";110111// Instance fields.112113/**114* The name type, from PrincipalName's name-type field.115*/116private final int nameType;117118/**119* The name strings, from PrincipalName's name-strings field. This field120* must be neither null nor empty. Each entry of it must also be neither121* null nor empty. Make sure to clone the field when it's passed in or out.122*/123private final String[] nameStrings;124125/**126* The realm this principal belongs to.127*/128private final Realm nameRealm; // not null129130131/**132* When constructing a PrincipalName, whether the realm is included in133* the input, or deduced from default realm or domain-realm mapping.134*/135private final boolean realmDeduced;136137// cached default salt, not used in clone138private transient String salt = null;139140// There are 3 basic constructors. All other constructors must call them.141// All basic constructors must call validateNameStrings.142// 1. From name components143// 2. From name144// 3. From DER encoding145146/**147* Creates a PrincipalName.148*/149public PrincipalName(int nameType, String[] nameStrings, Realm nameRealm) {150if (nameRealm == null) {151throw new IllegalArgumentException("Null realm not allowed");152}153validateNameStrings(nameStrings);154this.nameType = nameType;155this.nameStrings = nameStrings.clone();156this.nameRealm = nameRealm;157this.realmDeduced = false;158}159160// Warning: called by NativeCreds.c161public PrincipalName(String[] nameParts, String realm) throws RealmException {162this(KRB_NT_UNKNOWN, nameParts, new Realm(realm));163}164165// Validate a nameStrings argument166private static void validateNameStrings(String[] ns) {167if (ns == null) {168throw new IllegalArgumentException("Null nameStrings not allowed");169}170if (ns.length == 0) {171throw new IllegalArgumentException("Empty nameStrings not allowed");172}173for (String s: ns) {174if (s == null) {175throw new IllegalArgumentException("Null nameString not allowed");176}177if (s.isEmpty()) {178throw new IllegalArgumentException("Empty nameString not allowed");179}180}181}182183public Object clone() {184try {185PrincipalName pName = (PrincipalName) super.clone();186UNSAFE.putReference(this, NAME_STRINGS_OFFSET, nameStrings.clone());187return pName;188} catch (CloneNotSupportedException ex) {189throw new AssertionError("Should never happen");190}191}192193private static final long NAME_STRINGS_OFFSET;194private static final jdk.internal.misc.Unsafe UNSAFE;195static {196try {197jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe();198NAME_STRINGS_OFFSET = unsafe.objectFieldOffset(199PrincipalName.class.getDeclaredField("nameStrings"));200UNSAFE = unsafe;201} catch (ReflectiveOperationException e) {202throw new Error(e);203}204}205206@Override207public boolean equals(Object o) {208if (this == o) {209return true;210}211if (o instanceof PrincipalName) {212PrincipalName other = (PrincipalName)o;213return nameRealm.equals(other.nameRealm) &&214Arrays.equals(nameStrings, other.nameStrings);215}216return false;217}218219/**220* Returns the ASN.1 encoding of the221* <pre>{@code222* PrincipalName ::= SEQUENCE {223* name-type [0] Int32,224* name-string [1] SEQUENCE OF KerberosString225* }226*227* KerberosString ::= GeneralString (IA5String)228* }</pre>229*230* <p>231* This definition reflects the Network Working Group RFC 4120232* specification available at233* <a href="http://www.ietf.org/rfc/rfc4120.txt">234* http://www.ietf.org/rfc/rfc4120.txt</a>.235*236* @param encoding DER-encoded PrincipalName (without Realm)237* @param realm the realm for this name238* @exception Asn1Exception if an error occurs while decoding239* an ASN1 encoded data.240* @exception Asn1Exception if there is an ASN1 encoding error241* @exception IOException if an I/O error occurs242* @exception IllegalArgumentException if encoding is null243* reading encoded data.244*/245public PrincipalName(DerValue encoding, Realm realm)246throws Asn1Exception, IOException {247if (realm == null) {248throw new IllegalArgumentException("Null realm not allowed");249}250realmDeduced = false;251nameRealm = realm;252DerValue der;253if (encoding == null) {254throw new IllegalArgumentException("Null encoding not allowed");255}256if (encoding.getTag() != DerValue.tag_Sequence) {257throw new Asn1Exception(Krb5.ASN1_BAD_ID);258}259der = encoding.getData().getDerValue();260if ((der.getTag() & 0x1F) == 0x00) {261BigInteger bint = der.getData().getBigInteger();262nameType = bint.intValue();263} else {264throw new Asn1Exception(Krb5.ASN1_BAD_ID);265}266der = encoding.getData().getDerValue();267if ((der.getTag() & 0x01F) == 0x01) {268DerValue subDer = der.getData().getDerValue();269if (subDer.getTag() != DerValue.tag_SequenceOf) {270throw new Asn1Exception(Krb5.ASN1_BAD_ID);271}272Vector<String> v = new Vector<>();273DerValue subSubDer;274while(subDer.getData().available() > 0) {275subSubDer = subDer.getData().getDerValue();276String namePart = new KerberosString(subSubDer).toString();277v.addElement(namePart);278}279nameStrings = new String[v.size()];280v.copyInto(nameStrings);281validateNameStrings(nameStrings);282} else {283throw new Asn1Exception(Krb5.ASN1_BAD_ID);284}285}286287/**288* Parse (unmarshal) a <code>PrincipalName</code> from a DER289* input stream. This form290* parsing might be used when expanding a value which is part of291* a constructed sequence and uses explicitly tagged type.292*293* @exception Asn1Exception on error.294* @param data the Der input stream value, which contains one or295* more marshaled value.296* @param explicitTag tag number.297* @param optional indicate if this data field is optional298* @param realm the realm for the name299* @return an instance of <code>PrincipalName</code>, or null if the300* field is optional and missing.301*/302public static PrincipalName parse(DerInputStream data,303byte explicitTag, boolean304optional,305Realm realm)306throws Asn1Exception, IOException, RealmException {307308if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=309explicitTag))310return null;311DerValue der = data.getDerValue();312if (explicitTag != (der.getTag() & (byte)0x1F)) {313throw new Asn1Exception(Krb5.ASN1_BAD_ID);314} else {315DerValue subDer = der.getData().getDerValue();316if (realm == null) {317realm = Realm.getDefault();318}319return new PrincipalName(subDer, realm);320}321}322323324// XXX Error checkin consistent with MIT krb5_parse_name325// Code repetition, realm parsed again by class Realm326private static String[] parseName(String name) {327328Vector<String> tempStrings = new Vector<>();329String temp = name;330int i = 0;331int componentStart = 0;332String component;333334while (i < temp.length()) {335if (temp.charAt(i) == NAME_COMPONENT_SEPARATOR) {336/*337* If this separator is escaped then don't treat it338* as a separator339*/340if (i > 0 && temp.charAt(i - 1) == '\\') {341temp = temp.substring(0, i - 1) +342temp.substring(i, temp.length());343continue;344}345else {346if (componentStart <= i) {347component = temp.substring(componentStart, i);348tempStrings.addElement(component);349}350componentStart = i + 1;351}352} else {353if (temp.charAt(i) == NAME_REALM_SEPARATOR) {354/*355* If this separator is escaped then don't treat it356* as a separator357*/358if (i > 0 && temp.charAt(i - 1) == '\\') {359temp = temp.substring(0, i - 1) +360temp.substring(i, temp.length());361continue;362} else {363if (componentStart < i) {364component = temp.substring(componentStart, i);365tempStrings.addElement(component);366}367componentStart = i + 1;368break;369}370}371}372i++;373}374375if (i == temp.length()) {376component = temp.substring(componentStart, i);377tempStrings.addElement(component);378}379380String[] result = new String[tempStrings.size()];381tempStrings.copyInto(result);382return result;383}384385/**386* Constructs a PrincipalName from a string.387* @param name the name388* @param type the type389* @param realm the realm, null if not known. Note that when realm is not390* null, it will be always used even if there is a realm part in name. When391* realm is null, will read realm part from name, or try to map a realm392* (for KRB_NT_SRV_HST), or use the default realm, or fail393* @throws RealmException394*/395public PrincipalName(String name, int type, String realm)396throws RealmException {397if (name == null) {398throw new IllegalArgumentException("Null name not allowed");399}400String[] nameParts = parseName(name);401validateNameStrings(nameParts);402if (realm == null) {403realm = Realm.parseRealmAtSeparator(name);404}405406// No realm info from parameter and string, must deduce later407realmDeduced = realm == null;408409switch (type) {410case KRB_NT_SRV_HST:411if (nameParts.length >= 2) {412String hostName = nameParts[1];413Boolean option;414try {415// If true, try canonicalizing and accept it if it starts416// with the short name. Otherwise, never. Default true.417option = Config.getInstance().getBooleanObject(418"libdefaults", "dns_canonicalize_hostname");419} catch (KrbException e) {420option = null;421}422if (option != Boolean.FALSE) {423try {424// RFC4120 does not recommend canonicalizing a hostname.425// However, for compatibility reason, we will try426// canonicalizing it and see if the output looks better.427428String canonicalized = (InetAddress.getByName(hostName)).429getCanonicalHostName();430431// Looks if canonicalized is a longer format of hostName,432// we accept cases like433// bunny -> bunny.rabbit.hole434if (canonicalized.toLowerCase(Locale.ENGLISH).startsWith(435hostName.toLowerCase(Locale.ENGLISH) + ".")) {436hostName = canonicalized;437}438} catch (UnknownHostException | SecurityException e) {439// not canonicalized or no permission to do so, use old440}441if (hostName.endsWith(".")) {442hostName = hostName.substring(0, hostName.length() - 1);443}444}445nameParts[1] = hostName.toLowerCase(Locale.ENGLISH);446}447nameStrings = nameParts;448nameType = type;449450if (realm != null) {451nameRealm = new Realm(realm);452} else {453// We will try to get realm name from the mapping in454// the configuration. If it is not specified455// we will use the default realm. This nametype does456// not allow a realm to be specified. The name string must of457// the form service@host and this is internally changed into458// service/host by Kerberos459String mapRealm = mapHostToRealm(nameParts[1]);460if (mapRealm != null) {461nameRealm = new Realm(mapRealm);462} else {463nameRealm = Realm.getDefault();464}465}466break;467case KRB_NT_UNKNOWN:468case KRB_NT_PRINCIPAL:469case KRB_NT_SRV_INST:470case KRB_NT_SRV_XHST:471case KRB_NT_UID:472case KRB_NT_ENTERPRISE:473nameStrings = nameParts;474nameType = type;475if (realm != null) {476nameRealm = new Realm(realm);477} else {478nameRealm = Realm.getDefault();479}480break;481default:482throw new IllegalArgumentException("Illegal name type");483}484}485486// Warning: called by nativeccache.c487public PrincipalName(String name, int type) throws RealmException {488this(name, type, (String)null);489}490491public PrincipalName(String name) throws RealmException {492this(name, KRB_NT_UNKNOWN);493}494495public PrincipalName(String name, String realm) throws RealmException {496this(name, KRB_NT_UNKNOWN, realm);497}498499public static PrincipalName tgsService(String r1, String r2)500throws KrbException {501return new PrincipalName(PrincipalName.KRB_NT_SRV_INST,502new String[] {PrincipalName.TGS_DEFAULT_SRV_NAME, r1},503new Realm(r2));504}505506public String getRealmAsString() {507return getRealmString();508}509510public String getPrincipalNameAsString() {511StringBuilder temp = new StringBuilder(nameStrings[0]);512for (int i = 1; i < nameStrings.length; i++)513temp.append(nameStrings[i]);514return temp.toString();515}516517public int hashCode() {518return toString().hashCode();519}520521public String getName() {522return toString();523}524525public int getNameType() {526return nameType;527}528529public String[] getNameStrings() {530return nameStrings.clone();531}532533public byte[][] toByteArray() {534byte[][] result = new byte[nameStrings.length][];535for (int i = 0; i < nameStrings.length; i++) {536result[i] = nameStrings[i].getBytes();537}538return result;539}540541public String getRealmString() {542return nameRealm.toString();543}544545public Realm getRealm() {546return nameRealm;547}548549public String getSalt() {550if (salt == null) {551StringBuilder salt = new StringBuilder();552salt.append(nameRealm.toString());553for (int i = 0; i < nameStrings.length; i++) {554salt.append(nameStrings[i]);555}556return salt.toString();557}558return salt;559}560561public String toString() {562StringBuilder str = new StringBuilder();563for (int i = 0; i < nameStrings.length; i++) {564if (i > 0)565str.append("/");566String n = nameStrings[i];567n = n.replace("@", "\\@");568str.append(n);569}570str.append("@");571str.append(nameRealm.toString());572return str.toString();573}574575public String getNameString() {576StringBuilder str = new StringBuilder();577for (int i = 0; i < nameStrings.length; i++) {578if (i > 0)579str.append("/");580str.append(nameStrings[i]);581}582return str.toString();583}584585/**586* Encodes a <code>PrincipalName</code> object. Note that only the type and587* names are encoded. To encode the realm, call getRealm().asn1Encode().588* @return the byte array of the encoded PrncipalName object.589* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.590* @exception IOException if an I/O error occurs while reading encoded data.591*592*/593public byte[] asn1Encode() throws Asn1Exception, IOException {594DerOutputStream bytes = new DerOutputStream();595DerOutputStream temp = new DerOutputStream();596BigInteger bint = BigInteger.valueOf(this.nameType);597temp.putInteger(bint);598bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp);599temp = new DerOutputStream();600DerValue[] der = new DerValue[nameStrings.length];601for (int i = 0; i < nameStrings.length; i++) {602der[i] = new KerberosString(nameStrings[i]).toDerValue();603}604temp.putSequence(der);605bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp);606temp = new DerOutputStream();607temp.write(DerValue.tag_Sequence, bytes);608return temp.toByteArray();609}610611612/**613* Checks if two <code>PrincipalName</code> objects have identical values in their corresponding data fields.614*615* @param pname the other <code>PrincipalName</code> object.616* @return true if two have identical values, otherwise, return false.617*/618// It is used in <code>sun.security.krb5.internal.ccache</code> package.619public boolean match(PrincipalName pname) {620boolean matched = true;621//name type is just a hint, no two names can be the same ignoring name type.622// if (this.nameType != pname.nameType) {623// matched = false;624// }625if ((this.nameRealm != null) && (pname.nameRealm != null)) {626if (!(this.nameRealm.toString().equalsIgnoreCase(pname.nameRealm.toString()))) {627matched = false;628}629}630if (this.nameStrings.length != pname.nameStrings.length) {631matched = false;632} else {633for (int i = 0; i < this.nameStrings.length; i++) {634if (!(this.nameStrings[i].equalsIgnoreCase(pname.nameStrings[i]))) {635matched = false;636}637}638}639return matched;640}641642/**643* Writes data field values of <code>PrincipalName</code> in FCC format to an output stream.644*645* @param cos a <code>CCacheOutputStream</code> for writing data.646* @exception IOException if an I/O exception occurs.647* @see sun.security.krb5.internal.ccache.CCacheOutputStream648*/649public void writePrincipal(CCacheOutputStream cos) throws IOException {650cos.write32(nameType);651cos.write32(nameStrings.length);652byte[] realmBytes = null;653realmBytes = nameRealm.toString().getBytes();654cos.write32(realmBytes.length);655cos.write(realmBytes, 0, realmBytes.length);656byte[] bytes = null;657for (int i = 0; i < nameStrings.length; i++) {658bytes = nameStrings[i].getBytes();659cos.write32(bytes.length);660cos.write(bytes, 0, bytes.length);661}662}663664/**665* Returns the instance component of a name.666* In a multi-component name such as a KRB_NT_SRV_INST667* name, the second component is returned.668* Null is returned if there are not two or more669* components in the name.670*671* @return instance component of a multi-component name.672*/673public String getInstanceComponent()674{675if (nameStrings != null && nameStrings.length >= 2)676{677return new String(nameStrings[1]);678}679680return null;681}682683static String mapHostToRealm(String name) {684String result = null;685try {686String subname = null;687Config c = Config.getInstance();688if ((result = c.get("domain_realm", name)) != null)689return result;690else {691for (int i = 1; i < name.length(); i++) {692if ((name.charAt(i) == '.') && (i != name.length() - 1)) { //mapping could be .ibm.com = AUSTIN.IBM.COM693subname = name.substring(i);694result = c.get("domain_realm", subname);695if (result != null) {696break;697}698else {699subname = name.substring(i + 1); //or mapping could be ibm.com = AUSTIN.IBM.COM700result = c.get("domain_realm", subname);701if (result != null) {702break;703}704}705}706}707}708} catch (KrbException e) {709}710return result;711}712713public boolean isRealmDeduced() {714return realmDeduced;715}716}717718719