Subject: [Int-area] SOCKS6 and UDP

From: Ben Schwartz <[email-masked]>
Date: Tue, 10 Dec 2019 12:12:25 -0500

I have a few notes on the SOCKS 6 draft, related to UDP.

1. I would like to require servers to share resumption between TLS and
DTLS.  This would allow clients to keep a single resumption cache,
instead of two.

2. I think the protocol should support sending UDP datagrams over the
TCP socket being used for UDP ASSOCIATE.  If the client sends a
datagram on this channel, it would become the mapped channel for the
association.  (Alternatively, it could serve as a "fallback channel"
when there is no UDP-based channel available.)  This would be faster
for short-lived UDP transactions, because the client could include the
first upstream packet in the first flight, without needing to wait for
a reply containing the Association ID.  It would also help in cases
where the client-proxy path is UDP-intolerant.

3. The 64-bit Association ID seems like a potential security
vulnerability when using DTLS.  Suppose an attacker observes a DTLS
connection attempt from a client.  If the attacker can block the true
client's DTLS, they can start enumerating Association IDs until they
find the one that works.  Enumerating a 64-bit space is not easy, but
it is too small to be considered cryptographically strong.

Putting a very large Association ID at the beginning of every packet
is not appealing, but with DTLS there may be a more secure and more
efficient solution.  For example, the client could include a longer
Association ID only until it gets an acknowledgement from the server,
at which point both sides know that the UDP association is mapped to
this DTLS connection.  Alternatively, the client could send some
information about its DTLS ClientHello in the TLS channel that sent
the UDP ASSOCIATE, creating a secure binding.

4. SOCKS6-UDP is not compatible with PLPMTUD
(https://tools.ietf.org/html/draft-ietf-tsvwg-datagram-plpmtud),
because the client cannot control fragmentation at the proxy.  I think
we should add a "Don't Fragment" bit to the SOCKS 6 datagram header to
support PLPMTUD.

(I would love it if SOCKS 6 could pass ICMP messages for classical
PMTUD (and ping! and traceroute!) but I don't expect that to happen.)

--Ben

---

From: Vladimir Olteanu <[email-masked]>
Date: Tue, 10 Dec 2019 21:43:43 +0200

Hi Ben,

I've added my comments inline.

> DTLS.  This would allow clients to keep a single resumption cache,

This is the first time I hear about TLS and DTLS sharing sessions.

It is a nice performance improvement that, as far as I can tell, doesn't 
have any drawbacks. However, I wouldn't require it (as in MUST, REQUIRE 
or SHOULD) since it puts extra burden on the implementers. All that 
should be required is a functional TLS layer. (Session resumption might 
not be supported by the library, it becomes complicated if you're using 
an L4 load-balancer etc. etc.)

It's definitely a MAY, though.

> where the client-proxy path is UDP-intolerant.
Yes, we could do that.
> it is too small to be considered cryptographically strong.

Yes, guessing the Association ID is indeed a concern. We are planning to 
write something about it in the security considerations section.

The main idea is to narrow the attacker's window down to a few minutes. 
This is achieved in 3 ways (which aren't mutually exclusive):

(A) A hard timeout (on the order of minutes) at the proxy. If no first 
datagram is received, the association is killed.

(B) The client kills the association if it doesn't receive an 
Association Confirmation message after a timeout. It can keep resending 
empty datagrams in the meanwhile. The attacker might blackhole the TCP 
connection to counter this.

(C) The proxy keeps sending TCP keepalives or TLS heartbeats. Blackholed 
connections are detected and their associations are killed.

Assuming a 100Gbps link, bruteforcing 2^64 values would take 3700 years 
(or "just" 37 for a 1% chance of success). The attack can also be 
detected, unless done stealthily (and slowly).

I don't know what the standard is for deeming something "secure". Maybe 
we should also ask SAAG to take a look at this.

> the UDP ASSOCIATE, creating a secure binding.

Unfortunately, we can't ditch the Association ID entirely, because 
multiple UDP associations can be multiplexed over the same DTLS connection.

Your idea about having two different Association IDs is interesting. In 
essence, one would be "signed", and the other "unsigned". The only issue 
is that the ID eats into the maximum payload size, and clients would see 
the equivalent of a varying MTU.

Your second idea with the ClientHello seems promissing. We could include 
the session ID, connection ID or ticket in the SOCKS Request. (I'm not 
sure what to do if the DTLS connection has neither.)

Further, it can't be an attack if the TLS and DTLS connections share the 
same session or client credentials (but we can't always count on having 
these features available, especially the latter).

> we should add a "Don't Fragment" bit to the SOCKS 6 datagram header to

The path of least resistance would be a "don't fragment" stack option. 
However, that would apply to the whole UDP association and there would 
be no way to change it.

Of course, the client can create an association specifically for probing 
purposes.

> PMTUD (and ping! and traceroute!) but I don't expect that to happen.)
Yes, I don't think there's an elegant way to support ICMP.

Cheers,

Vlad

---

From: Ben Schwartz <[email-masked]>
Date: Wed, 11 Dec 2019 11:09:45 -0500

On Tue, Dec 10, 2019 at 2:43 PM Vladimir Olteanu <[email-masked]>
wrote:
> or SHOULD) since it puts extra burden on the implementers.

The problem is, if it isn't required for the server, then the client always
has to keep separate session caches, because it has no way to know if
resumption tickets/keys could be shared.

> All that

True, if the TLS termination is implemented separately from the SOCKS
server then this could be hard to achieve.  Port 1080 is reserved for SOCKS
on both TCP and UDP, so maybe this requirement could apply only when using
TLS and DTLS on the same host and port.

> The main idea is to narrow the attacker's window down to a few minutes.
...
> we should also ask SAAG to take a look at this.

That sounds like a good idea to me.

> the equivalent of a varying MTU.

Yeah, this is inconvenient.  One can imagine a new message type like
"ACCEPT ASSOCIATION" that is sent on the DTLS connection, which includes
both a long (e.g. 128-bit) server-selected Association ID and a short (e.g.
16-bit) client-selected key that identifies the association on this DTLS
connection (or 5-tuple).  However, I'm not sure how to do this without
adding a roundtrip.

> sure what to do if the DTLS connection has neither.)

