提交 e9cd6e76 编写于 作者: M Matt Caswell

Fix SSL_get_servername() and SNI behaviour

The SNI behaviour for TLSv1.3 and the behaviour of SSL_get_servername()
was not quite right, and not entirely consistent with the RFC.

The TLSv1.3 RFC explicitly says that SNI is negotiated on each handshake
and the server is not required to associate it with the session. This was
not quite reflected in the code so we fix that.

Additionally there were some additional checks around early_data checking
that the SNI between the original session and this session were
consistent. In fact the RFC does not require any such checks, so they are
removed.

Finally the behaviour of SSL_get_servername() was not quite right. The
behaviour was not consistent between resumption and normal handshakes,
and also not quite consistent with historical behaviour. We clarify the
behaviour in various scenarios and also attempt to make it match historical
behaviour as closely as possible.

Fixes #8822
Reviewed-by: NBen Kaduk <kaduk@mit.edu>
(Merged from https://github.com/openssl/openssl/pull/10018)

(cherry picked from commit 7955c1f16e72dc944677fd1dbf4b1300e75f1c84)
上级 081191e3
......@@ -2623,31 +2623,85 @@ char *SSL_get_shared_ciphers(const SSL *s, char *buf, int size)
return buf;
}
/** return a servername extension value if provided in Client Hello, or NULL.
* So far, only host_name types are defined (RFC 3546).
/**
* Return the requested servername (SNI) value. Note that the behaviour varies
* depending on:
* - whether this is called by the client or the server,
* - if we are before or during/after the handshake,
* - if a resumption or normal handshake is being attempted/has occurred
* - whether we have negotiated TLSv1.2 (or below) or TLSv1.3
*
* Note that only the host_name type is defined (RFC 3546).
*/
const char *SSL_get_servername(const SSL *s, const int type)
{
/*
* If we don't know if we are the client or the server yet then we assume
* client.
*/
int server = s->handshake_func == NULL ? 0 : s->server;
if (type != TLSEXT_NAMETYPE_host_name)
return NULL;
/*
* SNI is not negotiated in pre-TLS-1.3 resumption flows, so fake up an
* SNI value to return if we are resuming/resumed. N.B. that we still
* call the relevant callbacks for such resumption flows, and callbacks
* might error out if there is not a SNI value available.
*/
if (s->hit)
return s->session->ext.hostname;
if (server) {
/**
* Server side
* In TLSv1.3 on the server SNI is not associated with the session
* but in TLSv1.2 or below it is.
*
* Before the handshake:
* - return NULL
*
* During/after the handshake (TLSv1.2 or below resumption occurred):
* - If a servername was accepted by the server in the original
* handshake then it will return that servername, or NULL otherwise.
*
* During/after the handshake (TLSv1.2 or below resumption did not occur):
* - The function will return the servername requested by the client in
* this handshake or NULL if none was requested.
*/
if (s->hit && !SSL_IS_TLS13(s))
return s->session->ext.hostname;
} else {
/**
* Client side
*
* Before the handshake:
* - If a servername has been set via a call to
* SSL_set_tlsext_host_name() then it will return that servername
* - If one has not been set, but a TLSv1.2 resumption is being
* attempted and the session from the original handshake had a
* servername accepted by the server then it will return that
* servername
* - Otherwise it returns NULL
*
* During/after the handshake (TLSv1.2 or below resumption occurred):
* - If the session from the orignal handshake had a servername accepted
* by the server then it will return that servername.
* - Otherwise it returns the servername set via
* SSL_set_tlsext_host_name() (or NULL if it was not called).
*
* During/after the handshake (TLSv1.2 or below resumption did not occur):
* - It will return the servername set via SSL_set_tlsext_host_name()
* (or NULL if it was not called).
*/
if (SSL_in_before(s)) {
if (s->ext.hostname == NULL
&& s->session != NULL
&& s->session->ssl_version != TLS1_3_VERSION)
return s->session->ext.hostname;
} else {
if (!SSL_IS_TLS13(s) && s->hit && s->session->ext.hostname != NULL)
return s->session->ext.hostname;
}
}
return s->ext.hostname;
}
int SSL_get_servername_type(const SSL *s)
{
if (s->session
&& (!s->ext.hostname ? s->session->
ext.hostname : s->ext.hostname))
if (SSL_get_servername(s, TLSEXT_NAMETYPE_host_name) != NULL)
return TLSEXT_NAMETYPE_host_name;
return -1;
}
......
......@@ -949,7 +949,6 @@ static int final_server_name(SSL *s, unsigned int context, int sent)
* was successful.
*/
if (s->server) {
/* TODO(OpenSSL1.2) revisit !sent case */
if (sent && ret == SSL_TLSEXT_ERR_OK && !s->hit) {
/* Only store the hostname in the session if we accepted it. */
OPENSSL_free(s->session->ext.hostname);
......
......@@ -127,6 +127,10 @@ int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, unsigned int context,
return 0;
}
/*
* In TLSv1.2 and below the SNI is associated with the session. In TLSv1.3
* we always use the SNI value from the handshake.
*/
if (!s->hit || SSL_IS_TLS13(s)) {
if (PACKET_remaining(&hostname) > TLSEXT_MAXLEN_host_name) {
SSLfatal(s, SSL_AD_UNRECOGNIZED_NAME,
......@@ -155,8 +159,12 @@ int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, unsigned int context,
}
s->servername_done = 1;
}
if (s->hit) {
} else {
/*
* In TLSv1.2 and below we should check if the SNI is consistent between
* the initial handshake and the resumption. In TLSv1.3 SNI is not
* associated with the session.
*/
/*
* TODO(openssl-team): if the SNI doesn't match, we MUST
* fall back to a full handshake.
......@@ -164,9 +172,6 @@ int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, unsigned int context,
s->servername_done = (s->session->ext.hostname != NULL)
&& PACKET_equal(&hostname, s->session->ext.hostname,
strlen(s->session->ext.hostname));
if (!s->servername_done && s->session->ext.hostname != NULL)
s->ext.early_data_ok = 0;
}
return 1;
......@@ -1330,8 +1335,14 @@ EXT_RETURN tls_construct_stoc_server_name(SSL *s, WPACKET *pkt,
unsigned int context, X509 *x,
size_t chainidx)
{
if (s->hit || s->servername_done != 1
|| s->ext.hostname == NULL)
if (s->servername_done != 1)
return EXT_RETURN_NOT_SENT;
/*
* Prior to TLSv1.3 we ignore any SNI in the current handshake if resuming.
* We just use the servername from the initial handshake.
*/
if (s->hit && !SSL_IS_TLS13(s))
return EXT_RETURN_NOT_SENT;
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_name)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册