Skip to content
pfirmstone edited this page Sep 19, 2019 · 22 revisions

Welcome to the JGDMS wiki!

The intent of JGDMS is to provide a secure version of Apache River / Jini, over IPv6 networks and to encourage the Apache River project to adopt these changes.

News Update Apr 2018:

AtomicILFactory has been created to allow use of atomic serialization over JERI connections, and support for OSGi, this will be uploaded in the near future, watch this space.

Goals (Completed):

  • TLSv1.2 by default, with support for the latest cyphers, including elliptic curve cryptography.
  • Updated ConfidentialityStrength constraint for STRONG encryption.
  • Atomic serialization, for services and clients, to fix security issues that would be present using standard java serialization. De serialization has been completely re-implemented with security in mind.
  • An additional subclass interface of net.jini.core.lookup.ServiceRegistrar, SafeServiceRegistrar, with new lookup method; lookup search results are returned as dynamic bootstrap proxy's that have interfaces to obtain Entry attributes, smart proxy, constraints and ServiceUI directly from distributed service providers after authentication. This avoids unmarshalling untrusted code, such that ProxyTrust verification is not required.
  • IPv6 Discovery
  • Deprecation of existing ProxyTrust interfaces and classes, these are complex and don't protect against deserialization of unverified / unknown data, before trust (authentication) has been established over the connection, even worse, to establish the connection requires deserialization of unverified code and data.
  • Smart proxy codebase jar files contain required permissions, the client can configure a default permission set to grant to smart proxy's, the actual permissions granted will be the intersection of the sets of those required by the proxy and those the client allows, providing clients a way to limit proxy functionality for security reasons if necessary, while adhering to least privilege principles to limit security vulnerabilities.

Atomic Serialization example:

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import net.jini.core.lookup.ServiceMatches;
import org.apache.river.api.io.AtomicSerial;
import org.apache.river.api.io.AtomicSerial.GetArg;
import org.apache.river.api.io.Valid;

@AtomicSerial
class Matches implements Serializable {

    private static final long serialVersionUID = 2L;

    /**
     * ServiceMatches.items as an List of Item
     *
     * @serial
     */
    private final List<Item> items;
    /**
     * ServiceMatches.totalMatches
     *
     * @serial
     */
    private final int totalMatches;

    Matches(GetArg arg) throws IOException{
        this(Valid.copyCol(arg.get("items", null, List.class),
		           new LinkedList<Item>(),
		           Item.class),
            arg.get("totalMatches",0)
        );
    }

    /** Simple constructor. */
    Matches(List<Item> items, int totalMatches) {
        this.items = items;
        this.totalMatches = totalMatches;
    }

    /** Converts a Matches to a ServiceMatches. */
    ServiceMatches get() throws RemoteException {
        return new ServiceMatches(Item.toServiceItem(items), totalMatches);
    }
}

Discussion of example

Notice the annotation @AtomicSerial? this indicates the class has a constructor that accepts a single GetArg argument. Implementing @AtomicSerial is actually very simple, and in most cases only requires the addition of a constructor. Valid provides utility methods to simplify validation of invariants, in this case the List is being defensively copied into a new LinkedList and all elements are type checked, ensuring they are instances of Item. You may have also noticed that GetArg.get(String name, T default, Class type) performs type checking.

In case you're wondering why type checking is necessary, java Generic's only perform type checks at compile time, an attacker could send you a Collection containing an object you're not expecting, so it's important to validate the contents of your collections during construction.

It is a requirement of @AtomicSerial that all invariant checks are performed before calling a superclass constructor, this guarantees an instance of your object will not be created if invariants are not satisfied, thus an attacker cannot obtain an instance using a finalizer or stream reference. GetArg is caller sensitive, so you can pass it to a superclass constructor, the superclass and extending child class cannot get at each other's fields and they may even use the same field names without conflict, because each class has its own field namespace.

Note that the Item class above also implements @AtomicSerial, it too performs invariant validation. In fact, the administrator can use the java security policy to control which codebases may be involved in deserialization, by granting permission org.apache.river.api.io.DeSerializationPermission "ATOMIC"; Serializable Object's that only have primitive fields, or are stateless, don't need to implement @AtomicSerial and are not controlled by DeSerializationPermission at this time.

There's also a lot more going on under the hood, the developer need not be too concerned about, for example the above List of items is replaced with a safe List implementation during serialization by org.apache.river.api.io.AtomicMarshalOutputStream. Because Java Collections cannot be safely deserialized without the risk of DOS occurring, Collection, List, Set, SortedSet, Map and SortedMap's are all replaced in the stream, so most of the hard work is done for you, all you have to do is defensively copy during construction, perform type checks and check any other invariants you may have. Valid also provides a lot of other useful invariant validation methods a developer might typically perform, such as throwing an InvalidObjectException with a message if an argument shouldn't be null.

Unlike standard java serialization, the attacker doesn't have access to the full classpath, but a much reduced attack surface; only classes belonging to domains with DeSerializationPermission. Attackers are much less likely to be able to create a graph of objects with method call chains used for gadget attacks, provided scope has been limited by DeSerializationPermission and each object checks its own invariants.

Object's require permission to be created, while invariant checks and Java's strong type system limit the possible graph of object references an attacker can create. Circular references are prohibited to avoid infinite recursion and limits are placed on the serial Object cache and array lengths. Objects are responsible for recreating their own circular reference structures during construction if required.