go: crypto/tls: Allow sending unrecognized_name alert from GetCertificate
RFC 6066 says:
If the server understood the ClientHello extension but does not recognize the server name, the server SHOULD take one of two actions: either abort the handshake by sending a fatal-level unrecognized_name(112) alert or continue the handshake.
Currently returning an error from the GetCertificate
hook results in a generic internal_error
fatal alert. To implement the first action in the RFC, there should be a way to return an unrecognized_name
fatal alert to the client when a GetCertificate
hook is unable to find a certificate for the server name specified in the ClientHello. I propose the addition of a special error variable to the crypto/tls
package that triggers this alert:
// ErrUnrecognizedName sends an unrecognized_name fatal alert to the client
// when returned from a GetCertificate hook function call.
var ErrUnrecognizedName = errors.New("crypto/tls: unrecognized server name")
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 6
- Comments: 16 (10 by maintainers)
Commits related to this issue
- Send an “unrecognized name” alert (112) when no certificate is found This sends an “unrecognized name” alert (112) instead of an “internal error” (80) when no certificate is found. This addresses go... — committed to tmthrgd/tls-tris by tmthrgd 7 years ago
- Send an “unrecognized name” alert (112) when no certificate is found This sends an “unrecognized name” alert (112) instead of an “internal error” (80) when no certificate is found. This addresses go... — committed to tmthrgd/tls-tris by tmthrgd 7 years ago
While I am strongly in favour of this proposal, I don’t think a sentinel error is the right way to go about this.
The
GetCertificate
callback has three return cases:(certificate, nil)
selects a certificate to use for the handshake,(nil, error)
emits a fatalinternal_error
alert,(nil, nil)
continues with the existing certificate selection logic usingCertificates
orNameToCertificate
- essentially stating that the callback doesn’t have a certificate to match.The current behaviour is to emit an
internal_error
alert withtls: no certificates configured
ifGetCertificate
returns(nil, nil)
andCertificates
is empty. I would propose that in this case, and only in this case, anunrecognized_name
alert be sent to the client instead.This means the current behaviour of “no certificates configured” +
internal_error
is maintained for people who fail to set eitherGetCertificate
orCertificates
, and anunrecognized_name
can be signalled by returning(nil, nil)
- passing the buck - and leavingCertificates
empty.Not only do I think this is much cleaner than using a sentinel error, it avoids exporting any of the crypto/tls alert code or internal TLS implementation details - and I think that’s actually a really strong positive.
It also doesn’t require any sort of compatibility guarantee. Obviously a
ErrUnrecognizedName
error would be bound by the Go1 compatibility guarantee, which seems like an unnecessary negative here in my mind.I’m happy to take this on and submit a change for review, if others agree about doing it this way. It’s a relatively minor change.
I’ll implement this while reworking certificate selection in #32426.
We can just make unrecognized_name the alert we send whenever there are no certificates available. That can be reached easily both from
GetCertificate
andGetConfigForClient
by leavingCertificates
empty. (Let’s not document it for now not to commit to it under the compatibility promise.)I would accept a CL that implements that. (Once the tree opens.)
@tmthrgd I like that proposal. At least, from the perspective of my use case (Caddy), it should fit nicely since we don’t use the Certificates field at all. (We make heavy use of GetCertificate).
True, but I can also think of countless other types of problems that could occur in more advanced situations of getting a certificate, even if the name is recognized. For example, Caddy is subject to rate limits and, even if its configuration recognizes the name in the handshake, it may not be able to get a certificate for that hostname. In my opinion, that’s different than the server not being configured at all to connect with that name.
The special error value fundamentally says, “I don’t know this identity/name, what are you even doing??” (client’s mistake) as opposed to “I know this name, but [something went wrong] and I can’t prove it…” (server’s mistake)