1 /*
   2  * Copyright (c) 1999, 2013, 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}.  The {@code Subject}
  38  * is represented by a Set of Principals.
  39  *
  40  * <p> The target name of this {@code Permission} 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}.
  69  * If "PrincipalName" is "*", then access is granted to the
  70  * specified Credential owned by any {@code Subject} that has the
  71  * specified {@code Principal} (the actual PrincipalName doesn't matter).
  72  * For example, the following grants access to the
  73  * a.b.Credential owned by any {@code Subject} 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}.
  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} 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}.
 119      */
 120     private Set<Principal> 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}
 130      * with the specified {@code credentialClass} 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}
 157      * with the specified {@code name}.  The {@code name}
 158      * specifies both a Credential class and a {@code Principal} Set.
 159      *
 160      * <p>
 161      *
 162      * @param name the name specifying the Credential class and
 163      *          {@code Principal} Set. <p>
 164      *
 165      * @param actions the actions specifying that the Credential can be read.
 166      *
 167      * @throws IllegalArgumentException if {@code name} does not conform
 168      *          to the correct syntax or if {@code actions} 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}.
 182      *
 183      * <p>
 184      *
 185      * @return the Class name of the Credential associated with this
 186      *          {@code PrivateCredentialPermission}.
 187      */
 188     public String getCredentialClass() {
 189         return credentialClass;
 190     }
 191 
 192     /**
 193      * Returns the {@code Principal} classes and names
 194      * associated with this {@code PrivateCredentialPermission}.
 195      * The information is returned as a two-dimensional array (array[x][y]).
 196      * The 'x' value corresponds to the number of {@code Principal}
 197      * class and name pairs.  When (y==0), it corresponds to
 198      * the {@code Principal} class value, and when (y==1),
 199      * it corresponds to the {@code Principal} name value.
 200      * For example, array[0][0] corresponds to the class name of
 201      * the first {@code Principal} in the array.  array[0][1]
 202      * corresponds to the {@code Principal} name of the
 203      * first {@code Principal} in the array.
 204      *
 205      * <p>
 206      *
 207      * @return the {@code Principal} class and names associated
 208      *          with this {@code PrivateCredentialPermission}.
 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} implies
 226      * the specified {@code Permission}.
 227      *
 228      * <p>
 229      *
 230      * This method returns true if:
 231      * <ul>
 232      * <li> <i>p</i> is an instanceof PrivateCredentialPermission and
 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} to check against.
 245      *
 246      * @return true if this {@code PrivateCredentialPermission} implies
 247      * the specified {@code Permission}, 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} objects for
 264      * equality.  Checks that <i>obj</i> is a
 265      * {@code PrivateCredentialPermission},
 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},
 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}.
 315      * No such {@code PermissionCollection} is defined,
 316      * so this method always returns {@code null}.
 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 }