1 /* 2 * Copyright (c) 1999, 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; 27 28 import java.util; 29 import java.text.MessageFormat; 30 import java.security.Permission; 31 import java.security.PermissionCollection; 32 import java.security.Principal; 33 import sun.security.util.ResourcesMgr; 34 35 /** 36 * This class is used to protect access to private Credentials 37 * belonging to a particular <code>Subject</code>. The <code>Subject</code> 38 * is represented by a Set of Principals. 39 * 40 * <p> The target name of this <code>Permission</code> specifies 41 * a Credential class name, and a Set of Principals. 42 * The only valid value for this Permission's actions is, "read". 43 * The target name must abide by the following syntax: 44 * 45 * <pre> 46 * CredentialClass {PrincipalClass "PrincipalName"}* 47 * </pre> 48 * 49 * For example, the following permission grants access to the 50 * com.sun.PrivateCredential owned by Subjects which have 51 * a com.sun.Principal with the name, "duke". Note that although 52 * this example, as well as all the examples below, do not contain 53 * Codebase, SignedBy, or Principal information in the grant statement 54 * (for simplicity reasons), actual policy configurations should 55 * specify that information when appropriate. 56 * 57 * <pre> 58 * 59 * grant { 60 * permission javax.security.auth.PrivateCredentialPermission 61 * "com.sun.PrivateCredential com.sun.Principal \"duke\"", 62 * "read"; 63 * }; 64 * </pre> 65 * 66 * If CredentialClass is "*", then access is granted to 67 * all private Credentials belonging to the specified 68 * <code>Subject</code>. 69 * If "PrincipalName" is "*", then access is granted to the 70 * specified Credential owned by any <code>Subject</code> that has the 71 * specified <code>Principal</code> (the actual PrincipalName doesn't matter). 72 * For example, the following grants access to the 73 * a.b.Credential owned by any <code>Subject</code> that has 74 * an a.b.Principal. 75 * 76 * <pre> 77 * grant { 78 * permission javax.security.auth.PrivateCredentialPermission 79 * "a.b.Credential a.b.Principal "*"", 80 * "read"; 81 * }; 82 * </pre> 83 * 84 * If both the PrincipalClass and "PrincipalName" are "*", 85 * then access is granted to the specified Credential owned by 86 * any <code>Subject</code>. 87 * 88 * <p> In addition, the PrincipalClass/PrincipalName pairing may be repeated: 89 * 90 * <pre> 91 * grant { 92 * permission javax.security.auth.PrivateCredentialPermission 93 * "a.b.Credential a.b.Principal "duke" c.d.Principal "dukette"", 94 * "read"; 95 * }; 96 * </pre> 97 * 98 * The above grants access to the private Credential, "a.b.Credential", 99 * belonging to a <code>Subject</code> with at least two associated Principals: 100 * "a.b.Principal" with the name, "duke", and "c.d.Principal", with the name, 101 * "dukette". 102 * 103 */ 104 public final class PrivateCredentialPermission extends Permission { 105 106 private static final long serialVersionUID = 5284372143517237068L; 107 108 private static final CredOwner[] EMPTY_PRINCIPALS = new CredOwner[0]; 109 110 /** 111 * @serial 112 */ 113 private String credentialClass; 114 115 /** 116 * @serial The Principals associated with this permission. 117 * The set contains elements of type, 118 * <code>PrivateCredentialPermission.CredOwner</code>. 119 */ 120 private Set principals; // ignored - kept around for compatibility 121 private transient CredOwner[] credOwners; 122 123 /** 124 * @serial 125 */ 126 private boolean testing = false; 127 128 /** 129 * Create a new <code>PrivateCredentialPermission</code> 130 * with the specified <code>credentialClass</code> and Principals. 131 */ 132 PrivateCredentialPermission(String credentialClass, 133 Set<Principal> principals) { 134 135 super(credentialClass); 136 this.credentialClass = credentialClass; 137 138 synchronized(principals) { 139 if (principals.size() == 0) { 140 this.credOwners = EMPTY_PRINCIPALS; 141 } else { 142 this.credOwners = new CredOwner[principals.size()]; 143 int index = 0; 144 Iterator<Principal> i = principals.iterator(); 145 while (i.hasNext()) { 146 Principal p = i.next(); 147 this.credOwners[index++] = new CredOwner 148 (p.getClass().getName(), 149 p.getName()); 150 } 151 } 152 } 153 } 154 155 /** 156 * Creates a new <code>PrivateCredentialPermission</code> 157 * with the specified <code>name</code>. The <code>name</code> 158 * specifies both a Credential class and a <code>Principal</code> Set. 159 * 160 * <p> 161 * 162 * @param name the name specifying the Credential class and 163 * <code>Principal</code> Set. <p> 164 * 165 * @param actions the actions specifying that the Credential can be read. 166 * 167 * @throws IllegalArgumentException if <code>name</code> does not conform 168 * to the correct syntax or if <code>actions</code> is not "read". 169 */ 170 public PrivateCredentialPermission(String name, String actions) { 171 super(name); 172 173 if (!"read".equalsIgnoreCase(actions)) 174 throw new IllegalArgumentException 175 (ResourcesMgr.getString("actions.can.only.be.read.")); 176 init(name); 177 } 178 179 /** 180 * Returns the Class name of the Credential associated with this 181 * <code>PrivateCredentialPermission</code>. 182 * 183 * <p> 184 * 185 * @return the Class name of the Credential associated with this 186 * <code>PrivateCredentialPermission</code>. 187 */ 188 public String getCredentialClass() { 189 return credentialClass; 190 } 191 192 /** 193 * Returns the <code>Principal</code> classes and names 194 * associated with this <code>PrivateCredentialPermission</code>. 195 * The information is returned as a two-dimensional array (array[x][y]). 196 * The 'x' value corresponds to the number of <code>Principal</code> 197 * class and name pairs. When (y==0), it corresponds to 198 * the <code>Principal</code> class value, and when (y==1), 199 * it corresponds to the <code>Principal</code> name value. 200 * For example, array[0][0] corresponds to the class name of 201 * the first <code>Principal</code> in the array. array[0][1] 202 * corresponds to the <code>Principal</code> name of the 203 * first <code>Principal</code> in the array. 204 * 205 * <p> 206 * 207 * @return the <code>Principal</code> class and names associated 208 * with this <code>PrivateCredentialPermission</code>. 209 */ 210 public String[][] getPrincipals() { 211 212 if (credOwners == null || credOwners.length == 0) { 213 return new String[0][0]; 214 } 215 216 String[][] pArray = new String[credOwners.length][2]; 217 for (int i = 0; i < credOwners.length; i++) { 218 pArray[i][0] = credOwners[i].principalClass; 219 pArray[i][1] = credOwners[i].principalName; 220 } 221 return pArray; 222 } 223 224 /** 225 * Checks if this <code>PrivateCredentialPermission</code> implies 226 * the specified <code>Permission</code>. 227 * 228 * <p> 229 * 230 * This method returns true if: 231 * <p><ul> 232 * <li> <i>p</i> is an instanceof PrivateCredentialPermission and <p> 233 * <li> the target name for <i>p</i> is implied by this object's 234 * target name. For example: 235 * <pre> 236 * [* P1 "duke"] implies [a.b.Credential P1 "duke"]. 237 * [C1 P1 "duke"] implies [C1 P1 "duke" P2 "dukette"]. 238 * [C1 P2 "dukette"] implies [C1 P1 "duke" P2 "dukette"]. 239 * </pre> 240 * </ul> 241 * 242 * <p> 243 * 244 * @param p the <code>Permission</code> to check against. 245 * 246 * @return true if this <code>PrivateCredentialPermission</code> implies 247 * the specified <code>Permission</code>, false if not. 248 */ 249 public boolean implies(Permission p) { 250 251 if (p == null || !(p instanceof PrivateCredentialPermission)) 252 return false; 253 254 PrivateCredentialPermission that = (PrivateCredentialPermission)p; 255 256 if (!impliesCredentialClass(credentialClass, that.credentialClass)) 257 return false; 258 259 return impliesPrincipalSet(credOwners, that.credOwners); 260 } 261 262 /** 263 * Checks two <code>PrivateCredentialPermission</code> objects for 264 * equality. Checks that <i>obj</i> is a 265 * <code>PrivateCredentialPermission</code>, 266 * and has the same credential class as this object, 267 * as well as the same Principals as this object. 268 * The order of the Principals in the respective Permission's 269 * target names is not relevant. 270 * 271 * <p> 272 * 273 * @param obj the object we are testing for equality with this object. 274 * 275 * @return true if obj is a <code>PrivateCredentialPermission</code>, 276 * has the same credential class as this object, 277 * and has the same Principals as this object. 278 */ 279 public boolean equals(Object obj) { 280 if (obj == this) 281 return true; 282 283 if (! (obj instanceof PrivateCredentialPermission)) 284 return false; 285 286 PrivateCredentialPermission that = (PrivateCredentialPermission)obj; 287 288 return (this.implies(that) && that.implies(this)); 289 } 290 291 /** 292 * Returns the hash code value for this object. 293 * 294 * @return a hash code value for this object. 295 */ 296 public int hashCode() { 297 return this.credentialClass.hashCode(); 298 } 299 300 /** 301 * Returns the "canonical string representation" of the actions. 302 * This method always returns the String, "read". 303 * 304 * <p> 305 * 306 * @return the actions (always returns "read"). 307 */ 308 public String getActions() { 309 return "read"; 310 } 311 312 /** 313 * Return a homogeneous collection of PrivateCredentialPermissions 314 * in a <code>PermissionCollection</code>. 315 * No such <code>PermissionCollection</code> is defined, 316 * so this method always returns <code>null</code>. 317 * 318 * <p> 319 * 320 * @return null in all cases. 321 */ 322 public PermissionCollection newPermissionCollection() { 323 return null; 324 } 325 326 private void init(String name) { 327 328 if (name == null || name.trim().length() == 0) { 329 throw new IllegalArgumentException("invalid empty name"); 330 } 331 332 ArrayList<CredOwner> pList = new ArrayList<>(); 333 StringTokenizer tokenizer = new StringTokenizer(name, " ", true); 334 String principalClass = null; 335 String principalName = null; 336 337 if (testing) 338 System.out.println("whole name = " + name); 339 340 // get the Credential Class 341 credentialClass = tokenizer.nextToken(); 342 if (testing) 343 System.out.println("Credential Class = " + credentialClass); 344 345 if (tokenizer.hasMoreTokens() == false) { 346 MessageFormat form = new MessageFormat(ResourcesMgr.getString 347 ("permission.name.name.syntax.invalid.")); 348 Object[] source = {name}; 349 throw new IllegalArgumentException 350 (form.format(source) + ResourcesMgr.getString 351 ("Credential.Class.not.followed.by.a.Principal.Class.and.Name")); 352 } 353 354 while (tokenizer.hasMoreTokens()) { 355 356 // skip delimiter 357 tokenizer.nextToken(); 358 359 // get the Principal Class 360 principalClass = tokenizer.nextToken(); 361 if (testing) 362 System.out.println(" Principal Class = " + principalClass); 363 364 if (tokenizer.hasMoreTokens() == false) { 365 MessageFormat form = new MessageFormat(ResourcesMgr.getString 366 ("permission.name.name.syntax.invalid.")); 367 Object[] source = {name}; 368 throw new IllegalArgumentException 369 (form.format(source) + ResourcesMgr.getString 370 ("Principal.Class.not.followed.by.a.Principal.Name")); 371 } 372 373 // skip delimiter 374 tokenizer.nextToken(); 375 376 // get the Principal Name 377 principalName = tokenizer.nextToken(); 378 379 if (!principalName.startsWith("\"")) { 380 MessageFormat form = new MessageFormat(ResourcesMgr.getString 381 ("permission.name.name.syntax.invalid.")); 382 Object[] source = {name}; 383 throw new IllegalArgumentException 384 (form.format(source) + ResourcesMgr.getString 385 ("Principal.Name.must.be.surrounded.by.quotes")); 386 } 387 388 if (!principalName.endsWith("\"")) { 389 390 // we have a name with spaces in it -- 391 // keep parsing until we find the end quote, 392 // and keep the spaces in the name 393 394 while (tokenizer.hasMoreTokens()) { 395 principalName = principalName + tokenizer.nextToken(); 396 if (principalName.endsWith("\"")) 397 break; 398 } 399 400 if (!principalName.endsWith("\"")) { 401 MessageFormat form = new MessageFormat 402 (ResourcesMgr.getString 403 ("permission.name.name.syntax.invalid.")); 404 Object[] source = {name}; 405 throw new IllegalArgumentException 406 (form.format(source) + ResourcesMgr.getString 407 ("Principal.Name.missing.end.quote")); 408 } 409 } 410 411 if (testing) 412 System.out.println("\tprincipalName = '" + principalName + "'"); 413 414 principalName = principalName.substring 415 (1, principalName.length() - 1); 416 417 if (principalClass.equals("*") && 418 !principalName.equals("*")) { 419 throw new IllegalArgumentException(ResourcesMgr.getString 420 ("PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value")); 421 } 422 423 if (testing) 424 System.out.println("\tprincipalName = '" + principalName + "'"); 425 426 pList.add(new CredOwner(principalClass, principalName)); 427 } 428 429 this.credOwners = new CredOwner[pList.size()]; 430 pList.toArray(this.credOwners); 431 } 432 433 private boolean impliesCredentialClass(String thisC, String thatC) { 434 435 // this should never happen 436 if (thisC == null || thatC == null) 437 return false; 438 439 if (testing) 440 System.out.println("credential class comparison: " + 441 thisC + "/" + thatC); 442 443 if (thisC.equals("*")) 444 return true; 445 446 /** 447 * XXX let's not enable this for now -- 448 * if people want it, we'll enable it later 449 */ 450 /* 451 if (thisC.endsWith("*")) { 452 String cClass = thisC.substring(0, thisC.length() - 2); 453 return thatC.startsWith(cClass); 454 } 455 */ 456 457 return thisC.equals(thatC); 458 } 459 460 private boolean impliesPrincipalSet(CredOwner[] thisP, CredOwner[] thatP) { 461 462 // this should never happen 463 if (thisP == null || thatP == null) 464 return false; 465 466 if (thatP.length == 0) 467 return true; 468 469 if (thisP.length == 0) 470 return false; 471 472 for (int i = 0; i < thisP.length; i++) { 473 boolean foundMatch = false; 474 for (int j = 0; j < thatP.length; j++) { 475 if (thisP[i].implies(thatP[j])) { 476 foundMatch = true; 477 break; 478 } 479 } 480 if (!foundMatch) { 481 return false; 482 } 483 } 484 return true; 485 } 486 487 /** 488 * Reads this object from a stream (i.e., deserializes it) 489 */ 490 private void readObject(java.io.ObjectInputStream s) throws 491 java.io.IOException, 492 ClassNotFoundException { 493 494 s.defaultReadObject(); 495 496 // perform new initialization from the permission name 497 498 if (getName().indexOf(" ") == -1 && getName().indexOf("\"") == -1) { 499 500 // name only has a credential class specified 501 credentialClass = getName(); 502 credOwners = EMPTY_PRINCIPALS; 503 504 } else { 505 506 // perform regular initialization 507 init(getName()); 508 } 509 } 510 511 /** 512 * @serial include 513 */ 514 static class CredOwner implements java.io.Serializable { 515 516 private static final long serialVersionUID = -5607449830436408266L; 517 518 /** 519 * @serial 520 */ 521 String principalClass; 522 /** 523 * @serial 524 */ 525 String principalName; 526 527 CredOwner(String principalClass, String principalName) { 528 this.principalClass = principalClass; 529 this.principalName = principalName; 530 } 531 532 public boolean implies(Object obj) { 533 if (obj == null || !(obj instanceof CredOwner)) 534 return false; 535 536 CredOwner that = (CredOwner)obj; 537 538 if (principalClass.equals("*") || 539 principalClass.equals(that.principalClass)) { 540 541 if (principalName.equals("*") || 542 principalName.equals(that.principalName)) { 543 return true; 544 } 545 } 546 547 /** 548 * XXX no code yet to support a.b.* 549 */ 550 551 return false; 552 } 553 554 public String toString() { 555 MessageFormat form = new MessageFormat(ResourcesMgr.getString 556 ("CredOwner.Principal.Class.class.Principal.Name.name")); 557 Object[] source = {principalClass, principalName}; 558 return (form.format(source)); 559 } 560 } 561 }