1 /*
   2  * Copyright (c) 2000, 2013, 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.util.*;
  29 import java.security.Permission;
  30 import java.security.BasicPermission;
  31 import java.security.PermissionCollection;
  32 import java.io.ObjectStreamField;
  33 import java.io.ObjectOutputStream;
  34 import java.io.ObjectInputStream;
  35 import java.io.IOException;
  36 
  37 /**
  38  * This class is used to restrict the usage of the Kerberos
  39  * delegation model, ie: forwardable and proxiable tickets.
  40  * <p>
  41  * The target name of this {@code Permission} specifies a pair of
  42  * kerberos service principals. The first is the subordinate service principal
  43  * being entrusted to use the TGT. The second service principal designates
  44  * the target service the subordinate service principal is to
  45  * interact with on behalf of the initiating KerberosPrincipal. This
  46  * latter service principal is specified to restrict the use of a
  47  * proxiable ticket.
  48  * <p>
  49  * For example, to specify the "host" service use of a forwardable TGT the
  50  * target permission is specified as follows:
  51  *
  52  * <pre>
  53  *  DelegationPermission("\"host/foo.example.com@EXAMPLE.COM\" \"krbtgt/EXAMPLE.COM@EXAMPLE.COM\"");
  54  * </pre>
  55  * <p>
  56  * To give the "backup" service a proxiable nfs service ticket the target permission
  57  * might be specified:
  58  *
  59  * <pre>
  60  *  DelegationPermission("\"backup/bar.example.com@EXAMPLE.COM\" \"nfs/home.EXAMPLE.COM@EXAMPLE.COM\"");
  61  * </pre>
  62  *
  63  * @since 1.4
  64  */
  65 
  66 public final class DelegationPermission extends BasicPermission
  67     implements java.io.Serializable {
  68 
  69     private static final long serialVersionUID = 883133252142523922L;
  70 
  71     private transient String subordinate, service;
  72 
  73     /**
  74      * Create a new {@code DelegationPermission}
  75      * with the specified subordinate and target principals.
  76      *
  77      * <p>
  78      *
  79      * @param principals the name of the subordinate and target principals
  80      *
  81      * @throws NullPointerException if {@code principals} is {@code null}.
  82      * @throws IllegalArgumentException if {@code principals} is empty.
  83      */
  84     public DelegationPermission(String principals) {
  85         super(principals);
  86         init(principals);
  87     }
  88 
  89     /**
  90      * Create a new {@code DelegationPermission}
  91      * with the specified subordinate and target principals.
  92      * <p>
  93      *
  94      * @param principals the name of the subordinate and target principals
  95      * <p>
  96      * @param actions should be null.
  97      *
  98      * @throws NullPointerException if {@code principals} is {@code null}.
  99      * @throws IllegalArgumentException if {@code principals} is empty.
 100      */
 101     public DelegationPermission(String principals, String actions) {
 102         super(principals, actions);
 103         init(principals);
 104     }
 105 
 106 
 107     /**
 108      * Initialize the DelegationPermission object.
 109      */
 110     private void init(String target) {
 111 
 112         StringTokenizer t = null;
 113         if (!target.startsWith("\"")) {
 114             throw new IllegalArgumentException
 115                 ("service principal [" + target +
 116                  "] syntax invalid: " +
 117                  "improperly quoted");
 118         } else {
 119             t = new StringTokenizer(target, "\"", false);
 120             subordinate = t.nextToken();
 121             if (t.countTokens() == 2) {
 122                 t.nextToken();  // bypass whitespace
 123                 service = t.nextToken();
 124             } else if (t.countTokens() > 0) {
 125                 throw new IllegalArgumentException
 126                     ("service principal [" + t.nextToken() +
 127                      "] syntax invalid: " +
 128                      "improperly quoted");
 129             }
 130         }
 131     }
 132 
 133     /**
 134      * Checks if this Kerberos delegation permission object "implies" the
 135      * specified permission.
 136      * <P>
 137      * If none of the above are true, {@code implies} returns false.
 138      * @param p the permission to check against.
 139      *
 140      * @return true if the specified permission is implied by this object,
 141      * false if not.
 142      */
 143     public boolean implies(Permission p) {
 144         if (!(p instanceof DelegationPermission))
 145             return false;
 146 
 147         DelegationPermission that = (DelegationPermission) p;
 148         if (this.subordinate.equals(that.subordinate) &&
 149             this.service.equals(that.service))
 150             return true;
 151 
 152         return false;
 153     }
 154 
 155 
 156     /**
 157      * Checks two DelegationPermission objects for equality.
 158      * <P>
 159      * @param obj the object to test for equality with this object.
 160      *
 161      * @return true if <i>obj</i> is a DelegationPermission, and
 162      *  has the same subordinate and service principal as this.
 163      *  DelegationPermission object.
 164      */
 165     public boolean equals(Object obj) {
 166         if (obj == this)
 167             return true;
 168 
 169         if (! (obj instanceof DelegationPermission))
 170             return false;
 171 
 172         DelegationPermission that = (DelegationPermission) obj;
 173         return implies(that);
 174     }
 175 
 176     /**
 177      * Returns the hash code value for this object.
 178      *
 179      * @return a hash code value for this object.
 180      */
 181     public int hashCode() {
 182         return getName().hashCode();
 183     }
 184 
 185 
 186     /**
 187      * Returns a PermissionCollection object for storing
 188      * DelegationPermission objects.
 189      * <br>
 190      * DelegationPermission objects must be stored in a manner that
 191      * allows them to be inserted into the collection in any order, but
 192      * that also enables the PermissionCollection implies method to
 193      * be implemented in an efficient (and consistent) manner.
 194      *
 195      * @return a new PermissionCollection object suitable for storing
 196      * DelegationPermissions.
 197      */
 198 
 199     public PermissionCollection newPermissionCollection() {
 200         return new KrbDelegationPermissionCollection();
 201     }
 202 
 203     /**
 204      * WriteObject is called to save the state of the DelegationPermission
 205      * to a stream. The actions are serialized, and the superclass
 206      * takes care of the name.
 207      */
 208     private synchronized void writeObject(java.io.ObjectOutputStream s)
 209         throws IOException
 210     {
 211         s.defaultWriteObject();
 212     }
 213 
 214     /**
 215      * readObject is called to restore the state of the
 216      * DelegationPermission from a stream.
 217      */
 218     private synchronized void readObject(java.io.ObjectInputStream s)
 219          throws IOException, ClassNotFoundException
 220     {
 221         // Read in the action, then initialize the rest
 222         s.defaultReadObject();
 223         init(getName());
 224     }
 225 
 226     /*
 227       public static void main(String args[]) throws Exception {
 228       DelegationPermission this_ =
 229       new DelegationPermission(args[0]);
 230       DelegationPermission that_ =
 231       new DelegationPermission(args[1]);
 232       System.out.println("-----\n");
 233       System.out.println("this.implies(that) = " + this_.implies(that_));
 234       System.out.println("-----\n");
 235       System.out.println("this = "+this_);
 236       System.out.println("-----\n");
 237       System.out.println("that = "+that_);
 238       System.out.println("-----\n");
 239 
 240       KrbDelegationPermissionCollection nps =
 241       new KrbDelegationPermissionCollection();
 242       nps.add(this_);
 243       nps.add(new DelegationPermission("\"host/foo.example.com@EXAMPLE.COM\" \"CN=Gary Ellison/OU=JSN/O=SUNW/L=Palo Alto/ST=CA/C=US\""));
 244       try {
 245       nps.add(new DelegationPermission("host/foo.example.com@EXAMPLE.COM \"CN=Gary Ellison/OU=JSN/O=SUNW/L=Palo Alto/ST=CA/C=US\""));
 246       } catch (Exception e) {
 247       System.err.println(e);
 248       }
 249 
 250       System.out.println("nps.implies(that) = " + nps.implies(that_));
 251       System.out.println("-----\n");
 252 
 253       Enumeration e = nps.elements();
 254 
 255       while (e.hasMoreElements()) {
 256       DelegationPermission x =
 257       (DelegationPermission) e.nextElement();
 258       System.out.println("nps.e = " + x);
 259       }
 260       }
 261     */
 262 }
 263 
 264 
 265 final class KrbDelegationPermissionCollection extends PermissionCollection
 266     implements java.io.Serializable {
 267 
 268     // Not serialized; see serialization section at end of class.
 269     private transient List<Permission> perms;
 270 
 271     public KrbDelegationPermissionCollection() {
 272         perms = new ArrayList<Permission>();
 273     }
 274 
 275 
 276     /**
 277      * Check and see if this collection of permissions implies the permissions
 278      * expressed in "permission".
 279      *
 280      * @param permission the Permission object to compare
 281      *
 282      * @return true if "permission" is a proper subset of a permission in
 283      * the collection, false if not.
 284      */
 285     public boolean implies(Permission permission) {
 286         if (! (permission instanceof DelegationPermission))
 287                 return false;
 288 
 289         synchronized (this) {
 290             for (Permission x : perms) {
 291                 if (x.implies(permission))
 292                     return true;
 293             }
 294         }
 295         return false;
 296 
 297     }
 298 
 299     /**
 300      * Adds a permission to the DelegationPermissions. The key for
 301      * the hash is the name.
 302      *
 303      * @param permission the Permission object to add.
 304      *
 305      * @exception IllegalArgumentException - if the permission is not a
 306      *                                       DelegationPermission
 307      *
 308      * @exception SecurityException - if this PermissionCollection object
 309      *                                has been marked readonly
 310      */
 311     public void add(Permission permission) {
 312         if (! (permission instanceof DelegationPermission))
 313             throw new IllegalArgumentException("invalid permission: "+
 314                                                permission);
 315         if (isReadOnly())
 316             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
 317 
 318         synchronized (this) {
 319             perms.add(0, permission);
 320         }
 321     }
 322 
 323     /**
 324      * Returns an enumeration of all the DelegationPermission objects
 325      * in the container.
 326      *
 327      * @return an enumeration of all the DelegationPermission objects.
 328      */
 329     public Enumeration<Permission> elements() {
 330         // Convert Iterator into Enumeration
 331         synchronized (this) {
 332             return Collections.enumeration(perms);
 333         }
 334     }
 335 
 336     private static final long serialVersionUID = -3383936936589966948L;
 337 
 338     // Need to maintain serialization interoperability with earlier releases,
 339     // which had the serializable field:
 340     //    private Vector permissions;
 341     /**
 342      * @serialField permissions java.util.Vector
 343      *     A list of DelegationPermission objects.
 344      */
 345     private static final ObjectStreamField[] serialPersistentFields = {
 346         new ObjectStreamField("permissions", Vector.class),
 347     };
 348 
 349     /**
 350      * @serialData "permissions" field (a Vector containing the DelegationPermissions).
 351      */
 352     /*
 353      * Writes the contents of the perms field out as a Vector for
 354      * serialization compatibility with earlier releases.
 355      */
 356     private void writeObject(ObjectOutputStream out) throws IOException {
 357         // Don't call out.defaultWriteObject()
 358 
 359         // Write out Vector
 360         Vector<Permission> permissions = new Vector<>(perms.size());
 361 
 362         synchronized (this) {
 363             permissions.addAll(perms);
 364         }
 365 
 366         ObjectOutputStream.PutField pfields = out.putFields();
 367         pfields.put("permissions", permissions);
 368         out.writeFields();
 369     }
 370 
 371     /*
 372      * Reads in a Vector of DelegationPermissions and saves them in the perms field.
 373      */
 374     @SuppressWarnings("unchecked")
 375     private void readObject(ObjectInputStream in)
 376         throws IOException, ClassNotFoundException
 377     {
 378         // Don't call defaultReadObject()
 379 
 380         // Read in serialized fields
 381         ObjectInputStream.GetField gfields = in.readFields();
 382 
 383         // Get the one we want
 384         Vector<Permission> permissions =
 385                 (Vector<Permission>)gfields.get("permissions", null);
 386         perms = new ArrayList<Permission>(permissions.size());
 387         perms.addAll(permissions);
 388     }
 389 }