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:52:29 GMT
Code Signing has the additional advantage of preventing classes with 
different Certificate's from sharing a package.  It would also allow 
service providers to sign their jar files to prevent them being shared 
with other services.

Cheers,

Peter.

Peter Firmstone wrote:
> 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