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 }