It could just be the whole DTLS ClientHello, or a hash of it.

---

From: Vladimir Olteanu <[email-masked]>
Date: Thu, 12 Dec 2019 16:47:35 +0200

On 12/11/19 6:09 PM, Ben Schwartz wrote:
>
>
> On Tue, Dec 10, 2019 at 2:43 PM Vladimir Olteanu 
> <[email-masked] <mailto:[email-masked]>> wrote:
> >
> > Hi Ben,
> >
> The problem is, if it isn't required for the server, then the client 
> always has to keep separate session caches, because it has no way to 
> know if resumption tickets/keys could be shared.

The client can always probe whether or not sessions can cross over from 
TLS to DTLS. This has a one-time cost of 1 RTT.

> server then this could be hard to achieve.  Port 1080 is reserved for 
> SOCKS on both TCP and UDP, so maybe this requirement could apply only 
> when using TLS and DTLS on the same host and port.
>
It could be that TLS and DTLS are terminated by separate processes, or 
even separate machines, hence my apprehension about requiring this feature.

On the other hand, we can require (as in SHOULD) the client to try to 
reuse the session.

> "ACCEPT ASSOCIATION" that is sent on the DTLS connection, which 
> includes both a long (e.g. 128-bit) server-selected Association ID and 
> a short (e.g. 16-bit) client-selected key that identifies the 
> association on this DTLS connection (or 5-tuple).  However, I'm not 
> sure how to do this without adding a roundtrip.
>
Yes, adding round trips is something we are trying to avoid.
> It could just be the whole DTLS ClientHello, or a hash of it.
Having the proxy remember the whole ClientHello for every connection is 
costly. Hashes are doable, but it likely requires digging into the guts 
of whatever TLS library the endpoints are using.

Further, if there's a DTLS-terminating proxy, things get really messy. 
I'd hate to see SOCKS ALGs popping up.

Come to think of it, I'd say that we shouldn't rely on any cross-layer 
information for security, or for any other critical features.

Vlad

---

From: Joe Touch <[email-masked]>
Date: Thu, 12 Dec 2019 06:58:17 -0800

> On Dec 12, 2019, at 6:47 AM, Vladimir Olteanu <[email-masked]> wrote:
> 
>> > All that
>> True, if the TLS termination is implemented separately from the SOCKS server then this could be hard to achieve.  Port 1080 is reserved for SOCKS on both TCP and UDP, so maybe this requirement could apply only when using TLS and DTLS on the same host and port.
>> 
> It could be that TLS and DTLS are terminated by separate processes, or even separate machines, ..
> 
Same IP address means it would need to act as if it were the same machine. Coordinating shared use of an IP address between two separate hosts is the implementers problem, not the protocol designers.

Joe