Clean up scripting permissions.

Now that groovy is factored out, we contain this dangerous stuff there.

TODO: look into those test hacks inspecting class protection domains, maybe we can
clean that one up too.

TODO: generalize the GroovyCodeSourcePermission to something all script engines check,
before entering accesscontrollerblocks. this way e.g. groovy script cannot coerce
python engine into creating something with more privs if it gets ahold of it... we
should probably protect the aws/gce hacks in the same way.
This commit is contained in:
Robert Muir 2015-09-29 09:54:12 -04:00
parent cbf894a2e7
commit eeeb42abef
5 changed files with 60 additions and 23 deletions

View File

@ -166,6 +166,8 @@ final class Security {
m.put("repository-s3", "org.elasticsearch.plugin.repository.s3.S3RepositoryPlugin"); m.put("repository-s3", "org.elasticsearch.plugin.repository.s3.S3RepositoryPlugin");
m.put("discovery-ec2", "org.elasticsearch.plugin.discovery.ec2.Ec2DiscoveryPlugin"); m.put("discovery-ec2", "org.elasticsearch.plugin.discovery.ec2.Ec2DiscoveryPlugin");
m.put("cloud-gce", "org.elasticsearch.plugin.cloud.gce.CloudGcePlugin"); m.put("cloud-gce", "org.elasticsearch.plugin.cloud.gce.CloudGcePlugin");
m.put("lang-expression", "org.elasticsearch.script.expression.ExpressionPlugin");
m.put("lang-groovy", "org.elasticsearch.script.groovy.GroovyPlugin");
m.put("lang-javascript", "org.elasticsearch.plugin.javascript.JavaScriptPlugin"); m.put("lang-javascript", "org.elasticsearch.plugin.javascript.JavaScriptPlugin");
m.put("lang-python", "org.elasticsearch.plugin.python.PythonPlugin"); m.put("lang-python", "org.elasticsearch.plugin.python.PythonPlugin");
SPECIAL_PLUGINS = Collections.unmodifiableMap(m); SPECIAL_PLUGINS = Collections.unmodifiableMap(m);

View File

@ -57,6 +57,20 @@ grant codeBase "${es.security.plugin.cloud-gce}" {
permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
}; };
grant codeBase "${es.security.plugin.lang-expression}" {
// needed to generate runtime classes
permission java.lang.RuntimePermission "createClassLoader";
};
grant codeBase "${es.security.plugin.lang-groovy}" {
// needed to generate runtime classes
permission java.lang.RuntimePermission "createClassLoader";
// needed by groovy engine
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
// needed by GroovyScriptEngineService to close its classloader (why?)
permission java.lang.RuntimePermission "closeClassLoader";
};
grant codeBase "${es.security.plugin.lang-javascript}" { grant codeBase "${es.security.plugin.lang-javascript}" {
// needed to generate runtime classes // needed to generate runtime classes
permission java.lang.RuntimePermission "createClassLoader"; permission java.lang.RuntimePermission "createClassLoader";
@ -106,6 +120,8 @@ grant codeBase "${es.security.jar.randomizedtesting.junit4}" {
grant { grant {
// Allow executing groovy scripts with codesource of /groovy/script // Allow executing groovy scripts with codesource of /groovy/script
// TODO: make our own general ScriptServicePermission we check instead and
// check-before-createClassLoader for all scripting engines.
permission groovy.security.GroovyCodeSourcePermission "/groovy/script"; permission groovy.security.GroovyCodeSourcePermission "/groovy/script";
// Allow connecting to the internet anywhere // Allow connecting to the internet anywhere
@ -114,15 +130,9 @@ grant {
// Allow read/write to all system properties // Allow read/write to all system properties
permission java.util.PropertyPermission "*", "read,write"; permission java.util.PropertyPermission "*", "read,write";
// needed by scripting engines, etc
permission java.lang.RuntimePermission "createClassLoader";
// needed by lucene SPI currently // needed by lucene SPI currently
permission java.lang.RuntimePermission "getClassLoader"; permission java.lang.RuntimePermission "getClassLoader";
// needed by GroovyScriptEngineService
permission java.lang.RuntimePermission "closeClassLoader";
// needed by Settings // needed by Settings
permission java.lang.RuntimePermission "getenv.*"; permission java.lang.RuntimePermission "getenv.*";
@ -130,13 +140,11 @@ grant {
// otherwise can be provided only to test libraries // otherwise can be provided only to test libraries
permission java.lang.RuntimePermission "modifyThread"; permission java.lang.RuntimePermission "modifyThread";
// needed by groovy scripting // needed by ExceptionSerializationTests and RestTestCase for
// some hackish things they do. otherwise only needed by groovy
// (TODO: clean this up?)
permission java.lang.RuntimePermission "getProtectionDomain"; permission java.lang.RuntimePermission "getProtectionDomain";
// reflection hacks:
// needed by groovy engine
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
// likely not low hanging fruit... // likely not low hanging fruit...
permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.RuntimePermission "accessDeclaredMembers";

View File

@ -43,6 +43,8 @@ import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.search.MultiValueMode;
import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.lookup.SearchLookup;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.ParseException; import java.text.ParseException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Map; import java.util.Map;
@ -91,6 +93,10 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
@Override @Override
public Object compile(String script) { public Object compile(String script) {
// classloader created here
return AccessController.doPrivileged(new PrivilegedAction<Expression>() {
@Override
public Expression run() {
try { try {
// NOTE: validation is delayed to allow runtime vars, and we don't have access to per index stuff here // NOTE: validation is delayed to allow runtime vars, and we don't have access to per index stuff here
return JavascriptCompiler.compile(script); return JavascriptCompiler.compile(script);
@ -98,6 +104,8 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
throw new ScriptException("Failed to parse expression: " + script, e); throw new ScriptException("Failed to parse expression: " + script, e);
} }
} }
});
}
@Override @Override
public SearchScript search(CompiledScript compiledScript, SearchLookup lookup, @Nullable Map<String, Object> vars) { public SearchScript search(CompiledScript compiledScript, SearchLookup lookup, @Nullable Map<String, Object> vars) {

View File

@ -423,6 +423,7 @@ public class MoreExpressionTests extends ESIntegTestCase {
// series of unit test for using expressions as executable scripts // series of unit test for using expressions as executable scripts
public void testExecutableScripts() throws Exception { public void testExecutableScripts() throws Exception {
assumeTrue("test creates classes directly, cannot run with security manager", System.getSecurityManager() == null);
Map<String, Object> vars = new HashMap<>(); Map<String, Object> vars = new HashMap<>();
vars.put("a", 2.5); vars.put("a", 2.5);
vars.put("b", 3); vars.put("b", 3);

View File

@ -20,10 +20,13 @@
package org.elasticsearch.script.groovy; package org.elasticsearch.script.groovy;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import groovy.lang.Binding; import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyClassLoader;
import groovy.lang.Script; import groovy.lang.Script;
import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Scorer;
import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
@ -49,6 +52,8 @@ import org.elasticsearch.search.lookup.SearchLookup;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -99,17 +104,30 @@ public class GroovyScriptEngineService extends AbstractComponent implements Scri
} }
// Groovy class loader to isolate Groovy-land code // Groovy class loader to isolate Groovy-land code
this.loader = new GroovyClassLoader(getClass().getClassLoader(), config); // classloader created here
this.loader = AccessController.doPrivileged(new PrivilegedAction<GroovyClassLoader>() {
@Override
public GroovyClassLoader run() {
return new GroovyClassLoader(getClass().getClassLoader(), config);
}
});
} }
@Override @Override
public void close() { public void close() {
loader.clearCache(); loader.clearCache();
// close classloader here (why do we do this?)
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
try { try {
loader.close(); loader.close();
} catch (IOException e) { } catch (IOException e) {
logger.warn("Unable to close Groovy loader", e); logger.warn("Unable to close Groovy loader", e);
} }
return null;
}
});
} }
@Override @Override