1 /*
   2  * Copyright (c) 2000, 2015, 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.IOException;
  29 import java.io.ObjectInputStream;
  30 import java.io.ObjectOutputStream;
  31 import java.io.ObjectStreamField;
  32 import java.security.BasicPermission;
  33 import java.security.Permission;
  34 import java.security.PermissionCollection;
  35 import java.util.*;
  36 import java.util.concurrent.ConcurrentHashMap;
  37 
  38 /**
  39  * This class is used to restrict the usage of the Kerberos
  40  * delegation model, ie: forwardable and proxiable tickets.
  41  * <p>
  42  * The target name of this {@code Permission} specifies a pair of
  43  * kerberos service principals. The first is the subordinate service principal
  44  * being entrusted to use the TGT. The second service principal designates
  45  * the target service the subordinate service principal is to
  46  * interact with on behalf of the initiating KerberosPrincipal. This
  47  * latter service principal is specified to restrict the use of a
  48  * proxiable ticket.
  49  * <p>
  50  * For example, to specify the "host" service use of a forwardable TGT the
  51  * target permission is specified as follows:
  52  *
  53  * <pre>
  54  *  DelegationPermission("\"host/foo.example.com@EXAMPLE.COM\" \"krbtgt/EXAMPLE.COM@EXAMPLE.COM\"");
  55  * </pre>
  56  * <p>
  57  * To give the "backup" service a proxiable nfs service ticket the target permission
  58  * might be specified:
  59  *
  60  * <pre>
  61  *  DelegationPermission("\"backup/bar.example.com@EXAMPLE.COM\" \"nfs/home.EXAMPLE.COM@EXAMPLE.COM\"");
  62  * </pre>
  63  *
  64  * @since 1.4
  65  */
  66 
  67 public final class DelegationPermission extends BasicPermission
  68     implements java.io.Serializable {
  69 
  70     private static final long serialVersionUID = 883133252142523922L;
  71 
  72     private transient String subordinate, service;
  73 
  74     /**
  75      * Create a new {@code DelegationPermission}
  76      * with the specified subordinate and target principals.
  77      *
  78      * @param principals the name of the subordinate and target principals
  79      *
  80      * @throws NullPointerException if {@code principals} is {@code null}.
  81      * @throws IllegalArgumentException if {@code principals} is empty.
  82      */
  83     public DelegationPermission(String principals) {
  84         super(principals);
  85         init(principals);
  86     }
  87 
  88     /**
  89      * Create a new {@code DelegationPermission}
  90      * with the specified subordinate and target principals.
  91      *
  92      * @param principals the name of the subordinate and target principals
  93      *
  94      * @param actions should be null.
  95      *
  96      * @throws NullPointerException if {@code principals} is {@code null}.
  97      * @throws IllegalArgumentException if {@code principals} is empty.
  98      */
  99     public DelegationPermission(String principals, String actions) {
 100         super(principals, actions);
 101         init(principals);
 102     }
 103 
 104 
 105     /**
 106      * Initialize the DelegationPermission object.
 107      */
 108     private void init(String target) {
 109 
 110         StringTokenizer t = null;
 111         if (!target.startsWith("\"")) {
 112             throw new IllegalArgumentException
 113                 ("service principal [" + target +
 114                  "] syntax invalid: " +
 115                  "improperly quoted");
 116         } else {
 117             t = new StringTokenizer(target, "\"", false);
 118             subordinate = t.nextToken();
 119             if (t.countTokens() == 2) {
 120                 t.nextToken();  // bypass whitespace
 121                 service = t.nextToken();
 122             } else if (t.countTokens() > 0) {
 123                 throw new IllegalArgumentException
 124                     ("service principal [" + t.nextToken() +
 125                      "] syntax invalid: " +
 126                      "improperly quoted");
 127             }
 128         }
 129     }
 130 
 131     /**
 132      * Checks if this Kerberos delegation permission object "implies" the
 133      * specified permission.
 134      * <P>
 135      * If none of the above are true, {@code implies} returns false.
 136      * @param p the permission to check against.
 137      *
 138      * @return true if the specified permission is implied by this object,
 139      * false if not.
 140      */
 141     @Override
 142     public boolean implies(Permission p) {
 143         if (!(p instanceof DelegationPermission))
 144             return false;
 145 
 146         DelegationPermission that = (DelegationPermission) p;
 147         if (this.subordinate.equals(that.subordinate) &&
 148             this.service.equals(that.service))
 149             return true;
 150 
 151         return false;
 152     }
 153 
 154 
 155     /**
 156      * Checks two DelegationPermission objects for equality.
 157      *
 158      * @param obj the object to test for equality with this object.
 159      *
 160      * @return true if {@code obj} is a DelegationPermission, and
 161      *  has the same subordinate and service principal as this.
 162      *  DelegationPermission object.
 163      */
 164     @Override
 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     @Override
 182     public int hashCode() {
 183         return getName().hashCode();
 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     @Override
 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 ConcurrentHashMap<Permission, Boolean> perms;
 270 
 271     public KrbDelegationPermissionCollection() {
 272         perms = new ConcurrentHashMap<>();
 273     }
 274 
 275     /**
 276      * Check and see if this collection of permissions implies the permissions
 277      * expressed in "permission".
 278      *
 279      * @param permission the Permission object to compare
 280      *
 281      * @return true if "permission" is a proper subset of a permission in
 282      * the collection, false if not.
 283      */
 284     @Override
 285     public boolean implies(Permission permission) {
 286         if (! (permission instanceof DelegationPermission))
 287             return false;
 288 
 289         // if map contains key, then it automatically implies it
 290         return perms.containsKey(permission);
 291     }
 292 
 293     /**
 294      * Adds a permission to the DelegationPermissions. The key for
 295      * the hash is the name.
 296      *
 297      * @param permission the Permission object to add.
 298      *
 299      * @exception IllegalArgumentException - if the permission is not a
 300      *                                       DelegationPermission
 301      *
 302      * @exception SecurityException - if this PermissionCollection object
 303      *                                has been marked readonly
 304      */
 305     @Override
 306     public void add(Permission permission) {
 307         if (! (permission instanceof DelegationPermission))
 308             throw new IllegalArgumentException("invalid permission: "+
 309                                                permission);
 310         if (isReadOnly())
 311             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
 312 
 313         perms.put(permission, Boolean.TRUE);
 314     }
 315 
 316     /**
 317      * Returns an enumeration of all the DelegationPermission objects
 318      * in the container.
 319      *
 320      * @return an enumeration of all the DelegationPermission objects.
 321      */
 322     @Override
 323     public Enumeration<Permission> elements() {
 324         return perms.keys();
 325     }
 326 
 327     private static final long serialVersionUID = -3383936936589966948L;
 328 
 329     // Need to maintain serialization interoperability with earlier releases,
 330     // which had the serializable field:
 331     //    private Vector permissions;
 332     /**
 333      * @serialField permissions java.util.Vector
 334      *     A list of DelegationPermission objects.
 335      */
 336     private static final ObjectStreamField[] serialPersistentFields = {
 337         new ObjectStreamField("permissions", Vector.class),
 338     };
 339 
 340     /**
 341      * @serialData "permissions" field (a Vector containing the DelegationPermissions).
 342      */
 343     /*
 344      * Writes the contents of the perms field out as a Vector for
 345      * serialization compatibility with earlier releases.
 346      */
 347     private void writeObject(ObjectOutputStream out) throws IOException {
 348         // Don't call out.defaultWriteObject()
 349 
 350         // Write out Vector
 351         Vector<Permission> permissions = new Vector<>(perms.keySet());
 352 
 353         ObjectOutputStream.PutField pfields = out.putFields();
 354         pfields.put("permissions", permissions);
 355         out.writeFields();
 356     }
 357 
 358     /*
 359      * Reads in a Vector of DelegationPermissions and saves them in the perms field.
 360      */
 361     @SuppressWarnings("unchecked")
 362     private void readObject(ObjectInputStream in)
 363         throws IOException, ClassNotFoundException
 364     {
 365         // Don't call defaultReadObject()
 366 
 367         // Read in serialized fields
 368         ObjectInputStream.GetField gfields = in.readFields();
 369 
 370         // Get the one we want
 371         Vector<Permission> permissions =
 372             (Vector<Permission>)gfields.get("permissions", null);
 373         perms = new ConcurrentHashMap<>(permissions.size());
 374         for (Permission perm : permissions) {
 375             perms.put(perm, Boolean.TRUE);
 376         }
 377     }
 378 }