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 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..9fa433c63 --- /dev/null +++ b/include/proxysql_sslkeylog.h @@ -0,0 +1,11 @@ +#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_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/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_Monitor.cpp b/lib/MySQL_Monitor.cpp index a0a80abd4..118c69764 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 f9a998ec1..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; } @@ -5360,11 +5361,12 @@ 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); + proxysql_keylog_attach_callback(GloVars.get_SSL_ctx()); return; } diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 8f3a044e4..e6f77a894 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((char*)"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 SSLKEYLOGFILE '%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; } @@ -8773,6 +8790,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) + 2); + sprintf(sslkeylogfile, "%s/%s", GloVars.datadir, value); + } + if (proxysql_keylog_open(sslkeylogfile) == false) { + free(sslkeylogfile); + proxy_warning("Cannot open SSLKEYLOGFILE '%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_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/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..1f15cc80e 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 @@ -719,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; diff --git a/lib/proxysql_sslkeylog.cpp b/lib/proxysql_sslkeylog.cpp new file mode 100644 index 000000000..9a352e2cf --- /dev/null +++ b/lib/proxysql_sslkeylog.cpp @@ -0,0 +1,102 @@ +#include "proxysql_sslkeylog.h" + +// http://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format + +#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); +} + +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); + } +} 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);