1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

client: add alpn processing on mbedtls and openssl

This just supports "http/1.1" alpn and lets the client know it
can use keepalive earlier if it affirmitively negotiated
"http/1.1" on alpn.

mbedTLS wrapper needed a small adaptation to also allow
per-client-ssl control of the alpn negotiation list.
This commit is contained in:
Andy Green 2018-03-27 06:29:49 +08:00
parent 04e1661411
commit b45956fcb9
9 changed files with 111 additions and 10 deletions

View file

@ -1298,6 +1298,8 @@ CHECK_FUNCTION_EXISTS(X509_VERIFY_PARAM_set1_host LWS_HAVE_X509_VERIFY_PARAM_set
CHECK_FUNCTION_EXISTS(RSA_set0_key LWS_HAVE_RSA_SET0_KEY)
CHECK_FUNCTION_EXISTS(X509_get_key_usage LWS_HAVE_X509_get_key_usage)
CHECK_FUNCTION_EXISTS(SSL_CTX_get0_certificate LWS_HAVE_SSL_CTX_get0_certificate)
CHECK_FUNCTION_EXISTS(SSL_get0_alpn_selected LWS_HAVE_SSL_get0_alpn_selected)
CHECK_FUNCTION_EXISTS(SSL_set_alpn_protos LWS_HAVE_SSL_set_alpn_protos)
if (LWS_WITH_SSL AND NOT LWS_WITH_MBEDTLS)
CHECK_SYMBOL_EXISTS(SSL_CTX_get_extra_chain_certs_only openssl/ssl.h LWS_HAVE_SSL_EXTRA_CHAIN_CERTS)
endif()

View file

@ -163,6 +163,8 @@
#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD
#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
#cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
#cmakedefine LWS_HAVE_SSL_get0_alpn_selected
#cmakedefine LWS_HAVE_SSL_set_alpn_protos
#cmakedefine LWS_HAS_INTPTR_T

View file

@ -3269,6 +3269,7 @@ enum lws_client_connect_ssl_connection_flags {
LCCSCF_ALLOW_SELFSIGNED = (1 << 1),
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2),
LCCSCF_ALLOW_EXPIRED = (1 << 3),
LCCSCF_NOT_H2 = (1 << 4),
LCCSCF_PIPELINE = (1 << 16),
/**< Serialize / pipeline multiple client connections

View file

@ -27,6 +27,14 @@ OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
return 0;
}
struct alpn_ctx {
unsigned char *data;
unsigned short len;
};
static struct alpn_ctx protos = { (unsigned char *)
"\x08http/1.1", 3 + 9 };
int
lws_ssl_client_bio_create(struct lws *wsi)
{
@ -57,6 +65,8 @@ lws_ssl_client_bio_create(struct lws *wsi)
if (!wsi->ssl)
return -1;
SSL_set_alpn_select_cb(wsi->ssl, &protos);
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
@ -89,9 +99,24 @@ enum lws_ssl_capable_status
lws_tls_client_connect(struct lws *wsi)
{
int m, n = SSL_connect(wsi->ssl);
const unsigned char *prot;
unsigned int len;
if (n == 1) {
SSL_get0_alpn_selected(wsi->ssl, &prot, &len);
if (prot && !strcmp((char *)prot, "http/1.1"))
/*
* If alpn asserts it is http/1.1, KA is mandatory.
*
* Knowing this lets us proceed with sending
* pipelined headers before we received the first
* response headers.
*/
wsi->keepalive_active = 1;
if (n == 1)
return LWS_SSL_CAPABLE_DONE;
}
m = SSL_get_error(wsi->ssl, n);

View file

@ -203,6 +203,8 @@ struct ssl_st
const SSL_METHOD *method;
const char **alpn_protos;
RECORD_LAYER rlayer;
/* where we are */

View file

