All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager Maven / Gradle / Ivy

Go to download

The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.

There is a newer version: 5.0.2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.cassandra.cql3.functions;

import java.security.AccessControlException;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.Enumeration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.TurboFilterList;
import ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter;
import ch.qos.logback.classic.turbo.TurboFilter;
import org.apache.cassandra.config.DatabaseDescriptor;

/**
 * Custom {@link SecurityManager} and {@link Policy} implementation that only performs access checks
 * if explicitly enabled.
 * 

* This implementation gives no measurable performance panalty * (see see cstar test). * This is better than the penalty of 1 to 3 percent using a standard {@code SecurityManager} with an allow all policy. *

*/ public final class ThreadAwareSecurityManager extends SecurityManager { static final PermissionCollection noPermissions = new PermissionCollection() { public void add(Permission permission) { throw new UnsupportedOperationException(); } public boolean implies(Permission permission) { return false; } public Enumeration elements() { return Collections.emptyEnumeration(); } }; private static final RuntimePermission CHECK_MEMBER_ACCESS_PERMISSION = new RuntimePermission("accessDeclaredMembers"); private static final RuntimePermission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread"); private static final RuntimePermission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup"); private static final RuntimePermission SET_SECURITY_MANAGER_PERMISSION = new RuntimePermission("setSecurityManager"); private static volatile boolean installed; public static void install() { if (installed) return; System.setSecurityManager(new ThreadAwareSecurityManager()); // The default logback configuration in conf/logback.xml allows reloading the // configuration when the configuration file has changed (every 60 seconds by default). // This requires logback to use file I/O APIs. But file I/O is not allowed from UDFs. // I.e. if logback decides to check for a modification of the config file while // executiing a sandbox thread, the UDF execution and therefore the whole request // execution will fail with an AccessControlException. // To work around this, a custom ReconfigureOnChangeFilter is installed, that simply // prevents this configuration file check and possible reload of the configration, // while executing sandboxed UDF code. Logger l = LoggerFactory.getLogger(ThreadAwareSecurityManager.class); ch.qos.logback.classic.Logger logbackLogger = (ch.qos.logback.classic.Logger) l; LoggerContext ctx = logbackLogger.getLoggerContext(); TurboFilterList turboFilterList = ctx.getTurboFilterList(); for (int i = 0; i < turboFilterList.size(); i++) { TurboFilter turboFilter = turboFilterList.get(i); if (turboFilter instanceof ReconfigureOnChangeFilter) { ReconfigureOnChangeFilter reconfigureOnChangeFilter = (ReconfigureOnChangeFilter) turboFilter; turboFilterList.set(i, new SMAwareReconfigureOnChangeFilter(reconfigureOnChangeFilter)); break; } } installed = true; } /** * The purpose of this class is to prevent logback from checking for config file change, * if the current thread is executing a sandboxed thread to avoid {@link AccessControlException}s. */ private static class SMAwareReconfigureOnChangeFilter extends ReconfigureOnChangeFilter { SMAwareReconfigureOnChangeFilter(ReconfigureOnChangeFilter reconfigureOnChangeFilter) { setRefreshPeriod(reconfigureOnChangeFilter.getRefreshPeriod()); setName(reconfigureOnChangeFilter.getName()); setContext(reconfigureOnChangeFilter.getContext()); if (reconfigureOnChangeFilter.isStarted()) { reconfigureOnChangeFilter.stop(); start(); } } protected boolean changeDetected(long now) { if (isSecuredThread()) return false; return super.changeDetected(now); } } static { // // Use own security policy to be easier (and faster) since the C* has no fine grained permissions. // Either code has access to everything or code has access to nothing (UDFs). // This also removes the burden to maintain and configure policy files for production, unit tests etc. // // Note: a permission is only granted, if there is no objector. This means that // AccessController/AccessControlContext collect all applicable ProtectionDomains - only if none of these // applicable ProtectionDomains denies access, the permission is granted. // A ProtectionDomain can have its origin at an oridinary code-source or provided via a // AccessController.doPrivileded() call. // Policy.setPolicy(new Policy() { public PermissionCollection getPermissions(CodeSource codesource) { // contract of getPermissions() methods is to return a _mutable_ PermissionCollection Permissions perms = new Permissions(); if (codesource == null || codesource.getLocation() == null) return perms; switch (codesource.getLocation().getProtocol()) { case "file": // All JARs and class files reside on the file system - we can safely // assume that these classes are "good". perms.add(new AllPermission()); return perms; } return perms; } public PermissionCollection getPermissions(ProtectionDomain domain) { return getPermissions(domain.getCodeSource()); } public boolean implies(ProtectionDomain domain, Permission permission) { CodeSource codesource = domain.getCodeSource(); if (codesource == null || codesource.getLocation() == null) return false; switch (codesource.getLocation().getProtocol()) { case "file": // All JARs and class files reside on the file system - we can safely // assume that these classes are "good". return true; } return false; } }); } private static final ThreadLocal initializedThread = new ThreadLocal<>(); private ThreadAwareSecurityManager() { } private static boolean isSecuredThread() { ThreadGroup tg = Thread.currentThread().getThreadGroup(); if (!(tg instanceof SecurityThreadGroup)) return false; Boolean threadInitialized = initializedThread.get(); if (threadInitialized == null) { initializedThread.set(false); ((SecurityThreadGroup) tg).initializeThread(); initializedThread.set(true); threadInitialized = true; } return threadInitialized; } public void checkAccess(Thread t) { // need to override since the default implementation only checks the permission if the current thread's // in the root-thread-group if (isSecuredThread()) throw new AccessControlException("access denied: " + MODIFY_THREAD_PERMISSION, MODIFY_THREAD_PERMISSION); super.checkAccess(t); } public void checkAccess(ThreadGroup g) { // need to override since the default implementation only checks the permission if the current thread's // in the root-thread-group if (isSecuredThread()) throw new AccessControlException("access denied: " + MODIFY_THREADGROUP_PERMISSION, MODIFY_THREADGROUP_PERMISSION); super.checkAccess(g); } public void checkPermission(Permission perm) { if (!DatabaseDescriptor.enableUserDefinedFunctionsThreads() && !DatabaseDescriptor.allowExtraInsecureUDFs() && SET_SECURITY_MANAGER_PERMISSION.equals(perm)) throw new AccessControlException("Access denied"); if (!isSecuredThread()) return; // required by JavaDriver 2.2.0-rc3 and 3.0.0-a2 or newer // code in com.datastax.driver.core.CodecUtils uses Guava stuff, which in turns requires this permission if (CHECK_MEMBER_ACCESS_PERMISSION.equals(perm)) return; super.checkPermission(perm); } public void checkPermission(Permission perm, Object context) { if (isSecuredThread()) super.checkPermission(perm, context); } public void checkPackageAccess(String pkg) { if (!isSecuredThread()) return; if (!((SecurityThreadGroup) Thread.currentThread().getThreadGroup()).isPackageAllowed(pkg)) { RuntimePermission perm = new RuntimePermission("accessClassInPackage." + pkg); throw new AccessControlException("access denied: " + perm, perm); } super.checkPackageAccess(pkg); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy