    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 }