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 26package javax.security.auth.kerberos; 27 28import java.io.IOException; 29import java.io.ObjectInputStream; 30import java.io.ObjectOutputStream; 31import java.io.ObjectStreamField; 32import java.security.BasicPermission; 33import java.security.Permission; 34import java.security.PermissionCollection; 35import java.util.*; 36import 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 67public 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 * This method returns true if this {@code DelegationPermission} 136 * is equal to {@code p}, and returns false otherwise. 137 * 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 @Override 144 public boolean implies(Permission p) { 145 return equals(p); 146 } 147 148 /** 149 * Checks two DelegationPermission objects for equality. 150 * 151 * @param obj the object to test for equality with this object. 152 * 153 * @return true if {@code obj} is a DelegationPermission, and 154 * has the same subordinate and service principal as this 155 * DelegationPermission object. 156 */ 157 @Override 158 public boolean equals(Object obj) { 159 if (obj == this) { 160 return true; 161 } 162 163 if (!(obj instanceof DelegationPermission)) { 164 return false; 165 } 166 167 DelegationPermission that = (DelegationPermission) obj; 168 169 return this.subordinate.equals(that.subordinate) && 170 this.service.equals(that.service); 171 } 172 173 /** 174 * Returns the hash code value for this object. 175 * 176 * @return a hash code value for this object. 177 */ 178 @Override 179 public int hashCode() { 180 return 17 * subordinate.hashCode() + 31 * service.hashCode(); 181 } 182 183 /** 184 * Returns a PermissionCollection object for storing 185 * DelegationPermission objects. 186 * <br> 187 * DelegationPermission objects must be stored in a manner that 188 * allows them to be inserted into the collection in any order, but 189 * that also enables the PermissionCollection implies method to 190 * be implemented in an efficient (and consistent) manner. 191 * 192 * @return a new PermissionCollection object suitable for storing 193 * DelegationPermissions. 194 */ 195 @Override 196 public PermissionCollection newPermissionCollection() { 197 return new KrbDelegationPermissionCollection(); 198 } 199 200 /** 201 * WriteObject is called to save the state of the DelegationPermission 202 * to a stream. The actions are serialized, and the superclass 203 * takes care of the name. 204 */ 205 private synchronized void writeObject(java.io.ObjectOutputStream s) 206 throws IOException 207 { 208 s.defaultWriteObject(); 209 } 210 211 /** 212 * readObject is called to restore the state of the 213 * DelegationPermission from a stream. 214 */ 215 private synchronized void readObject(java.io.ObjectInputStream s) 216 throws IOException, ClassNotFoundException 217 { 218 // Read in the action, then initialize the rest 219 s.defaultReadObject(); 220 init(getName()); 221 } 222 223} 224 225 226final class KrbDelegationPermissionCollection extends PermissionCollection 227 implements java.io.Serializable { 228 229 // Not serialized; see serialization section at end of class. 230 private transient ConcurrentHashMap<Permission, Boolean> perms; 231 232 public KrbDelegationPermissionCollection() { 233 perms = new ConcurrentHashMap<>(); 234 } 235 236 /** 237 * Check and see if this collection of permissions implies the permissions 238 * expressed in "permission". 239 * 240 * @param permission the Permission object to compare 241 * 242 * @return true if "permission" is a proper subset of a permission in 243 * the collection, false if not. 244 */ 245 @Override 246 public boolean implies(Permission permission) { 247 if (! (permission instanceof DelegationPermission)) 248 return false; 249 250 // if map contains key, then it automatically implies it 251 return perms.containsKey(permission); 252 } 253 254 /** 255 * Adds a permission to the DelegationPermissions. The key for 256 * the hash is the name. 257 * 258 * @param permission the Permission object to add. 259 * 260 * @exception IllegalArgumentException - if the permission is not a 261 * DelegationPermission 262 * 263 * @exception SecurityException - if this PermissionCollection object 264 * has been marked readonly 265 */ 266 @Override 267 public void add(Permission permission) { 268 if (! (permission instanceof DelegationPermission)) 269 throw new IllegalArgumentException("invalid permission: "+ 270 permission); 271 if (isReadOnly()) 272 throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection"); 273 274 perms.put(permission, Boolean.TRUE); 275 } 276 277 /** 278 * Returns an enumeration of all the DelegationPermission objects 279 * in the container. 280 * 281 * @return an enumeration of all the DelegationPermission objects. 282 */ 283 @Override 284 public Enumeration<Permission> elements() { 285 return perms.keys(); 286 } 287 288 private static final long serialVersionUID = -3383936936589966948L; 289 290 // Need to maintain serialization interoperability with earlier releases, 291 // which had the serializable field: 292 // private Vector permissions; 293 /** 294 * @serialField permissions java.util.Vector 295 * A list of DelegationPermission objects. 296 */ 297 private static final ObjectStreamField[] serialPersistentFields = { 298 new ObjectStreamField("permissions", Vector.class), 299 }; 300 301 /** 302 * @serialData "permissions" field (a Vector containing the DelegationPermissions). 303 */ 304 /* 305 * Writes the contents of the perms field out as a Vector for 306 * serialization compatibility with earlier releases. 307 */ 308 private void writeObject(ObjectOutputStream out) throws IOException { 309 // Don't call out.defaultWriteObject() 310 311 // Write out Vector 312 Vector<Permission> permissions = new Vector<>(perms.keySet()); 313 314 ObjectOutputStream.PutField pfields = out.putFields(); 315 pfields.put("permissions", permissions); 316 out.writeFields(); 317 } 318 319 /* 320 * Reads in a Vector of DelegationPermissions and saves them in the perms field. 321 */ 322 @SuppressWarnings("unchecked") 323 private void readObject(ObjectInputStream in) 324 throws IOException, ClassNotFoundException 325 { 326 // Don't call defaultReadObject() 327 328 // Read in serialized fields 329 ObjectInputStream.GetField gfields = in.readFields(); 330 331 // Get the one we want 332 Vector<Permission> permissions = 333 (Vector<Permission>)gfields.get("permissions", null); 334 perms = new ConcurrentHashMap<>(permissions.size()); 335 for (Permission perm : permissions) { 336 perms.put(perm, Boolean.TRUE); 337 } 338 } 339} 340