Subject: [quicwg/base-drafts] Application close should be disallowed in Initial or Handshake (#3158)

From: Kazuho Oku <[email-masked]>
Date: Mon, 28 Oct 2019 02:23:39 -0700

At the moment, CONNECTION_CLOSE (0x1d) (i.e. the application-level close) is allowed "_in packets of any encryption level except 0-RTT._" ([TLS draft, section 4](https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#rfc.section.4)).

I think that it cannot be sent or received in an Initial or Handshake packets, as only the QUIC and TLS are interacting at that layer. Note that the application protocol might not have been determined when Initial / Handshake packets are exchanged (before the server returns the EE indicating which application protocol has been chosen).

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158

---

From: Martin Thomson <[email-masked]>
Date: Mon, 28 Oct 2019 02:26:14 -0700

Two choices:

1. no problem: let the application decide to use Handshake to abort a connection it doesn't want any more.

2. problem: force the application to use 1-RTT packets to express its discontent.

I can see reasons for either.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-546862145

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 28 Oct 2019 02:55:48 -0700

As stated above, I think option 1 is problematic in sense that the endpoint might not know which application protocol it is taking, though the error code being used is specific to the application protocol.

Even for Handshake packets it is hard to tell if the peer has learnt the application protocol in use, as CRYPTO frames and CONNECTION_CLOSE might get delivered out of order.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-546873058

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 28 Oct 2019 03:23:27 -0700

Another issue with option 1 is that we might leak sensitive data.

Consider the case where a server receives a coalesced Initial / 0–RTT packet, and decides to respond with an application-close as the outcome of processing the 0-RTT application data. In this case, application-close should not be sent in Initial packet, as it would be an act of an application responding to the client in cleartext.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-546883128

---

From: ianswett <[email-masked]>
Date: Mon, 28 Oct 2019 07:22:05 -0700

I believe I prefer 2, but if the application tries to close the connection with an application close before any data can be sent in 1-RTT, advise the transport close the connection at Initial or Handshake instead?  I believe that both honors the intent of the application and maintains some separation.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-546968842

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 28 Oct 2019 16:37:13 -0700

@ianswett Agreed.

And for that purpose, I think transport close with an error code of 0x15a (TLS alert user_canceled) fits naturally. Quoting from [RFC 8446 section 6.1](https://tools.ietf.org/html/rfc8446#section-6.1), _this alert notifies the recipient that the sender is canceling the handshake for some reason unrelated to a protocol failure._

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547191846

---

From: Nick Banks <[email-masked]>
Date: Mon, 28 Oct 2019 19:06:54 -0700

I disagree with making a change. We discussed this exact topic when we allowed CONNECTION_CLOSE in Initial, and it was decided at the time that if an application wanted to terminate the connection at this stage, it may do so.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547223456

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 28 Oct 2019 19:16:56 -0700

@nibanks Would you mind pointing out the link to the discussion?

Besides, if we are to allow CONNECTION_CLOSE in Initial / Handshake, I think we need to clarify how an endpoint selects the error code (see https://github.com/quicwg/base-drafts/issues/3158#issuecomment-546883128) and how it is interpreted (https://github.com/quicwg/base-drafts/issues/3158#issuecomment-546873058). I think that would be more complex to implement and maintain than limiting the use of application-level close to 1-RTT.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547225515

---

From: Nick Banks <[email-masked]>
Date: Mon, 28 Oct 2019 19:28:20 -0700

I can't seem to find the discussion right now. I believe a lot of it was in person at the time as well. Things may be difficult in some scenarios, but we can leave the decision on how to handle things up to the application layer. For the 0-RTT scenario, you can use a generic application error code. There's no ALPN ambiguity there, right, since they have to use the same one as previous? I don't think we should add extra restrictions at the transport layer.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547228218

---

From: Marten Seemann <[email-masked]>
Date: Mon, 28 Oct 2019 19:30:39 -0700

Would it make sense to allow connection-level CONNECTION_CLOSE in 0-RTT, while forbidding it in Initial and Handshake?

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547228733

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 28 Oct 2019 21:04:04 -0700

@marten-seemann I assume you mean _application_-level CONNECTION_CLOSE in 0-RTT.

I do not have a strong opinion on that, though I might point out that allowing that creates a wired state; a client would might send CONNECTION_CLOSE before it knows if the server has accepted or rejected 0-RTT. The question here is if the client is required to retransmit CONNECTION_CLOSE in 1-RTT  when the server rejects 0-RTT.

Considering that, I think we can just forbid the use of application-close in 0-RTT. The client can either wait for the handshake to complete then send an application-level close in 1-RTT, or it can close the connection using a QUIC-level close during the handshake (without exposing why).

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547246783

---

From: Kazuho Oku <[email-masked]>
Date: Mon, 28 Oct 2019 21:49:57 -0700

@nibanks Thank you for checking.

> Things may be difficult in some scenarios, but we can leave the decision on how to handle things up to the application layer.

The problem is that then there would be two requirements:
* Application protocols MUST define a generic error code that can be exposed unencrypted.
* QUIC stacks need to provide an API that tells the application if a CONNECTION_CLOSE frame is going to be sent using Initial packets. Each application needs to consult that in order to determine the error code to be sent.

> For the 0-RTT scenario, you can use a generic application error code. There's no ALPN ambiguity there, right, since they have to use the same one as previous?

There is. The case is where there is an error in the 0-RTT data that the client sends, and the server responds with a CONNECTION_CLOSE frame. In this case, there is no ambiguity on the server-side, as the server has committed to processing a particular application protocol. However, there is an issue on the client-side. The client cannot tell which application protocol has been selected, as the CONNECTION_CLOSE frame (carried in either Initial or Handshake) may arrive before it receives the Encrypted Extensions handshake message that carries the name of the negotiated protocol. We *can* say that a server sending an application-level close before the client sends an 1-RTT packet is an alternative way of negotiating the protocol. But are we really going to say that? I think it's too complicated.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547254865

---

From: MikkelFJ <[email-masked]>
Date: Mon, 28 Oct 2019 22:00:55 -0700

I agree with @nibanks there is an age old discussion on handling errors in early connection. Simply forbidding them without understanding this would be unfortunate.

Consider errors in early handshake where the server does not wish to proceed. It doesn't like the client, it doesn't have the resources, it is about to reboot, or whatever. It can either drop the packets or wait if it cannot close immediately. It doesn't want to wait so it drops. Drops then probably results in unnecessary resets.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547257034

---

From: Marten Seemann <[email-masked]>
Date: Mon, 28 Oct 2019 23:17:17 -0700

> Consider errors in early handshake where the server does not wish to proceed. It doesn't like the client, it doesn't have the resources, it is about to reboot, or whatever. It can either drop the p…

@mikkelfj That's not what this issue is about. CONNECTION_CLOSE using a transport error would still be permitted in Initial and Handshake. This is only about application-level error codes.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547273658

---

From: MikkelFJ <[email-masked]>
Date: Tue, 29 Oct 2019 00:55:20 -0700

Ah I see. I agree that application errors should not happen at that level.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547298907

---

From: MikkelFJ <[email-masked]>
Date: Tue, 29 Oct 2019 02:35:12 -0700

So with improved understanding, I think the correct approach is for send a PROTOCOL_VIOLATION the other direction, but since that is kind of pointless, all you can do it is to report a protocol violation the client. It would be wrong to forward the error code because that would promote bad behaviour and assumptions on application level design that might not hold in general.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547334434

---

From: Nick Banks <[email-masked]>
Date: Tue, 29 Oct 2019 07:06:29 -0700

@kazuho 

> - Application protocols MUST define a generic error code that can be exposed unencrypted.
> - QUIC stacks need to provide an API that tells the application if a CONNECTION_CLOSE frame is going to be sent using Initial packets. Each application needs to consult that in order to determine the error code to be sent.

For the first bullet, that's a decision for the application protocols.

For the second, I'd argue you don't need any special API. If the app terminates the connection before the "handshake complete" notification then any error code it would pass down to the QUIC API would not be sent in a 1-RTT packet. That state is enough for the app to make an intelligent decision. Another option is to have an API flag that indicate the application close is allowed to be send unencrypted.

> There is. The case is where there is an error in the 0-RTT data that the client sends, and the server responds with a CONNECTION_CLOSE frame. In this case, there is no ambiguity on the server-side, as the server has committed to processing a particular application protocol. However, there is an issue on the client-side. The client cannot tell which application protocol has been selected, as the CONNECTION_CLOSE frame (carried in either Initial or Handshake) may arrive before it receives the Encrypted Extensions handshake message that carries the name of the negotiated protocol. We can say that a server sending an application-level close before the client sends an 1-RTT packet is an alternative way of negotiating the protocol. But are we really going to say that? I think it's too complicated.

If you assume this is not the common case, then I don't think we need optimize for it. The app just closes the connection after the handshake completes.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547436840

---

From: Kazuho Oku <[email-masked]>
Date: Tue, 29 Oct 2019 22:50:26 -0700

@nibanks 
> For the first bullet, that's a decision for the application protocols.

At the very least, the transport draft needs to suggest that, otherwise an application protocol might leak information through APPLICATION_CLOSE frames carried in Initial packets.

> For the second, I'd argue you don't need any special API. If the app terminates the connection before the "handshake complete" notification then any error code it would pass down to the QUIC API wo…

Requiring such interaction seems more complicated than just forbidding the use of APPLICATION_CLOSE in Initial and Handshake packets in favor of CONNECTION_CLOSE(user_canceled). How a QUIC stack enforces that is an implementation issue, but for example, when an application instructs the QUIC stack to close the connection, the stack can decide which of the two frames to send based on the current state.

> If you assume this is not the common case, then I don't think we need optimize for it. The app just closes the connection after the handshake completes.

I'd dispute that this is an optimization. This is about addressing a security concern that we have so far left open.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547748333

---

From: MikkelFJ <[email-masked]>
Date: Wed, 30 Oct 2019 07:10:40 +0000 (UTC)

Maybe we need to add an API recommendation (not requirement) that says that "an application could be allowed to close a connection during handshake using a transport connection close frame". This is similar to my earlier suggestion that an application could be allowed to reject incoming connections because the server is busy or whatever - this suggestion was not accepted as a requirement because it isn't necessary, but it is still a good thing to support. The question is then, how is the connection taken down - i.e. silently or with a transport close?

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547766602

---

From: Nick Banks <[email-masked]>
Date: Wed, 30 Oct 2019 06:48:40 -0700

@kazuho Ok. You've convinced me that it is just easier (and probably safer) to restrict application close to 1-RTT. Though, I'd like to point out, there's nothing preventing an implementation from doing it anyways.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-547911935

---

From: Kazuho Oku <[email-masked]>
Date: Wed, 30 Oct 2019 16:45:28 -0700

> Though, I'd like to point out, there's nothing preventing an implementation from doing it anyways.

I agree with that observation.

I think the specification does not clarify what should be done when a CONNECTION_CLOSE frame is invalid. For example, we do not define how an endpoint should react when it receives a CONNECTION_CLOSE frame with a Reason that goes beyond the end of the packet payload.

I think there are two logical ways of handing the situation:
* a) An endpoint that receives an invalid CONNECTION_CLOSE frame acts as if it received a valid CONNECTION_CLOSE frame.
* b) An endpoint that receives an invalid CONNECTION_CLOSE frame responds with a CONNECTION_CLOSE frame carrying an appropriate error code.

We can mandate either a or b, or leave it up to the implementors (saying nothing in the text). Leaving it unspecified is a valid option here because the connection would promptly close regardless.

Anyways, I think the problem deserves a separate issue, _if_ some of us are interested in mandating a specific behavior. Personally, I do not care.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/3158#issuecomment-548158430