river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject Discovery - Denial of Service and DNS-SD
Date Sun, 07 Nov 2010 06:56:26 GMT
I've been examining the code relating to discovery, feel free to clarify 
if I've missed some important detail.

River currently has two discovery protocols,

Discovery V2 and V1.

V1 dates from Jini 1.0, during the discovery process, registrar proxy's 
are stored in java.rmi.MarshalledObject and transferred over the network 
in serialized form.

Discovery V1 utilised MarshalledObject, I haven't figured out a way 
prevent Denial of service attacks during unmarshalling of the registrar 
proxy for V1.

It is important to remember there are two occasions when we're 
susceptible to DOS attack, firstly during deserialization of 
MarshalledObject and secondly when we unmarshal the registrar proxy from 
the MarshalledObject.

The question is of course what to do with Discovery V1 going forward?  
Should we deprecate it?  If so, how should we ensure that V2 is used by 
default.  DiscoveryConstraints?

Discovery V2 (Unicast), uses MarshalledInstance serial form, there's a 
lot more we can do with V2, it can utilise multiple implementations via 
DiscoveryFormatProvider.

Discovery V2 supports constraints and has a number of pluggable 
implementations.  It has SSL and Kerberos implementations.

In LookupDiscovery, Multicast is used first to discover a registrar and 
create a MulticastAnnouncement which contains the following fields:

public class MulticastAnnouncement {

    /** The announcement sequence number. */
    protected long sequenceNumber;
    /** The lookup service host. */
    protected String host;
    /** The lookup service listen port. */
    protected int port;
    /** The lookup service member groups. */
    protected String[] groups;
    /** The lookup service ID. */
    protected ServiceID serviceID;


The information gathered during Multicasting is then used to for Unicast 
to get the Registrar proxy (from a serialized MarshalledInstance).

DNS-SD could be used to provide the same information as 
MulticastAnnouncement prior to performing Unicast discovery, this will 
allow services to be dynamically discovered over the internet proper.

Multicast uses datagram packets with a recommended limit of 512 bytes, I 
can't think of much opportunity for an attack on the client, it's a 
different story for the Join protocol and the registrar but since 
multicast is only utilised on private networks this shouldn't be a 
problem.  DNS-SD allows a range of addresses and ports to be utilised 
dynamically, multiple registrars for a group might assist to protect 
against packet storms, remembering that Multicast packets can't be 
utilised in a packet storm over the internet, because of TTL routers etc.

We will have to take DOS into account when designing a DNS-SD equivalent 
to Multicast.

Currently Unicast can be subjected to DOS, Notice in the Discovery V2 
Unicast implementation methods below, we're reading more than just the 
MarshalledInstance.  Since Unicast is not limited by packet size, an 
attacker can feed an unending stream of bytes (very large group string 
arrays etc) to protect against Denial of Service we need to run Unicast 
Discovery within an Executor thread that handles StackOverflowError's, 
until we can return a UnicastResponse.



Excerpt from com.sun.jini.discovery.internal.PlainText:

    /**
     * Reads unicast response according to the net.jini.discovery.plaintext
     * format.
     */
    public static UnicastResponse readUnicastResponse(
                        InputStream in,
                        ClassLoader defaultLoader,
                        boolean verifyCodebaseIntegrity,
                        ClassLoader verifierLoader,
                        Collection context)
    throws IOException, ClassNotFoundException
    {
    try {
        DataInput din = new DataInputStream(in);

        // read LUS host
        String host = din.readUTF();

        // read LUS port
        int port = din.readUnsignedShort();

        // read LUS member groups
        String[] groups = new String[din.readInt()];
        for (int i = 0; i < groups.length; i++) {
        groups[i] = din.readUTF();
        }

        // read LUS proxy
        MarshalledInstance mi =
        (MarshalledInstance) new ObjectInputStream(in).readObject();
        /* We have the opportunity to protect against unmarshalling
         * attacks, this is the place to do it.
         */
        ServiceRegistrar reg = (ServiceRegistrar) mi.get(
        defaultLoader,
        verifyCodebaseIntegrity,
        verifierLoader,
        context);

        return new UnicastResponse(host, port, groups, reg);

    } catch (RuntimeException e) {
        throw new DiscoveryProtocolException(null, e);
    }
    }


Excerpt from com.sun.jini.discovery.plaintext.Client:


