diff --git a/CMakeLists.txt b/CMakeLists.txt index 598febe5..cc50eb3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 1d935c8d..b9652dd0 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -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 diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index f77d23c3..befbe150 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -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 diff --git a/lib/tls/mbedtls/mbedtls-client.c b/lib/tls/mbedtls/mbedtls-client.c index 990edc63..37e11602 100644 --- a/lib/tls/mbedtls/mbedtls-client.c +++ b/lib/tls/mbedtls/mbedtls-client.c @@ -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); diff --git a/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h index 2ca438c4..ba19663d 100644 --- a/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h +++ b/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h @@ -203,6 +203,8 @@ struct ssl_st const SSL_METHOD *method; + const char **alpn_protos; + RECORD_LAYER rlayer; /* where we are */ diff --git a/lib/tls/mbedtls/wrapper/include/openssl/ssl.h b/lib/tls/mbedtls/wrapper/include/openssl/ssl.h index a6f950fb..aa585d00 100755 --- a/lib/tls/mbedtls/wrapper/include/openssl/ssl.h +++ b/lib/tls/mbedtls/wrapper/include/openssl/ssl.h @@ -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 diff --git a/lib/tls/mbedtls/wrapper/library/ssl_lib.c b/lib/tls/mbedtls/wrapper/library/ssl_lib.c index 22cc24bb..0b13cca1 100644 --- a/lib/tls/mbedtls/wrapper/library/ssl_lib.c +++ b/lib/tls/mbedtls/wrapper/library/ssl_lib.c @@ -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); } diff --git a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c index 3d70275e..4716c1ff 100755 --- a/lib/tls/mbedtls/wrapper/platform/ssl_pm.c +++ b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c @@ -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)) diff --git a/lib/tls/openssl/openssl-client.c b/lib/tls/openssl/openssl-client.c index d074ceeb..87758101 100644 --- a/lib/tls/openssl/openssl-client.c +++ b/lib/tls/openssl/openssl-client.c @@ -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);