@ -321,6 +321,7 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
void *arg),
void *arg);
void SSL_set_alpn_select_cb(SSL *ssl, void *arg);
/**
* @brief set the SSL context ALPN select protocol

View file

@ -351,6 +351,9 @@ void SSL_free(SSL *ssl)
SSL_SESSION_free(ssl->session);
if (ssl->alpn_protos)
ssl_mem_free(ssl->alpn_protos);
ssl_mem_free(ssl);
}
@ -1649,11 +1652,12 @@ struct alpn_ctx {
unsigned short len;
};
void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
static void
_openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos)
{
struct alpn_ctx *ac = arg;
unsigned char *p = ac->data, *q;
unsigned char len;
char **alpn_protos;
int count = 0;
/* find out how many entries he gave us */
@ -1675,18 +1679,20 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
/* allocate space for count + 1 pointers and the data afterwards */
ctx->alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1);
if (!ctx->alpn_protos)
alpn_protos = ssl_mem_zalloc((count + 1) * sizeof(char *) + ac->len + 1);
if (!alpn_protos)
return;
*palpn_protos = alpn_protos;
/* convert to mbedtls format */
q = (unsigned char *)ctx->alpn_protos + (count + 1) * sizeof(char *);
q = (unsigned char *)alpn_protos + (count + 1) * sizeof(char *);
p = ac->data;
count = 0;
len = *p++;
ctx->alpn_protos[count] = (char *)q;
alpn_protos[count] = (char *)q;
while (p - ac->data < ac->len) {
if (len--) {
*q++ = *p++;
@ -1695,11 +1701,27 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
*q++ = '\0';
count++;
len = *p++;
ctx->alpn_protos[count] = (char *)q;
alpn_protos[count] = (char *)q;
if (!len)
break;
}
ctx->alpn_protos[count] = NULL; /* last pointer ends list with NULL */
alpn_protos[count] = NULL; /* last pointer ends list with NULL */
}
void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, next_proto_cb cb, void *arg)
{
struct alpn_ctx *ac = arg;
ctx->alpn_cb = cb;
_openssl_alpn_to_mbedtls(ac, (char ***)&ctx->alpn_protos);
}
void SSL_set_alpn_select_cb(SSL *ssl, void *arg)
{
struct alpn_ctx *ac = arg;
_openssl_alpn_to_mbedtls(ac, (char ***)&ssl->alpn_protos);
_ssl_set_alpn_list(ssl);
}

View file

@ -829,6 +829,12 @@ int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
void _ssl_set_alpn_list(const SSL *ssl)
{
if (ssl->alpn_protos) {
if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->alpn_protos))
fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n");
return;
}
if (!ssl->ctx->alpn_protos)
return;
if (mbedtls_ssl_conf_alpn_protocols(&((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos))

View file

@ -86,6 +86,12 @@ OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
}
#endif
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
static const unsigned char client_alpn_protocols[] = {
8, 'h', 't', 't', 'p', '/', '1', '.', '1'
};
#endif
int
lws_ssl_client_bio_create(struct lws *wsi)
{
@ -93,6 +99,10 @@ lws_ssl_client_bio_create(struct lws *wsi)
X509_VERIFY_PARAM *param;
#endif
char hostname[128], *p;
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
const unsigned char *plist = client_alpn_protocols;
int n = sizeof(client_alpn_protocols);
#endif
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_HOST) <= 0) {
@ -122,6 +132,10 @@ lws_ssl_client_bio_create(struct lws *wsi)
return -1;
}
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
SSL_set_alpn_protos(wsi->ssl, plist, n);
#endif
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
@ -205,10 +219,36 @@ lws_ssl_client_bio_create(struct lws *wsi)
enum lws_ssl_capable_status
lws_tls_client_connect(struct lws *wsi)
{
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
const unsigned char *prot;
char a[32];
unsigned int len;
#endif
int m, n = SSL_connect(wsi->ssl);
if (n == 1)
if (n == 1) {
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
SSL_get0_alpn_selected(wsi->ssl, &prot, &len);
if (len > sizeof(a))
len = sizeof(a) - 1;
memcpy(a, (const char *)prot, len);
a[len] = '\0';
if (prot && !strcmp(a, "http/1.1"))
/*
* If alpn asserts it is http/1.1, KA is mandatory.
*
* Knowing this lets us proceed with sending
* pipelined headers before we received the first
* response headers.
*/
wsi->keepalive_active = 1;
lwsl_notice("client connect OK\n");
#endif
return LWS_SSL_CAPABLE_DONE;
}
m = lws_ssl_get_error(wsi, n);