    // documentation inherited from UnicastDiscoveryClient
    public UnicastResponse doUnicastDiscovery(
                    Socket socket,
                    InvocationConstraints constraints,
                    ClassLoader defaultLoader,
                    ClassLoader verifierLoader,
                    Collection context,
                    ByteBuffer sent,
                    ByteBuffer received)
    throws IOException, ClassNotFoundException
    {
    Plaintext.checkConstraints(constraints);
    return Plaintext.readUnicastResponse(
           new BufferedInputStream(socket.getInputStream()),
           defaultLoader,
           false,
           null,
           context);
    }


Excerpt from com.sun.jini.discovery.internal.EndpointBasedClient:


/**
 * Provides an abstract endpoint-based UnicastDiscoveryClient 
implementation,
 * which serves as a superclass for client-side providers for the
 * net.jini.discovery.ssl and net.jini.discovery.kerberos unicast discovery
 * formats.
 */
public abstract class EndpointBasedClient
    extends EndpointBasedProvider implements UnicastDiscoveryClient
{
    /**
     * Constructs instance with the given format name and object providing
     * access to non-public endpoint operations.
     */
    protected EndpointBasedClient(String formatName,
                  EndpointInternals endpointInternals)
    {
    super(formatName, endpointInternals);
    }

    // documentation inherited from UnicastDiscoveryClient
    public void checkUnicastDiscoveryConstraints(
                    InvocationConstraints constraints)
    throws UnsupportedConstraintException
    {
    if (constraints == null) {
        constraints = InvocationConstraints.EMPTY;
    }
    ConnectionInfo ci = getConnectionInfo(null, constraints);
    checkIntegrity(endpointInternals.getUnfulfilledConstraints(ci.handle));
    }

    // documentation inherited from UnicastDiscoveryClient
    public UnicastResponse doUnicastDiscovery(
                    Socket socket,
                    InvocationConstraints constraints,
                    ClassLoader defaultLoader,
                    ClassLoader verifierLoader,
                    Collection context,
                    ByteBuffer sent,
                    ByteBuffer received)
    throws IOException, ClassNotFoundException
    {
    if (socket == null || sent == null || received == null) {
        throw new NullPointerException();
    }
    if (constraints == null) {
        constraints = InvocationConstraints.EMPTY;
    }
    ConnectionInfo ci = getConnectionInfo(socket, constraints);
    Connection conn = ci.endpoint.connect(ci.handle);
    try {
        boolean integrity =
        checkIntegrity(conn.getUnfulfilledConstraints(ci.handle));
        OutputStream out =
        new BufferedOutputStream(conn.getOutputStream());
        conn.writeRequestData(ci.handle, out);
        out.write(calcHandshakeHash(sent, received));
        out.flush();

        InputStream in = new BufferedInputStream(conn.getInputStream());
        IOException e = conn.readResponseData(ci.handle, in);
        if (e != null) {
        throw e;
        }
        return Plaintext.readUnicastResponse(
        in, defaultLoader, integrity, verifierLoader, context);
    } finally {
        conn.close();
    }
    }

It looks as though Kerberos and SSL Discovery provides guarantees for 
integrity and authentication.  So is MarshalledInstance really subject 
to DOS attacks during unmarshalling in this case?  Can we use DNS-SD 
with Secure protocols?  For the registrar proxy, it would seem that it 
isn't subject to DOS attacks if Kerberos or SSL Discovery is used.

However if we're using the Registrar to lookup other services, we might 
not have the same trust relationship with them as we do the Registrar, 
so in that case it justifies requiring a service authenticate prior to 
unmarshalling it's proxy from MarshalledInstance.

There also seems to be a case still for Codebase services, we can 
determine codebase service trust prior to downloading a codebase from 
it.  This wouldn't prevent Maven from being utilised for codebase 
provisioning, although we still need to determine a suitable way to 
utilise Maven.

If we're using Kerberos and SSL discovery, from the Jini Discovery and 
Join Specification:


      The net.jini.discovery.ssl Format

The net.jini.discovery.ssl format specifies an encoding in which unicast 
response data is sent across a TLS/SSL (Transport Layer Security/Secure 
Socket Layer) connection. Encryption, authentication, and/or integrity 
protection may be provided by the underlying TLS/SSL connection, 
depending on the selected cipher suite. This discovery format does not 
apply to multicast requests or announcements. The discovery format ID 
for this format is 1816474798606646324. TLS/SSL is specified in /RFC 
2246 <http://tools.ietf.org/html/rfc2246>/.


        Unicast Responses

In the net.jini.discovery.ssl format, transmission of unicast response 
data involves three steps:

   1. The discovering entity establishes a TLS/SSL connection between
      itself and the lookup service on top of the TCP connection over
      which it sent the unicast request.
   2. The discovering entity transmits a hash of all of the data it has
      sent and received over the connection so far.
   3. The lookup service also computes a hash of all of the data it has
      received and sent over the connection so far. If this hash value
      matches that sent by the discovering entity, then the lookup
      service sends the unicast response data encoded as described later
      in this section. If the hash does not match, the lookup service
      terminates the connection.

The hash sent by the discovering entity to the lookup service is 
computed as follows: first, a sequence of bytes is assembled consisting 
of the entire unicast request immediately followed by the portion of the 
unicast response that is not discovery format data--the initial protocol 
version followed by the selected format ID, as specified in Section 
DJ.2.6.7, "Protocol Version 2 Response Format" 
<http://www.jini.org/wiki/Jini_Discovery_and_Join_Specification#Protocol_Version_2_Response_Format>.

This sequence of bytes is then used as input to the SHA-1 hash function; 
the 160-bit result is sent in its entirety in big-endian order as the 
hash value. The SHA-1 hash function is specified in /Federal Information 
Processing Standards Publication (FIPS PUB) 180-1/.

The unicast response data sent by the lookup service, if the hashes 
match, consists of the following values encoded (on top of the secured 
TLS/SSL connection) according to the net.jini.discovery.plaintext 
discovery format for unicast responses, specified in Section DJ.3.1.3 
<http://www.jini.org/wiki/Jini_Discovery_and_Join_Specification#Unicast_Responses>:



    * Unicast discovery host
    * Unicast discovery port
    * Member groups
    * Registrar proxy


      The net.jini.discovery.kerberos Format

The net.jini.discovery.kerberos format specifies an encoding in which 
unicast response data is sent across a connection secured using the 
Kerberos Version 5 GSS-API Mechanism, defined in /RFC 1964 
<http://tools.ietf.org/html/rfc1964>/. Kerberos provides authentication; 
encryption and integrity protection of the transmitted data may also be 
provided depending on the GSS-API context in use for the connection. 
This discovery format does not apply to multicast requests or 
announcements. The discovery format ID for this format is 
5724038453852586603. The Kerberos network authentication protocol is 
defined in /RFC 1510 <http://tools.ietf.org/html/rfc1510>/; the GSS-API 
is defined in /RFC 2743 <http://tools.ietf.org/html/rfc2743>/.


        Unicast Responses

Transmission of unicast response data in the net.jini.discovery.kerberos 
discovery format is similar to the net.jini.discovery.ssl format, except 
that the underlying connection is secured using the Kerberos Version 5 
GSS-API Mechanism instead of TLS/SSL:

   1. The discovering entity establishes a GSS-API context for
      communicating with the lookup service, whose Kerberos principal it
      knows in advance. It then uses GSS-API tokens obtained from the
      established context to wrap all subsequent data sent to the lookup
      service (over the TCP connection on which the unicast request was
      originally transmitted). Also, all data subsequently received over
      the connection is unwrapped using the GSS-API.
   2. The discovering entity transmits a hash of all of the data it has
      sent and received over the connection so far. Multiple GSS-API
      tokens may be used to convey the hash. The hash is computed as
      specified in Section DJ.3.4.1
      <http://www.jini.org/wiki/Jini_Discovery_and_Join_Specification#Unicast_Responses_2>

   3. The lookup service also computes a hash of all of the data it has
      received and sent over the connection so far. If this hash value
      matches that sent by the discovering entity, then the lookup
      service sends the unicast response data, encoded as specified in
      Section DJ.3.4.1
      <http://www.jini.org/wiki/Jini_Discovery_and_Join_Specification#Unicast_Responses_2>,
      over the TCP connection to the discovering entity, using GSS-API
      tokens as described in step 1. Multiple GSS-API tokens may be used
      to convey the unicast response data.


Cheers,

Peter.

Mime
View raw message