/* * Copyright 2015 MovingBlocks * * Licensed 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.terasology.module.sandbox; import java.security.Permission; /** * <p>ModuleSecurityManager enforces permission access for modules.</p> * <p>The actual permissions are determined by the PermissionProvider associated with the module</p> * <p> * When checking permissions, only the stack down to the calling module (if any) is considered. This means that a module cannot exploit a package with higher * permissions. * </p> * <p> * AccessController.doPrivileged() is fully supported by this system, so non-module code can use this to avoid needing to be explicitly registered as allowing a permission * to modules using it, if the code is intended to run at the engine's security level. * </p> * * @author Immortius * @see ModuleClassLoader */ public class ModuleSecurityManager extends SecurityManager { public static final Permission UPDATE_ALLOWED_PERMISSIONS = new ModuleSecurityPermission(ModuleSecurityPermission.UPDATE_ALLOWED_PERMISSIONS); public static final Permission UPDATE_API_CLASSES = new ModuleSecurityPermission(ModuleSecurityPermission.UPDATE_API_CLASSES); private ThreadLocal<Boolean> calculatingPermission = new ThreadLocal<>(); public ModuleSecurityManager() { } @Override public void checkPermission(Permission perm) { if (checkModuleDeniedAccess(perm)) { super.checkPermission(perm); } } @Override public void checkPermission(Permission perm, Object context) { if (checkModuleDeniedAccess(perm)) { super.checkPermission(perm); } } /** * Checks whether a permission is allowed under the current module context. * * @param perm The permission under question * @return Whether the permission is denied */ private boolean checkModuleDeniedAccess(Permission perm) { if (calculatingPermission.get() != null) { return false; } calculatingPermission.set(true); try { Class<?>[] stack = getClassContext(); for (int i = 0; i < stack.length; ++i) { ClassLoader owningLoader = stack[i].getClassLoader(); if (owningLoader != null && owningLoader instanceof ModuleClassLoader) { return !checkAPIPermissionsFor(perm, i, stack, ((ModuleClassLoader) owningLoader).getPermissionProvider()); } } } finally { calculatingPermission.set(null); } return true; } /** * Checks the stack down to the first module to see if the given permission is allowed to be triggered from a module context. * * @param permission The permission being checked * @param moduleDepth The depth of the first module class * @param stack The classes involved in the current stack * @return Whether the permission has been granted to any of the API classes involved. */ private boolean checkAPIPermissionsFor(Permission permission, int moduleDepth, Class<?>[] stack, PermissionProvider permissionProvider) { for (int i = moduleDepth - 1; i >= 0; i--) { if (permissionProvider.isPermitted(permission, stack[i])) { return true; } } return false; } }