package com.hp.hpl.jena.grddl.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilePermission;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.ReflectPermission;
import java.net.NetPermission;
import java.net.SocketPermission;
import java.net.URL;
import java.rmi.server.UID;
import java.security.Permission;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SecurityPermission;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.PropertyPermission;
import java.util.Set;
import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.PreparedStylesheet;
import net.sf.saxon.TransformerFactoryImpl;
import net.sf.saxon.event.Sender;
import net.sf.saxon.functions.SystemFunction;
import net.sf.saxon.functions.SystemFunctionLibrary;
import net.sf.saxon.functions.SystemProperty;
import net.sf.saxon.style.XSLGeneralIncorporate;
import net.sf.saxon.style.XSLStylesheet;
import net.sf.saxon.type.TypeHierarchy;
import org.apache.xerces.impl.XMLEntityManager;
import sun.net.NetworkClient;
import sun.net.www.http.HttpClient;
import sun.net.www.http.KeepAliveCache;
import com.hp.hpl.jena.rdf.model.AnonId;
import com.hp.hpl.jena.shared.BrokenException;
public class SecManager extends SecurityManager {
static {
if (System.getSecurityManager() == null)
System.setSecurityManager(new SecManager());
}
private class SecContext {
final boolean untrusted;
final Object fromSuper;
SecContext(Object fS) {
fromSuper = fS;
untrusted = untrustedThreadGroup();
}
}
public SecManager() {
}
static private ThreadGroup notTrustedTG = new ThreadGroup("untrusted");
private static int sb = 0;
boolean untrustedThreadGroup() {
return Thread.currentThread().getThreadGroup() == notTrustedTG;
}
public Object getSecurityContext() {
return new SecContext(super.getSecurityContext());
}
// static private String filesep = System.getProperty("file.separator");
static private String javaHome = System.getProperty("java.home");
static private Set<String> onClassPath = new HashSet<String>();
static private Set<String> allowProps = new HashSet<String>();
static private Set<String> nonJarsOnClassPath = new HashSet<String>();
static {
String classpath = System.getProperty("java.class.path");
String pathsep = System.getProperty("path.separator");
String cp[] = classpath.split(pathsep);
for (int i = 0; i < cp.length; i++) {
File f = new File(cp[i]);
try {
boolean isJar = false;
String absP = f.getAbsolutePath();
onClassPath.add(absP);
String absPLC = absP.toLowerCase();
if (absPLC.endsWith(".jar"))
isJar = true;
if (absPLC.endsWith(".zip"))
isJar = true;
if (!isJar)
nonJarsOnClassPath.add(absP);
String canP = f.getCanonicalPath();
onClassPath.add(canP);
if (!isJar)
nonJarsOnClassPath.add(canP);
} catch (IOException e) {
// TODO panic message
e.printStackTrace();
}
}
allowProps.add("javax.xml.parsers.SAXParserFactory");
allowProps.add("org.apache.xerces.xni.parser.XMLParserConfiguration");
allowProps.add("java.home");
allowProps.add("line.separator");
allowProps.add("jaxp.debug");
allowProps.add("java.version");
}
public void checkPermission(Permission p) {
checkPerm(p);
}
private void checkPerm(Permission p) {
String actions = p.getActions();
String name = p.getName();
if (untrustedThreadGroup()) {
try {
checkPerm(p, actions, name);
} catch (SecurityException e) {
trace(false, p, name, actions);
throw e;
}
trace(true, p, name, actions);
}
}
private void trace(boolean permit, Permission p, String name, String actions) {
if (true)
return;
if (p instanceof FilePermission)
return;
if (permit)
return;
System.err.println((permit ? "Allow: " : "Deny: ") + p);
if ((!permit)
// ||name.equals("createClassLoader")
)
dumpClassContext();
}
private void checkPerm(Permission p, String actions, String name) {
if (p instanceof FilePermission) {
File f = new File(name);
if (actions.equals("read")
&& (onClassPath.contains(name)
|| onClassPath.contains(f.getAbsolutePath()) || startsWithOKDir(name)))
return;
}
if (p instanceof PropertyPermission) {
if (actions.equals("read")) {
if (!checkStack(noSystemProperty)) {
if (allowProps.contains(name))
return;
if (p.getName().toLowerCase().contains("proxy"))
return;
if (isJenaAnonId(getClassContext())) {
return;
}
}
}
}
if (p instanceof NetPermission
&& (name.equals("getCookieHandler")
|| name.equals("getResponseCache") || name
.equals("getProxySelector"))) {
if (checkStack(xmlParserStack))
return;
if (checkStack(preparedStylesheetStack))
return;
if (checkStack(importStack))
return;
}
if (p instanceof SocketPermission) {
if (checkStack(xmlParserStack))
return;
if (checkStack(preparedStylesheetStack))
return;
if (checkStack(importStack))
return;
}
if (p instanceof ReflectPermission
&& name.equals("suppressAccessChecks")) {
if (isJenaAnonId(getClassContext())) {
return;
}
if (checkStack(classLoaderMiniStack))
return;
if (checkStack(accesibleMiniStack))
return;
if (checkStack(accesibleMethodStack))
return;
if (checkStack(methodInvokeStack))
return;
}
if (p instanceof RuntimePermission
&& name.startsWith("accessClassInPackage.")) {
// TODO more specific
if (isJenaAnonId(getClassContext())) {
return;
}
}
if (p instanceof RuntimePermission
&& name.equals("writeFileDescriptor")) {
if (checkStack(preparedStyleSheetWriteSocket))
return;
if (checkStack(xmlParserWriteSocket))
return;
if (checkStack(importWriteSocket))
return;
}
if (p instanceof RuntimePermission && name.equals("readFileDescriptor")) {
if (checkStack(preparedStyleSheetReadSocket))
return;
if (checkStack(xmlParserReadSocket))
return;
if (checkStack(importReadSocket))
return;
}
if (p instanceof RuntimePermission && name.equals("createClassLoader")) {
if (checkStack(classLoaderMiniStack))
return;
if (checkStack(methodInvokeStack))
return;
}
if (p instanceof RuntimePermission
&& (name.equals("modifyThreadGroup") || name
.equals("modifyThread"))) {
if (checkStack(httpClientStack))
return;
}
if (p instanceof SecurityPermission
&& (name.startsWith("getProperty.") || name
.startsWith("putProviderProperty."))) {
if (isJenaAnonId(getClassContext()))
return;
}
throw new SecurityException(p.toString());
}
private boolean checkStack(String[][] pattern) {
Class st[] = getClassContext();
int next = 0;
int matchedSoFar = 0;
for (int i = 0; i < st.length; i++) {
if (next > pattern.length)
return false;
if (pattern[next][0].equals("weak")
&& match(pattern[next + 1][1], st[i].getName())) {
next++;
}
if (pattern[next][0].equals("return")) {
if (pattern[next][1].equals("allowedNewInstance")) {
// System.err.println("Allowing new instance from: "+
// st[i].getName());
return allowedNewInstance.contains(st[i].getName());
}
throw new BrokenException("Internal error: detail: "
+ pattern[next][1]);
}
while (!match(pattern[next][1], st[i].getName())) {
if ((pattern[next][0].equals("some") && matchedSoFar > 0)
|| pattern[next][0].equals("optional")
|| pattern[next][0].equals("any")) {
next++;
matchedSoFar = 0;
continue;
}
return false;
}
if (pattern[next][0].equals("1")
|| pattern[next][0].equals("optional")) {
next++;
matchedSoFar = 0;
} else {
matchedSoFar++;
}
}
return next==pattern.length || next==pattern.length-1;
}
private static boolean match(String pattern, String name) {
boolean result = name.startsWith(pattern);
return result;
}
private boolean startsWithOKDir(String name) {
if (name.startsWith(javaHome)) {
return true;
}
Iterator<String> it = nonJarsOnClassPath.iterator();
while (it.hasNext()) {
if (name.startsWith(it.next()))
return true;
}
return false;
}
private boolean isJenaAnonId(Class[] classContext) {
int anonId = -1;
for (int i = 0; i < classContext.length; i++)
if (classContext[i] == AnonId.class) {
anonId = i;
break;
}
if (anonId == -1)
return false;
dumpClassContext();
// TODO: check stack??
if (true)
return true;
for (int i = 0; i < anonId; i++) {
Class c = classContext[i];
if (c == SecManager.class)
continue;
if (c == UID.class)
continue;
if (c == Security.class)
continue;
if (c == SecureRandom.class)
continue;
// if (c==Providers.class)
// continue;
// if (c==ProviderList.class)
// continue;
if (c.getName().startsWith("sun.security.jca.Provider"))
continue;
// continue;
return false;
}
return true;
}
public void checkPermission(Permission p, Object o) {
checkPerm(p, o);
}
public void checkPerm(Permission p, Object o) {
SecContext sc = (SecContext) o;
if (sc.untrusted) {
try {
super.checkPermission(p, sc.fromSuper);
} catch (SecurityException e) {
System.err.println("Deny: " + p);
throw e;
}
System.err.println("Permit: " + p);
}
}
private void dumpClassContext() {
Class st[] = getClassContext();
for (int i = 0; i < st.length; i++)
System.err.println(i + ": " + st[i].getName());
}
// TODO: max stack size param
public static void sandbox(Runnable r) {
Thread t = new Thread(notTrustedTG, r, "SandBoxed-" + sb++);
t.start();
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static final String[][] socketOutputSubstack = new String[][] {
{ "1", FileOutputStream.class.getName() },
{ "1", "java.net.SocketOutputStream" }, };
private static final String[][] socketInputSubstack = new String[][] {
{ "1", FileInputStream.class.getName() },
{ "1", "java.net.SocketInputStream" }, };
private static final String[][] xmlParserSubstack = new String[][] {
{ "any", "java.net." },
{ "optional", NetworkClient.class.getName() },
{ "any", HttpClient.class.getName() },
{ "some", "sun.net.www.protocol.http." },
{ "optional", URL.class.getName() },
{ "some", XMLEntityManager.class.getName() },
{ "some", "org.apache.xerces." },
{ "some", Sender.class.getName() },
{ "1", Controller.class.getName() } };
private static final String[][] preparedStyleSheetSubstack = new String[][] {
{ "any", "java.net." },
{ "optional", NetworkClient.class.getName() },
{ "any", HttpClient.class.getName() },
{ "some", "sun.net.www.protocol.http." },
{ "optional", URL.class.getName() },
{ "some", XMLEntityManager.class.getName() },
{ "some", "org.apache.xerces." },
{ "some", Sender.class.getName() },
{ "some", PreparedStylesheet.class.getName() },
{ "some", TransformerFactoryImpl.class.getName() } };
private static final String[][] importSubstack = new String[][] {
{ "any", "java.net." },
{ "optional", NetworkClient.class.getName() },
{ "any", HttpClient.class.getName() },
{ "some", "sun.net.www.protocol.http." },
{ "optional", URL.class.getName() },
{ "some", XMLEntityManager.class.getName() },
{ "some", "org.apache.xerces." },
{ "some", Sender.class.getName() },
{ "some", PreparedStylesheet.class.getName() },
{ "some", XSLGeneralIncorporate.class.getName() },
{ "some", XSLStylesheet.class.getName() },
{ "some", PreparedStylesheet.class.getName() },
{ "some", TransformerFactoryImpl.class.getName() } };
static private String[][] xmlParserStack = topTail(xmlParserSubstack);
static private String[][] preparedStylesheetStack = topTail(preparedStyleSheetSubstack);
static private String[][] importStack = topTail(importSubstack);
static private String[][] xmlParserWriteSocket = topTail(
concat(socketOutputSubstack, xmlParserSubstack));
static private String[][] preparedStyleSheetWriteSocket = topTail(concat(
socketOutputSubstack, preparedStyleSheetSubstack));
static private String[][] importWriteSocket = topTail(
concat(socketOutputSubstack, importSubstack));
static private String[][] xmlParserReadSocket = topTail(
concat(socketInputSubstack, xmlParserSubstack));
static private String[][] importReadSocket = topTail(
concat(socketInputSubstack, importSubstack));
static private String[][] preparedStyleSheetReadSocket = topTail(concat(
socketInputSubstack, preparedStyleSheetSubstack));
static private String[][] noSystemProperty = new String[][] {
{ "weak", "" }, { "1", SystemProperty.class.getName() },
{ "some", "" }, };
static private String[][] classLoaderMiniStack = new String[][] {
{ "weak", "" }, { "1", Constructor.class.getName() },
{ "1", Class.class.getName() }, { "1", Class.class.getName() },
{ "return", "allowedNewInstance" }, };
static private String[][] accesibleMiniStack = new String[][] {
{ "weak", "" },
{ "1", AccessibleObject.class.getName() },
{ "1", Class.class.getName() },
{ "1", Class.class.getName() },
{ "1", Class.class.getName() },
{ "return", "allowedNewInstance" }, };
static private String[][] accesibleMethodStack = new String[][] {
{ "weak", "" },
{ "1", AccessibleObject.class.getName() },
{ "1", Class.class.getName() },
{ "1", Class.class.getName() },
{ "1", Class.class.getName() },
{ "some", "sun.reflect.MethodAccessorGenerator" },
{ "some", "" }, };
static private String[][] httpClientStack = new String[][] {
{ "some", SecManager.class.getName() },
{ "optional", SecurityManager.class.getName() },
{ "any", ThreadGroup.class.getName() },
{ "any", Thread.class.getName() },
{ "any", KeepAliveCache.class.getName() },
{ "some", HttpClient.class.getName() }, { "some", "" } };
static private String[][] methodInvokeStack = new String[][] {
{ "weak", "" },
{ "some", "sun.reflect.ClassDefiner" },
{ "some", "sun.reflect.MethodAccessorGenerator" },
{ "some", "" } };
private static String[][] topTail(String[][] strings) {
String[][] result = new String[strings.length + 4][];
System.arraycopy(strings, 0, result, 2, strings.length);
result[0] = new String[] { "some", SecManager.class.getName() };
result[1] = new String[] { "optional", SecurityManager.class.getName() };
result[result.length - 1] = new String[] { "1", Thread.class.getName() };
result[result.length - 2] = new String[] { "some",
GRDDL.class.getName() };
return result;
}
private static final Set<String> allowedNewInstance = new HashSet<String>(
Arrays.asList(new String[] {
"org.apache.xerces.impl.dv.ObjectFactory",
SystemFunction.class.getName(),
SystemFunctionLibrary.class.getName(),
Configuration.class.getName(),
"javax.xml.parsers.FactoryFinder",
TypeHierarchy.class.getName(), }));
private static String[][] concat(String[][] a, String[][] b) {
String rslt[][] = new String[a.length + b.length][];
System.arraycopy(a, 0, rslt, 0, a.length);
System.arraycopy(b, 0, rslt, a.length, b.length);
return rslt;
}
}
/*
* java.net.ResponseCache 5: sun.net.www.protocol.http.HttpURLConnection$3 6:
* sun.net.www.protocol.http.HttpURLConnection 7:
* sun.net.www.protocol.http.Handler 8: sun.net.www.protocol.http.Handler 9:
* java.net.URL 10: org.apache.xerces.impl.XMLEntityManager 11:
* org.apache.xerces.impl.XMLEntityManager 12:
* org.apache.xerces.impl.XMLEntityManager 13:
* org.apache.xerces.impl.XMLDTDScannerImpl 14:
* org.apache.xerces.impl.XMLDocumentScannerImpl$DTDDispatcher 15:
* org.apache.xerces.impl.XMLDocumentFragmentScannerImpl 16:
* org.apache.xerces.parsers.XML11Configuration 17:
* org.apache.xerces.parsers.XML11Configuration 18:
* org.apache.xerces.parsers.XMLParser 19:
* org.apache.xerces.parsers.AbstractSAXParser 20: net.sf.saxon.event.Sender 21:
* net.sf.saxon.event.Sender 22: net.sf.saxon.event.Sender 23:
* net.sf.saxon.Controller 24: com.hp.hpl.jena.grddl.impl.GRDDL$1 25:
* com.hp.hpl.jena.grddl.impl.GRDDL$2 26: java.lang.Thread
*/