/* Structure to define a built-in extension */
typedef struct extensions_definition_st {
/* The defined type for the extension */
unsigned int type;
/*
* The context that this extension applies to, e.g. what messages and
* protocol versions
*/
unsigned int context;
/*
* Initialise extension before parsing. Always called for relevant contexts
* even if extension not present
*/
int (*init)(SSL *s, unsigned int context);
/* Parse extension sent from client to server */
int (*parse_ctos)(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
size_t chainidx);
/* Parse extension send from server to client */
int (*parse_stoc)(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
size_t chainidx);
/* Construct extension sent from server to client */
EXT_RETURN (*construct_stoc)(SSL *s, WPACKET *pkt, unsigned int context,
X509 *x, size_t chainidx);
/* Construct extension sent from client to server */
EXT_RETURN (*construct_ctos)(SSL *s, WPACKET *pkt, unsigned int context,
X509 *x, size_t chainidx);
/*
* Finalise extension after parsing. Always called where an extensions was
* initialised even if the extension was not present. |sent| is set to 1 if
* the extension was seen, or 0 otherwise.
*/
int (*final)(SSL *s, unsigned int context, int sent);
} EXTENSION_DEFINITION;
static const EXTENSION_DEFINITION ext_defs[] = {
{
TLSEXT_TYPE_renegotiate,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_SSL3_ALLOWED | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
NULL, tls_parse_ctos_renegotiate, tls_parse_stoc_renegotiate,
tls_construct_stoc_renegotiate, tls_construct_ctos_renegotiate,
final_renegotiate
},
{
TLSEXT_TYPE_server_name,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
init_server_name,
tls_parse_ctos_server_name, tls_parse_stoc_server_name,
tls_construct_stoc_server_name, tls_construct_ctos_server_name,
final_server_name
},
...
}
./statem/extensions_srvr.c
int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, unsigned int context,
X509 *x, size_t chainidx)
{
unsigned int servname_type;
PACKET sni, hostname;
if (!PACKET_as_length_prefixed_2(pkt, &sni)
/* ServerNameList must be at least 1 byte long. */
|| PACKET_remaining(&sni) == 0) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_SERVER_NAME,
SSL_R_BAD_EXTENSION);
return 0;
}
/*
* Although the intent was for server_name to be extensible, RFC 4366
* was not clear about it; and so OpenSSL among other implementations,
* always and only allows a 'host_name' name types.
* RFC 6066 corrected the mistake but adding new name types
* is nevertheless no longer feasible, so act as if no other
* SNI types can exist, to simplify parsing.
*
* Also note that the RFC permits only one SNI value per type,
* i.e., we can only have a single hostname.
*/
if (!PACKET_get_1(&sni, &servname_type)
|| servname_type != TLSEXT_NAMETYPE_host_name
|| !PACKET_as_length_prefixed_2(&sni, &hostname)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_SERVER_NAME,
SSL_R_BAD_EXTENSION);
return 0;
}
if (!s->hit || SSL_IS_TLS13(s)) {
if (PACKET_remaining(&hostname) > TLSEXT_MAXLEN_host_name) {
SSLfatal(s, SSL_AD_UNRECOGNIZED_NAME,
SSL_F_TLS_PARSE_CTOS_SERVER_NAME,
SSL_R_BAD_EXTENSION);
return 0;
}
if (PACKET_contains_zero_byte(&hostname)) {
SSLfatal(s, SSL_AD_UNRECOGNIZED_NAME,
SSL_F_TLS_PARSE_CTOS_SERVER_NAME,
SSL_R_BAD_EXTENSION);
return 0;
}
/*
* Store the requested SNI in the SSL as temporary storage.
* If we accept it, it will get stored in the SSL_SESSION as well.
*/
OPENSSL_free(s->ext.hostname);
s->ext.hostname = NULL;
if (!PACKET_strndup(&hostname, &s->ext.hostname)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_SERVER_NAME,
ERR_R_INTERNAL_ERROR);
return 0;
}
s->servername_done = 1;
}
if (s->hit) {
/*
* TODO(openssl-team): if the SNI doesn't match, we MUST
* fall back to a full handshake.
*/
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;
}
概述
随着互联网的发展,openssl早期的协议支持不了新的需求。例如:单ip支持https多虚拟主机。 http2还是http1.1的协商等。 因此,需要在原来的协议上再扩展。 现在就来看下扩展的代码的。我们从SNI是如何支持的来看起。
代码
根据上一篇的分析,从read_state_machine函数开始,调用ossl_statem_server_post_process_message函数再调用tls_post_process_client_hello再调用tls_early_post_process_client_hello,再调用tls_parse_all_extensions,该函数又调用tls_parse_extension。在这个函数根据调用了封装在ext_defs数组中的回调。
statem/extensions.c
./statem/extensions_srvr.c
最后分析一个完整的报文:
static ngx_uint_t ngx_pkt_peek_net_2(u_char p) { ngx_uint_t x = 0; x = p << 8; x |= *(p+1); return x; }
static ngx_int_t ngx_http_ssl_sni(u_char pkt, size_t len, ngx_str_t sni) { size_t off = 0; ngx_uint_t pkg_len, ext_len; if (len < 4) { return NGX_AGAIN; }
}
endif
[req] distinguished_name = req_distinguished_name x509_extensions = v3_req prompt = no
[req_distinguished_name] C = CN ST = Beijing L = Beijing O = vislee CN = vislee Root CA0
[v3_req] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer basicConstraints = CA:TRUE
ca1.cnf
server.cnf
总结