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.net.URL;
  27 import java.security.CodeSource;
  28 import java.security.Permission;
  29 import java.security.PermissionCollection;
  30 import java.security.Permissions;
  31 import java.security.Policy;
  32 import java.security.ProtectionDomain;
  33 import java.security.SecurityPermission;
  34 import java.util.Enumeration;
  35 import java.util.HashMap;
  36 import java.util.Map;
  37 import java.util.PropertyPermission;
  38 import java.util.Set;
  39 import java.util.StringJoiner;
  40 
  41 
  42 /*
  43  * This is a base class that every test class must extend if it needs to be run
  44  * with security mode.
  45  */
  46 public class JAXPPolicyManager {
  47     /*
  48      * Backing up policy.
  49      */
  50     private Policy policyBackup;
  51 
  52     /*
  53      * Backing up security manager.
  54      */
  55     private SecurityManager smBackup;
  56 
  57     /*
  58      * Current policy.
  59      */
  60     private TestPolicy policy = new TestPolicy();
  61 
  62     /*
  63      * JAXPPolicyManager singleton.
  64      */
  65     private static JAXPPolicyManager policyManager = null;
  66 
  67     /*
  68      * Install a SecurityManager along with a default Policy to allow testNG to
  69      * run when there is a security manager.
  70      */
  71     private JAXPPolicyManager() {
  72         // Backing up policy and security manager for restore
  73         policyBackup = Policy.getPolicy();
  74         smBackup = System.getSecurityManager();
  75 
  76         // Set customized policy
  77         setDefaultPermissions();
  78         Policy.setPolicy(policy);
  79         System.setSecurityManager(new SecurityManager());
  80     }
  81 
  82     static synchronized JAXPPolicyManager getJAXPPolicyManager(boolean createIfNone) {
  83         if (policyManager == null & createIfNone)
  84             policyManager = new JAXPPolicyManager();
  85         return policyManager;
  86     }
  87 
  88     private void teardown() throws Exception {
  89         System.setSecurityManager(smBackup);
  90         Policy.setPolicy(policyBackup);
  91     }
  92 
  93     /*
  94      * Restore the original Policy and SecurityManager.
  95      */
  96     static synchronized void teardownPolicyManager() throws Exception {
  97         if (policyManager != null) {
  98             policyManager.teardown();
  99             policyManager = null;
 100         }
 101     }
 102 
 103     /*
 104      * Set default permissions, sub-class of JAXPBaseTest should override this
 105      * method.
 106      */
 107     private void setDefaultPermissions() {
 108         //Permissions to set security manager and policy
 109         addPermission(new SecurityPermission("getPolicy"));
 110         addPermission(new SecurityPermission("setPolicy"));
 111         addPermission(new RuntimePermission("setSecurityManager"));
 112         addPermission(new PropertyPermission("test.src", "read"));
 113     }
 114 
 115     /*
 116      * Add permission to the TestPolicy.
 117      *
 118      * @param permission to be added.
 119      */
 120     void addPermission(Permission p) {
 121         policy.addPermission(p);
 122     }
 123 
 124     /*
 125      * Add a temporary permission in current thread context. This won't impact
 126      * global policy and doesn't support permission combination.
 127      *
 128      * @param permission
 129      *            to add.
 130      * @return index of the added permission.
 131      */
 132     int addTmpPermission(Permission p) {
 133         return policy.addTmpPermission(p);
 134     }
 135 
 136     /*
 137      * set allowAll in current thread context.
 138      */
 139     void setAllowAll(boolean allow) {
 140         policy.setAllowAll(allow);
 141     }
 142 
 143     /*
 144      * Remove a temporary permission from current thread context.
 145      *
 146      * @param index to remove.
 147      *
 148      * @throws RuntimeException if no temporary permission list in current
 149      *             thread context or no permission correlated to the index.
 150      */
 151     void removeTmpPermission(int index) {
 152         policy.removeTmpPermission(index);
 153     }
 154 
 155 
 156 }
 157 
 158 /*
 159  * Simple Policy class that supports the required Permissions to validate the
 160  * JAXP concrete classes.
 161  */
 162 class TestPolicy extends Policy {
 163     private final static Set<String> TEST_JARS =
 164          Set.of("jtreg.jar", "javatest.jar", "testng.jar", "jcommander.jar");
 165     private final PermissionCollection permissions = new Permissions();
 166 
 167     private ThreadLocal<Map<Integer, Permission>> transientPermissions = new ThreadLocal<>();
 168     private ThreadLocal<Boolean> allowAll = new ThreadLocal<>();
 169 
 170     private static Policy defaultPolicy = Policy.getPolicy();
 171 
 172     /*
 173      * Add permission to this policy.
 174      *
 175      * @param permission to be added.
 176      */
 177     void addPermission(Permission p) {
 178         permissions.add(p);
 179     }
 180 
 181     /*
 182      * Set all permissions. Caution: this should not called carefully unless
 183      * it's really needed.
 184      *
 185      * private void setAllPermissions() { permissions.add(new AllPermission());
 186      * }
 187      */
 188 
 189     /*
 190      * Overloaded methods from the Policy class.
 191      */
 192     @Override
 193     public String toString() {
 194         StringJoiner sj = new StringJoiner("\n", "policy: ", "");
 195         Enumeration<Permission> perms = permissions.elements();
 196         while (perms.hasMoreElements()) {
 197             sj.add(perms.nextElement().toString());
 198         }
 199         return sj.toString();
 200 
 201     }
 202 
 203     @Override
 204     public PermissionCollection getPermissions(ProtectionDomain domain) {
 205         return permissions;
 206     }
 207 
 208     @Override
 209     public PermissionCollection getPermissions(CodeSource codesource) {
 210         return permissions;
 211     }
 212 
 213     private boolean isTestMachineryDomain(ProtectionDomain domain) {
 214         CodeSource cs = (domain == null) ? null : domain.getCodeSource();
 215         URL loc = (cs == null) ? null : cs.getLocation();
 216         String path = (loc == null) ? null : loc.getPath();
 217         return path != null && TEST_JARS.stream()
 218                                 .filter(path::endsWith)
 219                                 .findAny()
 220                                 .isPresent();
 221     }
 222 
 223     @Override
 224     public boolean implies(ProtectionDomain domain, Permission perm) {
 225         if (allowAll())
 226             return true;
 227 
 228         if (defaultPolicy.implies(domain, perm))
 229             return true;
 230 
 231         if (permissions.implies(perm))
 232             return true;
 233 
 234         if (isTestMachineryDomain(domain))
 235             return true;
 236             
 237         return tmpImplies(perm);
 238     }
 239 
 240     /*
 241      * Add a temporary permission in current thread context. This won't impact
 242      * global policy and doesn't support permission combination.
 243      *
 244      * @param permission to add.
 245      * @return index of the added permission.
 246      */
 247     int addTmpPermission(Permission p) {
 248         Map<Integer, Permission> tmpPermissions = transientPermissions.get();
 249         if (tmpPermissions == null)
 250             tmpPermissions = new HashMap<>();
 251 
 252         int id = tmpPermissions.size();
 253         tmpPermissions.put(id, p);
 254         transientPermissions.set(tmpPermissions);
 255         return id;
 256     }
 257 
 258     /*
 259      * Remove a temporary permission from current thread context.
 260      *
 261      * @param index to remove.
 262      *
 263      * @throws RuntimeException if no temporary permission list in current
 264      *             thread context or no permission correlated to the index.
 265      */
 266     void removeTmpPermission(int index) {
 267         try {
 268             Map<Integer, Permission> tmpPermissions = transientPermissions.get();
 269             tmpPermissions.remove(index);
 270         } catch (NullPointerException | IndexOutOfBoundsException e) {
 271             throw new RuntimeException("Tried to delete a non-existent temporary permission", e);
 272         }
 273     }
 274 
 275     /*
 276      * Checks to see if the specified permission is implied by temporary
 277      * permission list in current thread context.
 278      *
 279      * @param permission the Permission object to compare.
 280      *
 281      * @return true if "permission" is implied by any permission in the
 282      *         temporary permission list, false if not.
 283      */
 284     private boolean tmpImplies(Permission perm) {
 285         Map<Integer, Permission> tmpPermissions = transientPermissions.get();
 286         if (tmpPermissions != null) {
 287             for (Permission p : tmpPermissions.values()) {
 288                 if (p.implies(perm))
 289                     return true;
 290             }
 291         }
 292         return false;
 293     }
 294 
 295     /*
 296      * Checks to see if allow all permission requests in current thread context.
 297      */
 298     private boolean allowAll() {
 299         Boolean allow = allowAll.get();
 300         if (allow != null) {
 301             return allow;
 302         }
 303         return false;
 304     }
 305 
 306     /*
 307      * set allowAll in current thread context.
 308      */
 309     void setAllowAll(boolean allow) {
 310         allowAll.set(allow);
 311     }
 312 }