1 /*
   2  * Copyright (c) 2015, 2016, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jaxp.library;
  24 
  25 
  26 import java.security.CodeSource;
  27 import java.security.Permission;
  28 import java.security.PermissionCollection;
  29 import java.security.Permissions;
  30 import java.security.Policy;
  31 import java.security.ProtectionDomain;
  32 import java.security.SecurityPermission;
  33 import java.util.Enumeration;
  34 import java.util.HashMap;
  35 import java.util.Map;
  36 import java.util.PropertyPermission;
  37 import java.util.StringJoiner;
  38 
  39 
  40 /*
  41  * This is a base class that every test class must extend if it needs to be run
  42  * with security mode.
  43  */
  44 public class JAXPPolicyManager {
  45     /*
  46      * Backing up policy.
  47      */
  48     private Policy policyBackup;
  49 
  50     /*
  51      * Backing up security manager.
  52      */
  53     private SecurityManager smBackup;
  54 
  55     /*
  56      * Current policy.
  57      */
  58     private TestPolicy policy = new TestPolicy();
  59 
  60     /*
  61      * JAXPPolicyManager singleton.
  62      */
  63     private static JAXPPolicyManager policyManager = null;
  64 
  65     /*
  66      * Install a SecurityManager along with a default Policy to allow testNG to
  67      * run when there is a security manager.
  68      */
  69     private JAXPPolicyManager() {
  70         // Backing up policy and security manager for restore
  71         policyBackup = Policy.getPolicy();
  72         smBackup = System.getSecurityManager();
  73 
  74         // Set customized policy
  75         setDefaultPermissions();
  76         Policy.setPolicy(policy);
  77         System.setSecurityManager(new SecurityManager());
  78     }
  79 
  80     static synchronized JAXPPolicyManager getJAXPPolicyManager(boolean createIfNone) {
  81         if (policyManager == null & createIfNone)
  82             policyManager = new JAXPPolicyManager();
  83         return policyManager;
  84     }
  85 
  86     private void teardown() throws Exception {
  87         System.setSecurityManager(smBackup);
  88         Policy.setPolicy(policyBackup);
  89     }
  90 
  91     /*
  92      * Restore the original Policy and SecurityManager.
  93      */
  94     static synchronized void teardownPolicyManager() throws Exception {
  95         if (policyManager != null) {
  96             policyManager.teardown();
  97             policyManager = null;
  98         }
  99     }
 100 
 101     /*
 102      * Set default permissions, sub-class of JAXPBaseTest should override this
 103      * method.
 104      */
 105     private void setDefaultPermissions() {
 106         //Permissions to set security manager and policy
 107         addPermission(new SecurityPermission("getPolicy"));
 108         addPermission(new SecurityPermission("setPolicy"));
 109         addPermission(new RuntimePermission("setSecurityManager"));
 110         //Properties that jtreg and TestNG require
 111         addPermission(new PropertyPermission("testng.show.stack.frames", "read"));
 112         addPermission(new PropertyPermission("test.src", "read"));
 113         addPermission(new PropertyPermission("test.classes", "read"));
 114         addPermission(new PropertyPermission("dataproviderthreadcount", "read"));
 115         addPermission(new PropertyPermission("experimental", "read"));
 116     }
 117 
 118     /*
 119      * Add permission to the TestPolicy.
 120      *
 121      * @param permission to be added.
 122      */
 123     void addPermission(Permission p) {
 124         policy.addPermission(p);
 125     }
 126 
 127     /*
 128      * Add a temporary permission in current thread context. This won't impact
 129      * global policy and doesn't support permission combination.
 130      *
 131      * @param permission
 132      *            to add.
 133      * @return index of the added permission.
 134      */
 135     int addTmpPermission(Permission p) {
 136         return policy.addTmpPermission(p);
 137     }
 138 
 139     /*
 140      * set allowAll in current thread context.
 141      */
 142     void setAllowAll(boolean allow) {
 143         policy.setAllowAll(allow);
 144     }
 145 
 146     /*
 147      * Remove a temporary permission from current thread context.
 148      *
 149      * @param index to remove.
 150      *
 151      * @throws RuntimeException if no temporary permission list in current
 152      *             thread context or no permission correlated to the index.
 153      */
 154     void removeTmpPermission(int index) {
 155         policy.removeTmpPermission(index);
 156     }
 157 
 158 
 159 }
 160 
 161 /*
 162  * Simple Policy class that supports the required Permissions to validate the
 163  * JAXP concrete classes.
 164  */
 165 class TestPolicy extends Policy {
 166     private final PermissionCollection permissions = new Permissions();
 167 
 168     private ThreadLocal<Map<Integer, Permission>> transientPermissions = new ThreadLocal<>();
 169     private ThreadLocal<Boolean> allowAll = new ThreadLocal<>();
 170 
 171     private static Policy defaultPolicy = Policy.getPolicy();
 172 
 173     /*
 174      * Add permission to this policy.
 175      *
 176      * @param permission to be added.
 177      */
 178     void addPermission(Permission p) {
 179         permissions.add(p);
 180     }
 181 
 182     /*
 183      * Set all permissions. Caution: this should not called carefully unless
 184      * it's really needed.
 185      *
 186      * private void setAllPermissions() { permissions.add(new AllPermission());
 187      * }
 188      */
 189 
 190     /*
 191      * Overloaded methods from the Policy class.
 192      */
 193     @Override
 194     public String toString() {
 195         StringJoiner sj = new StringJoiner("\n", "policy: ", "");
 196         Enumeration<Permission> perms = permissions.elements();
 197         while (perms.hasMoreElements()) {
 198             sj.add(perms.nextElement().toString());
 199         }
 200         return sj.toString();
 201 
 202     }
 203 
 204     @Override
 205     public PermissionCollection getPermissions(ProtectionDomain domain) {
 206         return permissions;
 207     }
 208 
 209     @Override
 210     public PermissionCollection getPermissions(CodeSource codesource) {
 211         return permissions;
 212     }
 213 
 214     @Override
 215     public boolean implies(ProtectionDomain domain, Permission perm) {
 216         if (allowAll())
 217             return true;
 218 
 219         if (defaultPolicy.implies(domain, perm))
 220             return true;
 221 
 222         if (permissions.implies(perm))
 223             return true;
 224         else
 225             return tmpImplies(perm);
 226     }
 227 
 228     /*
 229      * Add a temporary permission in current thread context. This won't impact
 230      * global policy and doesn't support permission combination.
 231      *
 232      * @param permission to add.
 233      * @return index of the added permission.
 234      */
 235     int addTmpPermission(Permission p) {
 236         Map<Integer, Permission> tmpPermissions = transientPermissions.get();
 237         if (tmpPermissions == null)
 238             tmpPermissions = new HashMap<>();
 239 
 240         int id = tmpPermissions.size();
 241         tmpPermissions.put(id, p);
 242         transientPermissions.set(tmpPermissions);
 243         return id;
 244     }
 245 
 246     /*
 247      * Remove a temporary permission from current thread context.
 248      *
 249      * @param index to remove.
 250      *
 251      * @throws RuntimeException if no temporary permission list in current
 252      *             thread context or no permission correlated to the index.
 253      */
 254     void removeTmpPermission(int index) {
 255         try {
 256             Map<Integer, Permission> tmpPermissions = transientPermissions.get();
 257             tmpPermissions.remove(index);
 258         } catch (NullPointerException | IndexOutOfBoundsException e) {
 259             throw new RuntimeException("Tried to delete a non-existent temporary permission", e);
 260         }
 261     }
 262 
 263     /*
 264      * Checks to see if the specified permission is implied by temporary
 265      * permission list in current thread context.
 266      *
 267      * @param permission the Permission object to compare.
 268      *
 269      * @return true if "permission" is implied by any permission in the
 270      *         temporary permission list, false if not.
 271      */
 272     private boolean tmpImplies(Permission perm) {
 273         Map<Integer, Permission> tmpPermissions = transientPermissions.get();
 274         if (tmpPermissions != null) {
 275             for (Permission p : tmpPermissions.values()) {
 276                 if (p.implies(perm))
 277                     return true;
 278             }
 279         }
 280         return false;
 281     }
 282 
 283     /*
 284      * Checks to see if allow all permission requests in current thread context.
 285      */
 286     private boolean allowAll() {
 287         Boolean allow = allowAll.get();
 288         if (allow != null) {
 289             return allow;
 290         }
 291         return false;
 292     }
 293 
 294     /*
 295      * set allowAll in current thread context.
 296      */
 297     void setAllowAll(boolean allow) {
 298         allowAll.set(allow);
 299     }
 300 }