From 3ff720ff6695622256eafb0b6792f8526a8c5215 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 20 Jun 2017 11:46:49 +0800 Subject: [PATCH] support openssl info callback https://github.com/warmcat/libwebsockets/issues/936 --- CMakeLists.txt | 1 + README.coding.md | 26 ++++++++++++++++++++++++++ lib/context.c | 11 +++++++++++ lib/libwebsockets.h | 17 +++++++++++++++++ lib/private-libwebsockets.h | 6 ++++++ lib/ssl-client.c | 5 +++++ lib/ssl.c | 41 +++++++++++++++++++++++++++++++++++++++++ lws_config.h.in | 1 + test-server/test-server-v2.0.c | 8 +++++++- 9 files changed, 115 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b596c20..f625c57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1036,6 +1036,7 @@ endforeach() set (temp ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${LIB_LIST}) 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) if (LWS_WITH_ESP32) set(LWS_HAVE_TLS_CLIENT_METHOD 1) diff --git a/README.coding.md b/README.coding.md index 9cd9fdd..c1c4a3f 100644 --- a/README.coding.md +++ b/README.coding.md @@ -568,6 +568,32 @@ ECDH Certs are now supported. Enable the CMake option to build in support and select it at runtime. +@section sslinfo SSL info callbacks + +OpenSSL allows you to receive callbacks for various events defined in a +bitmask in openssl/ssl.h. The events include stuff like TLS Alerts. + +By default, lws doesn't register for these callbacks. + +However if you set the info.ssl_info_event_mask to nonzero (ie, set some +of the bits in it like `SSL_CB_ALERT` at vhost creation time, then +connections to that vhost will call back using LWS_CALLBACK_SSL_INFO +for the wsi, and the `in` parameter will be pointing to a struct of +related args: + +``` +struct lws_ssl_info { + int where; + int ret; +}; +``` + +The default callback handler in lws has a handler for LWS_CALLBACK_SSL_INFO +which prints the related information, You can test it using the switch +-S -s on `libwebsockets-test-server-v2.0`. + +Returning nonzero from the callback will close the wsi. + @section smp SMP / Multithreaded service SMP support is integrated into LWS without any internal threading. It's diff --git a/lib/context.c b/lib/context.c index d9d94eb..7e875ea 100644 --- a/lib/context.c +++ b/lib/context.c @@ -381,6 +381,16 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, "sent %d only %d went", n, args->len); return n; #endif + + case LWS_CALLBACK_SSL_INFO: + { + struct lws_ssl_info *si = in; + + lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", + si->where, si->ret); + } + break; + default: break; } @@ -453,6 +463,7 @@ lws_create_vhost(struct lws_context *context, vh->options = info->options; vh->pvo = info->pvo; vh->headers = info->headers; + vh->ssl_info_event_mask = info->ssl_info_event_mask; if (info->keepalive_timeout) vh->keepalive_timeout = info->keepalive_timeout; else diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 23d6588..ad754b0 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -829,6 +829,10 @@ struct lws_extension; */ ///@{ +struct lws_ssl_info { + int where; + int ret; +}; /* * NOTE: These public enums are part of the abi. If you want to add one, @@ -1235,6 +1239,12 @@ enum lws_callback_reasons { /**< RAW mode file is writeable */ LWS_CALLBACK_RAW_CLOSE_FILE = 66, /**< RAW mode wsi that adopted a file is closing */ + LWS_CALLBACK_SSL_INFO = 67, + /**< SSL connections only. An event you registered an + * interest in at the vhost has occurred on a connection + * using the vhost. @in is a pointer to a + * struct lws_ssl_info containing information about the + * event*/ /****** add new things just above ---^ ******/ @@ -2040,6 +2050,12 @@ struct lws_context_creation_info { * members added above will see 0 (default) even if the app * was not built against the newer headers. */ + int ssl_info_event_mask; + /**< VHOST: mask of ssl events to be reported on LWS_CALLBACK_SSL_INFO + * callback for connections on this vhost. The mask values are of + * the form SSL_CB_ALERT, defined in openssl/ssl.h. The default of + * 0 means no info events will be reported. + */ void *_unused[8]; /**< dummy */ }; @@ -3574,6 +3590,7 @@ enum pending_timeout { PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY = 19, PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY = 20, PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY = 21, + PENDING_TIMEOUT_KILLED_BY_SSL_INFO = 22, /****** add new things just above ---^ ******/ }; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 845d4a5..420076e 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -890,6 +890,7 @@ struct lws_vhost { int ka_probes; int ka_interval; int keepalive_timeout; + int ssl_info_event_mask; #ifdef LWS_WITH_ACCESS_LOG int log_fd; #endif @@ -1158,6 +1159,7 @@ LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *in #else #define LWS_UNIX_SOCK_ENABLED(vhost) (0) #endif + enum uri_path_states { URIPS_IDLE, URIPS_SEEN_SLASH, @@ -2077,6 +2079,10 @@ lws_http_transaction_completed_client(struct lws *wsi); LWS_EXTERN int 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); + #else #define lws_context_init_client_ssl(_a, _b) (0) #endif diff --git a/lib/ssl-client.c b/lib/ssl-client.c index aba337b..dea9538 100644 --- a/lib/ssl-client.c +++ b/lib/ssl-client.c @@ -130,6 +130,11 @@ lws_ssl_client_bio_create(struct lws *wsi) 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; diff --git a/lib/ssl.c b/lib/ssl.c index 651757f..d65923c 100644 --- a/lib/ssl.c +++ b/lib/ssl.c @@ -543,6 +543,35 @@ lws_gate_accepts(struct lws_context *context, int on) 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) { @@ -551,6 +580,14 @@ lws_ssl_close(struct lws *wsi) 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); SSL_shutdown(wsi->ssl); compatible_close(n); @@ -608,6 +645,10 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) 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 */ diff --git a/lws_config.h.in b/lws_config.h.in index c8d5c38..483ad20 100644 --- a/lws_config.h.in +++ b/lws_config.h.in @@ -147,5 +147,6 @@ #cmakedefine LWS_HAVE_TLS_CLIENT_METHOD #cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD +#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK ${LWS_SIZEOFPTR_CODE} diff --git a/test-server/test-server-v2.0.c b/test-server/test-server-v2.0.c index 6041e70..40e0dd1 100644 --- a/test-server/test-server-v2.0.c +++ b/test-server/test-server-v2.0.c @@ -231,6 +231,7 @@ static const struct option options[] = { { "debug", required_argument, NULL, 'd' }, { "port", required_argument, NULL, 'p' }, { "ssl", no_argument, NULL, 's' }, + { "ssl-alerts", no_argument, NULL, 'S' }, { "allow-non-ssl", no_argument, NULL, 'a' }, { "interface", required_argument, NULL, 'i' }, { "ssl-cert", required_argument, NULL, 'C' }, @@ -281,7 +282,7 @@ int main(int argc, char **argv) info.port = 7681; while (n >= 0) { - n = getopt_long(argc, argv, "i:hsap:d:Dr:C:K:A:R:vu:g:", + n = getopt_long(argc, argv, "i:hsap:d:Dr:C:K:A:R:vu:g:S", (struct option *)options, NULL); if (n < 0) continue; @@ -306,6 +307,11 @@ int main(int argc, char **argv) case 's': use_ssl = 1; break; + case 'S': +#if defined(LWS_OPENSSL_SUPPORT) + info.ssl_info_event_mask |= SSL_CB_ALERT; +#endif + break; case 'a': opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; break; -- 2.7.4