/*
* RHQ Management Platform
* Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.client.security.test;
import java.io.IOException;
import javax.naming.Context;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.testng.Assert;
import org.testng.annotations.Test;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.ScriptableAbstractEJB3Test;
import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.system.SystemManagerBean;
import org.rhq.enterprise.server.util.LookupUtil;
/**
*
*
* @author Lukas Krejci
*/
@Test
public class JndiAccessTest extends ScriptableAbstractEJB3Test {
private static final boolean SECURITY_MANAGER_IS_ENABLED = (System.getProperty("java.security.manager") != null);
private static void failIfSecurityManagerEnabled(String msg) {
failIfSecurityManagerEnabled(msg, null);
}
private static void failIfSecurityManagerEnabled(String msg, Throwable t) {
if (SECURITY_MANAGER_IS_ENABLED) {
Assert.fail(msg, t);
} else {
System.out.println("This test would have failed, but the security manager is disabled so it will pass: "
+ msg);
if (t != null) {
t.printStackTrace();
}
}
}
public void testScriptCantOverrideSystemProperties() throws Exception {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
try {
engine.eval("java.lang.System.setProperty('java.naming.factory.url.pkgs', 'counterfeit');");
} catch (ScriptException e) {
Assert.assertTrue(
e.getMessage().contains(
"access denied (\"java.util.PropertyPermission\" \"java.naming.factory.url.pkgs\" \"write\")"),
"The script shouldn't have write access to the system properties.");
}
}
public void testEjbsAccessibleThroughPrivilegedCode() {
LookupUtil.getSubjectManager().getOverlord();
}
public void testEjbsAccessibleThroughLocalClient() throws ScriptException, IOException {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
engine.eval("SubjectManager.getSubjectByName('rhqadmin');");
}
public void testLocalEjbsInaccessibleThroughJndiLookup() throws ScriptException, IOException {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
String jndiName = "java:global/rhq/rhq-server/"
+ SystemManagerBean.class.getSimpleName()
+ "!"
+ SystemManagerBean.class.getName().replace("Bean", "Local");
try {
engine.eval(""
+ "var ctx = new javax.naming.InitialContext();\n"
+ "var systemManager = ctx.lookup('"
+ jndiName
+ "');\n"
+ "systemManager.isDebugModeEnabled();");
failIfSecurityManagerEnabled("The script shouldn't have been able to call local SLSB method.");
} catch (ScriptException e) {
checkIsDesiredSecurityException(e);
}
}
public void testLocalEjbsInaccessibleThroughJndiLookupWithCustomUrlPackages() throws ScriptException, IOException {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
String jndiName = "java:global/rhq/rhq-server/"
+ SystemManagerBean.class.getSimpleName()
+ "!"
+ SystemManagerBean.class.getName().replace("Bean", "Local");
try {
engine.eval(""
+ "var env = new java.util.Hashtable();\n"
+ "env.put('"
+ Context.URL_PKG_PREFIXES
+ "', 'org.jboss.as.naming.interfaces');\n"
+ "var ctx = new javax.naming.InitialContext(env);\n"
+ "var systemManager = ctx.lookup('"
+ jndiName
+ "');\n"
+ "systemManager.isDebugModeEnabled();");
failIfSecurityManagerEnabled("The script shouldn't have been able to call local SLSB method.");
} catch (ScriptException e) {
checkIsDesiredSecurityException(e);
}
}
public void testRemoteEjbsInaccessibleThroughJndiLookup() throws ScriptException, IOException {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
String jndiName = "java:global/rhq/rhq-server/"
+ SystemManagerBean.class.getSimpleName()
+ "!"
+ SystemManagerBean.class.getName().replace("Bean", "Remote");
try {
engine.eval(""
+ "var ctx = new javax.naming.InitialContext();\n"
+ "var systemManager = ctx.lookup('"
+ jndiName
+ "');\n"
+ "systemManager.getSystemSettings(subject);");
failIfSecurityManagerEnabled("The script shouldn't have been able to call remote SLSB method directly.");
} catch (ScriptException e) {
checkIsDesiredSecurityException(e);
}
}
public void testScriptCantUseSessionManagerMethods() throws Exception {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
final ScriptEngine engine = getEngine(overlord);
class G {
private String sessionManager = ""
+ "org.rhq.enterprise.server.auth.SessionManager.getInstance().";
public void testInvoke(String methodCall) throws ScriptException {
String code = sessionManager + methodCall;
try {
engine.eval(code);
failIfSecurityManagerEnabled("The script shouldn't have been able to call a method on a SessionManager: "
+ methodCall);
} catch (ScriptException e) {
checkIsDesiredSecurityException(e);
}
}
};
G manager = new G();
manager.testInvoke("getLastAccess(0);");
manager.testInvoke("getOverlord()");
manager.testInvoke("getSubject(2);");
manager.testInvoke("invalidate(0);");
manager.testInvoke("invalidate(\"\");");
manager.testInvoke("put(new org.rhq.core.domain.auth.Subject());");
manager.testInvoke("put(new org.rhq.core.domain.auth.Subject(), 0);");
}
public void testScriptCantObtainRawJDBCConnectionsWithoutCredentials() throws Exception {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
try {
engine.eval(""
+ "var ctx = new javax.naming.InitialContext();\n"
+ "var datasource = ctx.lookup('" + RHQConstants.DATASOURCE_JNDI_NAME + "');\n"
+ "con = datasource.getConnection();");
failIfSecurityManagerEnabled("The script shouldn't have been able to obtain the datasource from the JNDI.");
} catch (ScriptException e) {
checkIsDesiredSecurityException(e);
}
}
public void testScriptCantUseEntityManager() throws Exception {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
try {
engine.eval(""
+ "var ctx = new javax.naming.InitialContext();\n"
+ "var entityManagerFactory = ctx.lookup('" + RHQConstants.ENTITY_MANAGER_JNDI_NAME + "');\n"
+ "var entityManager = entityManagerFactory.createEntityManager();\n"
+ "entityManager.find(java.lang.Class.forName('org.rhq.core.domain.resource.Resource'), java.lang.Integer.valueOf('10001'));");
failIfSecurityManagerEnabled("The script shouldn't have been able to use the EntityManager.");
} catch (ScriptException e) {
checkIsDesiredSecurityException(e);
}
//try harder with manually specifying the initial context factory
try {
engine.eval(""
+ "var env = new java.util.Hashtable();"
+ "env.put('java.naming.factory.initial', 'org.jboss.as.naming.InitialContextFactory');"
//+ "env.put('java.naming.factory.url.pkgs', 'org.jboss.naming:org.jnp.interfaces');"
+ "var ctx = new javax.naming.InitialContext(env);\n"
+ "var entityManagerFactory = ctx.lookup('" + RHQConstants.ENTITY_MANAGER_JNDI_NAME + "');\n"
+ "var entityManager = entityManagerFactory.createEntityManager();\n"
+ "entityManager.find(java.lang.Class.forName('org.rhq.core.domain.resource.Resource'), java.lang.Integer.valueOf('10001'));");
failIfSecurityManagerEnabled("The script shouldn't have been able to use the EntityManager even using custom initial context factory.");
} catch (ScriptException e) {
checkIsDesiredSecurityException(e);
}
}
public void testProxyFactoryWorksWithSecuredScriptEngine() throws Exception {
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
ScriptEngine engine = getEngine(overlord);
try {
engine.eval("var resource = ProxyFactory.getResource(10001);");
} catch (ScriptException e) {
//if the script fails (there is no resource with ID 10001)
//it should not be because of an access control exception
checkIsNotASecurityException(e);
}
}
//THIS IS A NEW REQUIREMENT THAT DOESN'T CURRENTLY WORK...
// public void testInitialContextFactoryBuilderNotReplaceableUsingScripts() throws Exception {
// Subject overlord = LookupUtil.getSubjectManager().getOverlord();
//
// ScriptEngine engine = getEngine(overlord);
//
// try {
// engine.eval(""
// + "var mgrCls = java.lang.Class.forName('javax.naming.spi.NamingManager');\n"
// + "var fld = mgrCls.getDeclaredField('initctx_factory_builder');\n"
// + "fld.setAccessible(true);\n"
// + "fld.set(null, null);\n");
//
// Assert.fail("It should not be possible to use reflection to reset the initial context factory builder.");
// } catch (ScriptException e) {
// checkIsDesiredSecurityException(e);
// } finally {
// //restore the default behavior
// NamingHack.bruteForceInitialContextFactoryBuilder();
// }
// }
private static void checkIsDesiredSecurityException(ScriptException e) {
String message = e.getMessage();
String permissionTrace = "org.rhq.allow.server.internals.access";
if (!message.contains(permissionTrace)) {
failIfSecurityManagerEnabled(
"The script exception doesn't seem to be caused by the AllowRhqServerInternalsAccessPermission security exception.",
e);
}
}
private static void checkIsNotASecurityException(ScriptException e) {
String message = e.getMessage();
String permissionTrace = "org.rhq.allow.server.internals.access";
if (message.contains(permissionTrace)) {
Assert
.fail(
"The script exception does seem to be caused by the AllowRhqServerInternalsAccessPermission security exception although it shouldn't. ",
e);
}
}
}