1 /*
   2  * Copyright (c) 2000, 2011, 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</code> 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  * <p>
  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  * <p>
  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</code>
  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</code> is <code>null</code>.
  82      * @throws IllegalArgumentException if <code>principals</code> is empty.
  83      */
  84     public DelegationPermission(String principals) {
  85         super(principals);
  86         init(principals);
  87     }
  88 
  89     /**
  90      * Create a new <code>DelegationPermission</code>
  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</code> is <code>null</code>.
  99      * @throws IllegalArgumentException if <code>principals</code> 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</code> 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 
 182     public int hashCode() {
 183         return getName().hashCode();
 184     }
 185 
 186 
 187     /**
 188      * Returns a PermissionCollection object for storing
 189      * DelegationPermission objects.
 190      * <br>
 191      * DelegationPermission objects must be stored in a manner that
 192      * allows them to be inserted into the collection in any order, but
 193      * that also enables the PermissionCollection implies method to
 194      * be implemented in an efficient (and consistent) manner.
 195      *
 196      * @return a new PermissionCollection object suitable for storing
 197      * DelegationPermissions.
 198      */
 199 
 200     public PermissionCollection newPermissionCollection() {
 201         return new KrbDelegationPermissionCollection();
 202     }
 203 
 204     /**
 205      * WriteObject is called to save the state of the DelegationPermission
 206      * to a stream. The actions are serialized, and the superclass
 207      * takes care of the name.
 208      */
 209     private synchronized void writeObject(java.io.ObjectOutputStream s)
 210         throws IOException
 211     {
 212         s.defaultWriteObject();
 213     }
 214 
 215     /**
 216      * readObject is called to restore the state of the
 217      * DelegationPermission from a stream.
 218      */
 219     private synchronized void readObject(java.io.ObjectInputStream s)
 220          throws IOException, ClassNotFoundException
 221     {
 222         // Read in the action, then initialize the rest
 223         s.defaultReadObject();
 224         init(getName());
 225     }
 226 
 227     /*
 228       public static void main(String args[]) throws Exception {
 229       DelegationPermission this_ =
 230       new DelegationPermission(args[0]);
 231       DelegationPermission that_ =
 232       new DelegationPermission(args[1]);
 233       System.out.println("-----\n");
 234       System.out.println("this.implies(that) = " + this_.implies(that_));
 235       System.out.println("-----\n");
 236       System.out.println("this = "+this_);
 237       System.out.println("-----\n");
 238       System.out.println("that = "+that_);
 239       System.out.println("-----\n");
 240 
 241       KrbDelegationPermissionCollection nps =
 242       new KrbDelegationPermissionCollection();
 243       nps.add(this_);
 244       nps.add(new DelegationPermission("\"host/foo.example.com@EXAMPLE.COM\" \"CN=Gary Ellison/OU=JSN/O=SUNW/L=Palo Alto/ST=CA/C=US\""));
 245       try {
 246       nps.add(new DelegationPermission("host/foo.example.com@EXAMPLE.COM \"CN=Gary Ellison/OU=JSN/O=SUNW/L=Palo Alto/ST=CA/C=US\""));
 247       } catch (Exception e) {
 248       System.err.println(e);
 249       }
 250 
 251       System.out.println("nps.implies(that) = " + nps.implies(that_));
 252       System.out.println("-----\n");
 253 
 254       Enumeration e = nps.elements();
 255 
 256       while (e.hasMoreElements()) {
 257       DelegationPermission x =
 258       (DelegationPermission) e.nextElement();
 259       System.out.println("nps.e = " + x);
 260       }
 261       }
 262     */
 263 }
 264 
 265 
 266 final class KrbDelegationPermissionCollection extends PermissionCollection
 267     implements java.io.Serializable {
 268 
 269     // Not serialized; see serialization section at end of class.
 270     private transient List<Permission> perms;
 271 
 272     public KrbDelegationPermissionCollection() {
 273         perms = new ArrayList<Permission>();
 274     }
 275 
 276 
 277     /**
 278      * Check and see if this collection of permissions implies the permissions
 279      * expressed in "permission".
 280      *
 281      * @param p the Permission object to compare
 282      *
 283      * @return true if "permission" is a proper subset of a permission in
 284      * the collection, false if not.
 285      */
 286 
 287     public boolean implies(Permission permission) {
 288         if (! (permission instanceof DelegationPermission))
 289                 return false;
 290 
 291         synchronized (this) {
 292             for (Permission x : perms) {
 293                 if (x.implies(permission))
 294                     return true;
 295             }
 296         }
 297         return false;
 298 
 299     }
 300 
 301     /**
 302      * Adds a permission to the DelegationPermissions. The key for
 303      * the hash is the name.
 304      *
 305      * @param permission the Permission object to add.
 306      *
 307      * @exception IllegalArgumentException - if the permission is not a
 308      *                                       DelegationPermission
 309      *
 310      * @exception SecurityException - if this PermissionCollection object
 311      *                                has been marked readonly
 312      */
 313 
 314     public void add(Permission permission) {
 315         if (! (permission instanceof DelegationPermission))
 316             throw new IllegalArgumentException("invalid permission: "+
 317                                                permission);
 318         if (isReadOnly())
 319             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
 320 
 321         synchronized (this) {
 322             perms.add(0, permission);
 323         }
 324     }
 325 
 326     /**
 327      * Returns an enumeration of all the DelegationPermission objects
 328      * in the container.
 329      *
 330      * @return an enumeration of all the DelegationPermission objects.
 331      */
 332 
 333     public Enumeration<Permission> elements() {
 334         // Convert Iterator into Enumeration
 335         synchronized (this) {
 336             return Collections.enumeration(perms);
 337         }
 338     }
 339 
 340     private static final long serialVersionUID = -3383936936589966948L;
 341 
 342     // Need to maintain serialization interoperability with earlier releases,
 343     // which had the serializable field:
 344     //    private Vector permissions;
 345     /**
 346      * @serialField permissions java.util.Vector
 347      *     A list of DelegationPermission objects.
 348      */
 349     private static final ObjectStreamField[] serialPersistentFields = {
 350         new ObjectStreamField("permissions", Vector.class),
 351     };
 352 
 353     /**
 354      * @serialData "permissions" field (a Vector containing the DelegationPermissions).
 355      */
 356     /*
 357      * Writes the contents of the perms field out as a Vector for
 358      * serialization compatibility with earlier releases.
 359      */
 360     private void writeObject(ObjectOutputStream out) throws IOException {
 361         // Don't call out.defaultWriteObject()
 362 
 363         // Write out Vector
 364         Vector<Permission> permissions = new Vector<>(perms.size());
 365 
 366         synchronized (this) {
 367             permissions.addAll(perms);
 368         }
 369 
 370         ObjectOutputStream.PutField pfields = out.putFields();
 371         pfields.put("permissions", permissions);
 372         out.writeFields();
 373     }
 374 
 375     /*
 376      * Reads in a Vector of DelegationPermissions and saves them in the perms field.
 377      */
 378     @SuppressWarnings("unchecked")
 379     private void readObject(ObjectInputStream in) throws IOException,
 380     ClassNotFoundException {
 381         // Don't call defaultReadObject()
 382 
 383         // Read in serialized fields
 384         ObjectInputStream.GetField gfields = in.readFields();
 385 
 386         // Get the one we want
 387         Vector<Permission> permissions =
 388                 (Vector<Permission>)gfields.get("permissions", null);
 389         perms = new ArrayList<Permission>(permissions.size());
 390         perms.addAll(permissions);
 391     }
 392 }