river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject The internet - Proxy Isolation - Denial of Service Attack.
Date Sat, 05 Feb 2011 10:57:04 GMT
The following class, isolates a proxy on it's own thread and using 
reflection, isolates it completely from the rest of the executing 
environment, by passing method calls as tasks.

Forgive the tab formatting.

Cheers,

Peter.

/*
 * 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.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;
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;
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 subclass method returns, this would be 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.
 *
 * @author Peter Firmstone
 */
public class ProxyIsolate implements InvocationHandler {
    private volatile Object smartProxy;
    private volatile ExecutorService proxyExecutor;
    private volatile Throwable thrown;
    private final long timeout;
    private final TimeUnit unit;
   
    @SuppressWarnings("unchecked")
    public ProxyIsolate(MarshalledInstance proxy,
                ClassLoader defaultLoader,
                boolean verifyCodebaseIntegrity,
                ClassLoader verifierLoader,
                Collection context,
                            long timeout,
                            TimeUnit timeUnit
        ) throws TimeoutException, InterruptedException, ExecutionException{
    this.timeout = timeout;
        unit = timeUnit;
    thrown = null;
    proxyExecutor = Executors.newSingleThreadExecutor(new Factory(this));
    UnmarshallProxyTask task =
        new UnmarshallProxyTask(proxy, defaultLoader,
        verifyCodebaseIntegrity, verifierLoader, context);
        smartProxy = proxyExecutor.submit(task).get(timeout, timeUnit);
    }
   
    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 {
    if (isTerminated()) throw new IOException(thrown);
    Callable task = new MethodInvocationTask(this, proxy, method, args);
    return proxyExecutor.submit(task).get(timeout, unit);
    }
   
    private void terminate(Throwable e){
    thrown = e;
    smartProxy = null;
    proxyExecutor.shutdown();   
    }
   
    private boolean isTerminated(){
    if (smartProxy == null) return true;
    return false;
    }
   
    private static class Factory implements ThreadFactory{
    private static final ThreadGroup tg = new ThreadGroup("IsolatedProxy");
    {
        tg.setDaemon(true);
        tg.setMaxPriority(4);
    }
    private final ProxyIsolate proxy;
    Factory(ProxyIsolate spi){
        proxy = spi;
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(tg, r);
        t.setUncaughtExceptionHandler(new ExceptionHandler(proxy));
        return t;
    }   
    }
   
    private static class ExceptionHandler implements 
Thread.UncaughtExceptionHandler{
    private final ProxyIsolate proxy;
   
    ExceptionHandler(ProxyIsolate spi){
        proxy = spi;
    }

    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?
        proxy.terminate(e);
        }
    }
   
    }
   
    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 ProxyIsolate smartProxy;
    private final Object proxy;
    private final Method method;
    private final Object[] args;
    MethodInvocationTask(ProxyIsolate 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);
    }
       
    }

}


Mime
View raw message