      1 /*
      2  * Copyright (c) 2000, 2021, 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      * @since 13
     87      */
     88     public static final int KRB_NT_ENTERPRISE = 10;
     89
     90     private transient String fullName;
     91
     92     private transient String realm;
     93
     94     private transient int nameType;
     95
     96
     97     /**
     98      * Constructs a {@code KerberosPrincipal} from the provided string input.
     99      * The name type for this principal defaults to
    100      * {@link #KRB_NT_PRINCIPAL KRB_NT_PRINCIPAL}
    101      * This string is assumed to contain a name in the format
    102      * that is specified in Section 2.1.1. (Kerberos Principal Name Form) of
    103      * <a href=http://www.ietf.org/rfc/rfc1964.txt> RFC 1964 </a>
    104      * (for example, <i>duke@FOO.COM</i>, where <i>duke</i>
    105      * represents a principal, and <i>FOO.COM</i> represents a realm).
    106      *
    107      * <p>If the input name does not contain a realm, the default realm
    108      * is used. The default realm can be specified either in a Kerberos
    109      * configuration file or via the {@systemProperty java.security.krb5.realm}
    110      * system property. For more information, see the
    111      * {@extLink security_guide_jgss_tutorial Kerberos Requirements}.
    112      *
    113      * <p>Note that when this class or any other Kerberos-related class is
    114      * initially loaded and initialized, it may read and cache the default
    115      * realm from the Kerberos configuration file or via the
    116      * java.security.krb5.realm system property (the value will be empty if
    117      * no default realm is specified), such that any subsequent calls to set
    118      * or change the default realm by setting the java.security.krb5.realm
    119      * system property may be ignored.
    120      *
    121      * <p>Additionally, if a security manager is
    122      * installed, a {@link ServicePermission} must be granted and the service
    123      * principal of the permission must minimally be inside the
    124      * {@code KerberosPrincipal}'s realm. For example, if the result of
    125      * {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
    126      * then a {@code ServicePermission} with service principal
    127      * {@code host/www.example.com@EXAMPLE.COM} (and any action)
    128      * must be granted.
    129      *
    130      * @param name the principal name
    131      * @throws IllegalArgumentException if name is improperly
    132      * formatted, if name is null, or if name does not contain
    133      * the realm to use and the default realm is not specified
    134      * in either a Kerberos configuration file or via the
    135      * java.security.krb5.realm system property.
    136      * @throws SecurityException if a security manager is installed and
    137      * {@code name} does not contain the realm to use, and a proper
    138      * {@link ServicePermission} as described above is not granted.
    139      */
    140     public KerberosPrincipal(String name) {
    141         this(name, KRB_NT_PRINCIPAL);
    142     }
    143
    144     /**
    145      * Constructs a {@code KerberosPrincipal} from the provided string and
    146      * name type input.  The string is assumed to contain a name in the
    147      * format that is specified in Section 2.1 (Mandatory Name Forms) of
    148      * <a href=http://www.ietf.org/rfc/rfc1964.txt>RFC 1964</a>.
    149      * Valid name types are specified in Section 6.2 (Principal Names) of
    150      * <a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>.
    151      * The input name must be consistent with the provided name type.
    152      * (for example, <i>duke@FOO.COM</i>, is a valid input string for the
    153      * name type, KRB_NT_PRINCIPAL where <i>duke</i>
    154      * represents a principal, and <i>FOO.COM</i> represents a realm).
    155      *
    156      * <p>If the input name does not contain a realm, the default realm
    157      * is used. The default realm can be specified either in a Kerberos
    158      * configuration file or via the {@systemProperty java.security.krb5.realm}
    159      * system property. For more information, see the
    160      * {@extLink security_guide_jgss_tutorial Kerberos Requirements}.
    161      *
    162      * <p>Note that when this class or any other Kerberos-related class is
    163      * initially loaded and initialized, it may read and cache the default
    164      * realm from the Kerberos configuration file or via the
    165      * java.security.krb5.realm system property (the value will be empty if
    166      * no default realm is specified), such that any subsequent calls to set
    167      * or change the default realm by setting the java.security.krb5.realm
    168      * system property may be ignored.
    169      *
    170      * <p>Additionally, if a security manager is
    171      * installed, a {@link ServicePermission} must be granted and the service
    172      * principal of the permission must minimally be inside the
    173      * {@code KerberosPrincipal}'s realm. For example, if the result of
    174      * {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
    175      * then a {@code ServicePermission} with service principal
    176      * {@code host/www.example.com@EXAMPLE.COM} (and any action)
    177      * must be granted.
    178      *
    179      * @param name the principal name
    180      * @param nameType the name type of the principal
    181      * @throws IllegalArgumentException if name is improperly
    182      * formatted, if name is null, if the nameType is not supported,
    183      * or if name does not contain the realm to use and the default
    184      * realm is not specified in either a Kerberos configuration
    185      * file or via the java.security.krb5.realm system property.
    186      * @throws SecurityException if a security manager is installed and
    187      * {@code name} does not contain the realm to use, and a proper
    188      * {@link ServicePermission} as described above is not granted.
    189      */
    190
    191     public KerberosPrincipal(String name, int nameType) {
    192
    193         PrincipalName krb5Principal = null;
    194
    195         try {
    196             // Appends the default realm if it is missing
    197             krb5Principal  = new PrincipalName(name,nameType);
    198         } catch (KrbException e) {
    199             throw new IllegalArgumentException(e.getMessage());
    200         }
    201
    202         if (krb5Principal.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
    203             SecurityManager sm = System.getSecurityManager();
    204             if (sm != null) {
    205                 try {
    206                     sm.checkPermission(new ServicePermission(
    207                             "@" + krb5Principal.getRealmAsString(), "-"));
    208                 } catch (SecurityException se) {
    209                     // Swallow the actual exception to hide info
    210                     throw new SecurityException("Cannot read realm info");
    211                 }
    212             }
    213         }
    214         this.nameType = nameType;
    215         fullName = krb5Principal.toString();
    216         realm = krb5Principal.getRealmString();
    217     }
    218     /**
    219      * Returns the realm component of this Kerberos principal.
    220      *
    221      * @return the realm component of this Kerberos principal.
    222      */
    223     public String getRealm() {
    224         return realm;
    225     }
    226
    227     /**
    228      * Returns a hash code for this {@code KerberosPrincipal}. The hash code
    229      * is defined to be the result of the following calculation:
    230      * <pre>{@code
    231      *  hashCode = getName().hashCode();
    232      * }</pre>
    233      *
    234      * @return a hash code for this {@code KerberosPrincipal}.
    235      */
    236     public int hashCode() {
    237         return getName().hashCode();
    238     }
    239
    240     /**
    241      * Compares the specified object with this principal for equality.
    242      * Returns true if the given object is also a
    243      * {@code KerberosPrincipal} and the two
    244      * {@code KerberosPrincipal} instances are equivalent.
    245      * More formally two {@code KerberosPrincipal} instances are equal
    246      * if the values returned by {@code getName()} are equal.
    247      *
    248      * @param other the object to compare to
    249      * @return true if the object passed in represents the same principal
    250      * as this one, false otherwise.
    251      */
    252     public boolean equals(Object other) {
    253
    254         if (other == this)
    255             return true;
    256
    257         if (! (other instanceof KerberosPrincipal)) {
    258             return false;
    259         }
    260         String myFullName = getName();
    261         String otherFullName = ((KerberosPrincipal) other).getName();
    262         return myFullName.equals(otherFullName);
    263     }
    264
    265     /**
    266      * Save the {@code KerberosPrincipal} object to a stream
    267      *
    268      * @param  oos the {@code ObjectOutputStream} to which data is written
    269      * @throws IOException if an I/O error occurs
    270      *
    271      * @serialData this {@code KerberosPrincipal} is serialized
    272      *          by writing out the PrincipalName and the
    273      *          Realm in their DER-encoded form as specified in Section 5.2.2 of
    274      *          <a href=http://www.ietf.org/rfc/rfc4120.txt> RFC4120</a>.
    275      */
    276     private void writeObject(ObjectOutputStream oos)
    277             throws IOException {
    278
    279         PrincipalName krb5Principal;
    280         try {
    281             krb5Principal  = new PrincipalName(fullName, nameType);
    282             oos.writeObject(krb5Principal.asn1Encode());
    283             oos.writeObject(krb5Principal.getRealm().asn1Encode());
    284         } catch (Exception e) {
    285             throw new IOException(e);
    286         }
    287     }
    288
    289     /**
    290      * Reads this object from a stream (i.e., deserializes it)
    291      *
    292      * @param  ois the {@code ObjectInputStream} from which data is read
    293      * @throws IOException if an I/O error occurs
    294      * @throws ClassNotFoundException if a serialized class cannot be loaded
    295      */
    296     private void readObject(ObjectInputStream ois)
    297             throws IOException, ClassNotFoundException {
    298         byte[] asn1EncPrincipal = (byte [])ois.readObject();
    299         byte[] encRealm = (byte [])ois.readObject();
    300         try {
    301            Realm realmObject = new Realm(new DerValue(encRealm));
    302            PrincipalName krb5Principal = new PrincipalName(
    303                    new DerValue(asn1EncPrincipal), realmObject);
    304            realm = realmObject.toString();
    305            fullName = krb5Principal.toString();
    306            nameType = krb5Principal.getNameType();
    307         } catch (Exception e) {
    308             throw new IOException(e);
    309         }
    310     }
    311
    312     /**
    313      * The returned string corresponds to the single-string
    314      * representation of a Kerberos Principal name as specified in
    315      * Section 2.1 of <a href=http://www.ietf.org/rfc/rfc1964.txt>RFC 1964</a>.
    316      *
    317      * @return the principal name.
    318      */
    319     public String getName() {
    320         return fullName;
    321     }
    322
    323     /**
    324      * Returns the name type of the {@code KerberosPrincipal}. Valid name types
    325      * are specified in Section 6.2 of
    326      * <a href=http://www.ietf.org/rfc/rfc4120.txt> RFC4120</a>.
    327      *
    328      * @return the name type.
    329      */
    330     public int getNameType() {
    331         return nameType;
    332     }
    333
    334     /**
    335      * Returns an informative textual representation of this {@code KerberosPrincipal}.
    336      *
    337      * @return an informative textual representation of this {@code KerberosPrincipal}.
    338      */
    339     public String toString() {
    340         return getName();
    341     }
    342 }