1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.logging.security; 19 20 import java.io.FilePermission; 21 import java.security.Permission; 22 import java.security.Permissions; 23 24 /** 25 * Custom implementation of a security manager, so we can control the 26 * security environment for tests in this package. 27 */ 28 public class MockSecurityManager extends SecurityManager { 29 30 private static final Permission setSecurityManagerPerm = 31 new RuntimePermission("setSecurityManager"); 32 private final Permissions permissions = new Permissions(); 33 34 private int untrustedCodeCount; 35 36 public MockSecurityManager() { 37 permissions.add(setSecurityManagerPerm); 38 } 39 40 /** 41 * Define the set of permissions to be granted to classes in the o.a.c.l package, 42 * but NOT to unit-test classes in o.a.c.l.security package. 43 */ 44 public void addPermission(final Permission p) { 45 permissions.add(p); 46 } 47 48 @Override 49 public void checkPermission(final Permission p) throws SecurityException { 50 if (setSecurityManagerPerm.implies(p)) { 51 // ok, allow this; we don't want to block any calls to setSecurityManager 52 // otherwise this custom security manager cannot be reset to the original. 53 // System.out.println("setSecurityManager: granted"); 54 return; 55 } 56 57 // Allow read-only access to files, as this is needed to load classes! 58 // Ideally, we would limit this to just .class and .jar files. 59 if (p instanceof FilePermission) { 60 final FilePermission fp = (FilePermission) p; 61 if (fp.getActions().equals("read")) { 62 // System.out.println("Permit read of files"); 63 return; 64 } 65 } 66 67 System.out.println("\n\ntesting permission:" + p.getClass() + ":"+ p); 68 69 final Exception e = new Exception(); 70 e.fillInStackTrace(); 71 final StackTraceElement[] stack = e.getStackTrace(); 72 73 // scan the call stack from most recent to oldest. 74 // start at 1 to skip the entry in the stack for this method 75 for(int i=1; i<stack.length; ++i) { 76 final String cname = stack[i].getClassName(); 77 System.out.println("" + i + ":" + stack[i].getClassName() + 78 "." + stack[i].getMethodName() + ":" + stack[i].getLineNumber()); 79 80 if (cname.equals("java.util.logging.Handler") && stack[i].getMethodName().equals("setLevel")) { 81 // LOGGING CODE CAUSES ACCESSCONTROLEXCEPTION 82 // http://www-01.ibm.com/support/docview.wss?uid=swg1IZ51152 83 return; 84 } 85 86 if (cname.equals("java.util.logging.Level") && stack[i].getMethodName().equals("getLocalizedLevelName")) { 87 // LOGGING-156: OpenJDK 1.7 JULI code (java.util.logging.Level#getLocalizedLevelName) 88 // calls ResourceBundle#getBundle() without using AccessController#doPrivileged() 89 // requiring RuntimePermission: "accessClassInPackage.sun.util.logging.resources" 90 return; 91 } 92 93 if (cname.equals("java.security.AccessController")) { 94 // Presumably method name equals "doPrivileged" 95 // 96 // The previous iteration of this loop verified that the 97 // PrivilegedAction.run method associated with this 98 // doPrivileged method call had the right permissions, 99 // so we just return here. Effectively, the method invoking 100 // doPrivileged asserted that it checked the input params 101 // and found them safe, and that code is trusted, so we 102 // don't need to check the trust level of code higher in 103 // the call stack. 104 System.out.println("Access controller found: returning"); 105 return; 106 } 107 if (cname.startsWith("java.") 108 || cname.startsWith("javax.") 109 || cname.startsWith("junit.") 110 || cname.startsWith("org.apache.tools.ant.") 111 || cname.startsWith("sun.")) { 112 // Code in these packages is trusted if the caller is trusted. 113 // 114 // TODO: maybe check class is loaded via system loader or similar rather 115 // than checking name? Trusted domains may be different in alternative 116 // jvms.. 117 } else if (cname.startsWith("org.apache.commons.logging.security")) { 118 // this is the unit test code; treat this like an untrusted client 119 // app that is using JCL 120 ++untrustedCodeCount; 121 System.out.println("Untrusted code [test] found"); 122 throw new SecurityException("Untrusted code [test] found"); 123 } else if (cname.startsWith("org.apache.commons.logging.")) { 124 if (!permissions.implies(p)) { 125 System.out.println("Permission refused:" + p.getClass() + ":" + p); 126 throw new SecurityException("Permission refused:" + p.getClass() + ":" + p); 127 } 128 // Code here is trusted if the caller is trusted 129 System.out.println("Permission in allowed set for JCL class"); 130 } else { 131 // we found some code that is not trusted to perform this operation. 132 System.out.println("Unexpected code: permission refused:" + p.getClass() + ":" + p); 133 throw new SecurityException("Unexpected code: permission refused:" + p.getClass() + ":" + p); 134 } 135 } 136 } 137 138 /** 139 * This returns the number of times that a check of a permission failed 140 * due to stack-walking tracing up into untrusted code. Any non-zero 141 * value indicates a bug in JCL, that is, a situation where code was not 142 * correctly wrapped in an AccessController block. The result of such a 143 * bug is that signing JCL is not sufficient to allow JCL to perform 144 * the operation; the caller would need to be signed too. 145 */ 146 public int getUntrustedCodeCount() { 147 return untrustedCodeCount; 148 } 149 }