2017-10-18 09:41:44 +08:00
|
|
|
/*
|
2019-08-14 10:44:14 +01:00
|
|
|
* libwebsockets - small server side websockets and web server implementation
|
2017-10-18 09:41:44 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
2017-10-18 09:41:44 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
2017-10-18 09:41:44 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
2017-10-18 09:41:44 +08:00
|
|
|
*
|
2019-08-14 10:44:14 +01:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
2017-10-18 09:41:44 +08:00
|
|
|
*/
|
2020-08-30 14:35:27 +03:00
|
|
|
|
|
|
|
#include "lws_config.h"
|
|
|
|
#ifdef LWS_HAVE_X509_VERIFY_PARAM_set1_host
|
|
|
|
/* Before glibc 2.10, strnlen required _GNU_SOURCE */
|
2021-07-07 19:10:06 +01:00
|
|
|
#if !defined(_GNU_SOURCE)
|
2020-08-30 14:35:27 +03:00
|
|
|
#define _GNU_SOURCE
|
|
|
|
#endif
|
2021-07-07 19:10:06 +01:00
|
|
|
#endif
|
2020-04-29 12:18:41 -07:00
|
|
|
#include <string.h>
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2019-08-15 10:49:52 +01:00
|
|
|
#include "private-lib-core.h"
|
|
|
|
#include "private-lib-tls-openssl.h"
|
2019-03-17 10:03:22 +08:00
|
|
|
|
2018-12-13 20:05:12 +08:00
|
|
|
/*
|
|
|
|
* Care: many openssl apis return 1 for success. These are translated to the
|
|
|
|
* lws convention of 0 for success.
|
|
|
|
*/
|
|
|
|
|
2018-10-20 07:54:51 +08:00
|
|
|
int lws_openssl_describe_cipher(struct lws *wsi);
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
extern int openssl_websocket_private_data_index,
|
|
|
|
openssl_SSL_CTX_private_data_index;
|
|
|
|
|
|
|
|
#if !defined(USE_WOLFSSL)
|
|
|
|
|
2021-05-21 14:32:21 +01:00
|
|
|
#if 0
|
|
|
|
#if defined(LWS_WITH_TLS_JIT_TRUST)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Completion of sync or async JIT trust lookup
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
lws_tls_jit_trust_got_cert_cb(void *got_opaque, const uint8_t *der,
|
|
|
|
size_t der_len)
|
|
|
|
{
|
|
|
|
X509 *x = d2i_X509(NULL, &der, (long)der_len);
|
|
|
|
/** !!! this is not safe for async atm */
|
|
|
|
struct lws *wsi = (struct lws *)got_opaque;
|
|
|
|
X509_STORE *xs;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!x) {
|
|
|
|
lwsl_err("%s: failed\n", __func__);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
xs = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(wsi->tls.ssl));
|
|
|
|
if (xs) {
|
|
|
|
if (X509_STORE_add_cert(xs, x) != 1) {
|
|
|
|
lwsl_warn("%s: unable to set trusted CA\n", __func__);
|
|
|
|
ret = 1;
|
|
|
|
} else
|
|
|
|
lwsl_notice("%s: added trusted CA to CTX for next time\n",
|
|
|
|
__func__);
|
|
|
|
} else
|
|
|
|
lwsl_warn("%s: couldn't get cert store\n", __func__);
|
|
|
|
|
|
|
|
X509_free(x);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
static int
|
|
|
|
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|
|
|
{
|
|
|
|
SSL *ssl;
|
2021-05-21 14:32:21 +01:00
|
|
|
int n, err = 0;
|
2017-10-18 09:41:44 +08:00
|
|
|
struct lws *wsi;
|
|
|
|
|
|
|
|
/* keep old behaviour accepting self-signed server certs */
|
|
|
|
if (!preverify_ok) {
|
2021-05-21 14:32:21 +01:00
|
|
|
err = X509_STORE_CTX_get_error(x509_ctx);
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
if (err != X509_V_OK) {
|
2018-11-23 08:47:56 +08:00
|
|
|
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
|
|
|
|
SSL_get_ex_data_X509_STORE_CTX_idx());
|
|
|
|
wsi = SSL_get_ex_data(ssl,
|
|
|
|
openssl_websocket_private_data_index);
|
2019-04-03 08:04:48 +08:00
|
|
|
if (!wsi) {
|
|
|
|
lwsl_err("%s: can't get wsi from ssl privdata\n",
|
|
|
|
__func__);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
|
2018-11-23 08:47:56 +08:00
|
|
|
err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
|
|
|
|
wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
|
|
|
|
lwsl_notice("accepting self-signed "
|
|
|
|
"certificate (verify_callback)\n");
|
2017-10-18 09:41:44 +08:00
|
|
|
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
|
|
|
|
return 1; // ok
|
2019-09-18 10:00:59 +01:00
|
|
|
} else if ((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY ||
|
|
|
|
err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) &&
|
|
|
|
wsi->tls.use_ssl & LCCSCF_ALLOW_INSECURE) {
|
|
|
|
lwsl_notice("accepting non-trusted certificate\n");
|
|
|
|
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
|
|
|
|
return 1; /* ok */
|
2017-10-18 09:41:44 +08:00
|
|
|
} else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
|
2018-11-23 08:47:56 +08:00
|
|
|
err == X509_V_ERR_CERT_HAS_EXPIRED) &&
|
|
|
|
wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED) {
|
2017-10-18 09:41:44 +08:00
|
|
|
if (err == X509_V_ERR_CERT_NOT_YET_VALID)
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_notice("accepting not yet valid "
|
|
|
|
"certificate (verify_"
|
|
|
|
"callback)\n");
|
2017-10-18 09:41:44 +08:00
|
|
|
else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_notice("accepting expired "
|
|
|
|
"certificate (verify_"
|
|
|
|
"callback)\n");
|
2017-10-18 09:41:44 +08:00
|
|
|
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
|
|
|
|
return 1; // ok
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
|
|
|
|
SSL_get_ex_data_X509_STORE_CTX_idx());
|
2017-10-18 09:41:44 +08:00
|
|
|
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
|
2019-04-03 08:04:48 +08:00
|
|
|
if (!wsi) {
|
|
|
|
lwsl_err("%s: can't get wsi from ssl privdata\n", __func__);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2021-05-21 14:32:21 +01:00
|
|
|
#if defined(LWS_WITH_TLS_JIT_TRUST)
|
|
|
|
if (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
|
|
|
|
union lws_tls_cert_info_results ci;
|
|
|
|
STACK_OF(X509) *x509_stack;
|
|
|
|
|
|
|
|
x509_stack = X509_STORE_CTX_get1_chain(x509_ctx);
|
|
|
|
if (x509_stack) {
|
|
|
|
|
|
|
|
for (n = 0; n < OPENSSL_sk_num((const OPENSSL_STACK *)x509_stack) &&
|
|
|
|
wsi->tls.kid_chain.count !=
|
|
|
|
LWS_ARRAY_SIZE(wsi->tls.kid_chain.akid); n++) {
|
|
|
|
X509 *x509 = OPENSSL_sk_value((const OPENSSL_STACK *)x509_stack, n);
|
|
|
|
|
|
|
|
if (!lws_tls_openssl_cert_info(x509,
|
|
|
|
LWS_TLS_CERT_INFO_SUBJECT_KEY_ID,
|
|
|
|
&ci, 0))
|
|
|
|
lws_tls_kid_copy(&ci,
|
|
|
|
&wsi->tls.kid_chain.skid[
|
|
|
|
wsi->tls.kid_chain.count]);
|
|
|
|
|
|
|
|
if (!lws_tls_openssl_cert_info(x509,
|
|
|
|
LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID,
|
|
|
|
&ci, 0))
|
|
|
|
lws_tls_kid_copy(&ci,
|
|
|
|
&wsi->tls.kid_chain.akid[
|
|
|
|
wsi->tls.kid_chain.count]);
|
|
|
|
|
|
|
|
wsi->tls.kid_chain.count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk_X509_pop_free(x509_stack, X509_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
lws_tls_jit_trust_sort_kids(wsi, &wsi->tls.kid_chain);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
n = lws_get_context_protocol(wsi->a.context, 0).callback(wsi,
|
2017-10-18 09:41:44 +08:00
|
|
|
LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION,
|
2020-12-12 06:21:40 +00:00
|
|
|
x509_ctx, ssl, (unsigned int)preverify_ok);
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
/* keep old behaviour if something wrong with server certs */
|
|
|
|
/* if ssl error is overruled in callback and cert is ok,
|
|
|
|
* X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and
|
|
|
|
* return value is 0 from callback */
|
|
|
|
if (!preverify_ok) {
|
|
|
|
int err = X509_STORE_CTX_get_error(x509_ctx);
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
if (err != X509_V_OK) {
|
|
|
|
/* cert validation error was not handled in callback */
|
2017-10-18 09:41:44 +08:00
|
|
|
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
|
2018-11-23 08:47:56 +08:00
|
|
|
const char *msg = X509_verify_cert_error_string(err);
|
|
|
|
|
2021-04-15 09:30:43 +01:00
|
|
|
lws_strncpy(wsi->tls.err_helper, msg,
|
|
|
|
sizeof(wsi->tls.err_helper));
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;"
|
|
|
|
"depth=%d)\n", msg, preverify_ok, err, depth);
|
|
|
|
|
2021-01-06 15:08:22 +00:00
|
|
|
#if defined(LWS_WITH_SYS_METRICS)
|
|
|
|
{
|
|
|
|
char buckname[64];
|
|
|
|
|
|
|
|
lws_snprintf(buckname, sizeof(buckname),
|
|
|
|
"tls=\"%s\"", msg);
|
|
|
|
lws_metrics_hist_bump_describe_wsi(wsi,
|
|
|
|
lws_metrics_priv_to_pub(wsi->a.context->mth_conn_failures),
|
|
|
|
buckname);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
return preverify_ok; // not ok
|
|
|
|
}
|
|
|
|
}
|
2018-11-23 08:47:56 +08:00
|
|
|
/*
|
|
|
|
* convert callback return code from 0 = OK to verify callback
|
|
|
|
* return value 1 = OK
|
|
|
|
*/
|
2017-10-18 09:41:44 +08:00
|
|
|
return !n;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int
|
|
|
|
lws_ssl_client_bio_create(struct lws *wsi)
|
|
|
|
{
|
|
|
|
char hostname[128], *p;
|
2018-04-12 15:56:38 +08:00
|
|
|
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
|
|
|
|
defined(LWS_HAVE_SSL_get0_alpn_selected)
|
|
|
|
uint8_t openssl_alpn[40];
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
const char *alpn_comma = wsi->a.context->tls.alpn_default;
|
2018-04-12 15:56:38 +08:00
|
|
|
int n;
|
2018-03-27 06:29:49 +08:00
|
|
|
#endif
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2020-01-02 08:32:23 +00:00
|
|
|
if (wsi->stash) {
|
2019-11-20 10:24:48 +00:00
|
|
|
lws_strncpy(hostname, wsi->stash->cis[CIS_HOST], sizeof(hostname));
|
2020-01-02 08:32:23 +00:00
|
|
|
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
|
|
|
|
defined(LWS_HAVE_SSL_get0_alpn_selected)
|
|
|
|
alpn_comma = wsi->stash->cis[CIS_ALPN];
|
|
|
|
#endif
|
|
|
|
} else {
|
2018-04-27 19:16:50 +08:00
|
|
|
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
2019-11-20 10:24:48 +00:00
|
|
|
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
|
|
|
|
_WSI_TOKEN_CLIENT_HOST) <= 0)
|
2018-04-27 19:16:50 +08:00
|
|
|
#endif
|
2019-11-20 10:24:48 +00:00
|
|
|
{
|
|
|
|
lwsl_err("%s: Unable to get hostname\n", __func__);
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2019-11-20 10:24:48 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-10-18 09:41:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* remove any :port part on the hostname... necessary for network
|
|
|
|
* connection but typical certificates do not contain it
|
|
|
|
*/
|
|
|
|
p = hostname;
|
|
|
|
while (*p) {
|
|
|
|
if (*p == ':') {
|
|
|
|
*p = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
wsi->tls.ssl = SSL_new(wsi->a.vhost->tls.ssl_client_ctx);
|
2018-05-01 12:41:42 +08:00
|
|
|
if (!wsi->tls.ssl) {
|
2020-12-12 06:21:40 +00:00
|
|
|
const char *es = ERR_error_string(
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(uint32_t)
|
|
|
|
#else
|
|
|
|
(unsigned long)
|
|
|
|
#endif
|
|
|
|
lws_ssl_get_error(wsi, 0), NULL);
|
|
|
|
lwsl_err("SSL_new failed: %s\n", es);
|
2019-07-13 13:28:14 -07:00
|
|
|
lws_tls_err_describe_clear();
|
2017-10-18 09:41:44 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-03-25 08:37:36 +00:00
|
|
|
#if defined(LWS_WITH_TLS_SESSIONS)
|
|
|
|
if (!(wsi->a.vhost->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE))
|
|
|
|
lws_tls_reuse_session(wsi);
|
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
if (wsi->a.vhost->tls.ssl_info_event_mask)
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
|
2020-08-07 08:42:32 +01:00
|
|
|
#if defined(LWS_HAVE_X509_VERIFY_PARAM_set1_host)
|
2018-05-01 12:41:42 +08:00
|
|
|
if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
|
2020-08-07 08:42:32 +01:00
|
|
|
#if !defined(USE_WOLFSSL)
|
|
|
|
|
2018-10-10 13:54:43 +08:00
|
|
|
X509_VERIFY_PARAM *param = SSL_get0_param(wsi->tls.ssl);
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
/* Enable automatic hostname checks */
|
|
|
|
X509_VERIFY_PARAM_set_hostflags(param,
|
2018-11-23 08:47:56 +08:00
|
|
|
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
2020-04-29 12:18:41 -07:00
|
|
|
/* Handle the case where the hostname is an IP address */
|
2018-12-05 15:38:23 -08:00
|
|
|
if (!X509_VERIFY_PARAM_set1_ip_asc(param, hostname))
|
2020-04-29 12:18:41 -07:00
|
|
|
X509_VERIFY_PARAM_set1_host(param, hostname,
|
|
|
|
strnlen(hostname, sizeof(hostname)));
|
2020-08-07 08:42:32 +01:00
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
}
|
2020-01-16 19:49:56 +00:00
|
|
|
#else
|
|
|
|
if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
|
|
|
|
lwsl_err("%s: your tls lib is too old to have "
|
|
|
|
"X509_VERIFY_PARAM_set1_host, failing all client tls\n",
|
|
|
|
__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(USE_WOLFSSL)
|
|
|
|
#ifndef USE_OLD_CYASSL
|
|
|
|
/* OpenSSL_client_verify_callback will be called @ SSL_connect() */
|
2018-11-23 08:47:56 +08:00
|
|
|
SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER,
|
|
|
|
OpenSSL_client_verify_callback);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(USE_WOLFSSL)
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_set_mode(wsi->tls.ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* use server name indication (SNI), if supported,
|
|
|
|
* when establishing connection
|
|
|
|
*/
|
|
|
|
#ifdef USE_WOLFSSL
|
|
|
|
#ifdef USE_OLD_CYASSL
|
|
|
|
#ifdef CYASSL_SNI_HOST_NAME
|
2018-11-23 08:47:56 +08:00
|
|
|
CyaSSL_UseSNI(wsi->tls.ssl, CYASSL_SNI_HOST_NAME, hostname,
|
|
|
|
strlen(hostname));
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
#else
|
2021-10-06 06:25:03 +01:00
|
|
|
#if defined(WOLFSSL_SNI_HOST_NAME) || defined(HAVE_SNI)
|
2018-11-23 08:47:56 +08:00
|
|
|
wolfSSL_UseSNI(wsi->tls.ssl, WOLFSSL_SNI_HOST_NAME, hostname,
|
2021-10-06 06:25:03 +01:00
|
|
|
(unsigned short)strlen(hostname));
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_set_tlsext_host_name(wsi->tls.ssl, hostname);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_WOLFSSL
|
|
|
|
/*
|
|
|
|
* wolfSSL/CyaSSL does certificate verification differently
|
|
|
|
* from OpenSSL.
|
|
|
|
* If we should ignore the certificate, we need to set
|
|
|
|
* this before SSL_new and SSL_connect is called.
|
|
|
|
* Otherwise the connect will simply fail with error code -155
|
|
|
|
*/
|
|
|
|
#ifdef USE_OLD_CYASSL
|
2020-08-07 08:42:32 +01:00
|
|
|
if (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)
|
2018-05-01 12:41:42 +08:00
|
|
|
CyaSSL_set_verify(wsi->tls.ssl, SSL_VERIFY_NONE, NULL);
|
2017-10-18 09:41:44 +08:00
|
|
|
#else
|
2020-08-07 08:42:32 +01:00
|
|
|
if (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)
|
2018-05-01 12:41:42 +08:00
|
|
|
wolfSSL_set_verify(wsi->tls.ssl, SSL_VERIFY_NONE, NULL);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
#endif /* USE_WOLFSSL */
|
|
|
|
|
2020-06-03 06:48:06 +01:00
|
|
|
wsi->tls.client_bio = BIO_new_socket((int)(lws_intptr_t)wsi->desc.sockfd,
|
2018-11-23 08:47:56 +08:00
|
|
|
BIO_NOCLOSE);
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_set_bio(wsi->tls.ssl, wsi->tls.client_bio, wsi->tls.client_bio);
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
#ifdef USE_WOLFSSL
|
|
|
|
#ifdef USE_OLD_CYASSL
|
2018-05-01 12:41:42 +08:00
|
|
|
CyaSSL_set_using_nonblock(wsi->tls.ssl, 1);
|
2017-10-18 09:41:44 +08:00
|
|
|
#else
|
2018-05-01 12:41:42 +08:00
|
|
|
wolfSSL_set_using_nonblock(wsi->tls.ssl, 1);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
#else
|
2018-05-01 12:41:42 +08:00
|
|
|
BIO_set_nbio(wsi->tls.client_bio, 1); /* nonblocking */
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
|
2018-04-12 15:56:38 +08:00
|
|
|
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
|
|
|
|
defined(LWS_HAVE_SSL_get0_alpn_selected)
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
if (wsi->a.vhost->tls.alpn)
|
|
|
|
alpn_comma = wsi->a.vhost->tls.alpn;
|
2022-09-18 08:13:21 +01:00
|
|
|
if (wsi->stash) {
|
2020-01-02 08:32:23 +00:00
|
|
|
alpn_comma = wsi->stash->cis[CIS_ALPN];
|
2018-04-27 19:16:50 +08:00
|
|
|
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
2022-09-18 08:13:21 +01:00
|
|
|
} else {
|
|
|
|
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
|
|
|
|
_WSI_TOKEN_CLIENT_ALPN) > 0)
|
|
|
|
alpn_comma = hostname;
|
2018-04-27 19:16:50 +08:00
|
|
|
#endif
|
2022-09-18 08:13:21 +01:00
|
|
|
}
|
2018-04-12 15:56:38 +08:00
|
|
|
|
2020-01-02 08:32:23 +00:00
|
|
|
lwsl_info("%s client conn using alpn list '%s'\n", wsi->role_ops->name, alpn_comma);
|
2018-04-12 15:56:38 +08:00
|
|
|
|
|
|
|
n = lws_alpn_comma_to_openssl(alpn_comma, openssl_alpn,
|
|
|
|
sizeof(openssl_alpn) - 1);
|
|
|
|
|
2020-12-12 06:21:40 +00:00
|
|
|
SSL_set_alpn_protos(wsi->tls.ssl, openssl_alpn, (unsigned int)n);
|
2018-04-12 15:56:38 +08:00
|
|
|
#endif
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
SSL_set_ex_data(wsi->tls.ssl, openssl_websocket_private_data_index,
|
|
|
|
wsi);
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2019-12-08 03:04:03 +00:00
|
|
|
if (wsi->sys_tls_client_cert) {
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
lws_system_blob_t *b = lws_system_get_blob(wsi->a.context,
|
2019-12-08 03:04:03 +00:00
|
|
|
LWS_SYSBLOB_TYPE_CLIENT_CERT_DER,
|
|
|
|
wsi->sys_tls_client_cert - 1);
|
|
|
|
const uint8_t *data;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
if (!b)
|
|
|
|
goto no_client_cert;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the per-connection client cert
|
|
|
|
*/
|
|
|
|
|
|
|
|
size = lws_system_blob_get_size(b);
|
|
|
|
if (!size)
|
|
|
|
goto no_client_cert;
|
|
|
|
|
|
|
|
if (lws_system_blob_get_single_ptr(b, &data))
|
|
|
|
goto no_client_cert;
|
|
|
|
|
2020-03-20 09:35:54 +00:00
|
|
|
if (SSL_use_certificate_ASN1(wsi->tls.ssl,
|
|
|
|
#if defined(USE_WOLFSSL)
|
|
|
|
(unsigned char *)
|
|
|
|
#endif
|
2020-12-12 06:21:40 +00:00
|
|
|
data,
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(size_t)
|
|
|
|
#else
|
|
|
|
(int)
|
|
|
|
#endif
|
|
|
|
size) != 1) {
|
2019-12-08 03:04:03 +00:00
|
|
|
lwsl_err("%s: use_certificate failed\n", __func__);
|
|
|
|
lws_tls_err_describe_clear();
|
|
|
|
goto no_client_cert;
|
|
|
|
}
|
|
|
|
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
b = lws_system_get_blob(wsi->a.context,
|
2019-12-08 03:04:03 +00:00
|
|
|
LWS_SYSBLOB_TYPE_CLIENT_KEY_DER,
|
|
|
|
wsi->sys_tls_client_cert - 1);
|
|
|
|
if (!b)
|
|
|
|
goto no_client_cert;
|
|
|
|
|
|
|
|
size = lws_system_blob_get_size(b);
|
|
|
|
if (!size)
|
|
|
|
goto no_client_cert;
|
|
|
|
|
|
|
|
if (lws_system_blob_get_single_ptr(b, &data))
|
|
|
|
goto no_client_cert;
|
|
|
|
|
|
|
|
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, wsi->tls.ssl,
|
2020-03-20 09:35:54 +00:00
|
|
|
#if defined(USE_WOLFSSL)
|
|
|
|
(unsigned char *)
|
|
|
|
#endif
|
|
|
|
|
2020-12-12 06:21:40 +00:00
|
|
|
data,
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(size_t)
|
|
|
|
#else
|
|
|
|
(int)
|
|
|
|
#endif
|
|
|
|
size) != 1 &&
|
2019-12-08 03:04:03 +00:00
|
|
|
SSL_use_PrivateKey_ASN1(EVP_PKEY_EC, wsi->tls.ssl,
|
2020-03-20 09:35:54 +00:00
|
|
|
#if defined(USE_WOLFSSL)
|
|
|
|
(unsigned char *)
|
|
|
|
#endif
|
2020-12-12 06:21:40 +00:00
|
|
|
data,
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(size_t)
|
|
|
|
#else
|
|
|
|
(int)
|
|
|
|
#endif
|
|
|
|
size) != 1) {
|
2019-12-08 03:04:03 +00:00
|
|
|
lwsl_err("%s: use_privkey failed\n", __func__);
|
|
|
|
lws_tls_err_describe_clear();
|
|
|
|
goto no_client_cert;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SSL_check_private_key(wsi->tls.ssl) != 1) {
|
|
|
|
lwsl_err("Private SSL key doesn't match cert\n");
|
|
|
|
lws_tls_err_describe_clear();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwsl_notice("%s: set system client cert %u\n", __func__,
|
|
|
|
wsi->sys_tls_client_cert - 1);
|
|
|
|
}
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
return 0;
|
2019-12-08 03:04:03 +00:00
|
|
|
|
|
|
|
no_client_cert:
|
|
|
|
lwsl_err("%s: unable to set up system client cert %d\n", __func__,
|
|
|
|
wsi->sys_tls_client_cert - 1);
|
|
|
|
|
|
|
|
return 1;
|
2017-10-18 09:41:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
enum lws_ssl_capable_status
|
2020-12-12 06:21:40 +00:00
|
|
|
lws_tls_client_connect(struct lws *wsi, char *errbuf, size_t elen)
|
2017-10-18 09:41:44 +08:00
|
|
|
{
|
2018-11-23 08:47:56 +08:00
|
|
|
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
|
|
|
|
defined(LWS_HAVE_SSL_get0_alpn_selected)
|
2018-03-27 06:29:49 +08:00
|
|
|
const unsigned char *prot;
|
|
|
|
char a[32];
|
|
|
|
unsigned int len;
|
|
|
|
#endif
|
2020-04-14 19:04:13 +01:00
|
|
|
int m, n, en;
|
2023-10-20 17:42:35 -04:00
|
|
|
unsigned long l;
|
2021-03-25 08:37:36 +00:00
|
|
|
#if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_HAVE_SSL_SESSION_set_time)
|
|
|
|
SSL_SESSION *sess;
|
|
|
|
#endif
|
2019-07-10 20:34:24 +01:00
|
|
|
errno = 0;
|
|
|
|
ERR_clear_error();
|
2021-04-15 09:30:43 +01:00
|
|
|
wsi->tls.err_helper[0] = '\0';
|
2019-07-10 20:34:24 +01:00
|
|
|
n = SSL_connect(wsi->tls.ssl);
|
2020-03-01 05:45:13 +00:00
|
|
|
en = errno;
|
2020-04-14 19:04:13 +01:00
|
|
|
|
2020-03-01 05:45:13 +00:00
|
|
|
m = lws_ssl_get_error(wsi, n);
|
|
|
|
|
|
|
|
if (m == SSL_ERROR_SYSCALL
|
|
|
|
#if defined(WIN32)
|
|
|
|
&& en
|
|
|
|
#endif
|
|
|
|
) {
|
2020-04-22 06:32:15 +01:00
|
|
|
#if defined(WIN32) || (_LWS_ENABLED_LOGS & LLL_INFO)
|
2020-03-01 05:45:13 +00:00
|
|
|
lwsl_info("%s: n %d, m %d, errno %d\n", __func__, n, m, en);
|
2020-03-10 06:45:24 +00:00
|
|
|
#endif
|
2020-04-14 19:04:13 +01:00
|
|
|
lws_snprintf(errbuf, elen, "connect SYSCALL %d", en);
|
2020-03-01 05:45:13 +00:00
|
|
|
return LWS_SSL_CAPABLE_ERROR;
|
|
|
|
}
|
|
|
|
|
2020-04-14 19:04:13 +01:00
|
|
|
if (m == SSL_ERROR_SSL) {
|
2023-10-20 17:42:35 -04:00
|
|
|
l = ERR_get_error();
|
2021-04-15 09:30:43 +01:00
|
|
|
n = lws_snprintf(errbuf, elen, "tls: %s", wsi->tls.err_helper);
|
|
|
|
if (!wsi->tls.err_helper[0])
|
2023-10-20 17:42:35 -04:00
|
|
|
ERR_error_string_n((unsigned int)l, errbuf + n, (elen - (unsigned int)n));
|
2020-03-01 05:45:13 +00:00
|
|
|
return LWS_SSL_CAPABLE_ERROR;
|
2020-04-14 19:04:13 +01:00
|
|
|
}
|
2020-03-01 05:45:13 +00:00
|
|
|
|
2021-03-25 08:37:36 +00:00
|
|
|
#if defined(LWS_WITH_TLS_SESSIONS)
|
|
|
|
if (SSL_session_reused(wsi->tls.ssl)) {
|
|
|
|
#if defined(LWS_HAVE_SSL_SESSION_set_time)
|
|
|
|
sess = SSL_get_session(wsi->tls.ssl);
|
|
|
|
if (sess) /* should always be true */
|
|
|
|
#if defined(OPENSSL_IS_BORINGSSL)
|
|
|
|
SSL_SESSION_set_time(sess, (uint64_t)time(NULL)); /* extend session lifetime */
|
|
|
|
#else
|
|
|
|
SSL_SESSION_set_time(sess, (long)time(NULL)); /* extend session lifetime */
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-01 05:45:13 +00:00
|
|
|
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl))
|
|
|
|
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
|
|
|
|
|
|
|
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl))
|
|
|
|
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
|
|
|
|
|
|
|
|
if (n == 1 || m == SSL_ERROR_SYSCALL) {
|
2018-11-23 08:47:56 +08:00
|
|
|
#if defined(LWS_HAVE_SSL_set_alpn_protos) && \
|
|
|
|
defined(LWS_HAVE_SSL_get0_alpn_selected)
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_get0_alpn_selected(wsi->tls.ssl, &prot, &len);
|
2018-03-27 06:29:49 +08:00
|
|
|
|
2018-04-04 10:42:52 +08:00
|
|
|
if (len >= sizeof(a))
|
2018-03-27 06:29:49 +08:00
|
|
|
len = sizeof(a) - 1;
|
|
|
|
memcpy(a, (const char *)prot, len);
|
|
|
|
a[len] = '\0';
|
|
|
|
|
2018-04-12 15:56:38 +08:00
|
|
|
lws_role_call_alpn_negotiated(wsi, (const char *)a);
|
2018-03-27 06:29:49 +08:00
|
|
|
#endif
|
2021-04-28 16:54:08 +01:00
|
|
|
#if defined(LWS_TLS_SYNTHESIZE_CB)
|
|
|
|
lws_sul_schedule(wsi->a.context, wsi->tsi,
|
|
|
|
&wsi->tls.sul_cb_synth,
|
|
|
|
lws_sess_cache_synth_cb, 500 * LWS_US_PER_MS);
|
|
|
|
#endif
|
|
|
|
|
2018-04-12 15:56:38 +08:00
|
|
|
lwsl_info("client connect OK\n");
|
2018-10-20 07:54:51 +08:00
|
|
|
lws_openssl_describe_cipher(wsi);
|
2017-10-18 09:41:44 +08:00
|
|
|
return LWS_SSL_CAPABLE_DONE;
|
2018-03-27 06:29:49 +08:00
|
|
|
}
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
if (!n) /* we don't know what he wants, but he says to retry */
|
|
|
|
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
|
|
|
|
2020-04-14 19:04:13 +01:00
|
|
|
lws_snprintf(errbuf, elen, "connect unk %d", m);
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
return LWS_SSL_CAPABLE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2020-12-12 06:21:40 +00:00
|
|
|
lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, size_t ebuf_len)
|
2017-10-18 09:41:44 +08:00
|
|
|
{
|
2018-03-24 17:52:57 +01:00
|
|
|
#if !defined(USE_WOLFSSL)
|
fakewsi: replace with smaller substructure
Currently we always reserve a fakewsi per pt so events that don't have a related actual
wsi, like vhost-protocol-init or vhost cert init via protocol callback can make callbacks
that look reasonable to user protocol handler code expecting a valid wsi every time.
This patch splits out stuff that user callbacks often unconditionally expect to be in
a wsi, like context pointer, vhost pointer etc into a substructure, which is composed
into struct lws at the top of it. Internal references (struct lws is opaque, so there
are only internal references) are all updated to go via the substructre, the compiler
should make that a NOP.
Helpers are added when fakewsi is used and referenced.
If not PLAT_FREERTOS, we continue to provide a full fakewsi in the pt as before,
although the helpers improve consistency by zeroing down the substructure. There is
a huge amount of user code out there over the last 10 years that did not always have
the minimal examples to follow, some of it does some unexpected things.
If it is PLAT_FREERTOS, that is a newer thing in lws and users have the benefit of
being able to follow the minimal examples' approach. For PLAT_FREERTOS we don't
reserve the fakewsi in the pt any more, saving around 800 bytes. The helpers then
create a struct lws_a (the substructure) on the stack, zero it down (but it is only
like 4 pointers) and prepare it with whatever we know like the context.
Then we cast it to a struct lws * and use it in the user protocol handler call.
In this case, the remainder of the struct lws is undefined. However the amount of
old protocol handlers that might touch things outside of the substructure in
PLAT_FREERTOS is very limited compared to legacy lws user code and the saving is
significant on constrained devices.
User handlers should not be touching everything in a wsi every time anyway, there
are several cases where there is no valid wsi to do the call with. Dereference of
things outside the substructure should only happen when the callback reason shows
there is a valid wsi bound to the activity (as in all the minimal examples).
2020-07-19 08:33:46 +01:00
|
|
|
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
|
2017-10-18 09:41:44 +08:00
|
|
|
char *p = (char *)&pt->serv_buf[0];
|
2021-01-06 15:08:22 +00:00
|
|
|
const char *es, *type = "";
|
|
|
|
unsigned int avoid = 0;
|
2017-10-18 09:41:44 +08:00
|
|
|
char *sb = p;
|
2020-12-12 06:21:40 +00:00
|
|
|
long n;
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2019-07-10 20:34:24 +01:00
|
|
|
errno = 0;
|
|
|
|
ERR_clear_error();
|
2018-05-01 12:41:42 +08:00
|
|
|
n = SSL_get_verify_result(wsi->tls.ssl);
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2021-01-06 15:08:22 +00:00
|
|
|
switch (n) {
|
|
|
|
case X509_V_OK:
|
2018-03-24 17:52:57 +01:00
|
|
|
return 0;
|
|
|
|
|
2021-01-06 15:08:22 +00:00
|
|
|
case X509_V_ERR_HOSTNAME_MISMATCH:
|
|
|
|
type = "tls=hostname";
|
|
|
|
avoid = LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case X509_V_ERR_INVALID_CA:
|
|
|
|
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
|
|
|
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
|
|
|
|
type = "tls=invalidca";
|
|
|
|
avoid = LCCSCF_ALLOW_SELFSIGNED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case X509_V_ERR_CERT_NOT_YET_VALID:
|
|
|
|
type = "tls=notyetvalid";
|
|
|
|
avoid = LCCSCF_ALLOW_EXPIRED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case X509_V_ERR_CERT_HAS_EXPIRED:
|
|
|
|
type = "tls=expired";
|
|
|
|
avoid = LCCSCF_ALLOW_EXPIRED;
|
|
|
|
break;
|
2018-03-24 17:52:57 +01:00
|
|
|
}
|
2021-01-06 15:08:22 +00:00
|
|
|
|
|
|
|
lwsl_info("%s: cert problem: %s\n", __func__, type);
|
|
|
|
|
|
|
|
#if defined(LWS_WITH_SYS_METRICS)
|
|
|
|
lws_metrics_hist_bump_describe_wsi(wsi,
|
|
|
|
lws_metrics_priv_to_pub(wsi->a.context->mth_conn_failures), type);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (wsi->tls.use_ssl & avoid) {
|
|
|
|
lwsl_info("%s: allowing anyway\n", __func__);
|
|
|
|
|
2018-03-24 17:52:57 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2021-01-06 15:08:22 +00:00
|
|
|
|
2020-12-12 06:21:40 +00:00
|
|
|
es = ERR_error_string(
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(uint32_t)
|
|
|
|
#else
|
|
|
|
(unsigned long)
|
|
|
|
#endif
|
|
|
|
n, sb);
|
2018-03-24 17:52:57 +01:00
|
|
|
lws_snprintf(ebuf, ebuf_len,
|
2021-01-06 15:08:22 +00:00
|
|
|
"server's cert didn't look good, %s X509_V_ERR = %ld: %s\n",
|
|
|
|
type, n, es);
|
2018-03-24 17:52:57 +01:00
|
|
|
lwsl_info("%s\n", ebuf);
|
2019-07-13 13:28:14 -07:00
|
|
|
lws_tls_err_describe_clear();
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2018-03-24 17:52:57 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
#else /* USE_WOLFSSL */
|
2017-10-18 09:41:44 +08:00
|
|
|
return 0;
|
2018-03-24 17:52:57 +01:00
|
|
|
#endif
|
2017-10-18 09:41:44 +08:00
|
|
|
}
|
|
|
|
|
2019-08-21 20:22:40 +01:00
|
|
|
int
|
|
|
|
lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh,
|
|
|
|
const uint8_t *der, size_t der_len)
|
|
|
|
{
|
|
|
|
X509_STORE *st;
|
2020-12-12 06:21:40 +00:00
|
|
|
#if defined(USE_WOLFSSL)
|
|
|
|
X509 *x = d2i_X509(NULL, &der, (int)der_len);
|
|
|
|
#else
|
2020-01-11 14:04:50 +00:00
|
|
|
X509 *x = d2i_X509(NULL, &der, (long)der_len);
|
2020-12-12 06:21:40 +00:00
|
|
|
#endif
|
2019-08-21 20:22:40 +01:00
|
|
|
int n;
|
|
|
|
|
|
|
|
if (!x) {
|
|
|
|
lwsl_err("%s: Failed to load DER\n", __func__);
|
|
|
|
lws_tls_err_describe_clear();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
st = SSL_CTX_get_cert_store(vh->tls.ssl_client_ctx);
|
|
|
|
if (!st) {
|
|
|
|
lwsl_err("%s: failed to get cert store\n", __func__);
|
|
|
|
X509_free(x);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = X509_STORE_add_cert(st, x);
|
|
|
|
if (n != 1)
|
|
|
|
lwsl_err("%s: failed to add cert\n", __func__);
|
|
|
|
|
|
|
|
X509_free(x);
|
|
|
|
|
|
|
|
return n != 1;
|
|
|
|
}
|
|
|
|
|
2021-12-07 08:31:17 +00:00
|
|
|
#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \
|
|
|
|
defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT)
|
2024-02-09 14:56:10 +05:30
|
|
|
void
|
2021-12-07 08:31:17 +00:00
|
|
|
lws_klog_dump(const SSL *ssl, const char *line)
|
|
|
|
{
|
|
|
|
struct lws *wsi = SSL_get_ex_data(ssl,
|
|
|
|
openssl_websocket_private_data_index);
|
|
|
|
char path[128], hdr[128], ts[64];
|
|
|
|
size_t w = 0, wx = 0;
|
|
|
|
int fd, t;
|
|
|
|
|
|
|
|
if (!wsi || !wsi->a.context->keylog_file[0] || !wsi->a.vhost)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lws_snprintf(path, sizeof(path), "%s.%s", wsi->a.context->keylog_file,
|
|
|
|
wsi->a.vhost->name);
|
|
|
|
|
|
|
|
fd = open(path, O_CREAT | O_RDWR | O_APPEND, 0600);
|
|
|
|
if (fd == -1) {
|
|
|
|
lwsl_vhost_warn(wsi->a.vhost, "Failed to append %s", path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the first item in the chunk */
|
|
|
|
if (!strncmp(line, "SERVER_HANDSHAKE_TRAFFIC_SECRET", 31)) {
|
|
|
|
w += (size_t)write(fd, "\n# ", 3);
|
|
|
|
wx += 3;
|
|
|
|
t = lwsl_timestamp(LLL_WARN, ts, sizeof(ts));
|
|
|
|
wx += (size_t)t;
|
|
|
|
w += (size_t)write(fd, ts, (size_t)t);
|
|
|
|
|
|
|
|
t = lws_snprintf(hdr, sizeof(hdr), "%s\n", wsi->lc.gutag);
|
|
|
|
w += (size_t)write(fd, hdr, (size_t)t);
|
|
|
|
wx += (size_t)t;
|
|
|
|
|
|
|
|
lwsl_vhost_warn(wsi->a.vhost, "appended ssl keylog: %s", path);
|
|
|
|
}
|
|
|
|
|
|
|
|
wx += strlen(line) + 1;
|
|
|
|
w += (size_t)write(fd, line,
|
|
|
|
#if defined(WIN32)
|
|
|
|
(unsigned int)
|
|
|
|
#endif
|
|
|
|
strlen(line));
|
|
|
|
w += (size_t)write(fd, "\n", 1);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (w != wx) {
|
|
|
|
lwsl_vhost_warn(wsi->a.vhost, "Failed to write %s", path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
int
|
|
|
|
lws_tls_client_create_vhost_context(struct lws_vhost *vh,
|
2018-04-27 08:27:16 +08:00
|
|
|
const struct lws_context_creation_info *info,
|
2017-10-18 09:41:44 +08:00
|
|
|
const char *cipher_list,
|
|
|
|
const char *ca_filepath,
|
2018-07-16 11:45:55 +02:00
|
|
|
const void *ca_mem,
|
|
|
|
unsigned int ca_mem_len,
|
2017-10-18 09:41:44 +08:00
|
|
|
const char *cert_filepath,
|
2019-01-11 13:13:40 +08:00
|
|
|
const void *cert_mem,
|
|
|
|
unsigned int cert_mem_len,
|
2020-06-04 19:59:37 -07:00
|
|
|
const char *private_key_filepath,
|
|
|
|
const void *key_mem,
|
|
|
|
unsigned int key_mem_len
|
|
|
|
)
|
2017-10-18 09:41:44 +08:00
|
|
|
{
|
2019-03-17 10:03:22 +08:00
|
|
|
struct lws_tls_client_reuse *tcr;
|
2018-07-16 11:45:55 +02:00
|
|
|
X509_STORE *x509_store;
|
2019-03-17 10:03:22 +08:00
|
|
|
unsigned long error;
|
|
|
|
SSL_METHOD *method;
|
|
|
|
EVP_MD_CTX *mdctx;
|
|
|
|
unsigned int len;
|
|
|
|
uint8_t hash[32];
|
|
|
|
X509 *client_CA;
|
|
|
|
char c;
|
|
|
|
int n;
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
/* basic openssl init already happened in context init */
|
|
|
|
|
|
|
|
/* choose the most recent spin of the api */
|
|
|
|
#if defined(LWS_HAVE_TLS_CLIENT_METHOD)
|
|
|
|
method = (SSL_METHOD *)TLS_client_method();
|
|
|
|
#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD)
|
|
|
|
method = (SSL_METHOD *)TLSv1_2_client_method();
|
|
|
|
#else
|
|
|
|
method = (SSL_METHOD *)SSLv23_client_method();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!method) {
|
2020-12-12 06:21:40 +00:00
|
|
|
const char *es;
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
error = ERR_get_error();
|
2020-12-12 06:21:40 +00:00
|
|
|
es = ERR_error_string(
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(uint32_t)
|
|
|
|
#else
|
|
|
|
(unsigned long)
|
|
|
|
#endif
|
|
|
|
error, (char *)vh->context->pt[0].serv_buf);
|
2017-10-18 09:41:44 +08:00
|
|
|
lwsl_err("problem creating ssl method %lu: %s\n",
|
2020-12-12 06:21:40 +00:00
|
|
|
error, es);
|
2017-10-18 09:41:44 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2019-03-17 10:03:22 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* OpenSSL client contexts are quite expensive, because they bring in
|
|
|
|
* the system certificate bundle for each one. So if you have multiple
|
|
|
|
* vhosts, each with a client context, it can add up to several
|
|
|
|
* megabytes of heap. In the case the client contexts are configured
|
|
|
|
* identically, they could perfectly well have shared just the one.
|
|
|
|
*
|
|
|
|
* For that reason, use a hash to fingerprint the context configuration
|
|
|
|
* and prefer to reuse an existing one with the same fingerprint if
|
|
|
|
* possible.
|
|
|
|
*/
|
|
|
|
|
|
|
|
mdctx = EVP_MD_CTX_create();
|
|
|
|
if (!mdctx)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1) {
|
|
|
|
EVP_MD_CTX_destroy(mdctx);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->ssl_client_options_set)
|
|
|
|
EVP_DigestUpdate(mdctx, &info->ssl_client_options_set,
|
|
|
|
sizeof(info->ssl_client_options_set));
|
|
|
|
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
|
|
|
|
if (info->ssl_client_options_clear)
|
|
|
|
EVP_DigestUpdate(mdctx, &info->ssl_client_options_clear,
|
|
|
|
sizeof(info->ssl_client_options_clear));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (cipher_list)
|
|
|
|
EVP_DigestUpdate(mdctx, cipher_list, strlen(cipher_list));
|
|
|
|
|
|
|
|
#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites)
|
|
|
|
if (info->client_tls_1_3_plus_cipher_list)
|
|
|
|
EVP_DigestUpdate(mdctx, info->client_tls_1_3_plus_cipher_list,
|
|
|
|
strlen(info->client_tls_1_3_plus_cipher_list));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) {
|
|
|
|
c = 1;
|
|
|
|
EVP_DigestUpdate(mdctx, &c, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ca_filepath)
|
|
|
|
EVP_DigestUpdate(mdctx, ca_filepath, strlen(ca_filepath));
|
|
|
|
|
|
|
|
if (cert_filepath)
|
|
|
|
EVP_DigestUpdate(mdctx, cert_filepath, strlen(cert_filepath));
|
|
|
|
|
|
|
|
if (private_key_filepath)
|
|
|
|
EVP_DigestUpdate(mdctx, private_key_filepath,
|
|
|
|
strlen(private_key_filepath));
|
|
|
|
if (ca_mem && ca_mem_len)
|
|
|
|
EVP_DigestUpdate(mdctx, ca_mem, ca_mem_len);
|
|
|
|
|
|
|
|
if (cert_mem && cert_mem_len)
|
|
|
|
EVP_DigestUpdate(mdctx, cert_mem, cert_mem_len);
|
|
|
|
|
|
|
|
len = sizeof(hash);
|
|
|
|
EVP_DigestFinal_ex(mdctx, hash, &len);
|
|
|
|
EVP_MD_CTX_destroy(mdctx);
|
|
|
|
|
|
|
|
/* look for existing client context with same config already */
|
|
|
|
|
2019-08-08 16:58:55 +01:00
|
|
|
lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
|
|
|
|
lws_dll2_get_head(&vh->context->tls.cc_owner)) {
|
2019-03-17 10:03:22 +08:00
|
|
|
tcr = lws_container_of(p, struct lws_tls_client_reuse, cc_list);
|
|
|
|
|
|
|
|
if (!memcmp(hash, tcr->hash, len)) {
|
|
|
|
|
|
|
|
/* it's a match */
|
|
|
|
|
|
|
|
tcr->refcount++;
|
|
|
|
vh->tls.ssl_client_ctx = tcr->ssl_client_ctx;
|
2021-01-19 14:12:56 +00:00
|
|
|
vh->tls.tcr = tcr;
|
2019-03-17 10:03:22 +08:00
|
|
|
|
|
|
|
lwsl_info("%s: vh %s: reusing client ctx %d: use %d\n",
|
|
|
|
__func__, vh->name, tcr->index,
|
|
|
|
tcr->refcount);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} lws_end_foreach_dll_safe(p, tp);
|
|
|
|
|
|
|
|
/* no existing one the same... create new client SSL_CTX */
|
|
|
|
|
2019-07-10 20:34:24 +01:00
|
|
|
errno = 0;
|
|
|
|
ERR_clear_error();
|
2018-05-01 12:41:42 +08:00
|
|
|
vh->tls.ssl_client_ctx = SSL_CTX_new(method);
|
|
|
|
if (!vh->tls.ssl_client_ctx) {
|
2020-12-12 06:21:40 +00:00
|
|
|
const char *es;
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
error = ERR_get_error();
|
2020-12-12 06:21:40 +00:00
|
|
|
es = ERR_error_string(
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(uint32_t)
|
|
|
|
#else
|
|
|
|
(unsigned long)
|
|
|
|
#endif
|
|
|
|
error, (char *)vh->context->pt[0].serv_buf);
|
2017-10-18 09:41:44 +08:00
|
|
|
lwsl_err("problem creating ssl context %lu: %s\n",
|
2020-12-12 06:21:40 +00:00
|
|
|
error, es);
|
2017-10-18 09:41:44 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-10-20 13:25:09 -05:00
|
|
|
SSL_CTX_set_ex_data(vh->tls.ssl_client_ctx,
|
|
|
|
openssl_SSL_CTX_private_data_index,
|
|
|
|
(char *)vh->context);
|
|
|
|
|
2021-03-12 11:21:30 +05:30
|
|
|
lws_plat_vhost_tls_client_ctx_init(vh);
|
|
|
|
|
2019-03-17 10:03:22 +08:00
|
|
|
tcr = lws_zalloc(sizeof(*tcr), "client ctx tcr");
|
|
|
|
if (!tcr) {
|
|
|
|
SSL_CTX_free(vh->tls.ssl_client_ctx);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
tcr->ssl_client_ctx = vh->tls.ssl_client_ctx;
|
|
|
|
tcr->refcount = 1;
|
|
|
|
memcpy(tcr->hash, hash, len);
|
|
|
|
tcr->index = vh->context->tls.count_client_contexts++;
|
2019-08-08 16:58:55 +01:00
|
|
|
lws_dll2_add_head(&tcr->cc_list, &vh->context->tls.cc_owner);
|
2019-03-17 10:03:22 +08:00
|
|
|
|
|
|
|
lwsl_info("%s: vh %s: created new client ctx %d\n", __func__,
|
|
|
|
vh->name, tcr->index);
|
|
|
|
|
|
|
|
/* bind the tcr to the client context */
|
|
|
|
|
2021-01-19 14:12:56 +00:00
|
|
|
vh->tls.tcr = tcr;
|
2019-03-17 10:03:22 +08:00
|
|
|
|
2021-12-07 08:31:17 +00:00
|
|
|
#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \
|
|
|
|
defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT)
|
|
|
|
if (vh->context->keylog_file[0])
|
|
|
|
SSL_CTX_set_keylog_callback(vh->tls.ssl_client_ctx, lws_klog_dump);
|
|
|
|
#endif
|
|
|
|
|
2021-03-25 08:37:36 +00:00
|
|
|
#if defined(LWS_WITH_TLS_SESSIONS)
|
|
|
|
vh->tls_session_cache_max = info->tls_session_cache_max ?
|
|
|
|
info->tls_session_cache_max : 10;
|
|
|
|
lws_tls_session_cache(vh, info->tls_session_timeout);
|
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_CTX_set_options(vh->tls.ssl_client_ctx, SSL_OP_NO_COMPRESSION);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_CTX_set_options(vh->tls.ssl_client_ctx,
|
2017-10-18 09:41:44 +08:00
|
|
|
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
|
|
|
|
2019-03-16 10:17:28 +08:00
|
|
|
SSL_CTX_set_mode(vh->tls.ssl_client_ctx,
|
|
|
|
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
|
|
|
|
SSL_MODE_RELEASE_BUFFERS);
|
|
|
|
|
2022-02-08 04:58:13 +00:00
|
|
|
#if !defined(USE_WOLFSSL)
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
uint32_t
|
|
|
|
#else
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10003000l) && \
|
|
|
|
!defined(LIBRESSL_VERSION_NUMBER) /* not documented by openssl */
|
|
|
|
unsigned long
|
|
|
|
#else
|
|
|
|
long
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
long
|
|
|
|
#endif
|
|
|
|
ssl_client_options_set_value =
|
2020-12-12 06:21:40 +00:00
|
|
|
#if !defined(USE_WOLFSSL)
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(uint32_t)
|
|
|
|
#else
|
2021-02-08 19:35:47 +00:00
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10003000l) && \
|
|
|
|
!defined(LIBRESSL_VERSION_NUMBER) /* not documented by openssl */
|
2022-02-08 04:58:13 +00:00
|
|
|
(unsigned long)
|
2021-01-21 05:54:16 +00:00
|
|
|
#else
|
2022-02-08 04:58:13 +00:00
|
|
|
(long)
|
2021-01-21 05:54:16 +00:00
|
|
|
#endif
|
2020-12-12 06:21:40 +00:00
|
|
|
#endif
|
|
|
|
#endif
|
2022-02-08 04:58:13 +00:00
|
|
|
info->ssl_client_options_set;
|
|
|
|
|
|
|
|
if (info->ssl_client_options_set)
|
|
|
|
SSL_CTX_set_options(vh->tls.ssl_client_ctx, ssl_client_options_set_value);
|
2018-10-26 09:44:58 +08:00
|
|
|
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
|
2022-02-08 04:58:13 +00:00
|
|
|
|
|
|
|
/* SSL_clear_options introduced in 0.9.8m */
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
uint32_t
|
|
|
|
#else
|
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10003000l) && \
|
|
|
|
!defined(LIBRESSL_VERSION_NUMBER) /* not documented by openssl */
|
|
|
|
unsigned long
|
|
|
|
#else
|
|
|
|
long
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ssl_client_options_clear_value =
|
2020-12-12 06:21:40 +00:00
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(uint32_t)
|
|
|
|
#else
|
2021-02-08 19:35:47 +00:00
|
|
|
#if (OPENSSL_VERSION_NUMBER >= 0x10003000l) && \
|
|
|
|
!defined(LIBRESSL_VERSION_NUMBER) /* not documented by openssl */
|
2022-02-08 04:58:13 +00:00
|
|
|
(unsigned long)
|
2021-01-21 05:54:16 +00:00
|
|
|
#else
|
2022-02-08 04:58:13 +00:00
|
|
|
(long)
|
2021-01-21 05:54:16 +00:00
|
|
|
#endif
|
2020-12-12 06:21:40 +00:00
|
|
|
#endif
|
2022-02-08 04:58:13 +00:00
|
|
|
info->ssl_client_options_clear;
|
|
|
|
|
|
|
|
if (info->ssl_client_options_clear)
|
|
|
|
SSL_CTX_clear_options(vh->tls.ssl_client_ctx, ssl_client_options_clear_value);
|
2018-10-26 09:44:58 +08:00
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
if (cipher_list)
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_CTX_set_cipher_list(vh->tls.ssl_client_ctx, cipher_list);
|
2017-10-18 09:41:44 +08:00
|
|
|
|
2018-10-27 08:05:21 +08:00
|
|
|
#if defined(LWS_HAVE_SSL_CTX_set_ciphersuites)
|
|
|
|
if (info->client_tls_1_3_plus_cipher_list)
|
|
|
|
SSL_CTX_set_ciphersuites(vh->tls.ssl_client_ctx,
|
|
|
|
info->client_tls_1_3_plus_cipher_list);
|
|
|
|
#endif
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
|
|
|
|
if (!lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
|
|
|
|
/* loads OS default CA certs */
|
2018-05-01 12:41:42 +08:00
|
|
|
SSL_CTX_set_default_verify_paths(vh->tls.ssl_client_ctx);
|
2017-10-18 09:41:44 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* openssl init for cert verification (for client sockets) */
|
2018-07-16 11:45:55 +02:00
|
|
|
if (!ca_filepath && (!ca_mem || !ca_mem_len)) {
|
2020-05-29 11:07:22 +01:00
|
|
|
#if defined(LWS_HAVE_SSL_CTX_load_verify_dir)
|
|
|
|
if (!SSL_CTX_load_verify_dir(
|
|
|
|
vh->tls.ssl_client_ctx, LWS_OPENSSL_CLIENT_CERTS))
|
|
|
|
#else
|
2017-10-18 09:41:44 +08:00
|
|
|
if (!SSL_CTX_load_verify_locations(
|
2018-05-01 12:41:42 +08:00
|
|
|
vh->tls.ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS))
|
2020-05-29 11:07:22 +01:00
|
|
|
#endif
|
2017-10-18 09:41:44 +08:00
|
|
|
lwsl_err("Unable to load SSL Client certs from %s "
|
|
|
|
"(set by LWS_OPENSSL_CLIENT_CERTS) -- "
|
|
|
|
"client ssl isn't going to work\n",
|
|
|
|
LWS_OPENSSL_CLIENT_CERTS);
|
2018-07-16 11:45:55 +02:00
|
|
|
} else if (ca_filepath) {
|
2020-05-29 11:07:22 +01:00
|
|
|
#if defined(LWS_HAVE_SSL_CTX_load_verify_file)
|
|
|
|
if (!SSL_CTX_load_verify_file(
|
|
|
|
vh->tls.ssl_client_ctx, ca_filepath)) {
|
|
|
|
#else
|
2017-10-18 09:41:44 +08:00
|
|
|
if (!SSL_CTX_load_verify_locations(
|
2018-05-01 12:41:42 +08:00
|
|
|
vh->tls.ssl_client_ctx, ca_filepath, NULL)) {
|
2020-05-29 11:07:22 +01:00
|
|
|
#endif
|
2017-10-18 09:41:44 +08:00
|
|
|
lwsl_err(
|
|
|
|
"Unable to load SSL Client certs "
|
|
|
|
"file from %s -- client ssl isn't "
|
|
|
|
"going to work\n", ca_filepath);
|
2019-07-13 13:28:14 -07:00
|
|
|
lws_tls_err_describe_clear();
|
2017-10-18 09:41:44 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
lwsl_info("loaded ssl_ca_filepath\n");
|
2018-07-16 11:45:55 +02:00
|
|
|
} else {
|
2019-10-01 13:45:27 -07:00
|
|
|
|
|
|
|
lws_filepos_t amount = 0;
|
|
|
|
const uint8_t *up;
|
2020-06-04 19:59:37 -07:00
|
|
|
uint8_t *up1;
|
2019-10-01 13:45:27 -07:00
|
|
|
|
|
|
|
if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, ca_mem,
|
2020-06-04 19:59:37 -07:00
|
|
|
ca_mem_len, &up1, &amount)) {
|
2019-10-01 13:45:27 -07:00
|
|
|
lwsl_err("%s: Unable to decode x.509 mem\n", __func__);
|
|
|
|
lwsl_hexdump_notice(ca_mem, ca_mem_len);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
up = up1;
|
2020-12-12 06:21:40 +00:00
|
|
|
#if defined(USE_WOLFSSL)
|
|
|
|
client_CA = d2i_X509(NULL, &up, (int)amount);
|
|
|
|
#else
|
2020-01-11 14:04:50 +00:00
|
|
|
client_CA = d2i_X509(NULL, &up, (long)amount);
|
2020-12-12 06:21:40 +00:00
|
|
|
#endif
|
2019-10-01 13:45:27 -07:00
|
|
|
if (!client_CA) {
|
|
|
|
lwsl_err("%s: d2i_X509 failed\n", __func__);
|
2020-01-11 14:04:50 +00:00
|
|
|
lwsl_hexdump_notice(up1, (size_t)amount);
|
2019-07-13 13:28:14 -07:00
|
|
|
lws_tls_err_describe_clear();
|
2018-07-16 11:45:55 +02:00
|
|
|
} else {
|
2019-10-01 13:45:27 -07:00
|
|
|
x509_store = X509_STORE_new();
|
|
|
|
if (!X509_STORE_add_cert(x509_store, client_CA)) {
|
|
|
|
X509_STORE_free(x509_store);
|
|
|
|
lwsl_err("Unable to load SSL Client certs from "
|
|
|
|
"ssl_ca_mem -- client ssl isn't going to "
|
|
|
|
"work\n");
|
|
|
|
lws_tls_err_describe_clear();
|
|
|
|
} else {
|
|
|
|
/* it doesn't increment x509_store ref counter */
|
|
|
|
SSL_CTX_set_cert_store(vh->tls.ssl_client_ctx,
|
|
|
|
x509_store);
|
|
|
|
lwsl_info("loaded ssl_ca_mem\n");
|
|
|
|
}
|
2018-07-16 11:45:55 +02:00
|
|
|
}
|
|
|
|
if (client_CA)
|
|
|
|
X509_free(client_CA);
|
2019-10-01 13:45:27 -07:00
|
|
|
lws_free(up1);
|
2019-08-21 20:22:40 +01:00
|
|
|
// lws_tls_client_vhost_extra_cert_mem(vh, ca_mem, ca_mem_len);
|
2018-07-16 11:45:55 +02:00
|
|
|
}
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* callback allowing user code to load extra verification certs
|
|
|
|
* helping the client to verify server identity
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* support for client-side certificate authentication */
|
2020-06-04 19:59:37 -07:00
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
if (cert_filepath) {
|
2018-11-23 08:47:56 +08:00
|
|
|
if (lws_tls_use_any_upgrade_check_extant(cert_filepath) !=
|
|
|
|
LWS_TLS_EXTANT_YES &&
|
2017-11-02 06:22:44 +08:00
|
|
|
(info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT))
|
|
|
|
return 0;
|
|
|
|
|
2018-11-23 08:47:56 +08:00
|
|
|
lwsl_notice("%s: doing cert filepath %s\n", __func__,
|
|
|
|
cert_filepath);
|
2018-05-01 12:41:42 +08:00
|
|
|
n = SSL_CTX_use_certificate_chain_file(vh->tls.ssl_client_ctx,
|
2017-10-18 09:41:44 +08:00
|
|
|
cert_filepath);
|
|
|
|
if (n < 1) {
|
|
|
|
lwsl_err("problem %d getting cert '%s'\n", n,
|
|
|
|
cert_filepath);
|
2019-07-13 13:28:14 -07:00
|
|
|
lws_tls_err_describe_clear();
|
2017-10-18 09:41:44 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2020-12-29 12:10:24 +00:00
|
|
|
lwsl_info("Loaded client cert %s\n", cert_filepath);
|
2020-06-04 19:59:37 -07:00
|
|
|
|
2019-01-11 13:13:40 +08:00
|
|
|
} else if (cert_mem && cert_mem_len) {
|
2020-06-04 19:59:37 -07:00
|
|
|
lws_filepos_t flen;
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
|
|
if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, cert_mem,
|
|
|
|
cert_mem_len, &p, &flen)) {
|
|
|
|
lwsl_err("%s: couldn't read cert file\n", __func__);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-12-12 06:21:40 +00:00
|
|
|
n = SSL_CTX_use_certificate_ASN1(vh->tls.ssl_client_ctx,
|
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(size_t)
|
|
|
|
#else
|
|
|
|
(int)
|
|
|
|
#endif
|
|
|
|
flen, p);
|
2020-06-04 19:59:37 -07:00
|
|
|
|
2019-01-11 13:13:40 +08:00
|
|
|
if (n < 1) {
|
2020-06-04 19:59:37 -07:00
|
|
|
lwsl_err("%s: problem interpreting client cert\n", __func__);
|
2019-07-13 13:28:14 -07:00
|
|
|
lws_tls_err_describe_clear();
|
2019-01-11 13:13:40 +08:00
|
|
|
}
|
2020-06-04 19:59:37 -07:00
|
|
|
|
|
|
|
lws_free_set_NULL(p);
|
|
|
|
|
|
|
|
if (n != 1)
|
|
|
|
return 1;
|
|
|
|
|
2017-10-18 09:41:44 +08:00
|
|
|
}
|
|
|
|
if (private_key_filepath) {
|
2020-06-04 19:59:37 -07:00
|
|
|
lwsl_info("%s: using private key filepath\n", __func__);
|
2019-06-21 07:18:48 +01:00
|
|
|
lws_ssl_bind_passphrase(vh->tls.ssl_client_ctx, 1, info);
|
2017-10-18 09:41:44 +08:00
|
|
|
/* set the private key from KeyFile */
|
2018-05-01 12:41:42 +08:00
|
|
|
if (SSL_CTX_use_PrivateKey_file(vh->tls.ssl_client_ctx,
|
2017-10-18 09:41:44 +08:00
|
|
|
private_key_filepath, SSL_FILETYPE_PEM) != 1) {
|
|
|
|
lwsl_err("use_PrivateKey_file '%s'\n",
|
|
|
|
private_key_filepath);
|
2019-07-13 13:28:14 -07:00
|
|
|
lws_tls_err_describe_clear();
|
2017-10-18 09:41:44 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2020-06-04 19:59:37 -07:00
|
|
|
lwsl_info("Loaded client cert private key %s\n",
|
2017-10-18 09:41:44 +08:00
|
|
|
private_key_filepath);
|
|
|
|
|
|
|
|
/* verify private key */
|
2018-05-01 12:41:42 +08:00
|
|
|
if (!SSL_CTX_check_private_key(vh->tls.ssl_client_ctx)) {
|
2017-10-18 09:41:44 +08:00
|
|
|
lwsl_err("Private SSL key doesn't match cert\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2020-06-04 19:59:37 -07:00
|
|
|
else if (key_mem && key_mem_len) {
|
|
|
|
|
|
|
|
lws_filepos_t flen;
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
|
|
if (lws_tls_alloc_pem_to_der_file(vh->context, NULL, key_mem,
|
|
|
|
key_mem_len, &p, &flen)) {
|
|
|
|
lwsl_err("%s: couldn't use mem cert\n", __func__);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vh->tls.ssl_client_ctx, p,
|
2020-12-12 06:21:40 +00:00
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(size_t)
|
|
|
|
#else
|
|
|
|
(long)(lws_intptr_t)
|
|
|
|
#endif
|
|
|
|
flen);
|
2020-06-04 19:59:37 -07:00
|
|
|
if (n != 1)
|
|
|
|
n = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC,
|
|
|
|
vh->tls.ssl_client_ctx, p,
|
2020-12-12 06:21:40 +00:00
|
|
|
#if defined(LWS_WITH_BORINGSSL)
|
|
|
|
(size_t)
|
|
|
|
#else
|
|
|
|
(long)(lws_intptr_t)
|
|
|
|
#endif
|
|
|
|
flen);
|
2020-06-04 19:59:37 -07:00
|
|
|
|
|
|
|
lws_free_set_NULL(p);
|
|
|
|
|
|
|
|
if (n != 1) {
|
|
|
|
lwsl_err("%s: unable to use key_mem\n", __func__);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 09:41:44 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-08-21 20:22:40 +01:00
|
|
|
|
|
|
|
|