Author: peter_firmstone Date: Sun Feb 6 06:39:58 2011 New Revision: 1067602
URL: http://svn.apache.org/viewvc?rev=1067602&view=rev Log: Proxy Isolation refactoring Added: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/DiscoveryV2ReadUncastResponseTask.java (with props) incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolatedExecutor.java (with props) incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolationException.java (with props) incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/ProxyIsolationHandler.java (with props) incubator/river/jtsk/skunk/pepe/test/src/org/apache/river/impl/security/dos/ incubator/river/jtsk/skunk/pepe/test/src/org/apache/river/impl/security/dos/IsolateTest.java (with props) incubator/river/jtsk/skunk/pepe/test/src/tests/support/EndlessLoopTask.java (with props) Removed: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/ProxyIsolate.java Added: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/DiscoveryV2ReadUncastResponseTask.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/DiscoveryV2ReadUncastResponseTask.java?rev=1067602&view=auto ============================================================================== --- incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/DiscoveryV2ReadUncastResponseTask.java (added) +++ incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/DiscoveryV2ReadUncastResponseTask.java Sun Feb 6 06:39:58 2011 @@ -0,0 +1,73 @@ +/* + * 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.river.impl.security.dos; + +import com.sun.jini.discovery.DiscoveryProtocolException; +import com.sun.jini.discovery.UnicastResponse; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.util.Collection; +import java.util.concurrent.Callable; +import net.jini.core.lookup.ServiceRegistrar; +import net.jini.io.MarshalledInstance; + +class DiscoveryV2ReadUncastResponseTask implements Callable<UnicastResponse> { + + private final InputStream in; + private final ClassLoader defaultLoader; + private final boolean verifyCodebaseIntegrity; + private final ClassLoader verifierLoader; + private final Collection context; + + DiscoveryV2ReadUncastResponseTask(InputStream in, + ClassLoader defaultLoader, + boolean verifyCodebaseIntegrity, + ClassLoader verifierLoader, + Collection context) + { + this.in = in; + this.defaultLoader = defaultLoader; + this.verifyCodebaseIntegrity = verifyCodebaseIntegrity; + this.verifierLoader = verifierLoader; + this.context = context; + } + + public UnicastResponse call() throws Exception { + try { + DataInput din = new DataInputStream(in); + String host = din.readUTF(); + + // read LUS port + int port = din.readUnsignedShort(); + String[] groups = new String[din.readInt()]; + for (int i = 0; i < groups.length; i++) { + groups[i] = din.readUTF(); + } + MarshalledInstance mi = (MarshalledInstance) new ObjectInputStream(in).readObject(); + ServiceRegistrar reg = + (ServiceRegistrar) mi.get(defaultLoader, + verifyCodebaseIntegrity, verifierLoader, context); + return new UnicastResponse(host, port, groups, reg); + } catch (RuntimeException e) { + throw new DiscoveryProtocolException(null, e); + } + } +} Propchange: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/DiscoveryV2ReadUncastResponseTask.java ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolatedExecutor.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolatedExecutor.java?rev=1067602&view=auto ============================================================================== --- incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolatedExecutor.java (added) +++ incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolatedExecutor.java Sun Feb 6 06:39:58 2011 @@ -0,0 +1,114 @@ +/* + * 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.river.impl.security.dos; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Performs Callable tasks in an isolated thread, which is terminated + * if any Errors are experienced. This may take some time to terminate the thread + * as it is not forcibly stopped. The thread priority is minimal and a daemon + * thread so it would take a number of unterminated threads to cause problems. + * + * The caller can give up on the execution of the task by setting a timeout. + * + * @param T + * @author peter + */ +public class IsolatedExecutor<T> { + private volatile ExecutorService isolateExecutor; + private volatile Throwable thrown; + + public IsolatedExecutor() + { + thrown = null; + isolateExecutor = Executors.newSingleThreadExecutor(new Factory(this)); + } + + /** + * Process Callable tasks in isolation. + * If an ExecutionException has been thrown, the task should be abandoned. + * If an IsolationException has been thrown, an Error has occurred with + * the IsolatedExecutor and a new object should be created. + * + * @param task + * @param timeout + * @param timeUnit + * @return + * @throws org.apache.river.impl.security.dos.IsolatedExecutor.IsolationException + * @throws java.util.concurrent.ExecutionException + */ + public T process(Callable<T> task, long timeout, TimeUnit timeUnit) throws + IsolationException, ExecutionException, InterruptedException, + TimeoutException { + if ( thrown != null){ + throw new IsolationException ("Isolate has" + + "experienced an error during processing and " + + "cannot perform further " + + "tasks", thrown); + } + return isolateExecutor.submit(task).get(timeout, timeUnit); + } + + private void terminate(Throwable e){ + thrown = e; + isolateExecutor.shutdownNow(); + } + + private static class Factory implements ThreadFactory{ + private static final ThreadGroup tg = new ThreadGroup("Isolated"); + { + tg.setDaemon(true); + tg.setMaxPriority(4); + } + private final IsolatedExecutor isolate; + Factory(IsolatedExecutor uri){ + isolate = uri; + } + + public Thread newThread(Runnable r) { + Thread t = new Thread(tg, r); + t.setUncaughtExceptionHandler(new ExceptionHandler(isolate)); + return t; + } + } + + private static class ExceptionHandler implements Thread.UncaughtExceptionHandler{ + private final IsolatedExecutor isolate; + + ExceptionHandler(IsolatedExecutor uri){ + isolate = uri; + } + + public void uncaughtException(Thread t, Throwable e) { + // For all other Exceptions we let the ExecutorService handle it. + if ( e instanceof Error){ + // Do we want to take different actions based on the error? + isolate.terminate(e); + } + } + + } +} Propchange: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolatedExecutor.java ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolationException.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolationException.java?rev=1067602&view=auto ============================================================================== --- incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolationException.java (added) +++ incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolationException.java Sun Feb 6 06:39:58 2011 @@ -0,0 +1,22 @@ +package org.apache.river.impl.security.dos; + +public class IsolationException extends Exception { + + private static final long serialVersionUID = 1L; + + public IsolationException() { + super(); + } + + public IsolationException(String message) { + super(message); + } + + public IsolationException(String message, Throwable cause) { + super(message, cause); + } + + public IsolationException(Throwable cause) { + super(cause); + } +} Propchange: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/IsolationException.java ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/ProxyIsolationHandler.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/ProxyIsolationHandler.java?rev=1067602&view=auto ============================================================================== --- incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/ProxyIsolationHandler.java (added) +++ incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/ProxyIsolationHandler.java Sun Feb 6 06:39:58 2011 @@ -0,0 +1,174 @@ +/* + * 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.river.impl.security.dos; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import net.jini.io.MarshalledInstance; + +/** + * A preliminary experiment into Isolating a Smart Proxy. + * + * I think I'll investigate creating a Permission for additional threads + * for improved performance and also creating an event model so clients + * don't need to wait for remote method returns. I'll create a new interface + * for this, that can be implemented by services directly too. + * + * Alternative, only maintain this data structure for as long as it takes + * to verify proxy trust, then return proxy to the client or have the client + * thread call the smart proxy via the reflection proxy directly. + * + * REMIND: Investigate subclassed return values, this is simple, if the + * smart proxy is confined to it's own ClassLoader, we just check the class + * of the object returned isn't from that ClassLoader, unless the smart proxy + * has a ServiceAPISubclassPermission or something like that. The reason for + * checking that remote code hasn't escaped the IsolatedExecutor, is that we want to + * ensure it cannot harm the application threads. + * + * @author Peter Firmstone + */ +public class ProxyIsolationHandler implements InvocationHandler { + private volatile Object smartProxy; + private final IsolatedExecutor isolate; + private final long timeout; + private final TimeUnit unit; + private final ClassLoader proxyLoader; + + @SuppressWarnings("unchecked") + public ProxyIsolationHandler(MarshalledInstance proxy, + ClassLoader defaultLoader, + boolean verifyCodebaseIntegrity, + ClassLoader verifierLoader, + Collection context, + long timeout, + TimeUnit timeUnit + ) throws TimeoutException, InterruptedException, ExecutionException, IsolationException{ + if (defaultLoader == null) throw new IsolationException("A default " + + "ClassLoader must be defined for effective isolation"); + isolate = new IsolatedExecutor(); + this.timeout = timeout; + unit = timeUnit; + UnmarshallProxyTask task = + new UnmarshallProxyTask(proxy, defaultLoader, + verifyCodebaseIntegrity, verifierLoader, context); + smartProxy = isolate.process(task, timeout, timeUnit); + proxyLoader = defaultLoader; + } + + private Object taskInvoke(Object proxy, Method method, Object[] args) throws + Exception { + String methodName = method.getName(); + if (method.getDeclaringClass() == Object.class) { + // Handle the Object public methods. + if (methodName.equals("hashCode")) { + return new Integer(System.identityHashCode(proxy)); + } else if (methodName.equals("equals")) { + return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); + } else if (methodName.equals("toString")) { + return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); + } + } + return method.invoke(smartProxy, args); + } + + @SuppressWarnings("unchecked") + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Callable task = new MethodInvocationTask(this, proxy, method, args); + Object result = isolate.process(task, timeout, unit); + CheckReturn cr = new CheckReturn(result, proxyLoader); + return AccessController.doPrivileged(cr); + } + + private static class UnmarshallProxyTask implements Callable{ + private final MarshalledInstance mi; + private final ClassLoader defaultLoader; + private final boolean verifyCodebaseIntegrity; + private final ClassLoader verifierLoader; + private final Collection context; + + UnmarshallProxyTask(MarshalledInstance proxy, + ClassLoader defaultLoader, + boolean verifyCodebaseIntegrity, + ClassLoader verifierLoader, + Collection context) + { + mi = proxy; + this.defaultLoader = defaultLoader; + this.verifyCodebaseIntegrity = verifyCodebaseIntegrity; + this.verifierLoader = verifierLoader; + this.context = context; + } + + public Object call() throws Exception { + return mi.get(defaultLoader, verifyCodebaseIntegrity, + verifierLoader, context); + } + + } + + private static class MethodInvocationTask implements Callable { + private final ProxyIsolationHandler smartProxy; + private final Object proxy; + private final Method method; + private final Object[] args; + MethodInvocationTask(ProxyIsolationHandler target, Object proxy, Method method, Object[] args){ + smartProxy = target; + this.proxy = proxy; + this.method = method; + this.args = args; + } + public Object call() throws Exception { + return smartProxy.taskInvoke(proxy, method, args); + } + + } + + private static class CheckReturn implements PrivilegedExceptionAction{ + private final Object checked; + private final ClassLoader loader; + CheckReturn (Object check, ClassLoader loader){ + checked = check; + this.loader = loader; + } + public Object run() throws Exception { + Class clazz = checked.getClass(); + Class[] classes = clazz.getDeclaredClasses(); + Set<ClassLoader> loaders = new HashSet<ClassLoader>(); + loaders.add(clazz.getClassLoader()); + int l = classes.length; + for (int i = 0; i < l; i++){ + loaders.add(classes[i].getClassLoader()); + } + if (loaders.contains(loader)) throw new IsolationException("Attempt" + + " to return isolated code from isolation classloader"); + return checked; + } + + } + +} Propchange: incubator/river/jtsk/skunk/pepe/src/org/apache/river/impl/security/dos/ProxyIsolationHandler.java ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/river/jtsk/skunk/pepe/test/src/org/apache/river/impl/security/dos/IsolateTest.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/pepe/test/src/org/apache/river/impl/security/dos/IsolateTest.java?rev=1067602&view=auto ============================================================================== --- incubator/river/jtsk/skunk/pepe/test/src/org/apache/river/impl/security/dos/IsolateTest.java (added) +++ incubator/river/jtsk/skunk/pepe/test/src/org/apache/river/impl/security/dos/IsolateTest.java Sun Feb 6 06:39:58 2011 @@ -0,0 +1,72 @@ +/* + * 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.river.impl.security.dos; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import tests.support.EndlessLoopTask; +import static org.junit.Assert.*; + +/** + * + * @author peter + */ +public class IsolateTest { + + public IsolateTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + /** + * Test of process method, of class IsolatedExecutor. + */ + @Test + public void timeout() throws Exception { + System.out.println("process"); + Callable<Object> task = new EndlessLoopTask(); + long timeout = 10L; + IsolatedExecutor instance = new IsolatedExecutor(); + Exception e = null; + try { + instance.process(task, timeout, TimeUnit.SECONDS); + } catch ( TimeoutException ex ){ + e = ex; + System.out.println(ex); + } + assertTrue((e instanceof TimeoutException)); + // TODO review the generated test code and remove the default call to fail. + } + +} \ No newline at end of file Propchange: incubator/river/jtsk/skunk/pepe/test/src/org/apache/river/impl/security/dos/IsolateTest.java ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/river/jtsk/skunk/pepe/test/src/tests/support/EndlessLoopTask.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/pepe/test/src/tests/support/EndlessLoopTask.java?rev=1067602&view=auto ============================================================================== --- incubator/river/jtsk/skunk/pepe/test/src/tests/support/EndlessLoopTask.java (added) +++ incubator/river/jtsk/skunk/pepe/test/src/tests/support/EndlessLoopTask.java Sun Feb 6 06:39:58 2011 @@ -0,0 +1,36 @@ +/* + * 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 tests.support; + +import java.util.concurrent.Callable; + +/** + * + * @author peter + */ +public class EndlessLoopTask implements Callable { + + public Object call() throws Exception { + long count = 0; + while (true){ + count++; + } + } + +} Propchange: incubator/river/jtsk/skunk/pepe/test/src/tests/support/EndlessLoopTask.java ------------------------------------------------------------------------------ svn:eol-style = native