001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */ 
019     
020    package org.apache.commons.logging.security;
021    
022    import java.io.FilePermission;
023    import java.security.Permission;
024    import java.security.Permissions;
025    
026    
027    /**
028     * Custom implementation of a security manager, so we can control the
029     * security environment for tests in this package.
030     */
031    public class MockSecurityManager extends SecurityManager {
032    
033        private Permissions permissions = new Permissions();
034        private static final Permission setSecurityManagerPerm =
035            new RuntimePermission("setSecurityManager");
036        
037        private int untrustedCodeCount = 0;
038    
039        public MockSecurityManager() {
040            permissions.add(setSecurityManagerPerm);
041        }
042    
043        /**
044         * Define the set of permissions to be granted to classes in the o.a.c.l package,
045         * but NOT to unit-test classes in o.a.c.l.security package.
046         */
047        public void addPermission(Permission p) {
048            permissions.add(p);
049        }
050    
051        /**
052         * This returns the number of times that a check of a permission failed
053         * due to stack-walking tracing up into untrusted code. Any non-zero
054         * value indicates a bug in JCL, ie a situation where code was not
055         * correctly wrapped in an AccessController block. The result of such a
056         * bug is that signing JCL is not sufficient to allow JCL to perform
057         * the operation; the caller would need to be signed too. 
058         */
059        public int getUntrustedCodeCount() {
060            return untrustedCodeCount;
061        }
062    
063        public void checkPermission(Permission p) throws SecurityException {
064            if (setSecurityManagerPerm.implies(p)) {
065                // ok, allow this; we don't want to block any calls to setSecurityManager
066                // otherwise this custom security manager cannot be reset to the original.
067                // System.out.println("setSecurityManager: granted");
068                return;
069            }
070    
071            // Allow read-only access to files, as this is needed to load classes!
072            // Ideally, we would limit this to just .class and .jar files.
073            if (p instanceof FilePermission) {
074              FilePermission fp = (FilePermission) p;
075              if (fp.getActions().equals("read")) {
076                // System.out.println("Permit read of files");
077                return;
078              }
079            }
080    
081            System.out.println("\n\ntesting permission:" + p.getClass() + ":"+ p);
082    
083            Exception e = new Exception();
084            e.fillInStackTrace();
085            StackTraceElement[] stack = e.getStackTrace();
086            
087            // scan the call stack from most recent to oldest.
088            // start at 1 to skip the entry in the stack for this method
089            for(int i=1; i<stack.length; ++i) {
090                String cname = stack[i].getClassName();
091                System.out.println("" + i + ":" + stack[i].getClassName() + 
092                  "." + stack[i].getMethodName());
093    
094                if (cname.equals("java.security.AccessController")) {
095                    // Presumably method name equals "doPrivileged"
096                    //
097                    // The previous iteration of this loop verified that the
098                    // PrivilegedAction.run method associated with this
099                    // 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    }