blob: 462c26e5586da2131286dbb2f657d685d37021ec [file] [log] [blame]
/*
* 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);
}
}
}
}