river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject Re: Towards Internet Jini Services (trust)
Date Sun, 17 Oct 2010 05:21:56 GMT
Peter Firmstone wrote:
> Note this code doesn't protect against DNS cache poisoning attacks.  
> We could easily extend this to require the jar file be signed as per 
> Sim's suggestion, by a  Certificate[] known to the Service, currently 
> set to null in the CodeSource grant, easily passed into the 
> constructor or perhaps as static list, or perhaps even supplied by the 
> authentication proxy.

The CodeSource (containing Certificate[]'s) PermissionGrant, would 
contain something like a ClassLoadingPermission.

The DownloadPermission protects against unauthorised download, while the 
ClassLoadingPermission would protect against unauthorised class loading.

The ClassLoadingPermission would only apply if the downloaded codebase 
is signed by the required certificates, provided in advance by the proxy.

Any untrusted public http codebase server will be suitable.

If we isolate unmarshalling to an Executor Service Thread, handling 
error conditions like StackOverflowError, were starting to look 
relatively secure.  We can have Socket time-outs too. The Executor 
service thread doesn't run untrusted code, it's only really protecting 
against an untrusted http codebase from supplying excessively large files.

Cheers,

Peter.
>
> The Certificate[] would guarantee that a DNS cache poisoning attack 
> couldn't work.
>
> /*
> * 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.imp.security.io;
>
> import java.io.IOException;
> import java.io.OutputStream;
> import java.lang.reflect.Proxy;
> import java.util.Collection;
> import net.jini.io.MarshalOutputStream;
>
> /**
> * This code was inspired by Michal Kleczek's suggestions for solving the
> * DOS hole during unmarshalling of untrusted code.
> *
> * @author Peter Firmstone
> */
> public class AuthMarshalOutputStream extends MarshalOutputStream {
>    private final Proxy proxy;
>      public AuthMarshalOutputStream(OutputStream out,
>                    Collection context,
>                    Proxy authenticationProxy)
>    throws IOException
>    {
>    super(out, context);
>    if (authenticationProxy == null) {
>        throw new NullPointerException("Null Authentication Proxy");
>    }
>    proxy = authenticationProxy;
>    }
>      @Override
>    protected void writeAnnotation(String annotation) throws IOException {
>    writeObject(annotation);
>    writeObject(proxy);
>    }
>
> }
>
>
> /*
> * 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.imp.security.io;
>
> import java.io.IOException;
> import java.io.InputStream;
> import java.io.InvalidObjectException;
> import java.io.ObjectStreamClass;
> import java.lang.reflect.Proxy;
> import java.net.MalformedURLException;
> import java.net.URL;
> import java.security.CodeSource;
> import java.security.Policy;
> import java.security.cert.Certificate;
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.HashMap;
> import java.util.List;
> import java.util.Map;
> import java.util.StringTokenizer;
> import net.jini.io.MarshalInputStream;
> import net.jini.loader.ClassLoading;
> import net.jini.loader.DownloadPermission;
> import net.jini.security.ProxyPreparer;
> import org.apache.river.api.security.PermissionGrant;
> import org.apache.river.api.security.PermissionGrantBuilder;
> import org.apache.river.api.security.RevokeableDynamicPolicy;
>
> /**
> * AuthMarshalInputStream requires the AuthMarshalOutputStream to send 
> a proxy
> * in order to authenticate itself, it is best if the size of this 
> proxy is
> * kept to a minimum by only implementing ProxyPreparer and 
> RemoteMethodControl.
> *
> * Once the remote end has authenticated, the Annotation string can be 
> read
> * and a Class file returned.
> *
> * The AuthMarshalInputStream will dynamically grant DownloadPermission 
> to the
> * each URL CodeSource if the Server authenticates itself.
> *
> * Note that the DownloadPermission is not granted to the Server Principal
> * but to the CodeSource that the Server wants the client to download.
> *
> * Authentication is only used to determine if we trust the server to 
> inform
> * us of a suitable URL for download.  Once DownloadPermission has been 
> granted
> * to the CodeSource, the codebase (jar file) can be downloaded.
> *
> * Authentication is only performed once for each codebase String.
> *
> * @author Peter Firmstone
> */
> public class AuthMarshalInputStream extends MarshalInputStream {
>
>    /**
>     * maps keywords for primitive types and void to corresponding
>     * Class objects
>     **/
>    private static final Map<String,Class> specialClasses
>        = new HashMap<String,Class>(9);
>    static {
>    specialClasses.put("boolean", boolean.class);
>    specialClasses.put("byte", byte.class);
>    specialClasses.put("char", char.class);
>    specialClasses.put("short", short.class);
>    specialClasses.put("int", int.class);
>    specialClasses.put("long", long.class);
>    specialClasses.put("float", float.class);
>    specialClasses.put("double", double.class);
>    specialClasses.put("void", void.class);
>    }
>      private static final DownloadPermission[] perm = {new 
> DownloadPermission()};
>      private static List<String> dynamicGrants = new ArrayList<String>();
>
>    /**
>     * value to pass as the "default loader" argument to loadClass and
>     * loadProxyClass
>     **/
>    private final ClassLoader defaultLoader;
>
>    /** true if this stream verifies codebase integrity */
>    private final boolean verifyCodebaseIntegrity;
>
>    /** loader to pass to Security.verifyCodebaseIntegrity */
>    private final ClassLoader verifierLoader;
>
>    /**
>     * if false, pass null codebase values to loadClass and
>     * loadProxyClass methods; if true, pass codebase values from
>     * stream class annotations
>     **/
>    private boolean usingCodebaseAnnotations;
>      private final ProxyPreparer preparer;
>  
>    /**
>     * Creates a new <code>AuthMarshalInputStream</code> that reads
>     * marshalled data from the specified underlying
>     * <code>InputStream</code>.
>     *
>     * <p>This constructor passes <code>in</code> to the superclass
>     * constructor that has an <code>InputStream</code> parameter.
>     *
>     * <p><code>defaultLoader</code> will be passed as the
>     * <code>defaultLoader</code> argument to {@link
>     * ClassLoading#loadClass ClassLoading.loadClass} and {@link
>     * ClassLoading#loadProxyClass ClassLoading.loadProxyClass}
>     * whenever those methods are invoked by {@link #resolveClass
>     * resolveClass} and {@link #resolveProxyClass resolveProxyClass}.
>     *
>     * <p>If <code>verifyCodebaseIntegrity</code> is
>     * <code>true</code>, then the created stream will verify that all
>     * codebase annotation URLs that are used to load classes resolved
>     * by the stream provide content integrity, and whenever {@link
>     * Security#verifyCodebaseIntegrity
>     * Security.verifyCodebaseIntegrity} is invoked to enforce that
>     * verification, <code>verifierLoader</code> will be passed as the
>     * <code>loader</code> argument.  See {@link
>     * ClassLoading#loadClass ClassLoading.loadClass} and {@link
>     * ClassLoading#loadProxyClass ClassLoading.loadProxyClass} for
>     * details of how codebase integrity verification is performed.
>     *
>     * <p><code>context</code> will be used as the return value of the
>     * created stream's {@link #getObjectStreamContext
>     * getObjectStreamContext} method.
>     *
>     * @param in the input stream to read marshalled data from
>     *
>     * @param defaultLoader the class loader value (possibly
>     * <code>null</code>) to pass as the <code>defaultLoader</code>
>     * argument to <code>ClassLoading</code> methods
>     *
>     * @param verifyCodebaseIntegrity if <code>true</code>, this
>     * stream will verify that codebase annotation URLs used to load
>     * classes resolved by this stream provide content integrity
>     *
>     * @param verifierLoader the class loader value (possibly
>     * <code>null</code>) to pass to
>     * <code>Security.verifyCodebaseIntegrity</code>, if
>     * <code>verifyCodebaseIntegrity</code> is <code>true</code>
>     *
>     * @param context the collection of context information objects to
>     * be returned by this stream's {@link #getObjectStreamContext
>     * getObjectStreamContext} method
>     *
>     * @param preparer the proxy preparer used to authenticate the server
>     * prior to downloading any classes.
>     *
>     * @throws IOException if the superclass's constructor throws an
>     * <code>IOException</code>
>     *
>     * @throws SecurityException if the superclass's constructor
>     * throws a <code>SecurityException</code>
>     *
>     * @throws NullPointerException if <code>in</code> or
>     * <code>context</code> is <code>null</code>
>     **/
>
>    public AuthMarshalInputStream(InputStream in,
>                  ClassLoader defaultLoader,
>                  boolean verifyCodebaseIntegrity,
>                  ClassLoader verifierLoader,
>                  Collection context,
>                  ProxyPreparer preparer )
>    throws IOException
>    {
>    super (in, defaultLoader, verifyCodebaseIntegrity, verifierLoader,
>        context);
>    this.defaultLoader = defaultLoader;
>    this.verifyCodebaseIntegrity = verifyCodebaseIntegrity;
>    this.verifierLoader = verifierLoader;
>    this.preparer = preparer;
>    }
>
>    // Inherit documentation from MarshalInputStream
>    @Override
>    public void useCodebaseAnnotations() {
>    usingCodebaseAnnotations = true;
>    super.useCodebaseAnnotations();
>    }
>
>    // Inherit documentation from MarshalInputStream
>    @Override
>    protected Class resolveClass(ObjectStreamClass classDesc)
>    throws IOException, ClassNotFoundException
>    {
>    if (classDesc == null) {
>        throw new NullPointerException();
>    }
>
>    // must always consume annotation written by MarshalOutputStream
>    String annotation = readAnnotation();
>    String codebase = usingCodebaseAnnotations ? annotation : null;
>    authenticate(codebase);
>    String name = classDesc.getName();
>    try {
>        return ClassLoading.loadClass(codebase,
>                      name,
>                      defaultLoader,
>                      verifyCodebaseIntegrity,
>                      verifierLoader);
>    } catch (ClassNotFoundException e) {
>        Class c = specialClasses.get(name);
>        if (c != null) {
>        return c;
>        } else {
>        throw e;
>        }
>    }
>    }
>
>    // Inherit documentation from MarshalInputStream
>    @Override
>    protected Class resolveProxyClass(String[] interfaceNames)
>    throws IOException, ClassNotFoundException
>    {
>    for (int i = 0; i < interfaceNames.length; i++) {
>        if (interfaceNames[i] == null) {
>        throw new NullPointerException();
>        }
>    }
>
>    // must always consume annotation written by MarshalOutputStream
>    String annotation = readAnnotation();
>    String codebase = usingCodebaseAnnotations ? annotation : null;
>    authenticate(codebase);
>    return ClassLoading.loadProxyClass(codebase,
>                       interfaceNames,
>                       defaultLoader,
>                       verifyCodebaseIntegrity,
>                       verifierLoader);
>    }
>      private void authenticate(String codebase)
>        throws IOException, ClassNotFoundException{
>    // Always read the proxy from the AuthMarshalInputStream
>    Object proxy = readObject();
>    if (codebase == null) return;
>    if (dynamicGrants.contains(codebase)) return;
>    try {
>        //Authenticate
>        preparer.prepareProxy(proxy);              // Dynamically Grant 
> DownloadPermission for each URL via a
>        // CodeSource grant.
>        Policy policy = Policy.getPolicy();
>        if (policy instanceof RevokeableDynamicPolicy){
>        StringTokenizer st = new StringTokenizer(codebase); // divide 
> by spaces
>        URL[] urls = new URL[st.countTokens()];
>        for (int i = 0; st.hasMoreTokens(); i++) {
>            urls[i] = new URL(st.nextToken());
>        }
>        PermissionGrantBuilder pgb
>            = ((RevokeableDynamicPolicy) policy).getGrantBuilder();
>        pgb.permissions(perm);
>        int l = urls.length;
>        List<PermissionGrant> grants = new ArrayList<PermissionGrant>(l);
>        for (int i = 0; i < l; i++){
>            CodeSource cs = new CodeSource(urls[i], (Certificate[]) null);
>            PermissionGrant pg = pgb.codeSource(cs).build();
>            grants.add(pg);
>        }
>        ((RevokeableDynamicPolicy) policy).grant(grants);
>        }
>        dynamicGrants.add(codebase);
>    } catch (SecurityException e) {
>        throw new IOException(e);
>    } catch (MalformedURLException e){
>        throw new IOException(e);
>    }
>    }
> }
>
>


Mime
View raw message