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 }