| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.logging.security; |
| |
| import java.io.FilePermission; |
| import java.security.Permission; |
| import java.security.Permissions; |
| |
| |
| /** |
| * Custom implementation of a security manager, so we can control the |
| * security environment for tests in this package. |
| */ |
| public class MockSecurityManager extends SecurityManager { |
| |
| private final Permissions permissions = new Permissions(); |
| private static final Permission setSecurityManagerPerm = |
| new RuntimePermission("setSecurityManager"); |
| |
| private int untrustedCodeCount = 0; |
| |
| public MockSecurityManager() { |
| permissions.add(setSecurityManagerPerm); |
| } |
| |
| /** |
| * Define the set of permissions to be granted to classes in the o.a.c.l package, |
| * but NOT to unit-test classes in o.a.c.l.security package. |
| */ |
| public void addPermission(Permission p) { |
| permissions.add(p); |
| } |
| |
| /** |
| * This returns the number of times that a check of a permission failed |
| * due to stack-walking tracing up into untrusted code. Any non-zero |
| * value indicates a bug in JCL, ie a situation where code was not |
| * correctly wrapped in an AccessController block. The result of such a |
| * bug is that signing JCL is not sufficient to allow JCL to perform |
| * the operation; the caller would need to be signed too. |
| */ |
| public int getUntrustedCodeCount() { |
| return untrustedCodeCount; |
| } |
| |
| public void checkPermission(Permission p) throws SecurityException { |
| if (setSecurityManagerPerm.implies(p)) { |
| // ok, allow this; we don't want to block any calls to setSecurityManager |
| // otherwise this custom security manager cannot be reset to the original. |
| // System.out.println("setSecurityManager: granted"); |
| return; |
| } |
| |
| // Allow read-only access to files, as this is needed to load classes! |
| // Ideally, we would limit this to just .class and .jar files. |
| if (p instanceof FilePermission) { |
| FilePermission fp = (FilePermission) p; |
| if (fp.getActions().equals("read")) { |
| // System.out.println("Permit read of files"); |
| return; |
| } |
| } |
| |
| System.out.println("\n\ntesting permission:" + p.getClass() + ":"+ p); |
| |
| Exception e = new Exception(); |
| e.fillInStackTrace(); |
| StackTraceElement[] stack = e.getStackTrace(); |
| |
| // scan the call stack from most recent to oldest. |
| // start at 1 to skip the entry in the stack for this method |
| for(int i=1; i<stack.length; ++i) { |
| String cname = stack[i].getClassName(); |
| System.out.println("" + i + ":" + stack[i].getClassName() + |
| "." + stack[i].getMethodName() + ":" + stack[i].getLineNumber()); |
| |
| if (cname.equals("java.util.logging.Handler") && stack[i].getMethodName().equals("setLevel")) { |
| // LOGGING CODE CAUSES ACCESSCONTROLEXCEPTION |
| // http://www-01.ibm.com/support/docview.wss?uid=swg1IZ51152 |
| return; |
| } |
| |
| if (cname.equals("java.security.AccessController")) { |
| // Presumably method name equals "doPrivileged" |
| // |
| // The previous iteration of this loop verified that the |
| // PrivilegedAction.run method associated with this |
| // doPrivileged method call had the right permissions, |
| // so we just return here. Effectively, the method invoking |
| // doPrivileged asserted that it checked the input params |
| // and found them safe, and that code is trusted, so we |
| // don't need to check the trust level of code higher in |
| // the call stack. |
| System.out.println("Access controller found: returning"); |
| return; |
| } else if (cname.startsWith("java.") |
| || cname.startsWith("javax.") |
| || cname.startsWith("junit.") |
| || cname.startsWith("org.apache.tools.ant.") |
| || cname.startsWith("sun.")) { |
| // Code in these packages is trusted if the caller is trusted. |
| // |
| // TODO: maybe check class is loaded via system loader or similar rather |
| // than checking name? Trusted domains may be different in alternative |
| // jvms.. |
| } else if (cname.startsWith("org.apache.commons.logging.security")) { |
| // this is the unit test code; treat this like an untrusted client |
| // app that is using JCL |
| ++untrustedCodeCount; |
| System.out.println("Untrusted code [testcase] found"); |
| throw new SecurityException("Untrusted code [testcase] found"); |
| } else if (cname.startsWith("org.apache.commons.logging.")) { |
| if (permissions.implies(p)) { |
| // Code here is trusted if the caller is trusted |
| System.out.println("Permission in allowed set for JCL class"); |
| } else { |
| System.out.println("Permission refused:" + p.getClass() + ":" + p); |
| throw new SecurityException("Permission refused:" + p.getClass() + ":" + p); |
| } |
| } else { |
| // we found some code that is not trusted to perform this operation. |
| System.out.println("Unexpected code: permission refused:" + p.getClass() + ":" + p); |
| throw new SecurityException("Unexpected code: permission refused:" + p.getClass() + ":" + p); |
| } |
| } |
| } |
| } |