/* * Copyright (c) 2000, 2013, 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. */ /* * * (C) Copyright IBM Corp. 1999 All Rights Reserved. * Copyright 1997 The Open Group Research Institute. All rights reserved. */ package sun.security.krb5; import sun.security.krb5.internal.*; import sun.security.util.*; import java.net.*; import java.util.Vector; import java.util.Locale; import java.io.IOException; import java.math.BigInteger; import java.util.Arrays; import sun.security.krb5.internal.ccache.CCacheOutputStream; import sun.security.krb5.internal.util.KerberosString; /** * Implements the ASN.1 PrincipalName type and its realm in a single class. *
{@code * Realm ::= KerberosString * * PrincipalName ::= SEQUENCE { * name-type [0] Int32, * name-string [1] SEQUENCE OF KerberosString * } * }* This class is immutable. * @see Realm */ public class PrincipalName implements Cloneable { //name types /** * Name type not known */ public static final int KRB_NT_UNKNOWN = 0; /** * Just the name of the principal as in DCE, or for users */ public static final int KRB_NT_PRINCIPAL = 1; /** * Service and other unique instance (krbtgt) */ public static final int KRB_NT_SRV_INST = 2; /** * Service with host name as instance (telnet, rcommands) */ public static final int KRB_NT_SRV_HST = 3; /** * Service with host as remaining components */ public static final int KRB_NT_SRV_XHST = 4; /** * Unique ID */ public static final int KRB_NT_UID = 5; /** * TGS Name */ public static final String TGS_DEFAULT_SRV_NAME = "krbtgt"; public static final int TGS_DEFAULT_NT = KRB_NT_SRV_INST; public static final char NAME_COMPONENT_SEPARATOR = '/'; public static final char NAME_REALM_SEPARATOR = '@'; public static final char REALM_COMPONENT_SEPARATOR = '.'; public static final String NAME_COMPONENT_SEPARATOR_STR = "/"; public static final String NAME_REALM_SEPARATOR_STR = "@"; public static final String REALM_COMPONENT_SEPARATOR_STR = "."; // Instance fields. /** * The name type, from PrincipalName's name-type field. */ private final int nameType; /** * The name strings, from PrincipalName's name-strings field. This field * must be neither null nor empty. Each entry of it must also be neither * null nor empty. Make sure to clone the field when it's passed in or out. */ private final String[] nameStrings; /** * The realm this principal belongs to. */ private final Realm nameRealm; // not null /** * When constructing a PrincipalName, whether the realm is included in * the input, or deduced from default realm or domain-realm mapping. */ private final boolean realmDeduced; // cached default salt, not used in clone private transient String salt = null; // There are 3 basic constructors. All other constructors must call them. // All basic constructors must call validateNameStrings. // 1. From name components // 2. From name // 3. From DER encoding /** * Creates a PrincipalName. */ public PrincipalName(int nameType, String[] nameStrings, Realm nameRealm) { if (nameRealm == null) { throw new IllegalArgumentException("Null realm not allowed"); } validateNameStrings(nameStrings); this.nameType = nameType; this.nameStrings = nameStrings.clone(); this.nameRealm = nameRealm; this.realmDeduced = false; } // This method is called by Windows NativeCred.c public PrincipalName(String[] nameParts, String realm) throws RealmException { this(KRB_NT_UNKNOWN, nameParts, new Realm(realm)); } // Validate a nameStrings argument private static void validateNameStrings(String[] ns) { if (ns == null) { throw new IllegalArgumentException("Null nameStrings not allowed"); } if (ns.length == 0) { throw new IllegalArgumentException("Empty nameStrings not allowed"); } for (String s: ns) { if (s == null) { throw new IllegalArgumentException("Null nameString not allowed"); } if (s.isEmpty()) { throw new IllegalArgumentException("Empty nameString not allowed"); } } } public Object clone() { try { PrincipalName pName = (PrincipalName) super.clone(); UNSAFE.putObject(this, NAME_STRINGS_OFFSET, nameStrings.clone()); return pName; } catch (CloneNotSupportedException ex) { throw new AssertionError("Should never happen"); } } private static final long NAME_STRINGS_OFFSET; private static final jdk.internal.misc.Unsafe UNSAFE; static { try { jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); NAME_STRINGS_OFFSET = unsafe.objectFieldOffset( PrincipalName.class.getDeclaredField("nameStrings")); UNSAFE = unsafe; } catch (ReflectiveOperationException e) { throw new Error(e); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof PrincipalName) { PrincipalName other = (PrincipalName)o; return nameRealm.equals(other.nameRealm) && Arrays.equals(nameStrings, other.nameStrings); } return false; } /** * Returns the ASN.1 encoding of the *
{@code * PrincipalName ::= SEQUENCE { * name-type [0] Int32, * name-string [1] SEQUENCE OF KerberosString * } * * KerberosString ::= GeneralString (IA5String) * }* *
* This definition reflects the Network Working Group RFC 4120
* specification available at
*
* http://www.ietf.org/rfc/rfc4120.txt.
*
* @param encoding DER-encoded PrincipalName (without Realm)
* @param realm the realm for this name
* @exception Asn1Exception if an error occurs while decoding
* an ASN1 encoded data.
* @exception Asn1Exception if there is an ASN1 encoding error
* @exception IOException if an I/O error occurs
* @exception IllegalArgumentException if encoding is null
* reading encoded data.
*/
public PrincipalName(DerValue encoding, Realm realm)
throws Asn1Exception, IOException {
if (realm == null) {
throw new IllegalArgumentException("Null realm not allowed");
}
realmDeduced = false;
nameRealm = realm;
DerValue der;
if (encoding == null) {
throw new IllegalArgumentException("Null encoding not allowed");
}
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x00) {
BigInteger bint = der.getData().getBigInteger();
nameType = bint.intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x01F) == 0x01) {
DerValue subDer = der.getData().getDerValue();
if (subDer.getTag() != DerValue.tag_SequenceOf) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
VectorPrincipalName
from a DER
* input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @exception Asn1Exception on error.
* @param data the Der input stream value, which contains one or
* more marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @param realm the realm for the name
* @return an instance of PrincipalName
, or null if the
* field is optional and missing.
*/
public static PrincipalName parse(DerInputStream data,
byte explicitTag, boolean
optional,
Realm realm)
throws Asn1Exception, IOException, RealmException {
if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=
explicitTag))
return null;
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
if (realm == null) {
realm = Realm.getDefault();
}
return new PrincipalName(subDer, realm);
}
}
// XXX Error checkin consistent with MIT krb5_parse_name
// Code repetition, realm parsed again by class Realm
private static String[] parseName(String name) {
VectorPrincipalName
object. Note that only the type and
* names are encoded. To encode the realm, call getRealm().asn1Encode().
* @return the byte array of the encoded PrncipalName object.
* @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
BigInteger bint = BigInteger.valueOf(this.nameType);
temp.putInteger(bint);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp);
temp = new DerOutputStream();
DerValue[] der = new DerValue[nameStrings.length];
for (int i = 0; i < nameStrings.length; i++) {
der[i] = new KerberosString(nameStrings[i]).toDerValue();
}
temp.putSequence(der);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Checks if two PrincipalName
objects have identical values in their corresponding data fields.
*
* @param pname the other PrincipalName
object.
* @return true if two have identical values, otherwise, return false.
*/
// It is used in sun.security.krb5.internal.ccache
package.
public boolean match(PrincipalName pname) {
boolean matched = true;
//name type is just a hint, no two names can be the same ignoring name type.
// if (this.nameType != pname.nameType) {
// matched = false;
// }
if ((this.nameRealm != null) && (pname.nameRealm != null)) {
if (!(this.nameRealm.toString().equalsIgnoreCase(pname.nameRealm.toString()))) {
matched = false;
}
}
if (this.nameStrings.length != pname.nameStrings.length) {
matched = false;
} else {
for (int i = 0; i < this.nameStrings.length; i++) {
if (!(this.nameStrings[i].equalsIgnoreCase(pname.nameStrings[i]))) {
matched = false;
}
}
}
return matched;
}
/**
* Writes data field values of PrincipalName
in FCC format to an output stream.
*
* @param cos a CCacheOutputStream
for writing data.
* @exception IOException if an I/O exception occurs.
* @see sun.security.krb5.internal.ccache.CCacheOutputStream
*/
public void writePrincipal(CCacheOutputStream cos) throws IOException {
cos.write32(nameType);
cos.write32(nameStrings.length);
byte[] realmBytes = null;
realmBytes = nameRealm.toString().getBytes();
cos.write32(realmBytes.length);
cos.write(realmBytes, 0, realmBytes.length);
byte[] bytes = null;
for (int i = 0; i < nameStrings.length; i++) {
bytes = nameStrings[i].getBytes();
cos.write32(bytes.length);
cos.write(bytes, 0, bytes.length);
}
}
/**
* Returns the instance component of a name.
* In a multi-component name such as a KRB_NT_SRV_INST
* name, the second component is returned.
* Null is returned if there are not two or more
* components in the name.
*
* @return instance component of a multi-component name.
*/
public String getInstanceComponent()
{
if (nameStrings != null && nameStrings.length >= 2)
{
return new String(nameStrings[1]);
}
return null;
}
static String mapHostToRealm(String name) {
String result = null;
try {
String subname = null;
Config c = Config.getInstance();
if ((result = c.get("domain_realm", name)) != null)
return result;
else {
for (int i = 1; i < name.length(); i++) {
if ((name.charAt(i) == '.') && (i != name.length() - 1)) { //mapping could be .ibm.com = AUSTIN.IBM.COM
subname = name.substring(i);
result = c.get("domain_realm", subname);
if (result != null) {
break;
}
else {
subname = name.substring(i + 1); //or mapping could be ibm.com = AUSTIN.IBM.COM
result = c.get("domain_realm", subname);
if (result != null) {
break;
}
}
}
}
}
} catch (KrbException e) {
}
return result;
}
public boolean isRealmDeduced() {
return realmDeduced;
}
}