From 8f899e34f9fe5e1c95ccb62bd9c9a157b94bb89f Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Fri, 2 Jun 2023 01:42:55 +0500 Subject: [PATCH 1/4] Added support for SSLKEYLOGFILE * 'admin-ssl_keylog_file' variable has been introduced to allow users to specify the path for the SSLKEYLOG file. The file path can be an absolute path or relative to the ProxySQL data directory. * Assigning an empty path to the 'admin-ssl_keylog_file' variable signifies that the SSLKEYLOG file feature is disabled. * In case an invalid path is provided for the SSLKEYLOG file, ProxySQL will automatically revert to the last valid path value. * 'PROXYSQL FLUSH LOGS' command can be used to rotate SSLKEYLOG file. # Conflicts: # lib/mysql_connection.cpp --- include/proxysql.h | 1 + include/proxysql_admin.h | 1 + include/proxysql_glovars.hpp | 5 +- include/proxysql_sslkeylog.h | 10 ++++ lib/Makefile | 2 +- lib/MySQL_Session.cpp | 7 ++- lib/ProxySQL_Admin.cpp | 48 +++++++++++++++++- lib/ProxySQL_GloVars.cpp | 3 ++ lib/mysql_connection.cpp | 10 +++- lib/proxysql_sslkeylog.cpp | 96 ++++++++++++++++++++++++++++++++++++ src/proxysql_global.cpp | 8 ++- 11 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 include/proxysql_sslkeylog.h create mode 100644 lib/proxysql_sslkeylog.cpp diff --git a/include/proxysql.h b/include/proxysql.h index 8f4bd75a8..522bd4d1d 100644 --- a/include/proxysql.h +++ b/include/proxysql.h @@ -59,6 +59,7 @@ #include "proxysql_debug.h" #include "proxysql_macros.h" #include "proxysql_coredump.h" +#include "proxysql_sslkeylog.h" #include "jemalloc.h" #ifndef NOJEM diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index c16a6c984..24a365294 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -215,6 +215,7 @@ class ProxySQL_Admin { #endif /* DEBUG */ int coredump_generation_interval_ms; int coredump_generation_threshold; + char* ssl_keylog_file; } variables; unsigned long long last_p_memory_metrics_ts; diff --git a/include/proxysql_glovars.hpp b/include/proxysql_glovars.hpp index 7e861ce55..fe23e756f 100644 --- a/include/proxysql_glovars.hpp +++ b/include/proxysql_glovars.hpp @@ -88,7 +88,8 @@ class ProxySQL_GlobalVariables { char * sqlite3_plugin; char * web_interface_plugin; char * ldap_auth_plugin; - SSL * get_SSL_ctx(); + SSL_CTX *get_SSL_ctx(); + SSL *get_SSL_new(); void get_SSL_pem_mem(char **key, char **cert); std::shared_ptr prometheus_registry { nullptr }; struct { @@ -126,6 +127,8 @@ class ProxySQL_GlobalVariables { bool clickhouse_server; #endif /* PROXYSQLCLICKHOUSE */ pthread_mutex_t ext_glomth_mutex; + + bool ssl_keylog_enabled; } global; struct mysql { char *server_version; diff --git a/include/proxysql_sslkeylog.h b/include/proxysql_sslkeylog.h new file mode 100644 index 000000000..550598c7f --- /dev/null +++ b/include/proxysql_sslkeylog.h @@ -0,0 +1,10 @@ +#ifndef __PROXYSQL_SSLKEYLOG_H +#define __PROXYSQL_SSLKEYLOG_H +#include "proxysql.h" + +void proxysql_keylog_init(); +bool proxysql_keylog_open(const char* keylog_file); +void proxysql_keylog_close(bool lock = true); +void proxysql_keylog_write_line_callback(const SSL *ssl, const char* line); + +#endif // __PROXYSQL_SSLKEYLOG_H diff --git a/lib/Makefile b/lib/Makefile index c423bc451..73fb64ed1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -144,7 +144,7 @@ MYCXXFLAGS=-std=c++11 $(MYCFLAGS) $(PSQLCH) $(ENABLE_EPOLL) default: libproxysql.a .PHONY: default -_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo +_OBJ_CXX = ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo OBJ_CXX = $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) HEADERS = ../include/*.h ../include/*.hpp diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index f9a998ec1..3ab890c7f 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -5360,11 +5360,16 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( client_myds->DSS=STATE_SSL_INIT; client_myds->rbio_ssl = BIO_new(BIO_s_mem()); client_myds->wbio_ssl = BIO_new(BIO_s_mem()); - client_myds->ssl = GloVars.get_SSL_ctx(); + client_myds->ssl = GloVars.get_SSL_new(); SSL_set_fd(client_myds->ssl, client_myds->fd); SSL_set_accept_state(client_myds->ssl); SSL_set_bio(client_myds->ssl, client_myds->rbio_ssl, client_myds->wbio_ssl); l_free(pkt->size,pkt->ptr); + + SSL_CTX* ssl_ctx = GloVars.get_SSL_ctx(); + if (ssl_ctx && (SSL_CTX_get_keylog_callback(ssl_ctx) == (SSL_CTX_keylog_cb_func)NULL)) { + SSL_CTX_set_keylog_callback(ssl_ctx, proxysql_keylog_write_line_callback); + } return; } diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 29ac2026d..9d0f00a3e 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -28,7 +28,6 @@ #include "ProxySQL_Statistics.hpp" #include "MySQL_Logger.hpp" #include "SQLite3_Server.h" - #include "Web_Interface.hpp" #include @@ -684,6 +683,7 @@ static char * admin_variables_names[]= { #endif /* DEBUG */ (char *)"coredump_generation_interval_ms", (char *)"coredump_generation_threshold", + (char *)"ssl_keylog_file", NULL }; @@ -1803,6 +1803,18 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ GloMyLogger->flush_log(); } SPA->flush_error_log(); + proxysql_keylog_close(); + char* ssl_keylog_file = SPA->get_variable("ssl_keylog_file"); + if (ssl_keylog_file != NULL) { + if (strlen(ssl_keylog_file) > 0) { + if (proxysql_keylog_open(ssl_keylog_file) == false) { + // re-opening file failed, setting ssl_keylog_enabled to false + GloVars.global.ssl_keylog_enabled = false; + proxy_warning("Cannot open file '%s' for writing.\n", ssl_keylog_file); + } + } + free(ssl_keylog_file); + } SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL); return false; } @@ -5933,6 +5945,7 @@ ProxySQL_Admin::ProxySQL_Admin() : #endif /* DEBUG */ variables.coredump_generation_interval_ms = 30000; variables.coredump_generation_threshold = 10; + variables.ssl_keylog_file = strdup(""); last_p_memory_metrics_ts = 0; // create the scheduler scheduler=new ProxySQL_External_Scheduler(); @@ -6436,6 +6449,9 @@ void ProxySQL_Admin::admin_shutdown() { if (variables.telnet_stats_ifaces) { free(variables.telnet_stats_ifaces); } + if (variables.ssl_keylog_file) { + free(variables.ssl_keylog_file); + } }; ProxySQL_Admin::~ProxySQL_Admin() { @@ -8082,6 +8098,7 @@ char * ProxySQL_Admin::get_variable(char *name) { sprintf(intbuf,"%d",variables.coredump_generation_threshold); return strdup(intbuf); } + if (!strcasecmp(name,"ssl_keylog_file")) return s_strdup(variables.ssl_keylog_file); return NULL; } @@ -8775,6 +8792,35 @@ bool ProxySQL_Admin::set_variable(char *name, char *value, bool lock) { // this return false; } } + if (!strcasecmp(name, "ssl_keylog_file")) { + if (strcmp(variables.ssl_keylog_file, value)) { + if (vallen == 0 || strcmp(value, "(null)") == 0) { + proxysql_keylog_close(); + free(variables.ssl_keylog_file); + variables.ssl_keylog_file = strdup(""); + GloVars.global.ssl_keylog_enabled = false; + } else { + char* sslkeylogfile = NULL; + + if (value[0] == '/') { // absolute path + sslkeylogfile = strdup(value); + } else { // relative path + sslkeylogfile = (char*)malloc(strlen(GloVars.datadir) + strlen(value) + 1); + sprintf(sslkeylogfile, "%s/%s", GloVars.datadir, value); + } + if (proxysql_keylog_open(sslkeylogfile) == false) { + free(sslkeylogfile); + proxy_warning("Cannot open file '%s' for writing.\n", value); + return false; + } + //free(sslkeylogfile); + free(variables.ssl_keylog_file); + variables.ssl_keylog_file = sslkeylogfile; //strdup(value); + GloVars.global.ssl_keylog_enabled = true; + } + } + return true; + } return false; } diff --git a/lib/ProxySQL_GloVars.cpp b/lib/ProxySQL_GloVars.cpp index b34b25fe7..d09536167 100644 --- a/lib/ProxySQL_GloVars.cpp +++ b/lib/ProxySQL_GloVars.cpp @@ -148,6 +148,7 @@ ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() : #ifdef PROXYSQLCLICKHOUSE global.clickhouse_server=false; #endif /* PROXYSQLCLICKHOUSE */ + global.ssl_keylog_enabled = false; opt=new ez::ezOptionParser(); opt->overview="High Performance Advanced Proxy for MySQL"; opt->syntax="proxysql [OPTIONS]"; @@ -296,6 +297,8 @@ void ProxySQL_GlobalVariables::process_opts_pre() { init_debug_struct(); #endif init_coredump_struct(); + + proxysql_keylog_init(); }; void ProxySQL_GlobalVariables::process_opts_post() { diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 467dca398..1f8bce51e 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -9,7 +9,6 @@ #include "MySQL_Data_Stream.h" #include "query_processor.h" #include "MySQL_Variables.h" - #include // some of the code that follows is from mariadb client library memory allocator @@ -1167,6 +1166,15 @@ handler_again: //vio_blocking(mysql->net.vio, FALSE, 0); //fcntl(mysql->net.vio->sd, F_SETFL, O_RDWR|O_NONBLOCK); //} + if (mysql->options.use_ssl == 1) { + SSL_CTX* ssl_ctx = NULL; + P_MARIADB_TLS* matls = (P_MARIADB_TLS*)mysql->net.pvio->ctls; + if (matls != NULL) ssl_ctx = SSL_get_SSL_CTX((SSL*)matls->ssl); + + if (ssl_ctx && (SSL_CTX_get_keylog_callback(ssl_ctx) == (SSL_CTX_keylog_cb_func)NULL)) { + SSL_CTX_set_keylog_callback(ssl_ctx, proxysql_keylog_write_line_callback); + } + } MySQL_Monitor::update_dns_cache_from_mysql_conn(mysql); break; case ASYNC_CONNECT_FAILED: diff --git a/lib/proxysql_sslkeylog.cpp b/lib/proxysql_sslkeylog.cpp new file mode 100644 index 000000000..9dca19370 --- /dev/null +++ b/lib/proxysql_sslkeylog.cpp @@ -0,0 +1,96 @@ +#include "proxysql_sslkeylog.h" + +// https://firefox-source-docs.mozilla.org/security/nss/legacy/key_log_format/index.html + +#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) + +#define CLIENT_RANDOM_SIZE 32 + +/* + * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the + * secret size depends on the cipher suite's hash function which is 32 bytes + * for SHA-256 and 48 bytes for SHA-384. + */ +#define SECRET_MAXLEN 48 + +static pthread_rwlock_t keylog_file_rwlock; + +/* The fp for the open SSLKEYLOGFILE, or NULL if not open */ +static FILE *keylog_file_fp = NULL; + +FILE* proxysql_open_file(const char* file) { + FILE *file_tmp = fopen(file, "a+"); + if (file_tmp) { + if (setvbuf(file_tmp, NULL, _IOLBF, 4096)) { + fclose(file_tmp); + file_tmp = NULL; + goto __exit; + } + } +__exit: + return file_tmp; +} + +void proxysql_keylog_init() { + pthread_rwlock_init(&keylog_file_rwlock, nullptr); + keylog_file_fp = NULL; +} + +bool proxysql_keylog_open(const char* keylog_file) +{ + assert(keylog_file); + FILE* keylog_file_tmp = proxysql_open_file(keylog_file); + if (!keylog_file_tmp) return false; + pthread_rwlock_wrlock(&keylog_file_rwlock); + proxysql_keylog_close(false); + keylog_file_fp = keylog_file_tmp; + pthread_rwlock_unlock(&keylog_file_rwlock); + return true; +} + +void proxysql_keylog_close(bool lock) +{ + if (lock) + pthread_rwlock_wrlock(&keylog_file_rwlock); + if(keylog_file_fp) { + fclose(keylog_file_fp); + keylog_file_fp = NULL; + } + if (lock) + pthread_rwlock_unlock(&keylog_file_rwlock); +} + +void proxysql_keylog_write_line_callback(const SSL *ssl, const char *line) +{ + (void)ssl; // to fix warning + + // checking keylog_file_fp without acquiring a lock is safe, as it is checked again after acquring lock + if (!keylog_file_fp) return; + + /* The current maximum valid keylog line length LF and NUL is 195. */ + size_t linelen; + char buf[256]; + + pthread_rwlock_rdlock(&keylog_file_rwlock); + if(!keylog_file_fp || !line) { + goto __exit; + } + + linelen = strlen(line); + if(linelen == 0 || linelen > sizeof(buf) - 2) { + /* Empty line or too big to fit in a LF and NUL. */ + goto __exit; + } + + memcpy(buf, line, linelen); + if(line[linelen - 1] != '\n') { + buf[linelen++] = '\n'; + } + buf[linelen] = '\0'; + + /* as we are using rwlock, using fputs as it's thread-safe*/ + fputs(buf, keylog_file_fp); + +__exit: + pthread_rwlock_unlock(&keylog_file_rwlock); +} diff --git a/src/proxysql_global.cpp b/src/proxysql_global.cpp index 4273e4ece..4fae899dc 100644 --- a/src/proxysql_global.cpp +++ b/src/proxysql_global.cpp @@ -4,7 +4,13 @@ #include "cpp.h" //ProxySQL_GlobalVariables GloVars; -SSL * ProxySQL_GlobalVariables::get_SSL_ctx() { +SSL_CTX * ProxySQL_GlobalVariables::get_SSL_ctx() { + // take the mutex + std::lock_guard lock(global.ssl_mutex); + return GloVars.global.ssl_ctx; +} + +SSL * ProxySQL_GlobalVariables::get_SSL_new() { // take the mutex std::lock_guard lock(global.ssl_mutex); return SSL_new(GloVars.global.ssl_ctx); From beb93196bc31d86b04a66c03e6fbdd74786eaf88 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 6 Jun 2023 00:12:24 +0500 Subject: [PATCH 2/4] Added SSLKEYLOGFILE support in mariadb client library --- deps/Makefile | 4 +++ .../ma_common.h.sslkeylogfile.patch | 14 ++++++++ .../mariadb_lib.c.sslkeylogfile.patch | 32 +++++++++++++++++ .../mysql.h.sslkeylogfile.patch | 16 +++++++++ .../openssl.c.sslkeylogfile.patch | 34 +++++++++++++++++++ 5 files changed, 100 insertions(+) create mode 100644 deps/mariadb-client-library/ma_common.h.sslkeylogfile.patch create mode 100644 deps/mariadb-client-library/mariadb_lib.c.sslkeylogfile.patch create mode 100644 deps/mariadb-client-library/mysql.h.sslkeylogfile.patch create mode 100644 deps/mariadb-client-library/openssl.c.sslkeylogfile.patch diff --git a/deps/Makefile b/deps/Makefile index 1a1c99007..dbe495c00 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -280,6 +280,10 @@ endif # patches for x509 cache . See https://github.com/sysown/proxysql/issues/4117 (Slow connection time with SSL and large CA file , relevant on Aurora) cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_lib.c < ../mariadb_lib.c.x509cache.patch cd mariadb-client-library/mariadb_client && patch libmariadb/secure/openssl.c < ../openssl.c.x509cache.patch + cd mariadb-client-library/mariadb_client && patch include/mysql.h < ../mysql.h.sslkeylogfile.patch + cd mariadb-client-library/mariadb_client && patch include/ma_common.h < ../ma_common.h.sslkeylogfile.patch + cd mariadb-client-library/mariadb_client && patch libmariadb/mariadb_lib.c < ../mariadb_lib.c.sslkeylogfile.patch + cd mariadb-client-library/mariadb_client && patch libmariadb/secure/openssl.c < ../openssl.c.sslkeylogfile.patch cd mariadb-client-library/mariadb_client && CC=${CC} CXX=${CXX} ${MAKE} mariadbclient # cd mariadb-client-library/mariadb_client/include && make my_config.h diff --git a/deps/mariadb-client-library/ma_common.h.sslkeylogfile.patch b/deps/mariadb-client-library/ma_common.h.sslkeylogfile.patch new file mode 100644 index 000000000..af13a9449 --- /dev/null +++ b/deps/mariadb-client-library/ma_common.h.sslkeylogfile.patch @@ -0,0 +1,14 @@ +@@ -78,12 +78,13 @@ + my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value); + HASH userdata; + char *server_public_key; + char *proxy_header; + size_t proxy_header_len; + int (*io_wait)(my_socket handle, my_bool is_read, int timeout); ++ void (*ssl_keylog_callback)(const void *ssl, const char *line); + }; + + typedef struct st_connection_handler + { + struct st_ma_connection_plugin *plugin; + void *data; diff --git a/deps/mariadb-client-library/mariadb_lib.c.sslkeylogfile.patch b/deps/mariadb-client-library/mariadb_lib.c.sslkeylogfile.patch new file mode 100644 index 000000000..e0245b582 --- /dev/null +++ b/deps/mariadb-client-library/mariadb_lib.c.sslkeylogfile.patch @@ -0,0 +1,32 @@ +@@ -3277,12 +3277,15 @@ + case MYSQL_OPT_SSL_CRL: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1); + break; + case MYSQL_OPT_SSL_CRLPATH: + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1); + break; ++ case MARIADB_OPT_SSL_KEYLOG_CALLBACK: ++ OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_keylog_callback, arg1); ++ break; + case MYSQL_OPT_CONNECT_ATTR_DELETE: + { + uchar *h; + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (hash_inited(&mysql->options.extension->connect_attrs) && + (h= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1, +@@ -3614,12 +3617,15 @@ + case MYSQL_OPT_SSL_CRL: + *((char **)arg)= mysql->options.extension ? mysql->options.ssl_cipher : NULL; + break; + case MYSQL_OPT_SSL_CRLPATH: + *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL; + break; ++ case MARIADB_OPT_SSL_KEYLOG_CALLBACK: ++ *((void(**)(const void *, const char *))arg)= mysql->options.extension ? mysql->options.extension->ssl_keylog_callback : NULL; ++ break; + case MYSQL_OPT_CONNECT_ATTRS: + /* mysql_get_optionsv(mysql, MYSQL_OPT_CONNECT_ATTRS, keys, vals, elements) */ + { + unsigned int i, *elements; + char **key= NULL; + void *arg1; diff --git a/deps/mariadb-client-library/mysql.h.sslkeylogfile.patch b/deps/mariadb-client-library/mysql.h.sslkeylogfile.patch new file mode 100644 index 000000000..793d16f17 --- /dev/null +++ b/deps/mariadb-client-library/mysql.h.sslkeylogfile.patch @@ -0,0 +1,16 @@ +@@ -242,13 +242,14 @@ + MARIADB_OPT_DEBUG, + MARIADB_OPT_FOUND_ROWS, + MARIADB_OPT_MULTI_RESULTS, + MARIADB_OPT_MULTI_STATEMENTS, + MARIADB_OPT_INTERACTIVE, + MARIADB_OPT_PROXY_HEADER, +- MARIADB_OPT_IO_WAIT ++ MARIADB_OPT_IO_WAIT, ++ MARIADB_OPT_SSL_KEYLOG_CALLBACK + }; + + enum mariadb_value { + MARIADB_CHARSET_ID, + MARIADB_CHARSET_NAME, + MARIADB_CLIENT_ERRORS, diff --git a/deps/mariadb-client-library/openssl.c.sslkeylogfile.patch b/deps/mariadb-client-library/openssl.c.sslkeylogfile.patch new file mode 100644 index 000000000..631250e4c --- /dev/null +++ b/deps/mariadb-client-library/openssl.c.sslkeylogfile.patch @@ -0,0 +1,34 @@ +@@ -526,12 +526,19 @@ + memset(buf, 0, size); + if (userdata) + strncpy(buf, (char *)userdata, size); + return (int)strlen(buf); + } + ++static void ma_tls_set_sslkeylog_callback(MYSQL *mysql, SSL_CTX *ssl_ctx) ++{ ++ if (mysql->options.extension && mysql->options.extension->ssl_keylog_callback) ++ { ++ SSL_CTX_set_keylog_callback(ssl_ctx, (void(*)(const SSL*, const char*))mysql->options.extension->ssl_keylog_callback); ++ } ++} + + static int ma_tls_set_certs(MYSQL *mysql, SSL *ssl) + { + char *certfile= mysql->options.ssl_cert, + *keyfile= mysql->options.ssl_key; + char *pw= (mysql->options.extension) ? +@@ -653,12 +660,13 @@ + if (!(ctx= SSL_CTX_new(SSLv23_client_method()))) + #endif + goto error; + if (mysql->options.extension) + options|= ma_tls_version_options(mysql->options.extension->tls_version); + SSL_CTX_set_options(ctx, options); ++ ma_tls_set_sslkeylog_callback(mysql, ctx); + #ifdef HAVE_TLS_SESSION_CACHE + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); + ma_tls_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_tls_session) * ma_tls_session_cache_size); + SSL_CTX_sess_set_new_cb(ctx, ma_tls_session_cb); + SSL_CTX_sess_set_remove_cb(ctx, ma_tls_remove_session_cb); + #endif From a1fcf5d94f7cb55ef308fe5e0c4069a8f127ac86 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 6 Jun 2023 00:13:34 +0500 Subject: [PATCH 3/4] Added SSLKeylog in ProxySQL Modules --- include/proxysql_sslkeylog.h | 3 ++- lib/MySQL_Monitor.cpp | 1 + lib/MySQL_Session.cpp | 7 ++----- lib/ProxySQL_Admin.cpp | 4 ++-- lib/ProxySQL_Cluster.cpp | 30 ++++++++++++++++++++++++------ lib/mysql_connection.cpp | 10 +--------- lib/proxysql_sslkeylog.cpp | 6 ++++++ 7 files changed, 38 insertions(+), 23 deletions(-) diff --git a/include/proxysql_sslkeylog.h b/include/proxysql_sslkeylog.h index 550598c7f..9fa433c63 100644 --- a/include/proxysql_sslkeylog.h +++ b/include/proxysql_sslkeylog.h @@ -5,6 +5,7 @@ void proxysql_keylog_init(); bool proxysql_keylog_open(const char* keylog_file); void proxysql_keylog_close(bool lock = true); -void proxysql_keylog_write_line_callback(const SSL *ssl, const char* line); +void proxysql_keylog_attach_callback(SSL_CTX* ssl_ctx); +void proxysql_keylog_write_line_callback(const SSL* ssl, const char* line); #endif // __PROXYSQL_SSLKEYLOG_H diff --git a/lib/MySQL_Monitor.cpp b/lib/MySQL_Monitor.cpp index b905933b3..d766620ae 100644 --- a/lib/MySQL_Monitor.cpp +++ b/lib/MySQL_Monitor.cpp @@ -1510,6 +1510,7 @@ bool MySQL_Monitor_State_Data::create_new_connection() { mysql_thread___ssl_p2s_cipher); mysql_options(mysql, MYSQL_OPT_SSL_CRL, mysql_thread___ssl_p2s_crl); mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, mysql_thread___ssl_p2s_crlpath); + mysql_options(mysql, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } unsigned int timeout=mysql_thread___monitor_connect_timeout/1000; if (timeout==0) timeout=1; diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 3ab890c7f..e9cd3043a 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -224,6 +224,7 @@ void * kill_query_thread(void *arg) { mysql=mysql_init(NULL); mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", "proxysql_killer"); mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_server_host", ka->hostname); + //mysql_options(mysql, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); if (!mysql) { goto __exit_kill_query_thread; } @@ -5365,11 +5366,7 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( SSL_set_accept_state(client_myds->ssl); SSL_set_bio(client_myds->ssl, client_myds->rbio_ssl, client_myds->wbio_ssl); l_free(pkt->size,pkt->ptr); - - SSL_CTX* ssl_ctx = GloVars.get_SSL_ctx(); - if (ssl_ctx && (SSL_CTX_get_keylog_callback(ssl_ctx) == (SSL_CTX_keylog_cb_func)NULL)) { - SSL_CTX_set_keylog_callback(ssl_ctx, proxysql_keylog_write_line_callback); - } + proxysql_keylog_attach_callback(GloVars.get_SSL_ctx()); return; } diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 9d0f00a3e..f8e286657 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -1804,7 +1804,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ } SPA->flush_error_log(); proxysql_keylog_close(); - char* ssl_keylog_file = SPA->get_variable("ssl_keylog_file"); + char* ssl_keylog_file = SPA->get_variable((char*)"ssl_keylog_file"); if (ssl_keylog_file != NULL) { if (strlen(ssl_keylog_file) > 0) { if (proxysql_keylog_open(ssl_keylog_file) == false) { @@ -8805,7 +8805,7 @@ bool ProxySQL_Admin::set_variable(char *name, char *value, bool lock) { // this if (value[0] == '/') { // absolute path sslkeylogfile = strdup(value); } else { // relative path - sslkeylogfile = (char*)malloc(strlen(GloVars.datadir) + strlen(value) + 1); + sslkeylogfile = (char*)malloc(strlen(GloVars.datadir) + strlen(value) + 2); sprintf(sslkeylogfile, "%s/%s", GloVars.datadir, value); } if (proxysql_keylog_open(sslkeylogfile) == false) { diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 03ad96238..6d9d42f9f 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -93,7 +93,10 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); //mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); //mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); - { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); } + { + unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } //rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, CLIENT_COMPRESS); // FIXME: add optional support for compression rc_conn = mysql_real_connect(conn, node->get_host_address(), username, password, NULL, node->port, NULL, 0); // if (rc_conn) { @@ -1037,7 +1040,10 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); //mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); //mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); - { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); } + { + unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); if (rc_conn) { @@ -1308,7 +1314,10 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); //mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); //mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); - { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); } + { + unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } proxy_info("Cluster: Fetching MySQL Users from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); @@ -1631,7 +1640,10 @@ void ProxySQL_Cluster::pull_mysql_servers_from_peer(const std::string& checksum, mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); //mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); //mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); - { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); } + { + unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_checksum); rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); if (rc_conn) { @@ -2049,7 +2061,10 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); //mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); //mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); - { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); } + { + unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } proxy_info("Cluster: Fetching %s variables from peer %s:%d started\n", vars_type_str, hostname, port); rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); @@ -2201,7 +2216,10 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); //mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); //mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); - { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); } + { + unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } proxy_info( "Cluster: Fetching ProxySQL Servers from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str() diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 1f8bce51e..1f15cc80e 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -718,6 +718,7 @@ void MySQL_Connection::connect_start() { mysql_thread___ssl_p2s_cipher); mysql_options(mysql, MYSQL_OPT_SSL_CRL, mysql_thread___ssl_p2s_crl); mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, mysql_thread___ssl_p2s_crlpath); + mysql_options(mysql, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } unsigned int timeout= 1; const char *csname = NULL; @@ -1166,15 +1167,6 @@ handler_again: //vio_blocking(mysql->net.vio, FALSE, 0); //fcntl(mysql->net.vio->sd, F_SETFL, O_RDWR|O_NONBLOCK); //} - if (mysql->options.use_ssl == 1) { - SSL_CTX* ssl_ctx = NULL; - P_MARIADB_TLS* matls = (P_MARIADB_TLS*)mysql->net.pvio->ctls; - if (matls != NULL) ssl_ctx = SSL_get_SSL_CTX((SSL*)matls->ssl); - - if (ssl_ctx && (SSL_CTX_get_keylog_callback(ssl_ctx) == (SSL_CTX_keylog_cb_func)NULL)) { - SSL_CTX_set_keylog_callback(ssl_ctx, proxysql_keylog_write_line_callback); - } - } MySQL_Monitor::update_dns_cache_from_mysql_conn(mysql); break; case ASYNC_CONNECT_FAILED: diff --git a/lib/proxysql_sslkeylog.cpp b/lib/proxysql_sslkeylog.cpp index 9dca19370..5bdee5350 100644 --- a/lib/proxysql_sslkeylog.cpp +++ b/lib/proxysql_sslkeylog.cpp @@ -94,3 +94,9 @@ void proxysql_keylog_write_line_callback(const SSL *ssl, const char *line) __exit: pthread_rwlock_unlock(&keylog_file_rwlock); } + +void proxysql_keylog_attach_callback(SSL_CTX* ssl_ctx) { + if (ssl_ctx && (SSL_CTX_get_keylog_callback(ssl_ctx) == (SSL_CTX_keylog_cb_func)NULL)) { + SSL_CTX_set_keylog_callback(ssl_ctx, proxysql_keylog_write_line_callback); + } +} From dfd45f507155765cb49541a93fd6abe9ba4b5c75 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 6 Jun 2023 16:03:24 +0500 Subject: [PATCH 4/4] * Updated NSS Key Log Format URL * Updated proxysql sslkeylogfile warning message --- lib/ProxySQL_Admin.cpp | 4 ++-- lib/proxysql_sslkeylog.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index f8e286657..1fafeaa3d 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -1810,7 +1810,7 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ if (proxysql_keylog_open(ssl_keylog_file) == false) { // re-opening file failed, setting ssl_keylog_enabled to false GloVars.global.ssl_keylog_enabled = false; - proxy_warning("Cannot open file '%s' for writing.\n", ssl_keylog_file); + proxy_warning("Cannot open SSLKEYLOGFILE '%s' for writing.\n", ssl_keylog_file); } } free(ssl_keylog_file); @@ -8810,7 +8810,7 @@ bool ProxySQL_Admin::set_variable(char *name, char *value, bool lock) { // this } if (proxysql_keylog_open(sslkeylogfile) == false) { free(sslkeylogfile); - proxy_warning("Cannot open file '%s' for writing.\n", value); + proxy_warning("Cannot open SSLKEYLOGFILE '%s' for writing.\n", value); return false; } //free(sslkeylogfile); diff --git a/lib/proxysql_sslkeylog.cpp b/lib/proxysql_sslkeylog.cpp index 5bdee5350..9a352e2cf 100644 --- a/lib/proxysql_sslkeylog.cpp +++ b/lib/proxysql_sslkeylog.cpp @@ -1,6 +1,6 @@ #include "proxysql_sslkeylog.h" -// https://firefox-source-docs.mozilla.org/security/nss/legacy/key_log_format/index.html +// http://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format #define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)