mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
tls: split out common, openssl and mbedtls code
- introduce lib/tls/mbedtls lib/tls/openssl - move wrapper into lib/tls/mbedtls/wrapper - introduce private helpers to hide backend This patch doesn't replace or remove the wrapper, it moves it to lib/tls/mbedtls/wrapper. But it should be now that the ONLY functions directly consuming wrapper apis are isolated in - lib/tls/mbedtls/client.c (180 lines) - lib/tls/mbedtls/server.c (317 lines) - lib/tls/mbedtls/ssl.c (325 lines) In particular there are no uses of openssl or mbedtls-related constants outside of ./lib/tls any more.
This commit is contained in:
parent
bb04883097
commit
89cb55ea58
50 changed files with 2687 additions and 1993 deletions
|
@ -646,58 +646,81 @@ endif()
|
|||
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
set(LWS_WITH_SSL ON)
|
||||
|
||||
list(APPEND HDR_PRIVATE
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl3.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_code.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_types.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/tls1.h
|
||||
lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h)
|
||||
|
||||
list(APPEND HDR_PRIVATE
|
||||
lib/mbedtls_wrapper/include/internal/ssl3.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_cert.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_code.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_dbg.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_lib.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_methods.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_pkey.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_stack.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_types.h
|
||||
lib/mbedtls_wrapper/include/internal/ssl_x509.h
|
||||
lib/mbedtls_wrapper/include/internal/tls1.h
|
||||
lib/mbedtls_wrapper/include/internal/x509_vfy.h)
|
||||
lib/tls/mbedtls/wrapper/include/openssl/ssl.h)
|
||||
|
||||
list(APPEND HDR_PRIVATE
|
||||
lib/mbedtls_wrapper/include/openssl/ssl.h)
|
||||
lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h
|
||||
lib/tls/mbedtls/wrapper/include/platform/ssl_port.h)
|
||||
|
||||
list(APPEND HDR_PRIVATE
|
||||
lib/mbedtls_wrapper/include/platform/ssl_pm.h
|
||||
lib/mbedtls_wrapper/include/platform/ssl_port.h)
|
||||
|
||||
include_directories(lib/mbedtls_wrapper/include)
|
||||
include_directories(lib/mbedtls_wrapper/include/platform)
|
||||
include_directories(lib/mbedtls_wrapper/include/internal)
|
||||
include_directories(lib/mbedtls_wrapper/include/openssl)
|
||||
include_directories(lib/tls/mbedtls/wrapper/include)
|
||||
include_directories(lib/tls/mbedtls/wrapper/include/platform)
|
||||
include_directories(lib/tls/mbedtls/wrapper/include/internal)
|
||||
include_directories(lib/tls/mbedtls/wrapper/include/openssl)
|
||||
|
||||
list(APPEND SOURCES
|
||||
lib/mbedtls_wrapper/library/ssl_cert.c
|
||||
lib/mbedtls_wrapper/library/ssl_lib.c
|
||||
lib/mbedtls_wrapper/library/ssl_methods.c
|
||||
lib/mbedtls_wrapper/library/ssl_pkey.c
|
||||
lib/mbedtls_wrapper/library/ssl_stack.c
|
||||
lib/mbedtls_wrapper/library/ssl_x509.c)
|
||||
lib/tls/mbedtls/wrapper/library/ssl_cert.c
|
||||
lib/tls/mbedtls/wrapper/library/ssl_lib.c
|
||||
lib/tls/mbedtls/wrapper/library/ssl_methods.c
|
||||
lib/tls/mbedtls/wrapper/library/ssl_pkey.c
|
||||
lib/tls/mbedtls/wrapper/library/ssl_stack.c
|
||||
lib/tls/mbedtls/wrapper/library/ssl_x509.c)
|
||||
|
||||
list(APPEND SOURCES
|
||||
lib/mbedtls_wrapper/platform/ssl_pm.c
|
||||
lib/mbedtls_wrapper/platform/ssl_port.c)
|
||||
lib/tls/mbedtls/wrapper/platform/ssl_pm.c
|
||||
lib/tls/mbedtls/wrapper/platform/ssl_port.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_SSL)
|
||||
list(APPEND SOURCES
|
||||
lib/ssl.c
|
||||
lib/tls/tls.c
|
||||
lib/misc/lws-genhash.c)
|
||||
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/mbedtls/ssl.c)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
lib/tls/openssl/ssl.c)
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
list(APPEND SOURCES
|
||||
lib/server/ssl-server.c)
|
||||
lib/server/ssl-server.c)
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/mbedtls/server.c)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
lib/tls/openssl/server.c)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
lib/client/ssl-client.c)
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/mbedtls/client.c)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
lib/tls/openssl/client.c)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -1173,7 +1196,9 @@ CHECK_FUNCTION_EXISTS(SSL_CTX_set1_param LWS_HAVE_SSL_CTX_set1_param)
|
|||
CHECK_FUNCTION_EXISTS(SSL_set_info_callback LWS_HAVE_SSL_SET_INFO_CALLBACK)
|
||||
CHECK_FUNCTION_EXISTS(X509_VERIFY_PARAM_set1_host LWS_HAVE_X509_VERIFY_PARAM_set1_host)
|
||||
CHECK_FUNCTION_EXISTS(RSA_set0_key LWS_HAVE_RSA_SET0_KEY)
|
||||
|
||||
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()
|
||||
if (LWS_WITH_MBEDTLS)
|
||||
set(LWS_HAVE_TLS_CLIENT_METHOD 1)
|
||||
if (NOT LWS_WITH_ESP32)
|
||||
|
|
|
@ -150,6 +150,7 @@
|
|||
#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD
|
||||
#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD
|
||||
#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
|
||||
#cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
|
||||
|
||||
#cmakedefine LWS_HAS_INTPTR_T
|
||||
|
||||
|
|
|
@ -258,10 +258,8 @@ start_ws_handshake:
|
|||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
/* we can retry this... just cook the SSL BIO the first time */
|
||||
|
||||
if (wsi->use_ssl && !wsi->ssl) {
|
||||
if (lws_ssl_client_bio_create(wsi))
|
||||
return -1;
|
||||
}
|
||||
if (wsi->use_ssl && !wsi->ssl && lws_ssl_client_bio_create(wsi))
|
||||
return -1;
|
||||
|
||||
if (wsi->use_ssl) {
|
||||
n = lws_ssl_client_connect1(wsi);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
* libwebsockets - client-related ssl code independent of backend
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
|
@ -21,224 +21,6 @@
|
|||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
extern int openssl_websocket_private_data_index,
|
||||
openssl_SSL_CTX_private_data_index;
|
||||
|
||||
extern void
|
||||
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
|
||||
|
||||
extern int lws_ssl_get_error(struct lws *wsi, int n);
|
||||
|
||||
#if defined(USE_WOLFSSL)
|
||||
#else
|
||||
|
||||
static int
|
||||
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
lwsl_notice("%s\n", __func__);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
SSL *ssl;
|
||||
int n;
|
||||
struct lws *wsi;
|
||||
|
||||
/* keep old behaviour accepting self-signed server certs */
|
||||
if (!preverify_ok) {
|
||||
int err = X509_STORE_CTX_get_error(x509_ctx);
|
||||
|
||||
if (err != X509_V_OK) {
|
||||
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);
|
||||
|
||||
if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
|
||||
err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
|
||||
wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
|
||||
lwsl_notice("accepting self-signed certificate (verify_callback)\n");
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
|
||||
return 1; // ok
|
||||
} else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
|
||||
err == X509_V_ERR_CERT_HAS_EXPIRED) &&
|
||||
wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) {
|
||||
if (err == X509_V_ERR_CERT_NOT_YET_VALID)
|
||||
lwsl_notice("accepting not yet valid certificate (verify_callback)\n");
|
||||
else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
|
||||
lwsl_notice("accepting expired certificate (verify_callback)\n");
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
|
||||
return 1; // ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
n = lws_get_context_protocol(wsi->context, 0).callback(wsi,
|
||||
LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION,
|
||||
x509_ctx, ssl, preverify_ok);
|
||||
|
||||
/* 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);
|
||||
|
||||
if (err != X509_V_OK) { /* cert validation error was not handled in callback */
|
||||
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
|
||||
const char* msg = X509_verify_cert_error_string(err);
|
||||
lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth);
|
||||
return preverify_ok; // not ok
|
||||
}
|
||||
}
|
||||
/* convert callback return code from 0 = OK to verify callback return value 1 = OK */
|
||||
return !n;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_ssl_client_bio_create(struct lws *wsi)
|
||||
{
|
||||
char hostname[128], *p;
|
||||
|
||||
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
|
||||
_WSI_TOKEN_CLIENT_HOST) <= 0) {
|
||||
lwsl_err("%s: Unable to get hostname\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++;
|
||||
}
|
||||
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
|
||||
if (!wsi->ssl) {
|
||||
lwsl_err("SSL_new failed: %s\n",
|
||||
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
|
||||
lws_ssl_elaborate_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#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);
|
||||
#endif
|
||||
|
||||
#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
|
||||
X509_VERIFY_PARAM *param;
|
||||
(void)param;
|
||||
|
||||
if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
|
||||
param = SSL_get0_param(wsi->ssl);
|
||||
/* Enable automatic hostname checks */
|
||||
X509_VERIFY_PARAM_set_hostflags(param,
|
||||
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
|
||||
#ifndef USE_OLD_CYASSL
|
||||
/* OpenSSL_client_verify_callback will be called @ SSL_connect() */
|
||||
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
|
||||
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
#endif
|
||||
/*
|
||||
* use server name indication (SNI), if supported,
|
||||
* when establishing connection
|
||||
*/
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
#ifdef CYASSL_SNI_HOST_NAME
|
||||
CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname));
|
||||
#endif
|
||||
#else
|
||||
#ifdef WOLFSSL_SNI_HOST_NAME
|
||||
wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname));
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
if (wsi->vhost->x509_client_CA)
|
||||
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
|
||||
else
|
||||
SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
|
||||
|
||||
#else
|
||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||
SSL_set_tlsext_host_name(wsi->ssl, hostname);
|
||||
#endif
|
||||
#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
|
||||
if (wsi->use_ssl == 2)
|
||||
CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
|
||||
#else
|
||||
if (wsi->use_ssl == 2)
|
||||
wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
|
||||
#endif
|
||||
#endif /* USE_WOLFSSL */
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
|
||||
SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
|
||||
#else
|
||||
SSL_set_fd(wsi->ssl, wsi->desc.sockfd);
|
||||
#endif
|
||||
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
CyaSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#else
|
||||
wolfSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#endif
|
||||
#else
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
|
||||
wsi);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
int ERR_get_error(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_ssl_client_connect1(struct lws *wsi)
|
||||
{
|
||||
|
@ -246,187 +28,59 @@ lws_ssl_client_connect1(struct lws *wsi)
|
|||
int n = 0;
|
||||
|
||||
lws_latency_pre(context, wsi);
|
||||
|
||||
n = SSL_connect(wsi->ssl);
|
||||
|
||||
n = lws_tls_client_connect(wsi);
|
||||
lws_latency(context, wsi,
|
||||
"SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);
|
||||
|
||||
if (n < 0) {
|
||||
n = lws_ssl_get_error(wsi, n);
|
||||
|
||||
if (n == SSL_ERROR_WANT_READ)
|
||||
goto some_wait;
|
||||
|
||||
if (n == SSL_ERROR_WANT_WRITE) {
|
||||
/*
|
||||
* wants us to retry connect due to
|
||||
* state of the underlying ssl layer...
|
||||
* but since it may be stalled on
|
||||
* blocked write, no incoming data may
|
||||
* arrive to trigger the retry.
|
||||
* Force (possibly many times if the SSL
|
||||
* state persists in returning the
|
||||
* condition code, but other sockets
|
||||
* are getting serviced inbetweentimes)
|
||||
* us to get called back when writable.
|
||||
*/
|
||||
lwsl_info("%s: WANT_WRITE... retrying\n", __func__);
|
||||
lws_callback_on_writable(wsi);
|
||||
some_wait:
|
||||
wsi->mode = LWSCM_WSCL_WAITING_SSL;
|
||||
|
||||
return 0; /* no error */
|
||||
}
|
||||
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
char *p = (char *)&pt->serv_buf[0];
|
||||
char *sb = p;
|
||||
|
||||
lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n",
|
||||
n, ERR_error_string(n, sb));
|
||||
lws_ssl_elaborate_error();
|
||||
}
|
||||
|
||||
n = -1;
|
||||
switch (n) {
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
return -1;
|
||||
case LWS_SSL_CAPABLE_DONE:
|
||||
return 1; /* connected */
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
|
||||
lws_callback_on_writable(wsi);
|
||||
/* fallthru */
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
|
||||
wsi->mode = LWSCM_WSCL_WAITING_SSL;
|
||||
break;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n <= 0) {
|
||||
/*
|
||||
* retry if new data comes until we
|
||||
* run into the connection timeout or win
|
||||
*/
|
||||
|
||||
unsigned long error = ERR_get_error();
|
||||
|
||||
if (error != SSL_ERROR_NONE) {
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
char *p = (char *)&pt->serv_buf[0];
|
||||
char *sb = p;
|
||||
lwsl_err("SSL connect error %lu: %s\n",
|
||||
error, ERR_error_string(error, sb));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0; /* retry */
|
||||
}
|
||||
|
||||
int
|
||||
lws_ssl_client_connect2(struct lws *wsi)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
char *p = (char *)&pt->serv_buf[0];
|
||||
char *sb = p;
|
||||
int n = 0;
|
||||
|
||||
if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
|
||||
lws_latency_pre(context, wsi);
|
||||
n = SSL_connect(wsi->ssl);
|
||||
lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
|
||||
lws_latency_pre(wsi->context, wsi);
|
||||
|
||||
lws_latency(context, wsi,
|
||||
n = lws_tls_client_connect(wsi);
|
||||
lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
|
||||
lws_latency(wsi->context, wsi,
|
||||
"SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
|
||||
|
||||
if (n < 0) {
|
||||
n = lws_ssl_get_error(wsi, n);
|
||||
|
||||
if (n == SSL_ERROR_WANT_READ) {
|
||||
lwsl_info("SSL_connect WANT_READ... retrying\n");
|
||||
|
||||
wsi->mode = LWSCM_WSCL_WAITING_SSL;
|
||||
|
||||
return 0; /* no error */
|
||||
}
|
||||
|
||||
if (n == SSL_ERROR_WANT_WRITE) {
|
||||
/*
|
||||
* wants us to retry connect due to
|
||||
* state of the underlying ssl layer...
|
||||
* but since it may be stalled on
|
||||
* blocked write, no incoming data may
|
||||
* arrive to trigger the retry.
|
||||
* Force (possibly many times if the SSL
|
||||
* state persists in returning the
|
||||
* condition code, but other sockets
|
||||
* are getting serviced inbetweentimes)
|
||||
* us to get called back when writable.
|
||||
*/
|
||||
lwsl_info("SSL_connect WANT_WRITE... retrying\n");
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
wsi->mode = LWSCM_WSCL_WAITING_SSL;
|
||||
|
||||
return 0; /* no error */
|
||||
}
|
||||
|
||||
n = -1;
|
||||
}
|
||||
|
||||
if (n <= 0) {
|
||||
/*
|
||||
* retry if new data comes until we
|
||||
* run into the connection timeout or win
|
||||
*/
|
||||
unsigned long error = ERR_get_error();
|
||||
if (error != SSL_ERROR_NONE) {
|
||||
lwsl_err("SSL connect error %lu: %s\n",
|
||||
error, ERR_error_string(error, sb));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
{
|
||||
X509 *peer = SSL_get_peer_certificate(wsi->ssl);
|
||||
|
||||
if (!peer) {
|
||||
lwsl_notice("peer did not provide cert\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
lwsl_notice("peer provided cert\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef USE_WOLFSSL
|
||||
/*
|
||||
* See comment above about wolfSSL certificate
|
||||
* verification
|
||||
*/
|
||||
lws_latency_pre(context, wsi);
|
||||
n = SSL_get_verify_result(wsi->ssl);
|
||||
lws_latency(context, wsi,
|
||||
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
|
||||
|
||||
lwsl_debug("get_verify says %d\n", n);
|
||||
|
||||
if (n != X509_V_OK) {
|
||||
if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
|
||||
n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
|
||||
(wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
|
||||
lwsl_notice("accepting self-signed certificate\n");
|
||||
} else if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
|
||||
n == X509_V_ERR_CERT_HAS_EXPIRED) &&
|
||||
(wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) {
|
||||
lwsl_notice("accepting expired certificate\n");
|
||||
} else if (n == X509_V_ERR_CERT_NOT_YET_VALID) {
|
||||
lwsl_notice("Cert is from the future... "
|
||||
"probably our clock... accepting...\n");
|
||||
} else {
|
||||
lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
|
||||
n, ERR_error_string(n, sb));
|
||||
lws_ssl_elaborate_error();
|
||||
switch (n) {
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
return -1;
|
||||
case LWS_SSL_CAPABLE_DONE:
|
||||
break; /* connected */
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
|
||||
lws_callback_on_writable(wsi);
|
||||
/* fallthru */
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
|
||||
wsi->mode = LWSCM_WSCL_WAITING_SSL;
|
||||
/* fallthru */
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* USE_WOLFSSL */
|
||||
if (lws_tls_client_confirm_peer_cert(wsi))
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -435,15 +89,11 @@ lws_ssl_client_connect2(struct lws *wsi)
|
|||
int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost)
|
||||
{
|
||||
SSL_METHOD *method = NULL;
|
||||
struct lws wsi;
|
||||
unsigned long error;
|
||||
const char *ca_filepath = info->ssl_ca_filepath;
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
const char *cipher_list = info->ssl_cipher_list;
|
||||
const char *private_key_filepath = info->ssl_private_key_filepath;
|
||||
const char *cert_filepath = info->ssl_cert_filepath;
|
||||
int n;
|
||||
struct lws wsi;
|
||||
|
||||
if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW)
|
||||
return 0;
|
||||
|
@ -458,7 +108,7 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
cert_filepath = info->client_ssl_cert_filepath;
|
||||
if (info->client_ssl_private_key_filepath)
|
||||
private_key_filepath = info->client_ssl_private_key_filepath;
|
||||
#endif
|
||||
|
||||
if (info->client_ssl_ca_filepath)
|
||||
ca_filepath = info->client_ssl_ca_filepath;
|
||||
|
||||
|
@ -477,138 +127,13 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl method %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
if (lws_tls_client_create_vhost_context(vhost, info, cipher_list,
|
||||
ca_filepath, cert_filepath,
|
||||
private_key_filepath))
|
||||
return 1;
|
||||
}
|
||||
/* create context */
|
||||
vhost->ssl_client_ctx = SSL_CTX_new(method);
|
||||
if (!vhost->ssl_client_ctx) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl context %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_notice("created client ssl context for %s\n", vhost->name);
|
||||
|
||||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
if (ca_filepath) {
|
||||
lws_filepos_t len;
|
||||
uint8_t *buf;
|
||||
/*
|
||||
* prototype this here, the shim does not export it in the
|
||||
* header, and we need to use the shim unchanged for ESP32 case
|
||||
*/
|
||||
X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
|
||||
|
||||
if (alloc_file(vhost->context, ca_filepath, &buf, &len)) {
|
||||
lwsl_err("Load CA cert file %s failed\n", ca_filepath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vhost->x509_client_CA = d2i_X509(NULL, buf, len);
|
||||
free(buf);
|
||||
if (!vhost->x509_client_CA) {
|
||||
lwsl_err("client CA: x509 parse failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_CTX_add_client_CA(vhost->ssl_client_ctx,
|
||||
vhost->x509_client_CA);
|
||||
|
||||
lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
|
||||
}
|
||||
#else
|
||||
SSL_CTX_set_options(vhost->ssl_client_ctx,
|
||||
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
|
||||
if (cipher_list)
|
||||
SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list);
|
||||
|
||||
#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
|
||||
/* loads OS default CA certs */
|
||||
SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx);
|
||||
#endif
|
||||
|
||||
/* openssl init for cert verification (for client sockets) */
|
||||
if (!ca_filepath) {
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS))
|
||||
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);
|
||||
} else
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
vhost->ssl_client_ctx, ca_filepath, NULL)) {
|
||||
lwsl_err(
|
||||
"Unable to load SSL Client certs "
|
||||
"file from %s -- client ssl isn't "
|
||||
"going to work\n", info->client_ssl_ca_filepath);
|
||||
lws_ssl_elaborate_error();
|
||||
}
|
||||
else
|
||||
lwsl_info("loaded ssl_ca_filepath\n");
|
||||
|
||||
/*
|
||||
* callback allowing user code to load extra verification certs
|
||||
* helping the client to verify server identity
|
||||
*/
|
||||
|
||||
/* support for client-side certificate authentication */
|
||||
if (cert_filepath) {
|
||||
lwsl_notice("%s: doing cert filepath\n", __func__);
|
||||
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
|
||||
cert_filepath);
|
||||
if (n < 1) {
|
||||
lwsl_err("problem %d getting cert '%s'\n", n,
|
||||
cert_filepath);
|
||||
lws_ssl_elaborate_error();
|
||||
return 1;
|
||||
}
|
||||
lwsl_notice("Loaded client cert %s\n", cert_filepath);
|
||||
}
|
||||
if (private_key_filepath) {
|
||||
lwsl_notice("%s: doing private key filepath\n", __func__);
|
||||
lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
|
||||
/* set the private key from KeyFile */
|
||||
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
|
||||
private_key_filepath, SSL_FILETYPE_PEM) != 1) {
|
||||
lwsl_err("use_PrivateKey_file '%s'\n",
|
||||
private_key_filepath);
|
||||
lws_ssl_elaborate_error();
|
||||
return 1;
|
||||
}
|
||||
lwsl_notice("Loaded client cert private key %s\n",
|
||||
private_key_filepath);
|
||||
|
||||
/* verify private key */
|
||||
if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
|
||||
lwsl_err("Private SSL key doesn't match cert\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* give him a fake wsi with context set, so he can use
|
||||
* lws_get_context() in the callback
|
||||
|
|
|
@ -621,35 +621,19 @@ just_kill_connection:
|
|||
wsi->state != LWSS_CLIENT_UNCONNECTED &&
|
||||
reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
|
||||
!wsi->socket_is_permanently_unusable) {
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
if (lws_is_ssl(wsi) && wsi->ssl) {
|
||||
n = SSL_shutdown(wsi->ssl);
|
||||
/*
|
||||
* If finished the SSL shutdown, then do socket
|
||||
* shutdown, else need to retry SSL shutdown
|
||||
*/
|
||||
switch (n) {
|
||||
case 0:
|
||||
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
|
||||
break;
|
||||
case 1:
|
||||
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
||||
break;
|
||||
default:
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
if (lws_is_ssl(wsi) && wsi->ssl) {
|
||||
n = 0;
|
||||
switch (lws_tls_shutdown(wsi)) {
|
||||
case LWS_SSL_CAPABLE_DONE:
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
break;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
lwsl_info("%s: shutdown conn: %p (sock %d, state %d)\n",
|
||||
|
@ -1858,7 +1842,7 @@ lws_is_ssl(struct lws *wsi)
|
|||
}
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
LWS_VISIBLE SSL*
|
||||
LWS_VISIBLE lws_tls_conn*
|
||||
lws_get_ssl(struct lws *wsi)
|
||||
{
|
||||
return wsi->ssl;
|
||||
|
|
|
@ -210,11 +210,12 @@ typedef unsigned long long lws_intptr_t;
|
|||
#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
|
||||
#endif
|
||||
#include <mbedtls/ssl.h>
|
||||
#endif
|
||||
#else
|
||||
#include <openssl/ssl.h>
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif /* not USE_WOLFSSL */
|
||||
#endif
|
||||
|
||||
|
@ -2191,12 +2192,12 @@ struct lws_context_creation_info {
|
|||
int ka_interval;
|
||||
/**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes
|
||||
* attempt */
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
SSL_CTX *provided_client_ssl_ctx;
|
||||
/**< CONTEXT: If non-null, swap out libwebsockets ssl
|
||||
* implementation for the one provided by provided_ssl_ctx.
|
||||
* Libwebsockets no longer is responsible for freeing the context
|
||||
* if this option is selected. */
|
||||
* implementation for the one provided by provided_ssl_ctx.
|
||||
* Libwebsockets no longer is responsible for freeing the context
|
||||
* if this option is selected. */
|
||||
#else /* maintain structure layout either way */
|
||||
void *provided_client_ssl_ctx; /**< dummy if ssl disabled */
|
||||
#endif
|
||||
|
@ -4995,7 +4996,7 @@ lws_is_ssl(struct lws *wsi);
|
|||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_is_cgi(struct lws *wsi);
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
/**
|
||||
* lws_get_ssl() - Return wsi's SSL context structure
|
||||
* \param wsi: websocket connection
|
||||
|
|
|
@ -283,6 +283,7 @@ lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen);
|
|||
#if defined(LWS_WITH_MBEDTLS)
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/x509_crt.h>
|
||||
#include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */
|
||||
#else
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/evp.h>
|
||||
|
@ -477,6 +478,53 @@ extern "C" {
|
|||
#define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Choose the SSL backend
|
||||
*/
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_WITH_MBEDTLS________)
|
||||
struct lws_tls_mbed_ctx {
|
||||
|
||||
};
|
||||
struct lws_tls_mbed_conn {
|
||||
|
||||
};
|
||||
struct lws_tls_mbed_bio {
|
||||
|
||||
};
|
||||
struct lws_tls_mbed_x509 {
|
||||
|
||||
};
|
||||
typedef struct lws_tls_mbed_conn lws_tls_conn;
|
||||
typedef struct lws_tls_mbed_ctx lws_tls_ctx;
|
||||
typedef struct lws_tls_mbed_bio lws_tls_bio;
|
||||
typedef struct lws_tls_mbed_x509 lws_tls_x509;
|
||||
#else
|
||||
typedef SSL lws_tls_conn;
|
||||
typedef SSL_CTX lws_tls_ctx;
|
||||
typedef BIO lws_tls_bio;
|
||||
typedef X509 lws_tls_x509;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* All lws_tls...() functions must return this type, converting the
|
||||
* native backend result and doing the extra work to determine which one
|
||||
* as needed.
|
||||
*
|
||||
* Native TLS backend return codes are NOT ALLOWED outside the backend.
|
||||
*
|
||||
* Non-SSL mode also uses these types.
|
||||
*/
|
||||
enum lws_ssl_capable_status {
|
||||
LWS_SSL_CAPABLE_ERROR = -1, /* it failed */
|
||||
LWS_SSL_CAPABLE_DONE = 0, /* it succeeded */
|
||||
LWS_SSL_CAPABLE_MORE_SERVICE_READ = -2, /* retry WANT_READ */
|
||||
LWS_SSL_CAPABLE_MORE_SERVICE_WRITE = -3, /* retry WANT_WRITE */
|
||||
LWS_SSL_CAPABLE_MORE_SERVICE = -4, /* general retry */
|
||||
};
|
||||
|
||||
enum lws_websocket_opcodes_07 {
|
||||
LWSWSOPC_CONTINUATION = 0,
|
||||
LWSWSOPC_TEXT_FRAME = 1,
|
||||
|
@ -924,11 +972,11 @@ struct lws_vhost {
|
|||
const struct lws_protocol_vhost_options *headers;
|
||||
struct lws **same_vh_protocol_list;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL_CTX *ssl_client_ctx;
|
||||
lws_tls_ctx *ssl_ctx;
|
||||
lws_tls_ctx *ssl_client_ctx;
|
||||
#endif
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
X509 *x509_client_CA;
|
||||
lws_tls_x509 *x509_client_CA;
|
||||
#endif
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
const struct lws_extension *extensions;
|
||||
|
@ -1879,8 +1927,8 @@ struct lws {
|
|||
void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
#endif
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
SSL *ssl;
|
||||
BIO *client_bio;
|
||||
lws_tls_conn *ssl;
|
||||
lws_tls_bio *client_bio;
|
||||
struct lws *pending_read_list_prev, *pending_read_list_next;
|
||||
#if defined(LWS_WITH_STATS)
|
||||
uint64_t accept_start_us;
|
||||
|
@ -2272,11 +2320,6 @@ interface_to_sa(struct lws_vhost *vh, const char *ifname,
|
|||
#endif
|
||||
LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
|
||||
|
||||
enum lws_ssl_capable_status {
|
||||
LWS_SSL_CAPABLE_ERROR = -1,
|
||||
LWS_SSL_CAPABLE_MORE_SERVICE = -2,
|
||||
};
|
||||
|
||||
#ifndef LWS_OPENSSL_SUPPORT
|
||||
#define LWS_SSL_ENABLED(context) (0)
|
||||
#define lws_context_init_server_ssl(_a, _b) (0)
|
||||
|
@ -2323,6 +2366,13 @@ LWS_EXTERN void
|
|||
lws_ssl_elaborate_error(void);
|
||||
LWS_EXTERN int
|
||||
lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi);
|
||||
LWS_EXTERN int
|
||||
lws_gate_accepts(struct lws_context *context, int on);
|
||||
LWS_EXTERN void
|
||||
lws_ssl_bind_passphrase(lws_tls_ctx *ssl_ctx, struct lws_context_creation_info *info);
|
||||
LWS_EXTERN void
|
||||
lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
|
||||
|
||||
#ifndef LWS_NO_SERVER
|
||||
LWS_EXTERN int
|
||||
lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
||||
|
@ -2332,6 +2382,47 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
#endif
|
||||
LWS_EXTERN void
|
||||
lws_ssl_destroy(struct lws_vhost *vhost);
|
||||
LWS_EXTERN char *
|
||||
lws_ssl_get_error_string(int status, int ret, char *buf, size_t len);
|
||||
|
||||
/*
|
||||
* lws_tls_ abstract backend implementations
|
||||
*/
|
||||
|
||||
LWS_EXTERN int
|
||||
lws_tls_server_client_cert_verify_config(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vh);
|
||||
LWS_EXTERN int
|
||||
lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost, struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd);
|
||||
|
||||
LWS_EXTERN enum lws_ssl_capable_status
|
||||
lws_tls_server_accept(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN enum lws_ssl_capable_status
|
||||
lws_tls_server_abort_connection(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN enum lws_ssl_capable_status
|
||||
lws_tls_shutdown(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN enum lws_ssl_capable_status
|
||||
lws_tls_client_connect(struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
lws_tls_client_confirm_peer_cert(struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
lws_tls_client_create_vhost_context(struct lws_vhost *vh,
|
||||
struct lws_context_creation_info *info,
|
||||
const char *cipher_list,
|
||||
const char *ca_filepath,
|
||||
const char *cert_filepath,
|
||||
const char *private_key_filepath);
|
||||
LWS_EXTERN lws_tls_ctx *
|
||||
lws_tls_ctx_from_wsi(struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
lws_ssl_get_error(struct lws *wsi, int n);
|
||||
|
||||
/* HTTP2-related */
|
||||
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
|
@ -2437,7 +2528,7 @@ lws_context_init_client_ssl(struct lws_context_creation_info *info,
|
|||
struct lws_vhost *vhost);
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_ssl_info_callback(const SSL *ssl, int where, int ret);
|
||||
lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
|
||||
|
||||
#else
|
||||
#define lws_context_init_client_ssl(_a, _b) (0)
|
||||
|
@ -2560,8 +2651,7 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
|||
lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len);
|
||||
LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount);
|
||||
LWS_EXTERN int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount);
|
||||
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_same_vh_protocol_remove(struct lws *wsi);
|
||||
|
|
|
@ -21,185 +21,17 @@
|
|||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
extern int openssl_websocket_private_data_index,
|
||||
openssl_SSL_CTX_private_data_index;
|
||||
|
||||
extern void
|
||||
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
static int
|
||||
OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
SSL *ssl;
|
||||
int n;
|
||||
struct lws *wsi;
|
||||
|
||||
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
|
||||
SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
|
||||
/*
|
||||
* !!! nasty openssl requires the index to come as a library-scope
|
||||
* static
|
||||
*/
|
||||
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
|
||||
|
||||
n = wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
|
||||
x509_ctx, ssl, preverify_ok);
|
||||
|
||||
/* convert return code from 0 = OK to 1 = OK */
|
||||
return !n;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
lws_context_ssl_init_ecdh(struct lws_vhost *vhost)
|
||||
{
|
||||
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
|
||||
EC_KEY *EC_key = NULL;
|
||||
EVP_PKEY *pkey;
|
||||
int KeyType;
|
||||
X509 *x;
|
||||
|
||||
if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH))
|
||||
return 0;
|
||||
|
||||
lwsl_notice(" Using ECDH certificate support\n");
|
||||
|
||||
/* Get X509 certificate from ssl context */
|
||||
x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
|
||||
if (!x) {
|
||||
lwsl_err("%s: x is NULL\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
/* Get the public key from certificate */
|
||||
pkey = X509_get_pubkey(x);
|
||||
if (!pkey) {
|
||||
lwsl_err("%s: pkey is NULL\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* Get the key type */
|
||||
KeyType = EVP_PKEY_type(pkey->type);
|
||||
|
||||
if (EVP_PKEY_EC != KeyType) {
|
||||
lwsl_notice("Key type is not EC\n");
|
||||
return 0;
|
||||
}
|
||||
/* Get the key */
|
||||
EC_key = EVP_PKEY_get1_EC_KEY(pkey);
|
||||
/* Set ECDH parameter */
|
||||
if (!EC_key) {
|
||||
lwsl_err("%s: ECDH key is NULL \n", __func__);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key);
|
||||
EC_KEY_free(EC_key);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost)
|
||||
{
|
||||
#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS)
|
||||
EC_KEY *ecdh;
|
||||
int ecdh_nid;
|
||||
const char *ecdh_curve = "prime256v1";
|
||||
|
||||
if (info->ecdh_curve)
|
||||
ecdh_curve = info->ecdh_curve;
|
||||
|
||||
ecdh_nid = OBJ_sn2nid(ecdh_curve);
|
||||
if (NID_undef == ecdh_nid) {
|
||||
lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
|
||||
if (NULL == ecdh) {
|
||||
lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
|
||||
|
||||
lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
|
||||
#else
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
lwsl_notice(" OpenSSL doesn't support ECDH\n");
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT)
|
||||
static int
|
||||
lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
|
||||
{
|
||||
struct lws_context *context = (struct lws_context *)arg;
|
||||
struct lws_vhost *vhost, *vh;
|
||||
const char *servername;
|
||||
|
||||
if (!ssl)
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
|
||||
/*
|
||||
* We can only get ssl accepted connections by using a vhost's ssl_ctx
|
||||
* find out which listening one took us and only match vhosts on the
|
||||
* same port.
|
||||
*/
|
||||
vh = context->vhost_list;
|
||||
while (vh) {
|
||||
if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
|
||||
break;
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
if (!vh) {
|
||||
assert(vh); /* can't match the incoming vh? */
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||
if (!servername) {
|
||||
/* the client doesn't know what hostname it wants */
|
||||
lwsl_info("SNI: Unknown ServerName: %s\n", servername);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
vhost = lws_select_vhost(context, vh->listen_port, servername);
|
||||
if (!vhost) {
|
||||
lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
|
||||
|
||||
/* select the ssl ctx from the selected vhost for this conn */
|
||||
SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost)
|
||||
{
|
||||
struct lws_context *context = vhost->context;
|
||||
struct lws wsi;
|
||||
unsigned long error;
|
||||
int n;
|
||||
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
|
||||
if (!lws_check_opt(info->options,
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
|
||||
vhost->use_ssl = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -220,7 +52,8 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX);
|
||||
|
||||
if (vhost->use_ssl && info->ssl_cipher_list)
|
||||
lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
|
||||
lwsl_notice(" SSL ciphers: '%s'\n",
|
||||
info->ssl_cipher_list);
|
||||
|
||||
if (vhost->use_ssl)
|
||||
lwsl_notice(" Using SSL mode\n");
|
||||
|
@ -236,109 +69,23 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
wsi.vhost = vhost;
|
||||
wsi.context = context;
|
||||
|
||||
(void)n;
|
||||
(void)error;
|
||||
|
||||
/*
|
||||
* Firefox insists on SSLv23 not SSLv3
|
||||
* Konq disables SSLv2 by default now, SSLv23 works
|
||||
*
|
||||
* SSLv23_server_method() is the openssl method for "allow all TLS
|
||||
* versions", compared to e.g. TLSv1_2_server_method() which only allows
|
||||
* tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options()
|
||||
* as a server, if we are requiring clients to identify themselves
|
||||
* then set the backend up for it
|
||||
*/
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
{
|
||||
SSL_METHOD *method;
|
||||
|
||||
method = (SSL_METHOD *)SSLv23_server_method();
|
||||
if (!method) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl method %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
|
||||
if (!vhost->ssl_ctx) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl context %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
const SSL_METHOD *method = TLSv1_2_server_method();
|
||||
|
||||
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
|
||||
if (!vhost->ssl_ctx) {
|
||||
lwsl_err("problem creating ssl context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
|
||||
/* associate the lws context with the SSL_CTX */
|
||||
|
||||
SSL_CTX_set_ex_data(vhost->ssl_ctx,
|
||||
openssl_SSL_CTX_private_data_index, (char *)vhost->context);
|
||||
/* Disable SSLv2 and SSLv3 */
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
|
||||
if (info->ssl_cipher_list)
|
||||
SSL_CTX_set_cipher_list(vhost->ssl_ctx,
|
||||
info->ssl_cipher_list);
|
||||
#endif
|
||||
|
||||
/* as a server, are we requiring clients to identify themselves? */
|
||||
lws_tls_server_client_cert_verify_config(info, vhost);
|
||||
|
||||
if (lws_check_opt(info->options,
|
||||
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
|
||||
int verify_options = SSL_VERIFY_PEER;
|
||||
|
||||
if (!lws_check_opt(info->options,
|
||||
LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
|
||||
verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
SSL_CTX_set_session_id_context(vhost->ssl_ctx,
|
||||
(unsigned char *)context, sizeof(void *));
|
||||
|
||||
/* absolutely require the client cert */
|
||||
|
||||
SSL_CTX_set_verify(vhost->ssl_ctx,
|
||||
verify_options, OpenSSL_verify_callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
|
||||
SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
|
||||
lws_ssl_server_name_cb);
|
||||
SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context);
|
||||
#endif
|
||||
LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
|
||||
/* Normally SSL listener rejects non-ssl, optionally allow */
|
||||
vhost->allow_non_ssl_on_ssl_port = 1;
|
||||
|
||||
/*
|
||||
* give user code a chance to load certs into the server
|
||||
* allowing it to verify incoming client certs
|
||||
*/
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
if (info->ssl_ca_filepath &&
|
||||
!SSL_CTX_load_verify_locations(vhost->ssl_ctx,
|
||||
info->ssl_ca_filepath, NULL)) {
|
||||
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__);
|
||||
}
|
||||
#endif
|
||||
if (vhost->use_ssl) {
|
||||
if (lws_context_ssl_init_ecdh_curve(info, vhost))
|
||||
if (lws_tls_server_vhost_backend_init(info, vhost, &wsi))
|
||||
return -1;
|
||||
|
||||
vhost->protocols[0].callback(&wsi,
|
||||
|
@ -346,132 +93,213 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
vhost->ssl_ctx, NULL, 0);
|
||||
}
|
||||
|
||||
if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
|
||||
/* Normally SSL listener rejects non-ssl, optionally allow */
|
||||
vhost->allow_non_ssl_on_ssl_port = 1;
|
||||
|
||||
if (info->ssl_options_set)
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set);
|
||||
|
||||
/* SSL_clear_options introduced in 0.9.8m */
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
|
||||
if (info->ssl_options_clear)
|
||||
SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx));
|
||||
|
||||
if (vhost->use_ssl && info->ssl_cert_filepath) {
|
||||
/*
|
||||
* The user code can choose to either pass the cert and
|
||||
* key filepaths using the info members like this, or it can
|
||||
* leave them NULL; force the vhost SSL_CTX init using the info
|
||||
* options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
|
||||
* set up the cert himself using the user callback
|
||||
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
|
||||
* happened just above and has the vhost SSL_CTX * in the user
|
||||
* parameter.
|
||||
*/
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
/* set the local certificate from CertFile */
|
||||
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
|
||||
info->ssl_cert_filepath);
|
||||
if (n != 1) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem getting cert '%s' %lu: %s\n",
|
||||
info->ssl_cert_filepath,
|
||||
error,
|
||||
ERR_error_string(error,
|
||||
(char *)context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
|
||||
#else
|
||||
uint8_t *p;
|
||||
lws_filepos_t flen;
|
||||
int err;
|
||||
|
||||
if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
|
||||
&flen)) {
|
||||
lwsl_err("couldn't find cert file %s\n",
|
||||
info->ssl_cert_filepath);
|
||||
|
||||
return 1;
|
||||
}
|
||||
err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
|
||||
if (!err) {
|
||||
lwsl_err("Problem loading cert\n");
|
||||
return 1;
|
||||
}
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
free(p);
|
||||
p = NULL;
|
||||
#endif
|
||||
|
||||
if (info->ssl_private_key_filepath) {
|
||||
if (alloc_pem_to_der_file(vhost->context,
|
||||
info->ssl_private_key_filepath, &p, &flen)) {
|
||||
lwsl_err("couldn't find cert file %s\n",
|
||||
info->ssl_cert_filepath);
|
||||
|
||||
return 1;
|
||||
}
|
||||
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
|
||||
if (!err) {
|
||||
lwsl_err("Problem loading key\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
free(p);
|
||||
p = NULL;
|
||||
#endif
|
||||
#endif
|
||||
if (info->ssl_private_key_filepath != NULL) {
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
/* set the private key from KeyFile */
|
||||
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
|
||||
info->ssl_private_key_filepath,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("ssl problem getting key '%s' %lu: %s\n",
|
||||
info->ssl_private_key_filepath, error,
|
||||
ERR_error_string(error,
|
||||
(char *)context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
} else
|
||||
if (vhost->protocols[0].callback(&wsi,
|
||||
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
|
||||
vhost->ssl_ctx, NULL, 0)) {
|
||||
lwsl_err("ssl private key not set\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
/* verify private key */
|
||||
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
|
||||
lwsl_err("Private SSL key doesn't match cert\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (vhost->use_ssl) {
|
||||
if (lws_context_ssl_init_ecdh(vhost))
|
||||
return 1;
|
||||
|
||||
if (vhost->use_ssl)
|
||||
/*
|
||||
* SSL is happy and has a cert it's content with
|
||||
* If we're supporting HTTP2, initialize that
|
||||
*/
|
||||
lws_context_init_http2_ssl(vhost);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_vhost *vh;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n;
|
||||
char buf[256];
|
||||
|
||||
(void)buf;
|
||||
|
||||
if (!LWS_SSL_ENABLED(wsi->vhost))
|
||||
return 0;
|
||||
|
||||
switch (wsi->mode) {
|
||||
case LWSCM_SSL_INIT:
|
||||
case LWSCM_SSL_INIT_RAW:
|
||||
if (wsi->ssl)
|
||||
lwsl_err("%s: leaking ssl\n", __func__);
|
||||
if (accept_fd == LWS_SOCK_INVALID)
|
||||
assert(0);
|
||||
if (context->simultaneous_ssl_restriction &&
|
||||
context->simultaneous_ssl >= context->simultaneous_ssl_restriction) {
|
||||
lwsl_notice("unable to deal with SSL connection\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lws_tls_server_new_nonblocking(wsi, accept_fd)) {
|
||||
if (accept_fd != LWS_SOCK_INVALID)
|
||||
compatible_close(accept_fd);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (context->simultaneous_ssl_restriction &&
|
||||
++context->simultaneous_ssl == context->simultaneous_ssl_restriction)
|
||||
/* that was the last allowed SSL connection */
|
||||
lws_gate_accepts(context, 0);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
context->updated = 1;
|
||||
#endif
|
||||
/*
|
||||
* we are not accepted yet, but we need to enter ourselves
|
||||
* as a live connection. That way we can retry when more
|
||||
* pieces come if we're not sorted yet
|
||||
*/
|
||||
if (wsi->mode == LWSCM_SSL_INIT)
|
||||
wsi->mode = LWSCM_SSL_ACK_PENDING;
|
||||
else
|
||||
wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;
|
||||
|
||||
if (insert_wsi_socket_into_fds(context, wsi)) {
|
||||
lwsl_err("%s: failed to insert into fds\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
|
||||
context->timeout_secs);
|
||||
|
||||
lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWSCM_SSL_ACK_PENDING:
|
||||
case LWSCM_SSL_ACK_PENDING_RAW:
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_err("%s: lws_change_pollfd failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lws_latency_pre(context, wsi);
|
||||
|
||||
if (wsi->vhost->allow_non_ssl_on_ssl_port) {
|
||||
|
||||
n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
|
||||
context->pt_serv_buf_size, MSG_PEEK);
|
||||
|
||||
/*
|
||||
* optionally allow non-SSL connect on SSL listening socket
|
||||
* This is disabled by default, if enabled it goes around any
|
||||
* SSL-level access control (eg, client-side certs) so leave
|
||||
* it disabled unless you know it's not a problem for you
|
||||
*/
|
||||
|
||||
if (n >= 1 && pt->serv_buf[0] >= ' ') {
|
||||
/*
|
||||
* TLS content-type for Handshake is 0x16, and
|
||||
* for ChangeCipherSpec Record, it's 0x14
|
||||
*
|
||||
* A non-ssl session will start with the HTTP
|
||||
* method in ASCII. If we see it's not a legit
|
||||
* SSL handshake kill the SSL for this
|
||||
* connection and try to handle as a HTTP
|
||||
* connection upgrade directly.
|
||||
*/
|
||||
wsi->use_ssl = 0;
|
||||
|
||||
lws_tls_server_abort_connection(wsi);
|
||||
wsi->ssl = NULL;
|
||||
if (lws_check_opt(context->options,
|
||||
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
|
||||
wsi->redirect_to_https = 1;
|
||||
goto accepted;
|
||||
}
|
||||
if (!n) /*
|
||||
* connection is gone, or nothing to read
|
||||
* if it's gone, we will timeout on
|
||||
* PENDING_TIMEOUT_SSL_ACCEPT
|
||||
*/
|
||||
break;
|
||||
if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK)) {
|
||||
/*
|
||||
* well, we get no way to know ssl or not
|
||||
* so go around again waiting for something
|
||||
* to come and give us a hint, or timeout the
|
||||
* connection.
|
||||
*/
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
|
||||
lwsl_info("%s: WANT_READ change_pollfd failed\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwsl_info("SSL_ERROR_WANT_READ\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* normal SSL connection processing path */
|
||||
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!wsi->accept_start_us)
|
||||
wsi->accept_start_us = time_in_microseconds();
|
||||
#endif
|
||||
errno = 0;
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
|
||||
n = lws_tls_server_accept(wsi);
|
||||
lws_latency(context, wsi,
|
||||
"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
|
||||
lwsl_info("SSL_accept says %d\n", n);
|
||||
switch (n) {
|
||||
case LWS_SSL_CAPABLE_DONE:
|
||||
break;
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
|
||||
lwsl_info("SSL_accept failed socket %u: %d\n",
|
||||
wsi->desc.sockfd, n);
|
||||
goto fail;
|
||||
|
||||
default: /* MORE_SERVICE */
|
||||
return 0;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
|
||||
time_in_microseconds() - wsi->accept_start_us);
|
||||
wsi->accept_start_us = time_in_microseconds();
|
||||
#endif
|
||||
|
||||
accepted:
|
||||
|
||||
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
|
||||
vh = context->vhost_list;
|
||||
while (vh) {
|
||||
if (!vh->being_destroyed &&
|
||||
vh->ssl_ctx == lws_tls_ctx_from_wsi(wsi)) {
|
||||
lwsl_info("setting wsi to vh %s\n", vh->name);
|
||||
wsi->vhost = vh;
|
||||
break;
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
/* OK, we are accepted... give him some time to negotiate */
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
|
||||
context->timeout_secs);
|
||||
|
||||
if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
|
||||
wsi->mode = LWSCM_RAW;
|
||||
else
|
||||
wsi->mode = LWSCM_HTTP_SERVING;
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (lws_h2_configure_if_upgraded(wsi))
|
||||
goto fail;
|
||||
#endif
|
||||
lwsl_debug("accepted new SSL conn\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1190,46 +1190,24 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
|
|||
#endif
|
||||
|
||||
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
|
||||
(pollfd->revents & LWS_POLLHUP)) {
|
||||
(pollfd->revents & LWS_POLLHUP)) {
|
||||
lwsl_debug("pollhup\n");
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
goto close_and_handled;
|
||||
}
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
if ((wsi->state == LWSS_SHUTDOWN) && lws_is_ssl(wsi) && wsi->ssl) {
|
||||
n = SSL_shutdown(wsi->ssl);
|
||||
lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
|
||||
switch (n) {
|
||||
case 1:
|
||||
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
||||
if (wsi->state == LWSS_SHUTDOWN && lws_is_ssl(wsi) && wsi->ssl) {
|
||||
n = 0;
|
||||
switch (lws_tls_shutdown(wsi)) {
|
||||
case LWS_SSL_CAPABLE_DONE:
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
goto close_and_handled;
|
||||
|
||||
case 0:
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
n = 0;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
goto handled;
|
||||
|
||||
default:
|
||||
n = SSL_get_error(wsi->ssl, n);
|
||||
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("(wants read)\n");
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
n = 0;
|
||||
goto handled;
|
||||
}
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("(wants write)\n");
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
|
||||
n = 0;
|
||||
goto handled;
|
||||
}
|
||||
}
|
||||
|
||||
/* actual error occurred, just close the connection */
|
||||
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
||||
goto close_and_handled;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
976
lib/ssl.c
976
lib/ssl.c
|
@ -1,976 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
/* workaround for mingw */
|
||||
#if !defined(ECONNABORTED)
|
||||
#define ECONNABORTED 103
|
||||
#endif
|
||||
|
||||
int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount)
|
||||
{
|
||||
lws_filepos_t len;
|
||||
lws_fop_flags_t flags = LWS_O_RDONLY;
|
||||
lws_fop_fd_t fops_fd = lws_vfs_file_open(
|
||||
lws_get_fops(context), filename, &flags);
|
||||
int ret = 1;
|
||||
|
||||
if (!fops_fd)
|
||||
return 1;
|
||||
|
||||
len = lws_vfs_get_length(fops_fd);
|
||||
|
||||
*buf = lws_malloc((size_t)len, "lws_alloc_vfs_file");
|
||||
if (!*buf)
|
||||
goto bail;
|
||||
|
||||
if (lws_vfs_file_read(fops_fd, amount, *buf, len))
|
||||
goto bail;
|
||||
|
||||
ret = 0;
|
||||
bail:
|
||||
lws_vfs_file_close(&fops_fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount)
|
||||
{
|
||||
nvs_handle nvh;
|
||||
size_t s;
|
||||
int n = 0;
|
||||
|
||||
ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
|
||||
if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
*buf = lws_malloc(s, "alloc_file");
|
||||
if (!*buf) {
|
||||
n = 2;
|
||||
goto bail;
|
||||
}
|
||||
if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
|
||||
lws_free(*buf);
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
*amount = s;
|
||||
|
||||
bail:
|
||||
nvs_close(nvh);
|
||||
|
||||
return n;
|
||||
}
|
||||
#else
|
||||
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount)
|
||||
{
|
||||
FILE *f;
|
||||
size_t s;
|
||||
int n = 0;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_END) != 0) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
s = ftell(f);
|
||||
if (s == -1) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_SET) != 0) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
*buf = lws_malloc(s, "alloc_file");
|
||||
if (!*buf) {
|
||||
n = 2;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (fread(*buf, s, 1, f) != 1) {
|
||||
lws_free(*buf);
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
*amount = s;
|
||||
|
||||
bail:
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return n;
|
||||
|
||||
}
|
||||
#endif
|
||||
int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount)
|
||||
{
|
||||
uint8_t *pem, *p, *q, *end;
|
||||
lws_filepos_t len;
|
||||
int n;
|
||||
|
||||
n = alloc_file(context, filename, &pem, &len);
|
||||
if (n)
|
||||
return n;
|
||||
|
||||
/* trim the first line */
|
||||
|
||||
p = pem;
|
||||
end = p + len;
|
||||
if (strncmp((char *)p, "-----", 5))
|
||||
goto bail;
|
||||
p += 5;
|
||||
while (p < end && *p != '\n' && *p != '-')
|
||||
p++;
|
||||
|
||||
if (*p != '-')
|
||||
goto bail;
|
||||
|
||||
while (p < end && *p != '\n')
|
||||
p++;
|
||||
|
||||
if (p >= end)
|
||||
goto bail;
|
||||
|
||||
p++;
|
||||
|
||||
/* trim the last line */
|
||||
|
||||
q = end - 2;
|
||||
|
||||
while (q > pem && *q != '\n')
|
||||
q--;
|
||||
|
||||
if (*q != '\n')
|
||||
goto bail;
|
||||
|
||||
*q = '\0';
|
||||
|
||||
*amount = lws_b64_decode_string((char *)p, (char *)pem, len);
|
||||
*buf = pem;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(pem);
|
||||
|
||||
return 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
int openssl_websocket_private_data_index,
|
||||
openssl_SSL_CTX_private_data_index;
|
||||
|
||||
int lws_ssl_get_error(struct lws *wsi, int n)
|
||||
{
|
||||
int m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return 99;
|
||||
|
||||
m = SSL_get_error(wsi->ssl, n);
|
||||
lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* Copies a string describing the code returned by lws_ssl_get_error(),
|
||||
* which may also contain system error information in the case of SSL_ERROR_SYSCALL,
|
||||
* into buf up to len.
|
||||
* Returns a pointer to buf.
|
||||
*
|
||||
* Note: the lws_ssl_get_error() code is *not* an error code that can be passed
|
||||
* to ERR_error_string(),
|
||||
*
|
||||
* ret is the return value originally passed to lws_ssl_get_error(), needed to disambiguate
|
||||
* SYS_ERROR_SYSCALL.
|
||||
*
|
||||
* See man page for SSL_get_error().
|
||||
*
|
||||
* Not thread safe, uses strerror()
|
||||
*/
|
||||
char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) {
|
||||
switch (status) {
|
||||
case SSL_ERROR_NONE: return strncpy(buf, "SSL_ERROR_NONE", len);
|
||||
case SSL_ERROR_ZERO_RETURN: return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
|
||||
case SSL_ERROR_WANT_READ: return strncpy(buf, "SSL_ERROR_WANT_READ", len);
|
||||
case SSL_ERROR_WANT_WRITE: return strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
|
||||
case SSL_ERROR_WANT_CONNECT: return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
|
||||
case SSL_ERROR_WANT_ACCEPT: return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
|
||||
case SSL_ERROR_WANT_X509_LOOKUP: return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len);
|
||||
case SSL_ERROR_SYSCALL:
|
||||
switch (ret) {
|
||||
case 0:
|
||||
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF");
|
||||
return buf;
|
||||
case -1:
|
||||
#ifndef LWS_PLAT_OPTEE
|
||||
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", strerror(errno));
|
||||
#else
|
||||
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno);
|
||||
#endif
|
||||
return buf;
|
||||
default:
|
||||
return strncpy(buf, "SSL_ERROR_SYSCALL", len);
|
||||
}
|
||||
case SSL_ERROR_SSL: return "SSL_ERROR_SSL";
|
||||
default: return "SSL_ERROR_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_elaborate_error(void)
|
||||
{
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
#else
|
||||
char buf[256];
|
||||
u_long err;
|
||||
|
||||
while ((err = ERR_get_error()) != 0) {
|
||||
ERR_error_string_n(err, buf, sizeof(buf));
|
||||
lwsl_info("*** %s\n", buf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
|
||||
static int
|
||||
lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata)
|
||||
{
|
||||
struct lws_context_creation_info * info =
|
||||
(struct lws_context_creation_info *)userdata;
|
||||
|
||||
strncpy(buf, info->ssl_private_key_password, size);
|
||||
buf[size - 1] = '\0';
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info)
|
||||
{
|
||||
if (!info->ssl_private_key_password)
|
||||
return;
|
||||
/*
|
||||
* password provided, set ssl callback and user data
|
||||
* for checking password which will be trigered during
|
||||
* SSL_CTX_use_PrivateKey_file function
|
||||
*/
|
||||
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info);
|
||||
SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_context_init_ssl_library(struct lws_context_creation_info *info)
|
||||
{
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
lwsl_info(" Compiled with CyaSSL support\n");
|
||||
#else
|
||||
lwsl_info(" Compiled with wolfSSL support\n");
|
||||
#endif
|
||||
#else
|
||||
#if defined(LWS_WITH_BORINGSSL)
|
||||
lwsl_info(" Compiled with BoringSSL support\n");
|
||||
#else
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
lwsl_info(" Compiled with MbedTLS support\n");
|
||||
#else
|
||||
lwsl_info(" Compiled with OpenSSL support\n");
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
|
||||
lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* basic openssl init */
|
||||
|
||||
lwsl_info("Doing SSL library init\n");
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
|
||||
openssl_websocket_private_data_index =
|
||||
SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
|
||||
|
||||
openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0,
|
||||
NULL, NULL, NULL, NULL);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ssl_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (!lws_check_opt(vhost->context->options,
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
|
||||
return;
|
||||
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
if (vhost->x509_client_CA)
|
||||
X509_free(vhost->x509_client_CA);
|
||||
#else
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
|
||||
// <= 1.0.1f = old api, 1.0.1g+ = new api
|
||||
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL)
|
||||
ERR_remove_thread_state();
|
||||
#else
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
#endif
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws *wsi, *wsi_next;
|
||||
|
||||
wsi = pt->pending_read_list;
|
||||
while (wsi) {
|
||||
wsi_next = wsi->pending_read_list_next;
|
||||
pt->fds[wsi->position_in_fds_table].revents |=
|
||||
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
|
||||
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
|
||||
return 1;
|
||||
|
||||
wsi = wsi_next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
if (!wsi->pending_read_list_prev &&
|
||||
!wsi->pending_read_list_next &&
|
||||
pt->pending_read_list != wsi)
|
||||
/* we are not on the list */
|
||||
return;
|
||||
|
||||
/* point previous guy's next to our next */
|
||||
if (!wsi->pending_read_list_prev)
|
||||
pt->pending_read_list = wsi->pending_read_list_next;
|
||||
else
|
||||
wsi->pending_read_list_prev->pending_read_list_next =
|
||||
wsi->pending_read_list_next;
|
||||
|
||||
/* point next guy's previous to our previous */
|
||||
if (wsi->pending_read_list_next)
|
||||
wsi->pending_read_list_next->pending_read_list_prev =
|
||||
wsi->pending_read_list_prev;
|
||||
|
||||
wsi->pending_read_list_prev = NULL;
|
||||
wsi->pending_read_list_next = NULL;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n = 0, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
|
||||
|
||||
errno = 0;
|
||||
n = SSL_read(wsi->ssl, buf, len);
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
if (!n && errno == ENOTCONN) {
|
||||
lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
#endif
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!wsi->seen_rx) {
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY,
|
||||
time_in_microseconds() - wsi->accept_start_us);
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
|
||||
wsi->seen_rx = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
|
||||
/* manpage: returning 0 means connection shut down */
|
||||
if (!n) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
|
||||
if (m == SSL_ERROR_ZERO_RETURN ||
|
||||
m == SSL_ERROR_SYSCALL)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_READ\n", __func__);
|
||||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
||||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
||||
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
/*
|
||||
* if it was our buffer that limited what we read,
|
||||
* check if SSL has additional data pending inside SSL buffers.
|
||||
*
|
||||
* Because these won't signal at the network layer with POLLIN
|
||||
* and if we don't realize, this data will sit there forever
|
||||
*/
|
||||
if (n != len)
|
||||
goto bail;
|
||||
if (!wsi->ssl)
|
||||
goto bail;
|
||||
|
||||
if (!SSL_pending(wsi->ssl))
|
||||
goto bail;
|
||||
|
||||
if (wsi->pending_read_list_next)
|
||||
return n;
|
||||
if (wsi->pending_read_list_prev)
|
||||
return n;
|
||||
if (pt->pending_read_list == wsi)
|
||||
return n;
|
||||
|
||||
/* add us to the linked list of guys with pending ssl */
|
||||
if (pt->pending_read_list)
|
||||
pt->pending_read_list->pending_read_list_prev = wsi;
|
||||
|
||||
wsi->pending_read_list_next = pt->pending_read_list;
|
||||
wsi->pending_read_list_prev = NULL;
|
||||
pt->pending_read_list = wsi;
|
||||
|
||||
return n;
|
||||
bail:
|
||||
lws_ssl_remove_wsi_from_buffered_list(wsi);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_pending(struct lws *wsi)
|
||||
{
|
||||
if (!wsi->ssl)
|
||||
return 0;
|
||||
|
||||
return SSL_pending(wsi->ssl);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
int n, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
|
||||
|
||||
n = SSL_write(wsi->ssl, buf, len);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
if (m != SSL_ERROR_SYSCALL) {
|
||||
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_notice("%s: want read\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lws_set_blocking_send(wsi);
|
||||
|
||||
lwsl_notice("%s: want write\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
|
||||
lws_ssl_elaborate_error();
|
||||
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_gate_accepts(struct lws_context *context, int on)
|
||||
{
|
||||
struct lws_vhost *v = context->vhost_list;
|
||||
|
||||
lwsl_info("gating accepts %d\n", on);
|
||||
context->ssl_gate_accepts = !on;
|
||||
#if defined(LWS_WITH_STATS)
|
||||
context->updated = 1;
|
||||
#endif
|
||||
|
||||
while (v) {
|
||||
if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */
|
||||
if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
|
||||
(LWS_POLLIN) * on))
|
||||
lwsl_info("Unable to set accept POLLIN %d\n", on);
|
||||
|
||||
v = v->vhost_next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_info_callback(const SSL *ssl, int where, int ret)
|
||||
{
|
||||
struct lws *wsi;
|
||||
struct lws_context *context;
|
||||
struct lws_ssl_info si;
|
||||
|
||||
context = (struct lws_context *)SSL_CTX_get_ex_data(
|
||||
SSL_get_SSL_CTX(ssl),
|
||||
openssl_SSL_CTX_private_data_index);
|
||||
if (!context)
|
||||
return;
|
||||
wsi = wsi_from_fd(context, SSL_get_fd(ssl));
|
||||
if (!wsi)
|
||||
return;
|
||||
|
||||
if (!(where & wsi->vhost->ssl_info_event_mask))
|
||||
return;
|
||||
|
||||
si.where = where;
|
||||
si.ret = ret;
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_SSL_INFO,
|
||||
wsi->user_space, &si, 0))
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_close(struct lws *wsi)
|
||||
{
|
||||
lws_sockfd_type n;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return 0; /* not handled */
|
||||
|
||||
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
|
||||
/* kill ssl callbacks, becausse we will remove the fd from the
|
||||
* table linking it to the wsi
|
||||
*/
|
||||
if (wsi->vhost->ssl_info_event_mask)
|
||||
SSL_set_info_callback(wsi->ssl, NULL);
|
||||
#endif
|
||||
|
||||
n = SSL_get_fd(wsi->ssl);
|
||||
if (!wsi->socket_is_permanently_unusable)
|
||||
SSL_shutdown(wsi->ssl);
|
||||
compatible_close(n);
|
||||
SSL_free(wsi->ssl);
|
||||
wsi->ssl = NULL;
|
||||
|
||||
if (wsi->context->simultaneous_ssl_restriction &&
|
||||
wsi->context->simultaneous_ssl-- ==
|
||||
wsi->context->simultaneous_ssl_restriction)
|
||||
/* we made space and can do an accept */
|
||||
lws_gate_accepts(wsi->context, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
wsi->context->updated = 1;
|
||||
#endif
|
||||
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
/* leave all wsi close processing to the caller */
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_vhost *vh;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n, m;
|
||||
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
|
||||
BIO *bio;
|
||||
#endif
|
||||
char buf[256];
|
||||
|
||||
(void)buf;
|
||||
|
||||
if (!LWS_SSL_ENABLED(wsi->vhost))
|
||||
return 0;
|
||||
|
||||
switch (wsi->mode) {
|
||||
case LWSCM_SSL_INIT:
|
||||
case LWSCM_SSL_INIT_RAW:
|
||||
if (wsi->ssl)
|
||||
lwsl_err("%s: leaking ssl\n", __func__);
|
||||
if (accept_fd == LWS_SOCK_INVALID)
|
||||
assert(0);
|
||||
if (context->simultaneous_ssl_restriction &&
|
||||
context->simultaneous_ssl >= context->simultaneous_ssl_restriction) {
|
||||
lwsl_notice("unable to deal with SSL connection\n");
|
||||
return 1;
|
||||
}
|
||||
errno = 0;
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
|
||||
if (wsi->ssl == NULL) {
|
||||
lwsl_err("SSL_new failed: %d (errno %d)\n",
|
||||
lws_ssl_get_error(wsi, 0), errno);
|
||||
|
||||
lws_ssl_elaborate_error();
|
||||
if (accept_fd != LWS_SOCK_INVALID)
|
||||
compatible_close(accept_fd);
|
||||
goto fail;
|
||||
}
|
||||
#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);
|
||||
#endif
|
||||
if (context->simultaneous_ssl_restriction &&
|
||||
++context->simultaneous_ssl == context->simultaneous_ssl_restriction)
|
||||
/* that was the last allowed SSL connection */
|
||||
lws_gate_accepts(context, 0);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
context->updated = 1;
|
||||
#endif
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
SSL_set_ex_data(wsi->ssl,
|
||||
openssl_websocket_private_data_index, wsi);
|
||||
#endif
|
||||
SSL_set_fd(wsi->ssl, accept_fd);
|
||||
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
CyaSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#else
|
||||
wolfSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#endif
|
||||
#else
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
lws_plat_set_socket_options(wsi->vhost, accept_fd);
|
||||
#else
|
||||
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
bio = SSL_get_rbio(wsi->ssl);
|
||||
if (bio)
|
||||
BIO_set_nbio(bio, 1); /* nonblocking */
|
||||
else
|
||||
lwsl_notice("NULL rbio\n");
|
||||
bio = SSL_get_wbio(wsi->ssl);
|
||||
if (bio)
|
||||
BIO_set_nbio(bio, 1); /* nonblocking */
|
||||
else
|
||||
lwsl_notice("NULL rbio\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* we are not accepted yet, but we need to enter ourselves
|
||||
* as a live connection. That way we can retry when more
|
||||
* pieces come if we're not sorted yet
|
||||
*/
|
||||
|
||||
if (wsi->mode == LWSCM_SSL_INIT)
|
||||
wsi->mode = LWSCM_SSL_ACK_PENDING;
|
||||
else
|
||||
wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;
|
||||
|
||||
if (insert_wsi_socket_into_fds(context, wsi)) {
|
||||
lwsl_err("%s: failed to insert into fds\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
|
||||
context->timeout_secs);
|
||||
|
||||
lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n");
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWSCM_SSL_ACK_PENDING:
|
||||
case LWSCM_SSL_ACK_PENDING_RAW:
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_err("%s: lws_change_pollfd failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lws_latency_pre(context, wsi);
|
||||
|
||||
if (wsi->vhost->allow_non_ssl_on_ssl_port) {
|
||||
|
||||
n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
|
||||
context->pt_serv_buf_size, MSG_PEEK);
|
||||
|
||||
/*
|
||||
* optionally allow non-SSL connect on SSL listening socket
|
||||
* This is disabled by default, if enabled it goes around any
|
||||
* SSL-level access control (eg, client-side certs) so leave
|
||||
* it disabled unless you know it's not a problem for you
|
||||
*/
|
||||
|
||||
if (n >= 1 && pt->serv_buf[0] >= ' ') {
|
||||
/*
|
||||
* TLS content-type for Handshake is 0x16, and
|
||||
* for ChangeCipherSpec Record, it's 0x14
|
||||
*
|
||||
* A non-ssl session will start with the HTTP
|
||||
* method in ASCII. If we see it's not a legit
|
||||
* SSL handshake kill the SSL for this
|
||||
* connection and try to handle as a HTTP
|
||||
* connection upgrade directly.
|
||||
*/
|
||||
wsi->use_ssl = 0;
|
||||
|
||||
SSL_shutdown(wsi->ssl);
|
||||
SSL_free(wsi->ssl);
|
||||
wsi->ssl = NULL;
|
||||
if (lws_check_opt(context->options,
|
||||
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
|
||||
wsi->redirect_to_https = 1;
|
||||
goto accepted;
|
||||
}
|
||||
if (!n) /*
|
||||
* connection is gone, or nothing to read
|
||||
* if it's gone, we will timeout on
|
||||
* PENDING_TIMEOUT_SSL_ACCEPT
|
||||
*/
|
||||
break;
|
||||
if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK)) {
|
||||
/*
|
||||
* well, we get no way to know ssl or not
|
||||
* so go around again waiting for something
|
||||
* to come and give us a hint, or timeout the
|
||||
* connection.
|
||||
*/
|
||||
m = SSL_ERROR_WANT_READ;
|
||||
goto go_again;
|
||||
}
|
||||
}
|
||||
|
||||
/* normal SSL connection processing path */
|
||||
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!wsi->accept_start_us)
|
||||
wsi->accept_start_us = time_in_microseconds();
|
||||
#endif
|
||||
errno = 0;
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
|
||||
n = SSL_accept(wsi->ssl);
|
||||
lws_latency(context, wsi,
|
||||
"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
|
||||
lwsl_info("SSL_accept says %d\n", n);
|
||||
if (n == 1)
|
||||
goto accepted;
|
||||
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
if (m == SSL_ERROR_SYSCALL && errno == 11)
|
||||
m = SSL_ERROR_WANT_READ;
|
||||
#endif
|
||||
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
|
||||
goto failed;
|
||||
|
||||
go_again:
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
|
||||
lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lwsl_info("SSL_ERROR_WANT_READ\n");
|
||||
break;
|
||||
}
|
||||
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
||||
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
|
||||
lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
failed:
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
|
||||
lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
|
||||
lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
|
||||
lws_ssl_elaborate_error();
|
||||
goto fail;
|
||||
|
||||
accepted:
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
|
||||
time_in_microseconds() - wsi->accept_start_us);
|
||||
wsi->accept_start_us = time_in_microseconds();
|
||||
#endif
|
||||
|
||||
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
|
||||
vh = context->vhost_list;
|
||||
while (vh) {
|
||||
if (!vh->being_destroyed &&
|
||||
vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
|
||||
lwsl_info("setting wsi to vh %s\n", vh->name);
|
||||
wsi->vhost = vh;
|
||||
break;
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
/* OK, we are accepted... give him some time to negotiate */
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
|
||||
context->timeout_secs);
|
||||
|
||||
if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
|
||||
wsi->mode = LWSCM_RAW;
|
||||
else
|
||||
wsi->mode = LWSCM_HTTP_SERVING;
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (lws_h2_configure_if_upgraded(wsi))
|
||||
goto fail;
|
||||
#endif
|
||||
lwsl_debug("accepted new SSL conn\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_context_destroy(struct lws_context *context)
|
||||
{
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
|
||||
// <= 1.0.1f = old api, 1.0.1g+ = new api
|
||||
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL)
|
||||
ERR_remove_thread_state();
|
||||
#else
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
#endif
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
#endif
|
||||
}
|
180
lib/tls/mbedtls/client.c
Normal file
180
lib/tls/mbedtls/client.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* libwebsockets - mbedtls-specific client TLS code
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
static int
|
||||
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ssl_client_bio_create(struct lws *wsi)
|
||||
{
|
||||
X509_VERIFY_PARAM *param;
|
||||
char hostname[128], *p;
|
||||
|
||||
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
|
||||
_WSI_TOKEN_CLIENT_HOST) <= 0) {
|
||||
lwsl_err("%s: Unable to get hostname\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++;
|
||||
}
|
||||
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
|
||||
if (!wsi->ssl)
|
||||
return -1;
|
||||
|
||||
if (wsi->vhost->ssl_info_event_mask)
|
||||
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
|
||||
|
||||
if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
|
||||
param = SSL_get0_param(wsi->ssl);
|
||||
/* Enable automatic hostname checks */
|
||||
// X509_VERIFY_PARAM_set_hostflags(param,
|
||||
// X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* use server name indication (SNI), if supported,
|
||||
* when establishing connection
|
||||
*/
|
||||
if (wsi->vhost->x509_client_CA)
|
||||
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER,
|
||||
OpenSSL_client_verify_callback);
|
||||
else
|
||||
SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE,
|
||||
OpenSSL_client_verify_callback);
|
||||
|
||||
SSL_set_fd(wsi->ssl, wsi->desc.sockfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ERR_get_error(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum lws_ssl_capable_status
|
||||
lws_tls_client_connect(struct lws *wsi)
|
||||
{
|
||||
int m, n = SSL_connect(wsi->ssl);
|
||||
|
||||
if (n == 1)
|
||||
return LWS_SSL_CAPABLE_DONE;
|
||||
|
||||
m = SSL_get_error(wsi->ssl, n);
|
||||
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl))
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
||||
|
||||
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl))
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
|
||||
|
||||
if (!n) /* we don't know what he wants, but he says to retry */
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_client_confirm_peer_cert(struct lws *wsi)
|
||||
{
|
||||
X509 *peer = SSL_get_peer_certificate(wsi->ssl);
|
||||
|
||||
if (!peer) {
|
||||
lwsl_info("peer did not provide cert\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
lwsl_info("peer provided cert\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_client_create_vhost_context(struct lws_vhost *vh,
|
||||
struct lws_context_creation_info *info,
|
||||
const char *cipher_list,
|
||||
const char *ca_filepath,
|
||||
const char *cert_filepath,
|
||||
const char *private_key_filepath)
|
||||
{
|
||||
X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len);
|
||||
SSL_METHOD *method = (SSL_METHOD *)TLS_client_method();
|
||||
unsigned long error;
|
||||
lws_filepos_t len;
|
||||
uint8_t *buf;
|
||||
|
||||
if (!method) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl method %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vh->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
/* create context */
|
||||
vh->ssl_client_ctx = SSL_CTX_new(method);
|
||||
if (!vh->ssl_client_ctx) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl context %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vh->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ca_filepath)
|
||||
return 0;
|
||||
|
||||
if (alloc_file(vh->context, ca_filepath, &buf, &len)) {
|
||||
lwsl_err("Load CA cert file %s failed\n", ca_filepath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vh->x509_client_CA = d2i_X509(NULL, buf, len);
|
||||
free(buf);
|
||||
if (!vh->x509_client_CA) {
|
||||
lwsl_err("client CA: x509 parse failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_CTX_add_client_CA(vh->ssl_client_ctx, vh->x509_client_CA);
|
||||
|
||||
lwsl_notice("client loaded CA for verification %s\n", ca_filepath);
|
||||
|
||||
return 0;
|
||||
}
|
317
lib/tls/mbedtls/server.c
Normal file
317
lib/tls/mbedtls/server.c
Normal file
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* libwebsockets - mbedTLS-specific server functions
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
int
|
||||
lws_tls_server_client_cert_verify_config(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vh)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount)
|
||||
{
|
||||
nvs_handle nvh;
|
||||
size_t s;
|
||||
int n = 0;
|
||||
|
||||
ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
|
||||
if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
*buf = lws_malloc(s, "alloc_file");
|
||||
if (!*buf) {
|
||||
n = 2;
|
||||
goto bail;
|
||||
}
|
||||
if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
|
||||
lws_free(*buf);
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
*amount = s;
|
||||
|
||||
bail:
|
||||
nvs_close(nvh);
|
||||
|
||||
return n;
|
||||
}
|
||||
#else
|
||||
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount)
|
||||
{
|
||||
FILE *f;
|
||||
size_t s;
|
||||
int n = 0;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_END) != 0) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
s = ftell(f);
|
||||
if (s == -1) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_SET) != 0) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
*buf = lws_malloc(s, "alloc_file");
|
||||
if (!*buf) {
|
||||
n = 2;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (fread(*buf, s, 1, f) != 1) {
|
||||
lws_free(*buf);
|
||||
n = 1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
*amount = s;
|
||||
|
||||
bail:
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return n;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
alloc_pem_to_der_file(struct lws_context *context, const char *filename,
|
||||
uint8_t **buf, lws_filepos_t *amount)
|
||||
{
|
||||
uint8_t *pem, *p, *q, *end;
|
||||
lws_filepos_t len;
|
||||
int n;
|
||||
|
||||
n = alloc_file(context, filename, &pem, &len);
|
||||
if (n)
|
||||
return n;
|
||||
|
||||
/* trim the first line */
|
||||
|
||||
p = pem;
|
||||
end = p + len;
|
||||
if (strncmp((char *)p, "-----", 5))
|
||||
goto bail;
|
||||
p += 5;
|
||||
while (p < end && *p != '\n' && *p != '-')
|
||||
p++;
|
||||
|
||||
if (*p != '-')
|
||||
goto bail;
|
||||
|
||||
while (p < end && *p != '\n')
|
||||
p++;
|
||||
|
||||
if (p >= end)
|
||||
goto bail;
|
||||
|
||||
p++;
|
||||
|
||||
/* trim the last line */
|
||||
|
||||
q = end - 2;
|
||||
|
||||
while (q > pem && *q != '\n')
|
||||
q--;
|
||||
|
||||
if (*q != '\n')
|
||||
goto bail;
|
||||
|
||||
*q = '\0';
|
||||
|
||||
*amount = lws_b64_decode_string((char *)p, (char *)pem, len);
|
||||
*buf = pem;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(pem);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost, struct lws *wsi)
|
||||
{
|
||||
const SSL_METHOD *method = TLS_server_method();
|
||||
uint8_t *p;
|
||||
lws_filepos_t flen;
|
||||
int err;
|
||||
|
||||
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
|
||||
if (!vhost->ssl_ctx) {
|
||||
lwsl_err("problem creating ssl context\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!vhost->use_ssl || !info->ssl_cert_filepath)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The user code can choose to either pass the cert and
|
||||
* key filepaths using the info members like this, or it can
|
||||
* leave them NULL; force the vhost SSL_CTX init using the info
|
||||
* options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
|
||||
* set up the cert himself using the user callback
|
||||
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
|
||||
* happened just above and has the vhost SSL_CTX * in the user
|
||||
* parameter.
|
||||
*/
|
||||
if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
|
||||
&flen)) {
|
||||
lwsl_err("couldn't find cert file %s\n",
|
||||
info->ssl_cert_filepath);
|
||||
|
||||
return 1;
|
||||
}
|
||||
err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
|
||||
if (!err) {
|
||||
lwsl_err("Problem loading cert\n");
|
||||
return 1;
|
||||
}
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
free(p);
|
||||
p = NULL;
|
||||
#endif
|
||||
|
||||
if (info->ssl_private_key_filepath) {
|
||||
if (alloc_pem_to_der_file(vhost->context,
|
||||
info->ssl_private_key_filepath,
|
||||
&p, &flen)) {
|
||||
lwsl_err("couldn't find cert file %s\n",
|
||||
info->ssl_cert_filepath);
|
||||
|
||||
return 1;
|
||||
}
|
||||
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
|
||||
if (!err) {
|
||||
lwsl_err("Problem loading key\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
free(p);
|
||||
p = NULL;
|
||||
#endif
|
||||
|
||||
if (!info->ssl_private_key_filepath && vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
|
||||
vhost->ssl_ctx, NULL, 0)) {
|
||||
lwsl_err("ssl private key not set\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd)
|
||||
{
|
||||
errno = 0;
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
|
||||
if (wsi->ssl == NULL) {
|
||||
lwsl_err("SSL_new failed: errno %d\n", errno);
|
||||
|
||||
lws_ssl_elaborate_error();
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_set_fd(wsi->ssl, accept_fd);
|
||||
|
||||
if (wsi->vhost->ssl_info_event_mask)
|
||||
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_server_abort_connection(struct lws *wsi)
|
||||
{
|
||||
lws_tls_shutdown(wsi);
|
||||
SSL_free(wsi->ssl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum lws_ssl_capable_status
|
||||
lws_tls_server_accept(struct lws *wsi)
|
||||
{
|
||||
int m, n = SSL_accept(wsi->ssl);
|
||||
|
||||
if (n == 1)
|
||||
return LWS_SSL_CAPABLE_DONE;
|
||||
|
||||
m = SSL_get_error(wsi->ssl, n);
|
||||
|
||||
// mbedtls wrapper only
|
||||
if (m == SSL_ERROR_SYSCALL && errno == 11)
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
||||
|
||||
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
|
||||
lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
lwsl_info("SSL_ERROR_WANT_READ\n");
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
||||
}
|
||||
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
||||
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
|
||||
lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
|
||||
}
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
|
325
lib/tls/mbedtls/ssl.c
Normal file
325
lib/tls/mbedtls/ssl.c
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* libwebsockets - mbedTLS-specific lws apis
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
void
|
||||
lws_ssl_elaborate_error(void)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
lws_context_init_ssl_library(struct lws_context_creation_info *info)
|
||||
{
|
||||
lwsl_info(" Compiled with MbedTLS support\n");
|
||||
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
|
||||
lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ssl_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (!lws_check_opt(vhost->context->options,
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
|
||||
return;
|
||||
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
|
||||
if (vhost->x509_client_CA)
|
||||
X509_free(vhost->x509_client_CA);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n = 0, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
|
||||
|
||||
errno = 0;
|
||||
n = SSL_read(wsi->ssl, buf, len);
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
if (!n && errno == ENOTCONN) {
|
||||
lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
#endif
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!wsi->seen_rx) {
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_MS_SSL_RX_DELAY,
|
||||
time_in_microseconds() - wsi->accept_start_us);
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
|
||||
wsi->seen_rx = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
|
||||
/* manpage: returning 0 means connection shut down */
|
||||
if (!n) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
m = SSL_get_error(wsi->ssl, n);
|
||||
lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
|
||||
if (m == SSL_ERROR_ZERO_RETURN ||
|
||||
m == SSL_ERROR_SYSCALL)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_READ\n", __func__);
|
||||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
||||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
||||
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
/*
|
||||
* if it was our buffer that limited what we read,
|
||||
* check if SSL has additional data pending inside SSL buffers.
|
||||
*
|
||||
* Because these won't signal at the network layer with POLLIN
|
||||
* and if we don't realize, this data will sit there forever
|
||||
*/
|
||||
if (n != len)
|
||||
goto bail;
|
||||
if (!wsi->ssl)
|
||||
goto bail;
|
||||
|
||||
if (!SSL_pending(wsi->ssl))
|
||||
goto bail;
|
||||
|
||||
if (wsi->pending_read_list_next)
|
||||
return n;
|
||||
if (wsi->pending_read_list_prev)
|
||||
return n;
|
||||
if (pt->pending_read_list == wsi)
|
||||
return n;
|
||||
|
||||
/* add us to the linked list of guys with pending ssl */
|
||||
if (pt->pending_read_list)
|
||||
pt->pending_read_list->pending_read_list_prev = wsi;
|
||||
|
||||
wsi->pending_read_list_next = pt->pending_read_list;
|
||||
wsi->pending_read_list_prev = NULL;
|
||||
pt->pending_read_list = wsi;
|
||||
|
||||
return n;
|
||||
bail:
|
||||
lws_ssl_remove_wsi_from_buffered_list(wsi);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_pending(struct lws *wsi)
|
||||
{
|
||||
if (!wsi->ssl)
|
||||
return 0;
|
||||
|
||||
return SSL_pending(wsi->ssl);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
int n, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
|
||||
|
||||
n = SSL_write(wsi->ssl, buf, len);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
m = SSL_get_error(wsi->ssl, n);
|
||||
if (m != SSL_ERROR_SYSCALL) {
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
|
||||
lwsl_notice("%s: want read\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
|
||||
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
|
||||
lws_set_blocking_send(wsi);
|
||||
|
||||
lwsl_notice("%s: want write\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_debug("%s failed: %d\n",__func__, m);
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
int openssl_SSL_CTX_private_data_index;
|
||||
|
||||
void
|
||||
lws_ssl_info_callback(const SSL *ssl, int where, int ret)
|
||||
{
|
||||
struct lws *wsi;
|
||||
struct lws_context *context;
|
||||
struct lws_ssl_info si;
|
||||
|
||||
context = (struct lws_context *)SSL_CTX_get_ex_data(
|
||||
SSL_get_SSL_CTX(ssl),
|
||||
openssl_SSL_CTX_private_data_index);
|
||||
if (!context)
|
||||
return;
|
||||
wsi = wsi_from_fd(context, SSL_get_fd(ssl));
|
||||
if (!wsi)
|
||||
return;
|
||||
|
||||
if (!(where & wsi->vhost->ssl_info_event_mask))
|
||||
return;
|
||||
|
||||
si.where = where;
|
||||
si.ret = ret;
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_SSL_INFO,
|
||||
wsi->user_space, &si, 0))
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_close(struct lws *wsi)
|
||||
{
|
||||
lws_sockfd_type n;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return 0; /* not handled */
|
||||
|
||||
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
|
||||
/* kill ssl callbacks, becausse we will remove the fd from the
|
||||
* table linking it to the wsi
|
||||
*/
|
||||
if (wsi->vhost->ssl_info_event_mask)
|
||||
SSL_set_info_callback(wsi->ssl, NULL);
|
||||
#endif
|
||||
|
||||
n = SSL_get_fd(wsi->ssl);
|
||||
if (!wsi->socket_is_permanently_unusable)
|
||||
SSL_shutdown(wsi->ssl);
|
||||
compatible_close(n);
|
||||
SSL_free(wsi->ssl);
|
||||
wsi->ssl = NULL;
|
||||
|
||||
if (wsi->context->simultaneous_ssl_restriction &&
|
||||
wsi->context->simultaneous_ssl-- ==
|
||||
wsi->context->simultaneous_ssl_restriction)
|
||||
/* we made space and can do an accept */
|
||||
lws_gate_accepts(wsi->context, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
wsi->context->updated = 1;
|
||||
#endif
|
||||
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_context_destroy(struct lws_context *context)
|
||||
{
|
||||
}
|
||||
|
||||
lws_tls_ctx *
|
||||
lws_tls_ctx_from_wsi(struct lws *wsi)
|
||||
{
|
||||
return SSL_get_SSL_CTX(wsi->ssl);
|
||||
}
|
||||
|
||||
enum lws_ssl_capable_status
|
||||
lws_tls_shutdown(struct lws *wsi)
|
||||
{
|
||||
int n = SSL_shutdown(wsi->ssl);
|
||||
|
||||
lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
|
||||
|
||||
switch (n) {
|
||||
case 1: /* successful completion */
|
||||
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
||||
return LWS_SSL_CAPABLE_DONE;
|
||||
|
||||
case 0: /* needs a retry */
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
|
||||
default: /* fatal error, or WANT */
|
||||
n = SSL_get_error(wsi->ssl, n);
|
||||
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("(wants read)\n");
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
||||
}
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("(wants write)\n");
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
|
||||
}
|
||||
}
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
}
|
384
lib/tls/openssl/client.c
Normal file
384
lib/tls/openssl/client.c
Normal file
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* libwebsockets - openSSL-specific client tls code
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
extern int openssl_websocket_private_data_index,
|
||||
openssl_SSL_CTX_private_data_index;
|
||||
|
||||
#if !defined(USE_WOLFSSL)
|
||||
|
||||
static int
|
||||
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
SSL *ssl;
|
||||
int n;
|
||||
struct lws *wsi;
|
||||
|
||||
/* keep old behaviour accepting self-signed server certs */
|
||||
if (!preverify_ok) {
|
||||
int err = X509_STORE_CTX_get_error(x509_ctx);
|
||||
|
||||
if (err != X509_V_OK) {
|
||||
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);
|
||||
|
||||
if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
|
||||
err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
|
||||
wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
|
||||
lwsl_notice("accepting self-signed certificate (verify_callback)\n");
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
|
||||
return 1; // ok
|
||||
} else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
|
||||
err == X509_V_ERR_CERT_HAS_EXPIRED) &&
|
||||
wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) {
|
||||
if (err == X509_V_ERR_CERT_NOT_YET_VALID)
|
||||
lwsl_notice("accepting not yet valid certificate (verify_callback)\n");
|
||||
else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
|
||||
lwsl_notice("accepting expired certificate (verify_callback)\n");
|
||||
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
|
||||
return 1; // ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
n = lws_get_context_protocol(wsi->context, 0).callback(wsi,
|
||||
LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION,
|
||||
x509_ctx, ssl, preverify_ok);
|
||||
|
||||
/* 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);
|
||||
|
||||
if (err != X509_V_OK) { /* cert validation error was not handled in callback */
|
||||
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
|
||||
const char* msg = X509_verify_cert_error_string(err);
|
||||
lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth);
|
||||
return preverify_ok; // not ok
|
||||
}
|
||||
}
|
||||
/* convert callback return code from 0 = OK to verify callback return value 1 = OK */
|
||||
return !n;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_ssl_client_bio_create(struct lws *wsi)
|
||||
{
|
||||
#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
|
||||
X509_VERIFY_PARAM *param;
|
||||
#endif
|
||||
char hostname[128], *p;
|
||||
|
||||
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
|
||||
_WSI_TOKEN_CLIENT_HOST) <= 0) {
|
||||
lwsl_err("%s: Unable to get hostname\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++;
|
||||
}
|
||||
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
|
||||
if (!wsi->ssl) {
|
||||
lwsl_err("SSL_new failed: %s\n",
|
||||
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
|
||||
lws_ssl_elaborate_error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#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);
|
||||
#endif
|
||||
|
||||
#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
|
||||
if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
|
||||
param = SSL_get0_param(wsi->ssl);
|
||||
/* Enable automatic hostname checks */
|
||||
X509_VERIFY_PARAM_set_hostflags(param,
|
||||
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(USE_WOLFSSL)
|
||||
#ifndef USE_OLD_CYASSL
|
||||
/* OpenSSL_client_verify_callback will be called @ SSL_connect() */
|
||||
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(USE_WOLFSSL)
|
||||
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
#endif
|
||||
/*
|
||||
* use server name indication (SNI), if supported,
|
||||
* when establishing connection
|
||||
*/
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
#ifdef CYASSL_SNI_HOST_NAME
|
||||
CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname));
|
||||
#endif
|
||||
#else
|
||||
#ifdef WOLFSSL_SNI_HOST_NAME
|
||||
wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname));
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||
SSL_set_tlsext_host_name(wsi->ssl, hostname);
|
||||
#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
|
||||
if (wsi->use_ssl == 2)
|
||||
CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
|
||||
#else
|
||||
if (wsi->use_ssl == 2)
|
||||
wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
|
||||
#endif
|
||||
#endif /* USE_WOLFSSL */
|
||||
|
||||
wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
|
||||
SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
|
||||
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
CyaSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#else
|
||||
wolfSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#endif
|
||||
#else
|
||||
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
|
||||
#endif
|
||||
|
||||
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum lws_ssl_capable_status
|
||||
lws_tls_client_connect(struct lws *wsi)
|
||||
{
|
||||
int m, n = SSL_connect(wsi->ssl);
|
||||
|
||||
if (n == 1)
|
||||
return LWS_SSL_CAPABLE_DONE;
|
||||
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl))
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
||||
|
||||
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl))
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
|
||||
|
||||
if (!n) /* we don't know what he wants, but he says to retry */
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_client_confirm_peer_cert(struct lws *wsi)
|
||||
{
|
||||
#ifndef USE_WOLFSSL
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
char *p = (char *)&pt->serv_buf[0];
|
||||
char *sb = p;
|
||||
int n;
|
||||
|
||||
lws_latency_pre(wsi->context, wsi);
|
||||
n = SSL_get_verify_result(wsi->ssl);
|
||||
lws_latency(wsi->context, wsi,
|
||||
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
|
||||
|
||||
lwsl_debug("get_verify says %d\n", n);
|
||||
|
||||
if (n != X509_V_OK) {
|
||||
if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
|
||||
n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
|
||||
(wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
|
||||
lwsl_notice("accepting self-signed certificate\n");
|
||||
} else if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
|
||||
n == X509_V_ERR_CERT_HAS_EXPIRED) &&
|
||||
(wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) {
|
||||
lwsl_notice("accepting expired certificate\n");
|
||||
} else if (n == X509_V_ERR_CERT_NOT_YET_VALID) {
|
||||
lwsl_notice("Cert is from the future... "
|
||||
"probably our clock... accepting...\n");
|
||||
} else {
|
||||
lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
|
||||
n, ERR_error_string(n, sb));
|
||||
lws_ssl_elaborate_error();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* USE_WOLFSSL */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_client_create_vhost_context(struct lws_vhost *vh,
|
||||
struct lws_context_creation_info *info,
|
||||
const char *cipher_list,
|
||||
const char *ca_filepath,
|
||||
const char *cert_filepath,
|
||||
const char *private_key_filepath)
|
||||
{
|
||||
SSL_METHOD *method;
|
||||
unsigned long error;
|
||||
int n;
|
||||
|
||||
/* 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) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl method %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vh->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
/* create context */
|
||||
vh->ssl_client_ctx = SSL_CTX_new(method);
|
||||
if (!vh->ssl_client_ctx) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl context %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vh->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
SSL_CTX_set_options(vh->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
|
||||
SSL_CTX_set_options(vh->ssl_client_ctx,
|
||||
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
|
||||
if (cipher_list)
|
||||
SSL_CTX_set_cipher_list(vh->ssl_client_ctx, cipher_list);
|
||||
|
||||
#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 */
|
||||
SSL_CTX_set_default_verify_paths(vh->ssl_client_ctx);
|
||||
#endif
|
||||
|
||||
/* openssl init for cert verification (for client sockets) */
|
||||
if (!ca_filepath) {
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
vh->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS))
|
||||
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);
|
||||
} else
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
vh->ssl_client_ctx, ca_filepath, NULL)) {
|
||||
lwsl_err(
|
||||
"Unable to load SSL Client certs "
|
||||
"file from %s -- client ssl isn't "
|
||||
"going to work\n", ca_filepath);
|
||||
lws_ssl_elaborate_error();
|
||||
}
|
||||
else
|
||||
lwsl_info("loaded ssl_ca_filepath\n");
|
||||
|
||||
/*
|
||||
* callback allowing user code to load extra verification certs
|
||||
* helping the client to verify server identity
|
||||
*/
|
||||
|
||||
/* support for client-side certificate authentication */
|
||||
if (cert_filepath) {
|
||||
lwsl_notice("%s: doing cert filepath\n", __func__);
|
||||
n = SSL_CTX_use_certificate_chain_file(vh->ssl_client_ctx,
|
||||
cert_filepath);
|
||||
if (n < 1) {
|
||||
lwsl_err("problem %d getting cert '%s'\n", n,
|
||||
cert_filepath);
|
||||
lws_ssl_elaborate_error();
|
||||
return 1;
|
||||
}
|
||||
lwsl_notice("Loaded client cert %s\n", cert_filepath);
|
||||
}
|
||||
if (private_key_filepath) {
|
||||
lwsl_notice("%s: doing private key filepath\n", __func__);
|
||||
lws_ssl_bind_passphrase(vh->ssl_client_ctx, info);
|
||||
/* set the private key from KeyFile */
|
||||
if (SSL_CTX_use_PrivateKey_file(vh->ssl_client_ctx,
|
||||
private_key_filepath, SSL_FILETYPE_PEM) != 1) {
|
||||
lwsl_err("use_PrivateKey_file '%s'\n",
|
||||
private_key_filepath);
|
||||
lws_ssl_elaborate_error();
|
||||
return 1;
|
||||
}
|
||||
lwsl_notice("Loaded client cert private key %s\n",
|
||||
private_key_filepath);
|
||||
|
||||
/* verify private key */
|
||||
if (!SSL_CTX_check_private_key(vh->ssl_client_ctx)) {
|
||||
lwsl_err("Private SSL key doesn't match cert\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
417
lib/tls/openssl/server.c
Normal file
417
lib/tls/openssl/server.c
Normal file
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* libwebsockets - OpenSSL-specific server functions
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
extern int openssl_websocket_private_data_index,
|
||||
openssl_SSL_CTX_private_data_index;
|
||||
|
||||
static int
|
||||
OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
||||
{
|
||||
SSL *ssl;
|
||||
int n;
|
||||
struct lws *wsi;
|
||||
|
||||
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
|
||||
SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
|
||||
/*
|
||||
* !!! nasty openssl requires the index to come as a library-scope
|
||||
* static
|
||||
*/
|
||||
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
|
||||
|
||||
n = wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
|
||||
x509_ctx, ssl, preverify_ok);
|
||||
|
||||
/* convert return code from 0 = OK to 1 = OK */
|
||||
return !n;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_server_client_cert_verify_config(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vh)
|
||||
{
|
||||
int verify_options = SSL_VERIFY_PEER;
|
||||
|
||||
/* as a server, are we requiring clients to identify themselves? */
|
||||
|
||||
if (!lws_check_opt(info->options,
|
||||
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT))
|
||||
return 0;
|
||||
|
||||
if (!lws_check_opt(info->options,
|
||||
LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
|
||||
verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
|
||||
SSL_CTX_set_session_id_context(vh->ssl_ctx, (uint8_t *)vh->context,
|
||||
sizeof(void *));
|
||||
|
||||
/* absolutely require the client cert */
|
||||
SSL_CTX_set_verify(vh->ssl_ctx, verify_options, OpenSSL_verify_callback);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT)
|
||||
static int
|
||||
lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
|
||||
{
|
||||
struct lws_context *context = (struct lws_context *)arg;
|
||||
struct lws_vhost *vhost, *vh;
|
||||
const char *servername;
|
||||
|
||||
if (!ssl)
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
|
||||
/*
|
||||
* We can only get ssl accepted connections by using a vhost's ssl_ctx
|
||||
* find out which listening one took us and only match vhosts on the
|
||||
* same port.
|
||||
*/
|
||||
vh = context->vhost_list;
|
||||
while (vh) {
|
||||
if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
|
||||
break;
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
if (!vh) {
|
||||
assert(vh); /* can't match the incoming vh? */
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||
if (!servername) {
|
||||
/* the client doesn't know what hostname it wants */
|
||||
lwsl_info("SNI: Unknown ServerName: %s\n", servername);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
vhost = lws_select_vhost(context, vh->listen_port, servername);
|
||||
if (!vhost) {
|
||||
lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
|
||||
|
||||
/* select the ssl ctx from the selected vhost for this conn */
|
||||
SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost,
|
||||
struct lws *wsi)
|
||||
{
|
||||
#if defined(LWS_HAVE_OPENSSL_ECDH_H)
|
||||
const char *ecdh_curve = "prime256v1";
|
||||
EC_KEY *ecdh, *EC_key = NULL;
|
||||
EVP_PKEY *pkey;
|
||||
X509 *x = NULL;
|
||||
int ecdh_nid;
|
||||
int KeyType;
|
||||
#if defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS)
|
||||
STACK_OF(X509) *extra_certs = NULL;
|
||||
#endif
|
||||
#endif
|
||||
SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method();
|
||||
unsigned long error;
|
||||
int n;
|
||||
|
||||
if (!method) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl method %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
|
||||
if (!vhost->ssl_ctx) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem creating ssl context %lu: %s\n",
|
||||
error, ERR_error_string(error,
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_CTX_set_ex_data(vhost->ssl_ctx, openssl_SSL_CTX_private_data_index,
|
||||
(char *)vhost->context);
|
||||
/* Disable SSLv2 and SSLv3 */
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
|
||||
if (info->ssl_cipher_list)
|
||||
SSL_CTX_set_cipher_list(vhost->ssl_ctx, info->ssl_cipher_list);
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
|
||||
SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
|
||||
lws_ssl_server_name_cb);
|
||||
SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, vhost->context);
|
||||
#endif
|
||||
|
||||
if (info->ssl_ca_filepath &&
|
||||
!SSL_CTX_load_verify_locations(vhost->ssl_ctx,
|
||||
info->ssl_ca_filepath, NULL)) {
|
||||
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (info->ssl_options_set)
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set);
|
||||
|
||||
/* SSL_clear_options introduced in 0.9.8m */
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
|
||||
if (info->ssl_options_clear)
|
||||
SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear);
|
||||
#endif
|
||||
|
||||
lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx));
|
||||
if (!vhost->use_ssl || !info->ssl_cert_filepath)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The user code can choose to either pass the cert and
|
||||
* key filepaths using the info members like this, or it can
|
||||
* leave them NULL; force the vhost SSL_CTX init using the info
|
||||
* options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
|
||||
* set up the cert himself using the user callback
|
||||
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
|
||||
* happened just above and has the vhost SSL_CTX * in the user
|
||||
* parameter.
|
||||
*/
|
||||
/* set the local certificate from CertFile */
|
||||
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
|
||||
info->ssl_cert_filepath);
|
||||
if (n != 1) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("problem getting cert '%s' %lu: %s\n",
|
||||
info->ssl_cert_filepath, error, ERR_error_string(error,
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
|
||||
return 1;
|
||||
}
|
||||
lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
|
||||
|
||||
if (info->ssl_private_key_filepath != NULL) {
|
||||
/* set the private key from KeyFile */
|
||||
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
|
||||
info->ssl_private_key_filepath,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("ssl problem getting key '%s' %lu: %s\n",
|
||||
info->ssl_private_key_filepath, error,
|
||||
ERR_error_string(error,
|
||||
(char *)vhost->context->pt[0].serv_buf));
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
if (vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
|
||||
vhost->ssl_ctx, NULL, 0)) {
|
||||
lwsl_err("ssl private key not set\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* verify private key */
|
||||
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
|
||||
lwsl_err("Private SSL key doesn't match cert\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(LWS_HAVE_OPENSSL_ECDH_H)
|
||||
if (info->ecdh_curve)
|
||||
ecdh_curve = info->ecdh_curve;
|
||||
|
||||
ecdh_nid = OBJ_sn2nid(ecdh_curve);
|
||||
if (NID_undef == ecdh_nid) {
|
||||
lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
|
||||
if (NULL == ecdh) {
|
||||
lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
|
||||
|
||||
lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
|
||||
|
||||
if (lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH))
|
||||
lwsl_notice(" Using ECDH certificate support\n");
|
||||
|
||||
/* Get X509 certificate from ssl context */
|
||||
#if !defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS)
|
||||
x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0);
|
||||
#else
|
||||
SSL_CTX_get_extra_chain_certs_only(vhost->ssl_ctx, &extra_certs);
|
||||
if (extra_certs)
|
||||
x = sk_X509_value(extra_certs, 0);
|
||||
else
|
||||
lwsl_err("%s: no extra certs\n", __func__);
|
||||
#endif
|
||||
if (!x) {
|
||||
lwsl_err("%s: x is NULL\n", __func__);
|
||||
return 0; // !!!
|
||||
}
|
||||
/* Get the public key from certificate */
|
||||
pkey = X509_get_pubkey(x);
|
||||
if (!pkey) {
|
||||
lwsl_err("%s: pkey is NULL\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* Get the key type */
|
||||
KeyType = EVP_PKEY_type(EVP_PKEY_id(pkey));
|
||||
|
||||
if (EVP_PKEY_EC != KeyType) {
|
||||
lwsl_notice("Key type is not EC\n");
|
||||
return 0;
|
||||
}
|
||||
/* Get the key */
|
||||
EC_key = EVP_PKEY_get1_EC_KEY(pkey);
|
||||
/* Set ECDH parameter */
|
||||
if (!EC_key) {
|
||||
lwsl_err("%s: ECDH key is NULL \n", __func__);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key);
|
||||
|
||||
EC_KEY_free(EC_key);
|
||||
#else
|
||||
lwsl_notice(" OpenSSL doesn't support ECDH\n");
|
||||
#endif
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd)
|
||||
{
|
||||
#if !defined(USE_WOLFSSL)
|
||||
BIO *bio;
|
||||
#endif
|
||||
|
||||
errno = 0;
|
||||
wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
|
||||
if (wsi->ssl == NULL) {
|
||||
lwsl_err("SSL_new failed: %d (errno %d)\n",
|
||||
lws_ssl_get_error(wsi, 0), errno);
|
||||
|
||||
lws_ssl_elaborate_error();
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, wsi);
|
||||
SSL_set_fd(wsi->ssl, accept_fd);
|
||||
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
CyaSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#else
|
||||
wolfSSL_set_using_nonblock(wsi->ssl, 1);
|
||||
#endif
|
||||
#else
|
||||
|
||||
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
bio = SSL_get_rbio(wsi->ssl);
|
||||
if (bio)
|
||||
BIO_set_nbio(bio, 1); /* nonblocking */
|
||||
else
|
||||
lwsl_notice("NULL rbio\n");
|
||||
bio = SSL_get_wbio(wsi->ssl);
|
||||
if (bio)
|
||||
BIO_set_nbio(bio, 1); /* nonblocking */
|
||||
else
|
||||
lwsl_notice("NULL rbio\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);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_server_abort_connection(struct lws *wsi)
|
||||
{
|
||||
SSL_shutdown(wsi->ssl);
|
||||
SSL_free(wsi->ssl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum lws_ssl_capable_status
|
||||
lws_tls_server_accept(struct lws *wsi)
|
||||
{
|
||||
int m, n = SSL_accept(wsi->ssl);
|
||||
|
||||
if (n == 1)
|
||||
return LWS_SSL_CAPABLE_DONE;
|
||||
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
|
||||
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
|
||||
lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
lwsl_info("SSL_ERROR_WANT_READ\n");
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
||||
}
|
||||
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
||||
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
|
||||
lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
|
||||
}
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
486
lib/tls/openssl/ssl.c
Normal file
486
lib/tls/openssl/ssl.c
Normal file
|
@ -0,0 +1,486 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
/* workaround for mingw */
|
||||
#if !defined(ECONNABORTED)
|
||||
#define ECONNABORTED 103
|
||||
#endif
|
||||
|
||||
int openssl_websocket_private_data_index,
|
||||
openssl_SSL_CTX_private_data_index;
|
||||
|
||||
int lws_ssl_get_error(struct lws *wsi, int n)
|
||||
{
|
||||
int m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return 99;
|
||||
|
||||
m = SSL_get_error(wsi->ssl, n);
|
||||
lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) {
|
||||
switch (status) {
|
||||
case SSL_ERROR_NONE:
|
||||
return strncpy(buf, "SSL_ERROR_NONE", len);
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return strncpy(buf, "SSL_ERROR_WANT_READ", len);
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len);
|
||||
case SSL_ERROR_SYSCALL:
|
||||
switch (ret) {
|
||||
case 0:
|
||||
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF");
|
||||
return buf;
|
||||
case -1:
|
||||
#ifndef LWS_PLAT_OPTEE
|
||||
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s",
|
||||
strerror(errno));
|
||||
#else
|
||||
lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno);
|
||||
#endif
|
||||
return buf;
|
||||
default:
|
||||
return strncpy(buf, "SSL_ERROR_SYSCALL", len);
|
||||
}
|
||||
case SSL_ERROR_SSL:
|
||||
return "SSL_ERROR_SSL";
|
||||
default:
|
||||
return "SSL_ERROR_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_elaborate_error(void)
|
||||
{
|
||||
char buf[256];
|
||||
u_long err;
|
||||
|
||||
while ((err = ERR_get_error()) != 0) {
|
||||
ERR_error_string_n(err, buf, sizeof(buf));
|
||||
lwsl_info("*** %s\n", buf);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag,
|
||||
void *userdata)
|
||||
{
|
||||
struct lws_context_creation_info * info =
|
||||
(struct lws_context_creation_info *)userdata;
|
||||
|
||||
strncpy(buf, info->ssl_private_key_password, size);
|
||||
buf[size - 1] = '\0';
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info)
|
||||
{
|
||||
if (!info->ssl_private_key_password)
|
||||
return;
|
||||
/*
|
||||
* password provided, set ssl callback and user data
|
||||
* for checking password which will be trigered during
|
||||
* SSL_CTX_use_PrivateKey_file function
|
||||
*/
|
||||
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info);
|
||||
SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb);
|
||||
}
|
||||
|
||||
int
|
||||
lws_context_init_ssl_library(struct lws_context_creation_info *info)
|
||||
{
|
||||
#ifdef USE_WOLFSSL
|
||||
#ifdef USE_OLD_CYASSL
|
||||
lwsl_info(" Compiled with CyaSSL support\n");
|
||||
#else
|
||||
lwsl_info(" Compiled with wolfSSL support\n");
|
||||
#endif
|
||||
#else
|
||||
#if defined(LWS_WITH_BORINGSSL)
|
||||
lwsl_info(" Compiled with BoringSSL support\n");
|
||||
#else
|
||||
lwsl_info(" Compiled with OpenSSL support\n");
|
||||
#endif
|
||||
#endif
|
||||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
|
||||
lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* basic openssl init */
|
||||
|
||||
lwsl_info("Doing SSL library init\n");
|
||||
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
|
||||
openssl_websocket_private_data_index =
|
||||
SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
|
||||
|
||||
openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0,
|
||||
NULL, NULL, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ssl_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (!lws_check_opt(vhost->context->options,
|
||||
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
|
||||
return;
|
||||
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
|
||||
// <= 1.0.1f = old api, 1.0.1g+ = new api
|
||||
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL)
|
||||
ERR_remove_thread_state();
|
||||
#else
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
#endif
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n = 0, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
|
||||
|
||||
errno = 0;
|
||||
n = SSL_read(wsi->ssl, buf, len);
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
if (!n && errno == ENOTCONN) {
|
||||
lwsl_debug("%p: SSL_read ENOTCONN\n", wsi);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
#endif
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!wsi->seen_rx) {
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY,
|
||||
time_in_microseconds() - wsi->accept_start_us);
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1);
|
||||
wsi->seen_rx = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
|
||||
/* manpage: returning 0 means connection shut down */
|
||||
if (!n) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
|
||||
if (m == SSL_ERROR_ZERO_RETURN ||
|
||||
m == SSL_ERROR_SYSCALL)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_READ\n", __func__);
|
||||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
||||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
||||
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
/*
|
||||
* if it was our buffer that limited what we read,
|
||||
* check if SSL has additional data pending inside SSL buffers.
|
||||
*
|
||||
* Because these won't signal at the network layer with POLLIN
|
||||
* and if we don't realize, this data will sit there forever
|
||||
*/
|
||||
if (n != len)
|
||||
goto bail;
|
||||
if (!wsi->ssl)
|
||||
goto bail;
|
||||
|
||||
if (!SSL_pending(wsi->ssl))
|
||||
goto bail;
|
||||
|
||||
if (wsi->pending_read_list_next)
|
||||
return n;
|
||||
if (wsi->pending_read_list_prev)
|
||||
return n;
|
||||
if (pt->pending_read_list == wsi)
|
||||
return n;
|
||||
|
||||
/* add us to the linked list of guys with pending ssl */
|
||||
if (pt->pending_read_list)
|
||||
pt->pending_read_list->pending_read_list_prev = wsi;
|
||||
|
||||
wsi->pending_read_list_next = pt->pending_read_list;
|
||||
wsi->pending_read_list_prev = NULL;
|
||||
pt->pending_read_list = wsi;
|
||||
|
||||
return n;
|
||||
bail:
|
||||
lws_ssl_remove_wsi_from_buffered_list(wsi);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_pending(struct lws *wsi)
|
||||
{
|
||||
if (!wsi->ssl)
|
||||
return 0;
|
||||
|
||||
return SSL_pending(wsi->ssl);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
int n, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
|
||||
|
||||
n = SSL_write(wsi->ssl, buf, len);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
if (m != SSL_ERROR_SYSCALL) {
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
|
||||
lwsl_notice("%s: want read\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
|
||||
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
|
||||
lws_set_blocking_send(wsi);
|
||||
|
||||
lwsl_notice("%s: want write\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
|
||||
lws_ssl_elaborate_error();
|
||||
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_info_callback(const SSL *ssl, int where, int ret)
|
||||
{
|
||||
struct lws *wsi;
|
||||
struct lws_context *context;
|
||||
struct lws_ssl_info si;
|
||||
|
||||
context = (struct lws_context *)SSL_CTX_get_ex_data(
|
||||
SSL_get_SSL_CTX(ssl),
|
||||
openssl_SSL_CTX_private_data_index);
|
||||
if (!context)
|
||||
return;
|
||||
wsi = wsi_from_fd(context, SSL_get_fd(ssl));
|
||||
if (!wsi)
|
||||
return;
|
||||
|
||||
if (!(where & wsi->vhost->ssl_info_event_mask))
|
||||
return;
|
||||
|
||||
si.where = where;
|
||||
si.ret = ret;
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_SSL_INFO,
|
||||
wsi->user_space, &si, 0))
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_close(struct lws *wsi)
|
||||
{
|
||||
lws_sockfd_type n;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return 0; /* not handled */
|
||||
|
||||
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
|
||||
/* kill ssl callbacks, becausse we will remove the fd from the
|
||||
* table linking it to the wsi
|
||||
*/
|
||||
if (wsi->vhost->ssl_info_event_mask)
|
||||
SSL_set_info_callback(wsi->ssl, NULL);
|
||||
#endif
|
||||
|
||||
n = SSL_get_fd(wsi->ssl);
|
||||
if (!wsi->socket_is_permanently_unusable)
|
||||
SSL_shutdown(wsi->ssl);
|
||||
compatible_close(n);
|
||||
SSL_free(wsi->ssl);
|
||||
wsi->ssl = NULL;
|
||||
|
||||
if (wsi->context->simultaneous_ssl_restriction &&
|
||||
wsi->context->simultaneous_ssl-- ==
|
||||
wsi->context->simultaneous_ssl_restriction)
|
||||
/* we made space and can do an accept */
|
||||
lws_gate_accepts(wsi->context, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
wsi->context->updated = 1;
|
||||
#endif
|
||||
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
|
||||
{
|
||||
if (vhost->ssl_ctx)
|
||||
SSL_CTX_free(vhost->ssl_ctx);
|
||||
|
||||
if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx)
|
||||
SSL_CTX_free(vhost->ssl_client_ctx);
|
||||
}
|
||||
|
||||
void
|
||||
lws_ssl_context_destroy(struct lws_context *context)
|
||||
{
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
|
||||
// <= 1.0.1f = old api, 1.0.1g+ = new api
|
||||
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL)
|
||||
ERR_remove_thread_state();
|
||||
#else
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
#endif
|
||||
// after 1.1.0 no need
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
}
|
||||
|
||||
lws_tls_ctx *
|
||||
lws_tls_ctx_from_wsi(struct lws *wsi)
|
||||
{
|
||||
return SSL_get_SSL_CTX(wsi->ssl);
|
||||
}
|
||||
|
||||
enum lws_ssl_capable_status
|
||||
lws_tls_shutdown(struct lws *wsi)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = SSL_shutdown(wsi->ssl);
|
||||
lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
|
||||
switch (n) {
|
||||
case 1: /* successful completion */
|
||||
n = shutdown(wsi->desc.sockfd, SHUT_WR);
|
||||
return LWS_SSL_CAPABLE_DONE;
|
||||
|
||||
case 0: /* needs a retry */
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
|
||||
default: /* fatal error, or WANT */
|
||||
n = SSL_get_error(wsi->ssl, n);
|
||||
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("(wants read)\n");
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
|
||||
}
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lwsl_debug("(wants write)\n");
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
|
||||
}
|
||||
}
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
}
|
122
lib/tls/tls.c
Normal file
122
lib/tls/tls.c
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
||||
lws_filepos_t *amount)
|
||||
{
|
||||
lws_filepos_t len;
|
||||
lws_fop_flags_t flags = LWS_O_RDONLY;
|
||||
lws_fop_fd_t fops_fd = lws_vfs_file_open(
|
||||
lws_get_fops(context), filename, &flags);
|
||||
int ret = 1;
|
||||
|
||||
if (!fops_fd)
|
||||
return 1;
|
||||
|
||||
len = lws_vfs_get_length(fops_fd);
|
||||
|
||||
*buf = lws_malloc((size_t)len, "lws_alloc_vfs_file");
|
||||
if (!*buf)
|
||||
goto bail;
|
||||
|
||||
if (lws_vfs_file_read(fops_fd, amount, *buf, len))
|
||||
goto bail;
|
||||
|
||||
ret = 0;
|
||||
bail:
|
||||
lws_vfs_file_close(&fops_fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws *wsi, *wsi_next;
|
||||
|
||||
wsi = pt->pending_read_list;
|
||||
while (wsi) {
|
||||
wsi_next = wsi->pending_read_list_next;
|
||||
pt->fds[wsi->position_in_fds_table].revents |=
|
||||
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
|
||||
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN)
|
||||
return 1;
|
||||
|
||||
wsi = wsi_next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
if (!wsi->pending_read_list_prev &&
|
||||
!wsi->pending_read_list_next &&
|
||||
pt->pending_read_list != wsi)
|
||||
/* we are not on the list */
|
||||
return;
|
||||
|
||||
/* point previous guy's next to our next */
|
||||
if (!wsi->pending_read_list_prev)
|
||||
pt->pending_read_list = wsi->pending_read_list_next;
|
||||
else
|
||||
wsi->pending_read_list_prev->pending_read_list_next =
|
||||
wsi->pending_read_list_next;
|
||||
|
||||
/* point next guy's previous to our previous */
|
||||
if (wsi->pending_read_list_next)
|
||||
wsi->pending_read_list_next->pending_read_list_prev =
|
||||
wsi->pending_read_list_prev;
|
||||
|
||||
wsi->pending_read_list_prev = NULL;
|
||||
wsi->pending_read_list_next = NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lws_gate_accepts(struct lws_context *context, int on)
|
||||
{
|
||||
struct lws_vhost *v = context->vhost_list;
|
||||
|
||||
lwsl_info("gating accepts %d\n", on);
|
||||
context->ssl_gate_accepts = !on;
|
||||
#if defined(LWS_WITH_STATS)
|
||||
context->updated = 1;
|
||||
#endif
|
||||
|
||||
while (v) {
|
||||
if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */
|
||||
if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on,
|
||||
(LWS_POLLIN) * on))
|
||||
lwsl_info("Unable to set accept POLLIN %d\n", on);
|
||||
|
||||
v = v->vhost_next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
#include "private-lwsgs.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* keep changes in sync with the enum in lwsgs.h */
|
||||
static const char * const param_names[] = {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <sqlite3.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct per_vhost_data__gs_mb {
|
||||
struct lws_vhost *vh;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
#include "private-lwsgs.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void
|
||||
sha1_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "../lib/libwebsockets.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <uv.h>
|
||||
|
||||
struct fobj {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <lws-ssh.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TEST_SERVER_KEY_PATH "/etc/lws-test-sshd-server-key"
|
||||
|
||||
|
@ -160,7 +161,7 @@ ssh_ops_tx(void *_priv, int stdch, uint8_t *buf, size_t len)
|
|||
if (stdch != LWS_STDOUT)
|
||||
return 0;
|
||||
|
||||
if (priv->len - priv->pos < chunk)
|
||||
if ((size_t)(priv->len - priv->pos) < chunk)
|
||||
chunk = priv->len - priv->pos;
|
||||
|
||||
if (!chunk)
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <io.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct per_session_data__post_demo {
|
||||
struct lws_spa *spa;
|
||||
|
|
|
@ -8,6 +8,7 @@ Public domain.
|
|||
#include "lws-ssh.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct chacha_ctx {
|
||||
u_int input[16];
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "lws-ssh.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void *sshd_zalloc(size_t s)
|
||||
{
|
||||
|
|
|
@ -764,7 +764,7 @@ bail:
|
|||
|
||||
break;
|
||||
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
|
||||
/* Verify the client certificate */
|
||||
if (!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)) {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#ifndef WIN32
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
/* windows has no SIGUSR1 */
|
||||
#if !defined(WIN32) && !defined(_WIN32)
|
||||
|
@ -365,7 +366,7 @@ int main(int argc, char **argv)
|
|||
use_ssl = 1;
|
||||
break;
|
||||
case 'S':
|
||||
#if defined(LWS_OPENSSL_SUPPORT)
|
||||
#if defined(LWS_OPENSSL_SUPPORT) && !defined(LWS_WITH_MBEDTLS)
|
||||
info.ssl_info_event_mask |= SSL_CB_ALERT;
|
||||
#endif
|
||||
break;
|
||||
|
|
Loading…
Add table
Reference in a new issue