diff --git a/CMakeLists.txt b/CMakeLists.txt index c7eb7e9d..f5c641fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 0da150f5..2c429e0b 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -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 diff --git a/lib/client/client.c b/lib/client/client.c index 20450aa9..f7fa4c76 100644 --- a/lib/client/client.c +++ b/lib/client/client.c @@ -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); diff --git a/lib/client/ssl-client.c b/lib/client/ssl-client.c index b69fd2da..50f5dd04 100644 --- a/lib/client/ssl-client.c +++ b/lib/client/ssl-client.c @@ -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 * @@ -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 diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 669f4edf..55d15611 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -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; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index e1d5110a..f78560b6 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -210,11 +210,12 @@ typedef unsigned long long lws_intptr_t; #define MBEDTLS_CONFIG_FILE #endif #include -#endif +#else #include #if !defined(LWS_WITH_MBEDTLS) #include #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 diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 74ce7c55..28b58923 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -283,6 +283,7 @@ lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen); #if defined(LWS_WITH_MBEDTLS) #include #include +#include "tls/mbedtls/wrapper/include/openssl/ssl.h" /* wrapper !!!! */ #else #include #include @@ -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); diff --git a/lib/server/ssl-server.c b/lib/server/ssl-server.c index a9516f22..f5531e40 100644 --- a/lib/server/ssl-server.c +++ b/lib/server/ssl-server.c @@ -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; +} + diff --git a/lib/service.c b/lib/service.c index 49760f82..37366757 100644 --- a/lib/service.c +++ b/lib/service.c @@ -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 diff --git a/lib/ssl.c b/lib/ssl.c deleted file mode 100644 index 0a647b46..00000000 --- a/lib/ssl.c +++ /dev/null @@ -1,976 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2017 Andy Green - * - * 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 -} diff --git a/lib/tls/mbedtls/client.c b/lib/tls/mbedtls/client.c new file mode 100644 index 00000000..5e971e04 --- /dev/null +++ b/lib/tls/mbedtls/client.c @@ -0,0 +1,180 @@ +/* + * libwebsockets - mbedtls-specific client TLS code + * + * Copyright (C) 2010-2017 Andy Green + * + * 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; +} diff --git a/lib/tls/mbedtls/server.c b/lib/tls/mbedtls/server.c new file mode 100644 index 00000000..e137b73d --- /dev/null +++ b/lib/tls/mbedtls/server.c @@ -0,0 +1,317 @@ +/* + * libwebsockets - mbedTLS-specific server functions + * + * Copyright (C) 2010-2017 Andy Green + * + * 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; +} + + diff --git a/lib/tls/mbedtls/ssl.c b/lib/tls/mbedtls/ssl.c new file mode 100644 index 00000000..a9d019ce --- /dev/null +++ b/lib/tls/mbedtls/ssl.c @@ -0,0 +1,325 @@ +/* + * libwebsockets - mbedTLS-specific lws apis + * + * Copyright (C) 2010-2017 Andy Green + * + * 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; + } +} diff --git a/lib/mbedtls_wrapper/include/internal/ssl3.h b/lib/tls/mbedtls/wrapper/include/internal/ssl3.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl3.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl3.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_cert.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_cert.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_code.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_code.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_code.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_dbg.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_dbg.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_lib.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_lib.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_methods.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_methods.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_pkey.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_pkey.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_stack.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_stack.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_types.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_types.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_types.h diff --git a/lib/mbedtls_wrapper/include/internal/ssl_x509.h b/lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/ssl_x509.h rename to lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h diff --git a/lib/mbedtls_wrapper/include/internal/tls1.h b/lib/tls/mbedtls/wrapper/include/internal/tls1.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/tls1.h rename to lib/tls/mbedtls/wrapper/include/internal/tls1.h diff --git a/lib/mbedtls_wrapper/include/internal/x509_vfy.h b/lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h similarity index 100% rename from lib/mbedtls_wrapper/include/internal/x509_vfy.h rename to lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h diff --git a/lib/mbedtls_wrapper/include/openssl/ssl.h b/lib/tls/mbedtls/wrapper/include/openssl/ssl.h similarity index 100% rename from lib/mbedtls_wrapper/include/openssl/ssl.h rename to lib/tls/mbedtls/wrapper/include/openssl/ssl.h diff --git a/lib/mbedtls_wrapper/include/platform/ssl_pm.h b/lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h similarity index 100% rename from lib/mbedtls_wrapper/include/platform/ssl_pm.h rename to lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h diff --git a/lib/mbedtls_wrapper/include/platform/ssl_port.h b/lib/tls/mbedtls/wrapper/include/platform/ssl_port.h similarity index 100% rename from lib/mbedtls_wrapper/include/platform/ssl_port.h rename to lib/tls/mbedtls/wrapper/include/platform/ssl_port.h diff --git a/lib/mbedtls_wrapper/library/ssl_cert.c b/lib/tls/mbedtls/wrapper/library/ssl_cert.c similarity index 100% rename from lib/mbedtls_wrapper/library/ssl_cert.c rename to lib/tls/mbedtls/wrapper/library/ssl_cert.c diff --git a/lib/mbedtls_wrapper/library/ssl_lib.c b/lib/tls/mbedtls/wrapper/library/ssl_lib.c similarity index 100% rename from lib/mbedtls_wrapper/library/ssl_lib.c rename to lib/tls/mbedtls/wrapper/library/ssl_lib.c diff --git a/lib/mbedtls_wrapper/library/ssl_methods.c b/lib/tls/mbedtls/wrapper/library/ssl_methods.c similarity index 100% rename from lib/mbedtls_wrapper/library/ssl_methods.c rename to lib/tls/mbedtls/wrapper/library/ssl_methods.c diff --git a/lib/mbedtls_wrapper/library/ssl_pkey.c b/lib/tls/mbedtls/wrapper/library/ssl_pkey.c similarity index 100% rename from lib/mbedtls_wrapper/library/ssl_pkey.c rename to lib/tls/mbedtls/wrapper/library/ssl_pkey.c diff --git a/lib/mbedtls_wrapper/library/ssl_stack.c b/lib/tls/mbedtls/wrapper/library/ssl_stack.c similarity index 100% rename from lib/mbedtls_wrapper/library/ssl_stack.c rename to lib/tls/mbedtls/wrapper/library/ssl_stack.c diff --git a/lib/mbedtls_wrapper/library/ssl_x509.c b/lib/tls/mbedtls/wrapper/library/ssl_x509.c similarity index 100% rename from lib/mbedtls_wrapper/library/ssl_x509.c rename to lib/tls/mbedtls/wrapper/library/ssl_x509.c diff --git a/lib/mbedtls_wrapper/platform/ssl_pm.c b/lib/tls/mbedtls/wrapper/platform/ssl_pm.c similarity index 100% rename from lib/mbedtls_wrapper/platform/ssl_pm.c rename to lib/tls/mbedtls/wrapper/platform/ssl_pm.c diff --git a/lib/mbedtls_wrapper/platform/ssl_port.c b/lib/tls/mbedtls/wrapper/platform/ssl_port.c similarity index 100% rename from lib/mbedtls_wrapper/platform/ssl_port.c rename to lib/tls/mbedtls/wrapper/platform/ssl_port.c diff --git a/lib/tls/openssl/client.c b/lib/tls/openssl/client.c new file mode 100644 index 00000000..1eb9f19b --- /dev/null +++ b/lib/tls/openssl/client.c @@ -0,0 +1,384 @@ +/* + * libwebsockets - openSSL-specific client tls code + * + * Copyright (C) 2010-2017 Andy Green + * + * 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; +} + diff --git a/lib/tls/openssl/server.c b/lib/tls/openssl/server.c new file mode 100644 index 00000000..2a5032fa --- /dev/null +++ b/lib/tls/openssl/server.c @@ -0,0 +1,417 @@ +/* + * libwebsockets - OpenSSL-specific server functions + * + * Copyright (C) 2010-2017 Andy Green + * + * 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; +} + diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c new file mode 100644 index 00000000..5b46809d --- /dev/null +++ b/lib/tls/openssl/ssl.c @@ -0,0 +1,486 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * 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; + } +} diff --git a/lib/tls/tls.c b/lib/tls/tls.c new file mode 100644 index 00000000..e804fb06 --- /dev/null +++ b/lib/tls/tls.c @@ -0,0 +1,122 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green + * + * 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; +} diff --git a/plugins/generic-sessions/protocol_generic_sessions.c b/plugins/generic-sessions/protocol_generic_sessions.c index 521b5398..31c41fc1 100644 --- a/plugins/generic-sessions/protocol_generic_sessions.c +++ b/plugins/generic-sessions/protocol_generic_sessions.c @@ -20,6 +20,7 @@ */ #include "private-lwsgs.h" +#include /* keep changes in sync with the enum in lwsgs.h */ static const char * const param_names[] = { diff --git a/plugins/generic-sessions/protocol_lws_messageboard.c b/plugins/generic-sessions/protocol_lws_messageboard.c index 8e66cd5b..19b9f2a0 100644 --- a/plugins/generic-sessions/protocol_lws_messageboard.c +++ b/plugins/generic-sessions/protocol_lws_messageboard.c @@ -25,6 +25,7 @@ #include #include +#include struct per_vhost_data__gs_mb { struct lws_vhost *vh; diff --git a/plugins/generic-sessions/utils.c b/plugins/generic-sessions/utils.c index c90b91d1..c6adc5e1 100644 --- a/plugins/generic-sessions/utils.c +++ b/plugins/generic-sessions/utils.c @@ -20,6 +20,7 @@ */ #include "private-lwsgs.h" +#include void sha1_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash) diff --git a/plugins/generic-table/protocol_table_dirlisting.c b/plugins/generic-table/protocol_table_dirlisting.c index 49a2bb48..0734a14b 100644 --- a/plugins/generic-table/protocol_table_dirlisting.c +++ b/plugins/generic-table/protocol_table_dirlisting.c @@ -24,6 +24,7 @@ #include "../lib/libwebsockets.h" #include +#include #include struct fobj { diff --git a/plugins/protocol_lws_sshd_demo.c b/plugins/protocol_lws_sshd_demo.c index db8164ac..47f7471e 100644 --- a/plugins/protocol_lws_sshd_demo.c +++ b/plugins/protocol_lws_sshd_demo.c @@ -27,6 +27,7 @@ #include #include +#include #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) diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c index 8880fe2e..6be73fbd 100644 --- a/plugins/protocol_post_demo.c +++ b/plugins/protocol_post_demo.c @@ -32,6 +32,7 @@ #include #endif #include +#include struct per_session_data__post_demo { struct lws_spa *spa; diff --git a/plugins/ssh-base/crypto/chacha.c b/plugins/ssh-base/crypto/chacha.c index 46d46cb8..741991ac 100644 --- a/plugins/ssh-base/crypto/chacha.c +++ b/plugins/ssh-base/crypto/chacha.c @@ -8,6 +8,7 @@ Public domain. #include "lws-ssh.h" #include +#include struct chacha_ctx { u_int input[16]; diff --git a/plugins/ssh-base/sshd.c b/plugins/ssh-base/sshd.c index 345e1b85..c4b0e0ff 100644 --- a/plugins/ssh-base/sshd.c +++ b/plugins/ssh-base/sshd.c @@ -23,6 +23,7 @@ #include "lws-ssh.h" #include +#include void *sshd_zalloc(size_t s) { diff --git a/test-apps/test-server-http.c b/test-apps/test-server-http.c index 155e493e..2e8126b4 100644 --- a/test-apps/test-server-http.c +++ b/test-apps/test-server-http.c @@ -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)) { diff --git a/test-apps/test-server-v2.0.c b/test-apps/test-server-v2.0.c index d26e2b2a..8c45ae95 100644 --- a/test-apps/test-server-v2.0.c +++ b/test-apps/test-server-v2.0.c @@ -24,6 +24,7 @@ #ifndef WIN32 #include #endif +#include /* 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;