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