1 /*
   2  * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.security.auth.kerberos;
  27 
  28 import java.io.*;
  29 import sun.security.krb5.KrbException;
  30 import sun.security.krb5.PrincipalName;
  31 import sun.security.krb5.Realm;
  32 import sun.security.util.*;
  33 
  34 /**
  35  * This class encapsulates a Kerberos principal.
  36  *
  37  * @author Mayank Upadhyay
  38  * @since 1.4
  39  */
  40 
  41 public final class KerberosPrincipal
  42     implements java.security.Principal, java.io.Serializable {
  43 
  44     private static final long serialVersionUID = -7374788026156829911L;
  45 
  46     //name types
  47 
  48     /**
  49      * unknown name type.
  50      */
  51 
  52     public static final int KRB_NT_UNKNOWN =   0;
  53 
  54     /**
  55      * user principal name type.
  56      */
  57 
  58     public static final int KRB_NT_PRINCIPAL = 1;
  59 
  60     /**
  61      * service and other unique instance (krbtgt) name type.
  62      */
  63     public static final int KRB_NT_SRV_INST =  2;
  64 
  65     /**
  66      * service with host name as instance (telnet, rcommands) name type.
  67      */
  68 
  69     public static final int KRB_NT_SRV_HST =   3;
  70 
  71     /**
  72      * service with host as remaining components name type.
  73      */
  74 
  75     public static final int KRB_NT_SRV_XHST =  4;
  76 
  77     /**
  78      * unique ID name type.
  79      */
  80 
  81     public static final int KRB_NT_UID = 5;
  82 
  83     /**
  84      * Enterprise name (alias)
  85      */
  86     public static final int KRB_NT_ENTERPRISE = 10;
  87 
  88     private transient String fullName;
  89 
  90     private transient String realm;
  91 
  92     private transient int nameType;
  93 
  94 
  95     /**
  96      * Constructs a {@code KerberosPrincipal} from the provided string input.
  97      * The name type for this principal defaults to
  98      * {@link #KRB_NT_PRINCIPAL KRB_NT_PRINCIPAL}
  99      * This string is assumed to contain a name in the format
 100      * that is specified in Section 2.1.1. (Kerberos Principal Name Form) of
 101      * <a href=http://www.ietf.org/rfc/rfc1964.txt> RFC 1964 </a>
 102      * (for example, <i>duke@FOO.COM</i>, where <i>duke</i>
 103      * represents a principal, and <i>FOO.COM</i> represents a realm).
 104      *
 105      * <p>If the input name does not contain a realm, the default realm
 106      * is used. The default realm can be specified either in a Kerberos
 107      * configuration file or via the java.security.krb5.realm
 108      * system property. For more information, see the
 109      * {@extLink security_guide_jgss_tutorial Kerberos Requirements}.
 110      * Additionally, if a security manager is
 111      * installed, a {@link ServicePermission} must be granted and the service
 112      * principal of the permission must minimally be inside the
 113      * {@code KerberosPrincipal}'s realm. For example, if the result of
 114      * {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
 115      * then a {@code ServicePermission} with service principal
 116      * {@code host/www.example.com@EXAMPLE.COM} (and any action)
 117      * must be granted.
 118      *
 119      * @param name the principal name
 120      * @throws IllegalArgumentException if name is improperly
 121      * formatted, if name is null, or if name does not contain
 122      * the realm to use and the default realm is not specified
 123      * in either a Kerberos configuration file or via the
 124      * java.security.krb5.realm system property.
 125      * @throws SecurityException if a security manager is installed and
 126      * {@code name} does not contain the realm to use, and a proper
 127      * {@link ServicePermission} as described above is not granted.
 128      */
 129     public KerberosPrincipal(String name) {
 130         this(name, KRB_NT_PRINCIPAL);
 131     }
 132 
 133     /**
 134      * Constructs a {@code KerberosPrincipal} from the provided string and
 135      * name type input.  The string is assumed to contain a name in the
 136      * format that is specified in Section 2.1 (Mandatory Name Forms) of
 137      * <a href=http://www.ietf.org/rfc/rfc1964.txt>RFC 1964</a>.
 138      * Valid name types are specified in Section 6.2 (Principal Names) of
 139      * <a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>.
 140      * The input name must be consistent with the provided name type.
 141      * (for example, <i>duke@FOO.COM</i>, is a valid input string for the
 142      * name type, KRB_NT_PRINCIPAL where <i>duke</i>
 143      * represents a principal, and <i>FOO.COM</i> represents a realm).
 144      *
 145      * <p>If the input name does not contain a realm, the default realm
 146      * is used. The default realm can be specified either in a Kerberos
 147      * configuration file or via the java.security.krb5.realm
 148      * system property. For more information, see the
 149      * {@extLink security_guide_jgss_tutorial Kerberos Requirements}.
 150      * Additionally, if a security manager is
 151      * installed, a {@link ServicePermission} must be granted and the service
 152      * principal of the permission must minimally be inside the
 153      * {@code KerberosPrincipal}'s realm. For example, if the result of
 154      * {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
 155      * then a {@code ServicePermission} with service principal
 156      * {@code host/www.example.com@EXAMPLE.COM} (and any action)
 157      * must be granted.
 158      *
 159      * @param name the principal name
 160      * @param nameType the name type of the principal
 161      * @throws IllegalArgumentException if name is improperly
 162      * formatted, if name is null, if the nameType is not supported,
 163      * or if name does not contain the realm to use and the default
 164      * realm is not specified in either a Kerberos configuration
 165      * file or via the java.security.krb5.realm system property.
 166      * @throws SecurityException if a security manager is installed and
 167      * {@code name} does not contain the realm to use, and a proper
 168      * {@link ServicePermission} as described above is not granted.
 169      */
 170 
 171     public KerberosPrincipal(String name, int nameType) {
 172 
 173         PrincipalName krb5Principal = null;
 174 
 175         try {
 176             // Appends the default realm if it is missing
 177             krb5Principal  = new PrincipalName(name,nameType);
 178         } catch (KrbException e) {
 179             throw new IllegalArgumentException(e.getMessage());
 180         }
 181 
 182         if (krb5Principal.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
 183             SecurityManager sm = System.getSecurityManager();
 184             if (sm != null) {
 185                 try {
 186                     sm.checkPermission(new ServicePermission(
 187                             "@" + krb5Principal.getRealmAsString(), "-"));
 188                 } catch (SecurityException se) {
 189                     // Swallow the actual exception to hide info
 190                     throw new SecurityException("Cannot read realm info");
 191                 }
 192             }
 193         }
 194         this.nameType = nameType;
 195         fullName = krb5Principal.toString();
 196         realm = krb5Principal.getRealmString();
 197     }
 198     /**
 199      * Returns the realm component of this Kerberos principal.
 200      *
 201      * @return the realm component of this Kerberos principal.
 202      */
 203     public String getRealm() {
 204         return realm;
 205     }
 206 
 207     /**
 208      * Returns a hash code for this {@code KerberosPrincipal}. The hash code
 209      * is defined to be the result of the following calculation:
 210      * <pre>{@code
 211      *  hashCode = getName().hashCode();
 212      * }</pre>
 213      *
 214      * @return a hash code for this {@code KerberosPrincipal}.
 215      */
 216     public int hashCode() {
 217         return getName().hashCode();
 218     }
 219 
 220     /**
 221      * Compares the specified object with this principal for equality.
 222      * Returns true if the given object is also a
 223      * {@code KerberosPrincipal} and the two
 224      * {@code KerberosPrincipal} instances are equivalent.
 225      * More formally two {@code KerberosPrincipal} instances are equal
 226      * if the values returned by {@code getName()} are equal.
 227      *
 228      * @param other the object to compare to
 229      * @return true if the object passed in represents the same principal
 230      * as this one, false otherwise.
 231      */
 232     public boolean equals(Object other) {
 233 
 234         if (other == this)
 235             return true;
 236 
 237         if (! (other instanceof KerberosPrincipal)) {
 238             return false;
 239         }
 240         String myFullName = getName();
 241         String otherFullName = ((KerberosPrincipal) other).getName();
 242         return myFullName.equals(otherFullName);
 243     }
 244 
 245     /**
 246      * Save the {@code KerberosPrincipal} object to a stream
 247      *
 248      * @serialData this {@code KerberosPrincipal} is serialized
 249      *          by writing out the PrincipalName and the
 250      *          Realm in their DER-encoded form as specified in Section 5.2.2 of
 251      *          <a href=http://www.ietf.org/rfc/rfc4120.txt> RFC4120</a>.
 252      */
 253     private void writeObject(ObjectOutputStream oos)
 254             throws IOException {
 255 
 256         PrincipalName krb5Principal;
 257         try {
 258             krb5Principal  = new PrincipalName(fullName, nameType);
 259             oos.writeObject(krb5Principal.asn1Encode());
 260             oos.writeObject(krb5Principal.getRealm().asn1Encode());
 261         } catch (Exception e) {
 262             throw new IOException(e);
 263         }
 264     }
 265 
 266     /**
 267      * Reads this object from a stream (i.e., deserializes it)
 268      */
 269     private void readObject(ObjectInputStream ois)
 270             throws IOException, ClassNotFoundException {
 271         byte[] asn1EncPrincipal = (byte [])ois.readObject();
 272         byte[] encRealm = (byte [])ois.readObject();
 273         try {
 274            Realm realmObject = new Realm(new DerValue(encRealm));
 275            PrincipalName krb5Principal = new PrincipalName(
 276                    new DerValue(asn1EncPrincipal), realmObject);
 277            realm = realmObject.toString();
 278            fullName = krb5Principal.toString();
 279            nameType = krb5Principal.getNameType();
 280         } catch (Exception e) {
 281             throw new IOException(e);
 282         }
 283     }
 284 
 285     /**
 286      * The returned string corresponds to the single-string
 287      * representation of a Kerberos Principal name as specified in
 288      * Section 2.1 of <a href=http://www.ietf.org/rfc/rfc1964.txt>RFC 1964</a>.
 289      *
 290      * @return the principal name.
 291      */
 292     public String getName() {
 293         return fullName;
 294     }
 295 
 296     /**
 297      * Returns the name type of the {@code KerberosPrincipal}. Valid name types
 298      * are specified in Section 6.2 of
 299      * <a href=http://www.ietf.org/rfc/rfc4120.txt> RFC4120</a>.
 300      *
 301      * @return the name type.
 302      */
 303     public int getNameType() {
 304         return nameType;
 305     }
 306 
 307     /**
 308      * Returns an informative textual representation of this {@code KerberosPrincipal}.
 309      *
 310      * @return an informative textual representation of this {@code KerberosPrincipal}.
 311      */
 312     public String toString() {
 313         return getName();
 314     }
 315 }