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