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 }