diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index f676260a64..f3a2d2df94 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -334,8 +334,21 @@ public class Config * "tell" a user that there's something really wrong with the UDF. * When you disable async UDF execution, users MUST pay attention to read-timeouts since these may indicate * UDFs that run too long or forever - and this can destabilize the cluster. + * + * This requires allow_insecure_udfs to be true */ public boolean enable_user_defined_functions_threads = true; + + /** + * Set this to true to allow running insecure UDFs. + */ + public boolean allow_insecure_udfs = false; + + /** + * Set this to allow UDFs accessing java.lang.System.* methods, which basically allows UDFs to execute any arbitrary code on the system. + */ + public boolean allow_extra_insecure_udfs = false; + /** * Time in milliseconds after a warning will be emitted to the log and to the client that a UDF runs too long. * (Only valid, if enable_user_defined_functions_threads==true) diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index 867cf5922f..ca946ce355 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -755,6 +755,12 @@ public class DatabaseDescriptor if (seedProvider.getSeeds().size() == 0) throw new ConfigurationException("The seed provider lists no seeds.", false); + if (!conf.allow_insecure_udfs && !conf.enable_user_defined_functions_threads) + throw new ConfigurationException("To be able to set enable_user_defined_functions_threads: false you need to set allow_insecure_udfs: true - this is an unsafe configuration and is not recommended."); + + if (conf.allow_extra_insecure_udfs) + logger.warn("Allowing java.lang.System.* access in UDFs is dangerous and not recommended. Set allow_extra_insecure_udfs: false to disable."); + if (conf.user_defined_function_fail_timeout < 0) throw new ConfigurationException("user_defined_function_fail_timeout must not be negative", false); if (conf.user_defined_function_warn_timeout < 0) @@ -2175,6 +2181,16 @@ public class DatabaseDescriptor conf.user_defined_function_warn_timeout = userDefinedFunctionWarnTimeout; } + public static boolean allowInsecureUDFs() + { + return conf.allow_insecure_udfs; + } + + public static boolean allowExtraInsecureUDFs() + { + return conf.allow_extra_insecure_udfs; + } + public static boolean enableMaterializedViews() { return conf.enable_materialized_views; diff --git a/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java b/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java index 13d1945f43..782d500fee 100644 --- a/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java +++ b/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java @@ -36,6 +36,7 @@ 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 @@ -69,6 +70,7 @@ public final class ThreadAwareSecurityManager extends SecurityManager 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; @@ -236,6 +238,9 @@ public final class ThreadAwareSecurityManager extends SecurityManager public void checkPermission(Permission perm) { + if (!DatabaseDescriptor.enableUserDefinedFunctionsThreads() && !DatabaseDescriptor.allowExtraInsecureUDFs() && SET_SECURITY_MANAGER_PERMISSION.equals(perm)) + throw new AccessControlException("Access denied"); + if (!isSecuredThread()) return; diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java index f15155409c..e45d6b5ae3 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java @@ -149,6 +149,11 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct "java/util/zip/", }; + private static final String[] disallowedPatternsSyncUDF = + { + "java/lang/System.class" + }; + static boolean secureResource(String resource) { while (resource.startsWith("/")) @@ -157,14 +162,26 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct for (String allowed : allowedPatterns) if (resource.startsWith(allowed)) { - // resource is in allowedPatterns, let's see if it is not explicitly disallowed for (String disallowed : disallowedPatterns) + { if (resource.startsWith(disallowed)) { logger.trace("access denied: resource {}", resource); return false; } + } + if (!DatabaseDescriptor.enableUserDefinedFunctionsThreads() && !DatabaseDescriptor.allowExtraInsecureUDFs()) + { + for (String disallowed : disallowedPatternsSyncUDF) + { + if (resource.startsWith(disallowed)) + { + logger.trace("access denied: resource {}", resource); + return false; + } + } + } return true; }