diff --git a/include/MySQL_LDAP_Authentication.hpp b/include/MySQL_LDAP_Authentication.hpp index 3d155a857..b4aea1dde 100644 --- a/include/MySQL_LDAP_Authentication.hpp +++ b/include/MySQL_LDAP_Authentication.hpp @@ -1,92 +1,31 @@ #ifndef CLASS_MYSQL_LDAP_AUTHENTICATION_H #define CLASS_MYSQL_LDAP_AUTHENTICATION_H -/* -#include "proxysql.h" -#include "cpp.h" - -#ifndef LDAP_ACCOUNT_DETAILS_T -#define LDAP_ACCOUNT_DETAILS_T -typedef struct _ldap_account_details_t { - char *username; - char *password; - void *sha1_pass; - bool use_ssl; - int default_hostgroup; - char *default_schema; - bool schema_locked; - bool transaction_persistent; -// bool fast_forward; -// int max_connections; -// int num_connections_used; -// bool __frontend; // this is used only during the dump -// bool __backend; // this is used only during the dump -// bool __active; - unsigned long long inserted_at; - char *ad_group; -} ldap_account_details_t; - -typedef std::map umap_auth; -#endif // LDAP_ACCOUNT_DETAILS_T - -#ifdef DEBUG -#define DEB "_DEBUG" -#else -#define DEB "" -#endif // DEBUG -#define MYSQL_LDAP_AUTHENTICATION_VERSION "1.0.0000" DEB - - -class PtrArray; - -#ifndef CREDS_GROUPS_T -#define CREDS_GROUPS_T -typedef struct _creds_group_t { - pthread_rwlock_t lock; - umap_auth bt_map; - PtrArray *cred_array; -} creds_group_t; -#endif // CREDS_GROUPS_T -*/ - - class MySQL_LDAP_Authentication { -/* - private: -// creds_group_t creds_backends; - creds_group_t creds_frontends; - bool _reset(enum cred_username_type usertype); -// uint64_t _get_runtime_checksum(enum cred_username_type usertype); -*/ - public: - MySQL_LDAP_Authentication() {}; - virtual ~MySQL_LDAP_Authentication() {}; - virtual bool add(char *username, char *backend_username, char *password, enum cred_username_type usertype, bool use_ssl, int default_hostgroup, char *default_schema, bool schema_locked, bool transaction_persistent, bool fast_forward, int max_connections) {return false;}; - virtual bool del(char *username, enum cred_username_type usertype, bool set_lock=true) {return false;}; - virtual bool reset() {return false;}; - virtual void print_version() {}; - virtual char * lookup(void *ldap_ctx, char *username, char *pass, enum cred_username_type usertype, bool *use_ssl, int *default_hostgroup, char **default_schema, bool *schema_locked, bool *transaction_persistent, bool *fast_forward, int *max_connections, void **sha1_pass, char **backend_username) {return NULL;}; - //virtual int dump_all_users(account_details_t ***, bool _complete=true) {return 0;}; - virtual int increase_frontend_user_connections(char *username, int *mc=NULL) {return 0;}; +public: + virtual char * lookup(char *username, char *pass, + enum cred_username_type usertype, bool *use_ssl, int *default_hostgroup, + char **default_schema, bool *schema_locked, bool *transaction_persistent, + bool *fast_forward, int *max_connections, void **sha1_pass, char **attributes, + char **backend_username) {return NULL;}; + + virtual int increase_frontend_user_connections(char *username, int *max_connections = NULL) { return 0; }; virtual void decrease_frontend_user_connections(char *username) {}; - virtual void set_all_inactive(enum cred_username_type usertype) {}; - virtual void remove_inactives(enum cred_username_type usertype) {}; - virtual bool set_SHA1(char *username, enum cred_username_type usertype, void *sha_pass) {return false;}; -// unsigned int memory_usage(); -// uint64_t get_runtime_checksum(); - virtual void * ldap_ctx_init() {return NULL;}; - virtual void ldap_ctx_free(void *) {}; - virtual char **get_variables_list() {return NULL;} - virtual bool has_variable(const char *name) {return false;}; + virtual void wrlock() {}; virtual void wrunlock() {}; + + virtual char **get_variables_list() {return NULL;} + virtual bool has_variable(const char *name) {return false;}; virtual char * get_variable(char *name) {return NULL;}; virtual bool set_variable(char *name, char *value) {return false;}; - virtual int password_matches(char *u, char *pass) {return 0;}; // 0 = not match , 1 = matches , 2 = not present + virtual void load_mysql_ldap_mapping(SQLite3_result *result) {}; virtual SQLite3_result * dump_table_mysql_ldap_mapping() { return NULL; }; virtual uint64_t get_ldap_mapping_runtime_checksum() { return 0; }; virtual SQLite3_result * SQL3_getStats() { return NULL; } + + virtual void print_version() {}; }; typedef MySQL_LDAP_Authentication * create_MySQL_LDAP_Authentication_t(); diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index bf2c72502..73d9d7dcb 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -249,8 +249,8 @@ class MySQL_Session Session_Regex **match_regexes; - void *ldap_ctx; ProxySQL_Node_Address * proxysql_node_address; // this is used ONLY for Admin, and only if the other party is another proxysql instance part of a cluster + bool use_ldap_auth; // this variable is relevant only if status == SETTING_VARIABLE enum variable_name changing_variable_idx; diff --git a/include/proxysql_glovars.hpp b/include/proxysql_glovars.hpp index 30e47bb68..ddd19eb2f 100644 --- a/include/proxysql_glovars.hpp +++ b/include/proxysql_glovars.hpp @@ -69,6 +69,8 @@ class ProxySQL_GlobalVariables { char * sqlite3_plugin; char * web_interface_plugin; char * ldap_auth_plugin; + SSL * get_SSL_ctx(); + void get_SSL_pem_mem(char **key, char **cert); std::shared_ptr prometheus_registry { nullptr }; struct { unsigned long long start_time; @@ -93,7 +95,12 @@ class ProxySQL_GlobalVariables { char *pidfile; bool restart_on_error; int restart_delay; + std::mutex ssl_mutex; SSL_CTX *ssl_ctx; + SSL_CTX *tmp_ssl_ctx; + // these two buffers are used for the web interface + char * ssl_key_pem_mem; + char * ssl_cert_pem_mem; bool sqlite3_server; #ifdef PROXYSQLCLICKHOUSE bool clickhouse_server; diff --git a/lib/MySQL_Monitor.cpp b/lib/MySQL_Monitor.cpp index 7120e177b..fb7e6eed4 100644 --- a/lib/MySQL_Monitor.cpp +++ b/lib/MySQL_Monitor.cpp @@ -2154,7 +2154,7 @@ void * monitor_replication_lag_thread(void *arg) { int l = strlen(percona_heartbeat_table); if (l) { use_percona_heartbeat = true; - char *base_query = (char *)"SELECT MIN(ROUND(TIMESTAMPDIFF(MICROSECOND, ts, SYSDATE(6))/1000000)) AS Seconds_Behind_Master FROM %s"; + char *base_query = (char *)"SELECT MAX(ROUND(TIMESTAMPDIFF(MICROSECOND, ts, SYSDATE(6))/1000000)) AS Seconds_Behind_Master FROM %s"; char *replication_query = (char *)malloc(strlen(base_query)+l); sprintf(replication_query,base_query,percona_heartbeat_table); mmsd->async_exit_status=mysql_query_start(&mmsd->interr,mmsd->mysql,replication_query); diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index c795ca14b..054d1e6cd 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -1501,26 +1501,26 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,pkt,len); } #endif - bool ret=false; + bool ret = false; unsigned int charset; uint32_t capabilities = 0; uint32_t max_pkt; uint32_t pass_len; - unsigned char *user=NULL; - char *db=NULL; + unsigned char *user = NULL; + char *db = NULL; char *db_tmp = NULL; unsigned char *pass = NULL; MySQL_Connection *myconn = NULL; - char *password=NULL; - bool use_ssl=false; - bool _ret_use_ssl=false; + char *password = NULL; + bool use_ssl = false; + bool _ret_use_ssl = false; unsigned char *auth_plugin = NULL; int auth_plugin_id = 0; char reply[SHA_DIGEST_LENGTH+1]; reply[SHA_DIGEST_LENGTH]='\0'; int default_hostgroup=-1; - char *default_schema=NULL; + char *default_schema = NULL; char *attributes = NULL; bool schema_locked; bool transaction_persistent = true; @@ -1811,7 +1811,7 @@ __do_auth: (*myds)->sess->session_fast_forward=fast_forward; (*myds)->sess->user_max_connections=max_connections; } - if (password==NULL) { + if (password == NULL) { // this is a workaround for bug #603 if ( ((*myds)->sess->session_type == PROXYSQL_SESSION_ADMIN) @@ -1854,8 +1854,10 @@ __do_auth: } #endif // debug char *backend_username = NULL; - (*myds)->sess->ldap_ctx = GloMyLdapAuth->ldap_ctx_init(); - password = GloMyLdapAuth->lookup((*myds)->sess->ldap_ctx, (char *)user, (char *)pass, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, &backend_username); + (*myds)->sess->use_ldap_auth = true; + password = GloMyLdapAuth->lookup((char *) user, (char *) pass, USERNAME_FRONTEND, + &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, + &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, &attributes, &backend_username); if (password) { #ifdef DEBUG char *tmp_pass=strdup(password); @@ -1868,7 +1870,7 @@ __do_auth: #endif // debug (*myds)->sess->default_hostgroup=default_hostgroup; (*myds)->sess->default_schema=default_schema; // just the pointer is passed - (*myds)->sess->user_attributes = attributes; // just the pointer is passed , but for now not available in LDAP + (*myds)->sess->user_attributes = attributes; // just the pointer is passed, LDAP returns empty string #ifdef DEBUG debug_spiffe_id(user,attributes, __LINE__, __func__); #endif @@ -1876,7 +1878,7 @@ __do_auth: (*myds)->sess->transaction_persistent=transaction_persistent; (*myds)->sess->session_fast_forward=fast_forward; (*myds)->sess->user_max_connections=max_connections; - if (strncmp(password,(char *)pass,strlen(password))==0) { + if (strcmp(password, (char *) pass) == 0) { if (backend_username) { free(password); password=NULL; @@ -1935,8 +1937,8 @@ __do_auth: ret=true; } } else { // mysql_clear_password - if (strncmp(password,(char *)pass,strlen(password))==0) { - ret=true; + if (strcmp(password, (char *) pass) == 0) { + ret = true; } } } else { diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 1d3285d21..adb7cfd58 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -504,8 +504,8 @@ MySQL_Session::MySQL_Session() { last_insert_id=0; // #1093 last_HG_affected_rows = -1; // #1421 : advanced support for LAST_INSERT_ID() - ldap_ctx = NULL; proxysql_node_address = NULL; + use_ldap_auth = false; } void MySQL_Session::init() { @@ -589,6 +589,7 @@ MySQL_Session::~MySQL_Session() { } if (user_attributes) { free(user_attributes); + user_attributes = NULL; } proxy_debug(PROXY_DEBUG_NET,1,"Thread=%p, Session=%p -- Shutdown Session %p\n" , this->thread, this, this); delete command_counters; @@ -602,10 +603,6 @@ MySQL_Session::~MySQL_Session() { __sync_sub_and_fetch(&GloMTH->status_variables.mirror_sessions_current,1); GloMTH->status_variables.p_gauge_array[p_th_gauge::mirror_concurrency]->Decrement(); } - if (ldap_ctx) { - GloMyLdapAuth->ldap_ctx_free(ldap_ctx); - ldap_ctx = NULL; - } if (proxysql_node_address) { delete proxysql_node_address; proxysql_node_address = NULL; @@ -2038,7 +2035,7 @@ bool MySQL_Session::handler_again___status_SETTING_LDAP_USER_VARIABLE(int *_rc) enum session_status st=status; if ( - (GloMyLdapAuth==NULL) || (ldap_ctx==NULL) + (GloMyLdapAuth==NULL) || (use_ldap_auth==false) || (client_myds==NULL || client_myds->myconn==NULL || client_myds->myconn->userinfo==NULL) ) { // nothing to do @@ -4380,7 +4377,7 @@ handler_again: if (handler_again___verify_init_connect()) { goto handler_again; } - if (ldap_ctx) { + if (use_ldap_auth) { if (handler_again___verify_ldap_user_variable()) { goto handler_again; } @@ -4835,7 +4832,7 @@ 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=SSL_new(GloVars.global.ssl_ctx); + client_myds->ssl = GloVars.get_SSL_ctx(); 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); @@ -4902,10 +4899,10 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( //#endif // TEST_AURORA || TEST_GALERA || TEST_GROUPREP case PROXYSQL_SESSION_MYSQL: proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , session_type=PROXYSQL_SESSION_MYSQL\n", this, client_myds); - if (ldap_ctx==NULL) { - free_users=GloMyAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->username, &used_users); + if (use_ldap_auth == false) { + free_users = GloMyAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->username, &used_users); } else { - free_users=GloMyLdapAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->username, &used_users); + free_users = GloMyLdapAuth->increase_frontend_user_connections(client_myds->myconn->userinfo->fe_username, &used_users); } break; #ifdef PROXYSQLCLICKHOUSE @@ -6186,10 +6183,10 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C reset(); init(); if (client_authenticated) { - if (ldap_ctx==NULL) { + if (use_ldap_auth == false) { GloMyAuth->decrease_frontend_user_connections(client_myds->myconn->userinfo->username); } else { - GloMyLdapAuth->decrease_frontend_user_connections(client_myds->myconn->userinfo->username); + GloMyLdapAuth->decrease_frontend_user_connections(client_myds->myconn->userinfo->fe_username); } } client_authenticated=false; @@ -6929,7 +6926,7 @@ bool MySQL_Session::handle_command_query_kill(PtrSize_t *pkt) { void MySQL_Session::add_ldap_comment_to_pkt(PtrSize_t *_pkt) { if (GloMyLdapAuth==NULL) return; - if (ldap_ctx==NULL) + if (use_ldap_auth == false) return; if (client_myds==NULL || client_myds->myconn==NULL || client_myds->myconn->userinfo==NULL) return; diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 6be74098b..f2ef64c4c 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -84,6 +84,7 @@ char * proxysql_version = NULL; MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char *name); +/* static long get_file_size (const char *filename) { FILE *fp; @@ -121,7 +122,7 @@ static char * load_file (const char *filename) { fclose (fp); return buffer; } - +*/ static int round_intv_to_time_interval(int& intv) { if (intv > 300) { @@ -299,6 +300,8 @@ extern SQLite3_Server *GloSQLite3Server; extern char * binary_sha1; +extern int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg); + #define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } pthread_mutex_t sock_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -1603,6 +1606,19 @@ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_ return false; } + if (strcasecmp("PROXYSQL RELOAD TLS",query_no_space) == 0) { + proxy_info("Received %s command\n", query_no_space); + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + std::string s; + int rc = ProxySQL_create_or_load_TLS(false, s); + if (rc == 0) { + SPA->send_MySQL_OK(&sess->client_myds->myprot, s.length() ? (char *)s.c_str() : NULL); + } else { + SPA->send_MySQL_ERR(&sess->client_myds->myprot, s.length() ? (char *)s.c_str() : (char *)"RELOAD TLS failed"); + } + return false; + } + #ifndef NOJEM if (query_no_space_length==strlen("PROXYSQL MEMPROFILE START") && !strncasecmp("PROXYSQL MEMPROFILE START",query_no_space, query_no_space_length)) { bool en=true; @@ -6175,8 +6191,7 @@ void ProxySQL_Admin::flush_admin_variables___database_to_runtime(SQLite3DB *db, if (GloVars.web_interface_plugin == NULL) { char *key_pem; char *cert_pem; - key_pem = load_file(ssl_key_fp); - cert_pem = load_file(ssl_cert_fp); + GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, variables.web_port, NULL, NULL, http_handler, NULL, @@ -6239,8 +6254,7 @@ void ProxySQL_Admin::flush_admin_variables___database_to_runtime(SQLite3DB *db, Admin_HTTP_Server = NULL; char *key_pem; char *cert_pem; - key_pem = load_file(ssl_key_fp); - cert_pem = load_file(ssl_cert_fp); + GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, variables.web_port, NULL, NULL, http_handler, NULL, diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 91a6d28ab..1324e8d96 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -167,26 +167,43 @@ enum sslstatus MySQL_Data_Stream::do_ssl_handshake() { int n = SSL_do_handshake(ssl); if (n == 1) { //proxy_info("SSL handshake completed\n"); - long rc = SSL_get_verify_result(ssl); - if (rc != X509_V_OK && rc != X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN && rc != X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) { - proxy_error("Disconnecting %s:%d: X509 client SSL certificate verify error: (%d:%s)\n" , addr.addr, addr.port, rc, X509_verify_cert_error_string(rc)); - return SSLSTATUS_FAIL; - } else { - X509 *cert; - cert = SSL_get_peer_certificate(ssl); - if (cert) { - ASN1_STRING *str; - GENERAL_NAME *sanName; - STACK_OF(GENERAL_NAME) *san_names = NULL; - san_names = (stack_st_GENERAL_NAME *)X509_get_ext_d2i((X509 *) cert, NID_subject_alt_name, NULL, NULL); - if (san_names) { - sanName = sk_GENERAL_NAME_value(san_names, 0); - str = sanName->d.dNSName; - proxy_info("%s\n" , str->data); - x509_subject_alt_name = strdup((const char*)str->data); + X509 *cert; + cert = SSL_get_peer_certificate(ssl); + if (cert) { + GENERAL_NAMES *alt_names = (stack_st_GENERAL_NAME *)X509_get_ext_d2i((X509*)cert, NID_subject_alt_name, 0, 0); + int alt_name_count = sk_GENERAL_NAME_num(alt_names); + + // Iterate all the SAN names, looking for SPIFFE identifier + for (int i = 0; i < alt_name_count; i++) { + GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i); + + // We only care about URI names + if (san->type == GEN_URI) { + if (san->d.uniformResourceIdentifier->data) { + const char* resource_data = + reinterpret_cast(san->d.uniformResourceIdentifier->data); + const char* spiffe_loc = strstr(resource_data, "spiffe"); + + // First name starting with 'spiffe' is considered the match. + if (spiffe_loc == resource_data) { + x509_subject_alt_name = strdup(resource_data); + } + } } - } else { - proxy_error("X509 error: no required certificate sent by client\n"); + } + } else { + // we currently disable this annoying error + // in future we can configure this as per user level, specifying if the certificate is mandatory or not + // see issue #3424 + //proxy_error("X509 error: no required certificate sent by client\n"); + } + // In case the supplied certificate has a 'SAN'-'URI' identifier + // starting with 'spiffe', client certificate verification is performed. + if (x509_subject_alt_name != NULL) { + long rc = SSL_get_verify_result(ssl); + if (rc != X509_V_OK) { + proxy_error("Disconnecting %s:%d: X509 client SSL certificate verify error: (%d:%s)\n" , addr.addr, addr.port, rc, X509_verify_cert_error_string(rc)); + return SSLSTATUS_FAIL; } } } @@ -349,6 +366,14 @@ MySQL_Data_Stream::~MySQL_Data_Stream() { } if ( (myconn) && (myds_type==MYDS_FRONTEND) ) { delete myconn; myconn=NULL; } if (encrypted) { + if (ssl) { + // NOTE: SSL standard requires a final 'close_notify' alert on socket + // shutdown. But for avoiding any kind of locking IO waiting for the + // other part, we perform a 'quiet' shutdown. For more context see + // MYSQL #29579. + SSL_set_quiet_shutdown(ssl, 1); + SSL_shutdown(ssl); + } if (ssl) SSL_free(ssl); /* SSL_free() should also take care of these @@ -428,7 +453,12 @@ void MySQL_Data_Stream::shut_hard() { proxy_debug(PROXY_DEBUG_NET, 4, "Shutdown hard fd=%d. Session=%p, DataStream=%p\n", fd, sess, this); set_net_failure(); if (encrypted) { + // NOTE: SSL standard requires a final 'close_notify' alert on socket + // shutdown. But for avoiding any kind of locking IO waiting for the + // other part, we perform a 'quiet' shutdown. For more context see + // MYSQL #29579. SSL_set_quiet_shutdown(ssl, 1); + SSL_shutdown(ssl); } if (fd >= 0) { shutdown(fd, SHUT_RDWR); diff --git a/src/Makefile b/src/Makefile index 53d3dcaf6..70987b269 100644 --- a/src/Makefile +++ b/src/Makefile @@ -153,7 +153,7 @@ ODIR= obj EXECUTABLE=proxysql -_OBJ = main.o proxysql_global.o SQLite3_Server.o +_OBJ = main.o proxysql_global.o SQLite3_Server.o proxy_tls.o OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) $(ODIR)/%.o: %.cpp diff --git a/src/main.cpp b/src/main.cpp index 65927fe29..fb2b34470 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,10 +58,8 @@ void * __mysql_ldap_auth; volatile create_Web_Interface_t * create_Web_Interface = NULL; void * __web_interface; -// absolute path of ssl files -char *ssl_key_fp = NULL; -char *ssl_cert_fp = NULL; -char *ssl_ca_fp = NULL; + +extern int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg); char *binary_sha1 = NULL; @@ -70,29 +68,6 @@ char *binary_sha1 = NULL; #undef dlerror #endif -struct dh_st { - int pad; - int version; - BIGNUM *p; - BIGNUM *g; - long length; - BIGNUM *pub_key; - BIGNUM *priv_key; - int flags; - BN_MONT_CTX *method_mont_p; - BIGNUM *q; - BIGNUM *j; - unsigned char *seed; - int seedlen; - BIGNUM *counter; - int references; - CRYPTO_EX_DATA ex_data; - const DH_METHOD *meth; - ENGINE *engine; - CRYPTO_RWLOCK *lock; -}; - - static pthread_mutex_t *lockarray; #include @@ -107,69 +82,6 @@ static void * waitpid_thread(void *arg) { } -/* - -generated with: $ openssl dhparam -5 -C 2048 - ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEAtS5UPzxesyj7QtLe6hRGE1Cv4TnDbSzKTmy0izFabdn0wR1QVmij -S8YSb1jE+O7IGImtk84Wg4y141PAHkCMTEeCMKH5tOD0WfiVyuQDTp4Vbt0vOReM -hK7tgLHLC1P3v0nxFCcce3U6IXmXBQ9IkNMFcXSRIAdBOjPkFPfbZ648qSgcoX+z -gfEP9WAXeeNGk62rDb3R0mguA9HcQ4NyKk6ETBVsZD4bTAcSIBaX05ISV7qY2eLj -9HFYBXYX4cxBfMyiqGrCj2IMg8aRKmf7rTvwBQXT0cWmu+kpnlpXIjx6vdpBmeKd -hSypLEcUVIvzc6rtfWlYKT35wQ+AGKNADwIBBQ== ------END DH PARAMETERS----- - -*/ - -int callback_ssl_verify_peer(int ok, X509_STORE_CTX* ctx) { - // for now only return 1 - return 1; -} - - - -#ifndef HEADER_DH_H -#include -#endif -DH *get_dh2048() - { - static unsigned char dh2048_p[]={ - 0xB5,0x2E,0x54,0x3F,0x3C,0x5E,0xB3,0x28,0xFB,0x42,0xD2,0xDE, - 0xEA,0x14,0x46,0x13,0x50,0xAF,0xE1,0x39,0xC3,0x6D,0x2C,0xCA, - 0x4E,0x6C,0xB4,0x8B,0x31,0x5A,0x6D,0xD9,0xF4,0xC1,0x1D,0x50, - 0x56,0x68,0xA3,0x4B,0xC6,0x12,0x6F,0x58,0xC4,0xF8,0xEE,0xC8, - 0x18,0x89,0xAD,0x93,0xCE,0x16,0x83,0x8C,0xB5,0xE3,0x53,0xC0, - 0x1E,0x40,0x8C,0x4C,0x47,0x82,0x30,0xA1,0xF9,0xB4,0xE0,0xF4, - 0x59,0xF8,0x95,0xCA,0xE4,0x03,0x4E,0x9E,0x15,0x6E,0xDD,0x2F, - 0x39,0x17,0x8C,0x84,0xAE,0xED,0x80,0xB1,0xCB,0x0B,0x53,0xF7, - 0xBF,0x49,0xF1,0x14,0x27,0x1C,0x7B,0x75,0x3A,0x21,0x79,0x97, - 0x05,0x0F,0x48,0x90,0xD3,0x05,0x71,0x74,0x91,0x20,0x07,0x41, - 0x3A,0x33,0xE4,0x14,0xF7,0xDB,0x67,0xAE,0x3C,0xA9,0x28,0x1C, - 0xA1,0x7F,0xB3,0x81,0xF1,0x0F,0xF5,0x60,0x17,0x79,0xE3,0x46, - 0x93,0xAD,0xAB,0x0D,0xBD,0xD1,0xD2,0x68,0x2E,0x03,0xD1,0xDC, - 0x43,0x83,0x72,0x2A,0x4E,0x84,0x4C,0x15,0x6C,0x64,0x3E,0x1B, - 0x4C,0x07,0x12,0x20,0x16,0x97,0xD3,0x92,0x12,0x57,0xBA,0x98, - 0xD9,0xE2,0xE3,0xF4,0x71,0x58,0x05,0x76,0x17,0xE1,0xCC,0x41, - 0x7C,0xCC,0xA2,0xA8,0x6A,0xC2,0x8F,0x62,0x0C,0x83,0xC6,0x91, - 0x2A,0x67,0xFB,0xAD,0x3B,0xF0,0x05,0x05,0xD3,0xD1,0xC5,0xA6, - 0xBB,0xE9,0x29,0x9E,0x5A,0x57,0x22,0x3C,0x7A,0xBD,0xDA,0x41, - 0x99,0xE2,0x9D,0x85,0x2C,0xA9,0x2C,0x47,0x14,0x54,0x8B,0xF3, - 0x73,0xAA,0xED,0x7D,0x69,0x58,0x29,0x3D,0xF9,0xC1,0x0F,0x80, - 0x18,0xA3,0x40,0x0F, - }; - static unsigned char dh2048_g[]={ - 0x05, - }; - DH *dh; - - if ((dh=DH_new()) == NULL) return(NULL); - dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); - dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - { DH_free(dh); return(NULL); } - return(dh); -} struct MemoryStruct { char *memory; @@ -347,255 +259,9 @@ static void init_locks(void) { //CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))lock_callback); } -X509 * generate_x509(EVP_PKEY *pkey, const unsigned char *cn, uint32_t serial, int days, X509 *ca_x509, EVP_PKEY *ca_pkey) { - int rc; - X509 * x = NULL; - X509_NAME * name= NULL; - X509_EXTENSION* ext = NULL; - X509V3_CTX v3_ctx; - if ((x = X509_new()) == NULL) { - proxy_error("Unable to run X509_new()\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - X509_set_version(x, 2); - ASN1_INTEGER_set(X509_get_serialNumber(x), serial); - X509_gmtime_adj(X509_get_notBefore(x), 0); - X509_gmtime_adj(X509_get_notAfter(x), (long)60 * 60 * 24 * days); - rc = X509_set_pubkey(x, pkey); - if (rc==0){ - proxy_error("Unable to set pubkey: %s\n", ERR_error_string(ERR_get_error(),NULL)); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - name = X509_get_subject_name(x); - - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cn, -1, -1, 0); - - if (ca_x509) { - rc = X509_set_issuer_name(x, X509_get_subject_name(ca_x509)); - } else { - rc = X509_set_issuer_name(x, name); - } - if (rc==0) { - proxy_error("Unable to set issuer: %s\n", ERR_error_string(ERR_get_error(),NULL)); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - - // set the context - X509V3_set_ctx(&v3_ctx, ca_x509 ? ca_x509 : x, x, NULL, NULL, 0); - - ext = X509V3_EXT_conf_nid( - NULL, &v3_ctx, NID_basic_constraints, ca_x509 ? "critical, CA:FALSE" : "critical, CA:TRUE"); - if (ext) { - X509_add_ext(x, ext, -1); - X509_EXTENSION_free(ext); - } else { - proxy_error("Unable to set certificate extensions: %s\n", ERR_error_string(ERR_get_error(),NULL)); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - - if (ca_pkey) { - rc = X509_sign(x, ca_pkey, EVP_sha256()); - } else { - rc = X509_sign(x, pkey, EVP_sha256()); - } - if (rc==0) { - proxy_error("Unable to X509 sign: %s\n", ERR_error_string(ERR_get_error(),NULL)); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - return x; -} - -void write_x509(const char *filen, X509 *x) { - BIO * x509file = NULL; - x509file = BIO_new_file(filen, "w" ); - if (!x509file ) { - proxy_error("Error on BIO_new_file\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - if (!PEM_write_bio_X509( x509file, x)) { - proxy_error("Error on PEM_write_bio_X509 for %s\n", filen); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - BIO_free_all( x509file ); -} - -void write_rsa_key(const char *filen, RSA *rsa) { - BIO* pOut = BIO_new_file(filen, "w"); - if (!pOut) { - proxy_error("Error on BIO_new_file\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - if (!PEM_write_bio_RSAPrivateKey( pOut, rsa, NULL, NULL, 0, NULL, NULL)) { - proxy_error("Error on PEM_write_bio_RSAPrivateKey for %s\n", filen); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - BIO_free_all( pOut ); -} - -EVP_PKEY * rsa_key_read(const char *filen) { - EVP_PKEY * pkey = NULL; - RSA * rsa = NULL; - BIO * pIn = BIO_new_file(filen,"r"); - if (!pIn) { - proxy_error("Error on BIO_new_file\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - rsa= PEM_read_bio_RSAPrivateKey( pIn , NULL, NULL, NULL); - if (rsa==NULL) { - proxy_error("Error on PEM_read_bio_RSAPrivateKey for %s\n", filen); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - pkey = EVP_PKEY_new(); - EVP_PKEY_assign_RSA(pkey, rsa); - BIO_free(pIn); - return pkey; -} - -X509 * read_x509(const char *filen) { - X509 * x = NULL; - BIO * x509file = NULL; - x509file = BIO_new_file(filen, "r" ); - if (!x509file ) { - proxy_error("Error on BIO_new_file\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - x = PEM_read_bio_X509( x509file, NULL, NULL, NULL); - if (x == NULL) { - proxy_error("Error on PEM_read_bio_X509 for %s\n", filen); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - BIO_free_all( x509file ); - return x; -} -int ssl_mkit(X509 **x509ca, X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days) { - X509 *x1; - X509 *x2; - EVP_PKEY *pk; - RSA *rsa; - DH *dh; - //X509_NAME *name = NULL; - - // relative path to datadir of ssl files - const char * ssl_key_rp = (const char *)"proxysql-key.pem"; - const char * ssl_cert_rp = (const char *)"proxysql-cert.pem"; - const char * ssl_ca_rp = (const char *)"proxysql-ca.pem"; - -/* - // absolute path of ssl files - char *ssl_key_fp = NULL; - char *ssl_cert_fp = NULL; - char *ssl_ca_fp = NULL; -*/ - // how many files exists ? - int nfiles = 0; - bool ssl_key_exists = true; - bool ssl_cert_exists = true; - bool ssl_ca_exists = true; - - // check if files exists - ssl_key_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_key_rp)+8); - sprintf(ssl_key_fp,"%s/%s",GloVars.datadir,ssl_key_rp); - if (access(ssl_key_fp, R_OK)) { - ssl_key_exists = false; - //free(ssl_key); - //ssl_key = NULL; - } - - ssl_cert_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_cert_rp)+8); - sprintf(ssl_cert_fp,"%s/%s",GloVars.datadir,ssl_cert_rp); - if (access(ssl_cert_fp, R_OK)) { - ssl_cert_exists = false; - //free(ssl_cert); - //ssl_cert = NULL; - } - - ssl_ca_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_ca_rp)+8); - sprintf(ssl_ca_fp,"%s/%s",GloVars.datadir,ssl_ca_rp); - if (access(ssl_ca_fp, R_OK)) { - ssl_ca_exists = false; - //free(ssl_ca); - //ssl_ca = NULL; - } - - nfiles += (ssl_key_exists ? 1 : 0); - nfiles += (ssl_cert_exists ? 1 : 0); - nfiles += (ssl_ca_exists ? 1 : 0); - - if ((nfiles != 0 && nfiles != 3)) { - proxy_error("Only some SSL files are present. Either all files are present, or none. Exiting.\n"); - proxy_error("%s : %s\n" , ssl_key_rp, (ssl_key_exists ? (char *)"YES" : (char *)"NO")); - proxy_error("%s : %s\n" , ssl_cert_rp, (ssl_cert_exists ? (char *)"YES" : (char *)"NO")); - proxy_error("%s : %s\n" , ssl_ca_rp, (ssl_ca_exists ? (char *)"YES" : (char *)"NO")); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - - if (nfiles == 0) { - proxy_info("No SSL keys/certificates found in datadir (%s). Generating new keys/certificates.\n", GloVars.datadir); - if ((pkeyp == NULL) || (*pkeyp == NULL)) { - if ((pk = EVP_PKEY_new()) == NULL) { - proxy_error("Unable to run EVP_PKEY_new()\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - } else - pk = *pkeyp; - - rsa = RSA_new(); - - if (!rsa) { - proxy_error("Unable to run RSA_new()\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - BIGNUM *e= BN_new(); - if (!e) { - proxy_error("Unable to run BN_new()\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - if (!BN_set_word(e, RSA_F4) || !RSA_generate_key_ex(rsa, bits, e, NULL)) { - RSA_free(rsa); - BN_free(e); - proxy_error("Unable to run BN_new()\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - BN_free(e); - - - write_rsa_key(ssl_key_fp, rsa); - - if (!EVP_PKEY_assign_RSA(pk, rsa)) { - proxy_error("Unable to run EVP_PKEY_assign_RSA()\n"); - exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted - } - time_t t = time(NULL); - x1 = generate_x509(pk, (const unsigned char *)"ProxySQL_Auto_Generated_CA_Certificate", t, 3650, NULL, NULL); - write_x509(ssl_ca_fp, x1); - x2 = generate_x509(pk, (const unsigned char *)"ProxySQL_Auto_Generated_Server_Certificate", t, 3650, x1, pk); - write_x509(ssl_cert_fp, x2); - - rsa = NULL; - } else { - proxy_info("SSL keys/certificates found in datadir (%s): loading them.\n", GloVars.datadir); - pk = rsa_key_read(ssl_key_fp); - x1 = read_x509(ssl_ca_fp); - x2 = read_x509(ssl_cert_fp); - } - *x509ca = x1; - *x509p = x2; - *pkeyp = pk; - - dh = get_dh2048(); - - if (SSL_CTX_set_tmp_dh(GloVars.global.ssl_ctx, dh) == 0) { - proxy_error("Error in SSL while initializing DH: %s . Shutting down.\n",ERR_error_string(ERR_get_error(), NULL)); - exit(EXIT_SUCCESS); // EXIT_SUCCESS to avoid a restart loop - } - - - return 1; -} - void ProxySQL_Main_init_SSL_module() { int rc = SSL_library_init(); if (rc==0) { @@ -630,51 +296,14 @@ void ProxySQL_Main_init_SSL_module() { char buf[130]; for(int i = 0; i < num; i++){ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i); - fprintf(stderr,"%s: %s\n", SSL_CIPHER_get_name(cipher), SSL_CIPHER_description(cipher, buf, 128)); + fprintf(stderr,"%s: %s", SSL_CIPHER_get_name(cipher), SSL_CIPHER_description(cipher, buf, 128)); } } + fprintf(stderr,"\n"); } #endif - BIO *bio_err; - X509 *x509 = NULL; - X509 *x509ca = NULL; - EVP_PKEY *pkey = NULL; - - CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF); - - bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); - - if (ssl_mkit(&x509ca, &x509, &pkey, 2048, 0, 730) == 0) { - proxy_error("Unable to initialize SSL. Shutting down...\n"); - exit(EXIT_SUCCESS); // we exit gracefully to not be restarted - } - - if ( SSL_CTX_use_certificate(GloVars.global.ssl_ctx, x509) <= 0 ) { - ERR_print_errors_fp(stderr); - proxy_error("Unable to use SSL certificate. Shutting down...\n"); - exit(EXIT_SUCCESS); // we exit gracefully to not be restarted - } - if ( SSL_CTX_add_extra_chain_cert(GloVars.global.ssl_ctx, x509ca) <= 0 ) { - ERR_print_errors_fp(stderr); - proxy_error("Unable to use SSL CA chain. Shutting down...\n"); - exit(EXIT_SUCCESS); // we exit gracefully to not be restarted - } - if ( SSL_CTX_use_PrivateKey(GloVars.global.ssl_ctx, pkey) <= 0 ) { - ERR_print_errors_fp(stderr); - proxy_error("Unable to use SSL key. Shutting down...\n"); - exit(EXIT_SUCCESS); // we exit gracefully to not be restarted - } - if ( !SSL_CTX_check_private_key(GloVars.global.ssl_ctx) ) { - proxy_error("Private key does not match the public certificate\n"); - exit(EXIT_SUCCESS); // we exit gracefully to not be restarted - } - - SSL_CTX_set_verify(GloVars.global.ssl_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, callback_ssl_verify_peer); - X509_free(x509); - EVP_PKEY_free(pkey); - - - BIO_free(bio_err); + std::string msg = ""; + ProxySQL_create_or_load_TLS(true, msg); } diff --git a/src/proxy_tls.cpp b/src/proxy_tls.cpp new file mode 100644 index 000000000..eff307a11 --- /dev/null +++ b/src/proxy_tls.cpp @@ -0,0 +1,574 @@ +//#include +//#include +#include "proxysql.h" + +//#include +//#include + +#include "cpp.h" + +//#include "ProxySQL_Statistics.hpp" +//#include "MySQL_PreparedStatement.h" +//#include "ProxySQL_Cluster.hpp" +//#include "MySQL_Logger.hpp" +//#include "SQLite3_Server.h" +//#include "query_processor.h" +//#include "MySQL_Authentication.hpp" +//#include "MySQL_LDAP_Authentication.hpp" +//#include "proxysql_restapi.h" +//#include "Web_Interface.hpp" + + + +#include + +static long +get_file_size (const char *filename) { + FILE *fp; + fp = fopen (filename, "rb"); + if (fp) { + long size; + if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp)))) + size = 0; + fclose (fp); + return size; + } else + return 0; +} + +static char * load_file (const char *filename) { + FILE *fp; + char *buffer; + long size; + size = get_file_size (filename); + if (0 == size) + return NULL; + fp = fopen (filename, "rb"); + if (! fp) + return NULL; + buffer = (char *)malloc (size + 1); + if (! buffer) { + fclose (fp); + return NULL; + } + buffer[size] = '\0'; + if (size != (long)fread (buffer, 1, size, fp)) { + free (buffer); + buffer = NULL; + } + fclose (fp); + return buffer; +} + +// absolute path of ssl files +static char *ssl_key_fp = NULL; +static char *ssl_cert_fp = NULL; +static char *ssl_ca_fp = NULL; + +struct dh_st { + int pad; + int version; + BIGNUM *p; + BIGNUM *g; + long length; + BIGNUM *pub_key; + BIGNUM *priv_key; + int flags; + BN_MONT_CTX *method_mont_p; + BIGNUM *q; + BIGNUM *j; + unsigned char *seed; + int seedlen; + BIGNUM *counter; + int references; + CRYPTO_EX_DATA ex_data; + const DH_METHOD *meth; + ENGINE *engine; + CRYPTO_RWLOCK *lock; +}; + +int callback_ssl_verify_peer(int ok, X509_STORE_CTX* ctx) { + // for now only return 1 + return 1; +} + +/* + +generated with: $ openssl dhparam -5 -C 2048 + +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAtS5UPzxesyj7QtLe6hRGE1Cv4TnDbSzKTmy0izFabdn0wR1QVmij +S8YSb1jE+O7IGImtk84Wg4y141PAHkCMTEeCMKH5tOD0WfiVyuQDTp4Vbt0vOReM +hK7tgLHLC1P3v0nxFCcce3U6IXmXBQ9IkNMFcXSRIAdBOjPkFPfbZ648qSgcoX+z +gfEP9WAXeeNGk62rDb3R0mguA9HcQ4NyKk6ETBVsZD4bTAcSIBaX05ISV7qY2eLj +9HFYBXYX4cxBfMyiqGrCj2IMg8aRKmf7rTvwBQXT0cWmu+kpnlpXIjx6vdpBmeKd +hSypLEcUVIvzc6rtfWlYKT35wQ+AGKNADwIBBQ== +-----END DH PARAMETERS----- + +*/ + + + +#ifndef HEADER_DH_H +#include +#endif +DH *get_dh2048() + { + static unsigned char dh2048_p[]={ + 0xB5,0x2E,0x54,0x3F,0x3C,0x5E,0xB3,0x28,0xFB,0x42,0xD2,0xDE, + 0xEA,0x14,0x46,0x13,0x50,0xAF,0xE1,0x39,0xC3,0x6D,0x2C,0xCA, + 0x4E,0x6C,0xB4,0x8B,0x31,0x5A,0x6D,0xD9,0xF4,0xC1,0x1D,0x50, + 0x56,0x68,0xA3,0x4B,0xC6,0x12,0x6F,0x58,0xC4,0xF8,0xEE,0xC8, + 0x18,0x89,0xAD,0x93,0xCE,0x16,0x83,0x8C,0xB5,0xE3,0x53,0xC0, + 0x1E,0x40,0x8C,0x4C,0x47,0x82,0x30,0xA1,0xF9,0xB4,0xE0,0xF4, + 0x59,0xF8,0x95,0xCA,0xE4,0x03,0x4E,0x9E,0x15,0x6E,0xDD,0x2F, + 0x39,0x17,0x8C,0x84,0xAE,0xED,0x80,0xB1,0xCB,0x0B,0x53,0xF7, + 0xBF,0x49,0xF1,0x14,0x27,0x1C,0x7B,0x75,0x3A,0x21,0x79,0x97, + 0x05,0x0F,0x48,0x90,0xD3,0x05,0x71,0x74,0x91,0x20,0x07,0x41, + 0x3A,0x33,0xE4,0x14,0xF7,0xDB,0x67,0xAE,0x3C,0xA9,0x28,0x1C, + 0xA1,0x7F,0xB3,0x81,0xF1,0x0F,0xF5,0x60,0x17,0x79,0xE3,0x46, + 0x93,0xAD,0xAB,0x0D,0xBD,0xD1,0xD2,0x68,0x2E,0x03,0xD1,0xDC, + 0x43,0x83,0x72,0x2A,0x4E,0x84,0x4C,0x15,0x6C,0x64,0x3E,0x1B, + 0x4C,0x07,0x12,0x20,0x16,0x97,0xD3,0x92,0x12,0x57,0xBA,0x98, + 0xD9,0xE2,0xE3,0xF4,0x71,0x58,0x05,0x76,0x17,0xE1,0xCC,0x41, + 0x7C,0xCC,0xA2,0xA8,0x6A,0xC2,0x8F,0x62,0x0C,0x83,0xC6,0x91, + 0x2A,0x67,0xFB,0xAD,0x3B,0xF0,0x05,0x05,0xD3,0xD1,0xC5,0xA6, + 0xBB,0xE9,0x29,0x9E,0x5A,0x57,0x22,0x3C,0x7A,0xBD,0xDA,0x41, + 0x99,0xE2,0x9D,0x85,0x2C,0xA9,0x2C,0x47,0x14,0x54,0x8B,0xF3, + 0x73,0xAA,0xED,0x7D,0x69,0x58,0x29,0x3D,0xF9,0xC1,0x0F,0x80, + 0x18,0xA3,0x40,0x0F, + }; + static unsigned char dh2048_g[]={ + 0x05, + }; + DH *dh; + + if ((dh=DH_new()) == NULL) return(NULL); + dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); + dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); + if ((dh->p == NULL) || (dh->g == NULL)) + { DH_free(dh); return(NULL); } + return(dh); +} + + + +X509 * generate_x509(EVP_PKEY *pkey, const unsigned char *cn, uint32_t serial, int days, X509 *ca_x509, EVP_PKEY *ca_pkey) { + int rc; + X509 * x = NULL; + X509_NAME * name= NULL; + X509_EXTENSION* ext = NULL; + X509V3_CTX v3_ctx; + if ((x = X509_new()) == NULL) { + proxy_error("Unable to run X509_new()\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + X509_set_version(x, 2); + ASN1_INTEGER_set(X509_get_serialNumber(x), serial); + X509_gmtime_adj(X509_get_notBefore(x), 0); + X509_gmtime_adj(X509_get_notAfter(x), (long)60 * 60 * 24 * days); + rc = X509_set_pubkey(x, pkey); + if (rc==0){ + proxy_error("Unable to set pubkey: %s\n", ERR_error_string(ERR_get_error(),NULL)); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + name = X509_get_subject_name(x); + + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cn, -1, -1, 0); + + if (ca_x509) { + rc = X509_set_issuer_name(x, X509_get_subject_name(ca_x509)); + } else { + rc = X509_set_issuer_name(x, name); + } + if (rc==0) { + proxy_error("Unable to set issuer: %s\n", ERR_error_string(ERR_get_error(),NULL)); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + + // set the context + X509V3_set_ctx(&v3_ctx, ca_x509 ? ca_x509 : x, x, NULL, NULL, 0); + + ext = X509V3_EXT_conf_nid( + NULL, &v3_ctx, NID_basic_constraints, ca_x509 ? "critical, CA:FALSE" : "critical, CA:TRUE"); + if (ext) { + X509_add_ext(x, ext, -1); + X509_EXTENSION_free(ext); + } else { + proxy_error("Unable to set certificate extensions: %s\n", ERR_error_string(ERR_get_error(),NULL)); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + + if (ca_pkey) { + rc = X509_sign(x, ca_pkey, EVP_sha256()); + } else { + rc = X509_sign(x, pkey, EVP_sha256()); + } + if (rc==0) { + proxy_error("Unable to X509 sign: %s\n", ERR_error_string(ERR_get_error(),NULL)); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + return x; +} + +void write_x509(const char *filen, X509 *x) { + BIO * x509file = NULL; + x509file = BIO_new_file(filen, "w" ); + if (!x509file ) { + proxy_error("Error on BIO_new_file\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + if (!PEM_write_bio_X509( x509file, x)) { + proxy_error("Error on PEM_write_bio_X509 for %s\n", filen); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + BIO_free_all( x509file ); +} + +void write_rsa_key(const char *filen, RSA *rsa) { + BIO* pOut = BIO_new_file(filen, "w"); + if (!pOut) { + proxy_error("Error on BIO_new_file\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + if (!PEM_write_bio_RSAPrivateKey( pOut, rsa, NULL, NULL, 0, NULL, NULL)) { + proxy_error("Error on PEM_write_bio_RSAPrivateKey for %s\n", filen); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + BIO_free_all( pOut ); +} + + +EVP_PKEY * proxy_key_read(const char *filen, bool bootstrap, std::string& msg) { + EVP_PKEY * pkey = NULL; + + BIO * pIn = BIO_new_file(filen,"r"); + if (!pIn) { + proxy_error("Error on BIO_new_file() while reading %s\n", filen); + if (bootstrap == true) { + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } else { + msg = "Error on BIO_new_file() while reading " + std::string(filen); + return pkey; + } + } + pkey = PEM_read_bio_PrivateKey( pIn , NULL, NULL, NULL); + if (pkey == NULL) { + proxy_error("Error on PEM_read_bio_PrivateKey for %s\n", filen); + if (bootstrap == true) { + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } else { + msg = "Error on PEM_read_bio_PrivateKey() for " + std::string(filen); + BIO_free(pIn); + return pkey; + } + } + BIO_free(pIn); + return pkey; +} + +X509 * proxy_read_x509(const char *filen, bool bootstrap, std::string& msg) { + X509 * x = NULL; + BIO * x509file = NULL; + x509file = BIO_new_file(filen, "r" ); + if (!x509file ) { + proxy_error("Error on BIO_new_file() while reading %s\n", filen); + if (bootstrap == true) { + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } else { + msg = "Error on BIO_new_file() while reading " + std::string(filen); + return x; + } + } + x = PEM_read_bio_X509( x509file, NULL, NULL, NULL); + if (x == NULL) { + proxy_error("Error on PEM_read_bio_X509 for %s\n", filen); + if (bootstrap == true) { + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } else { + msg = "Error on PEM_read_bio_X509() for " + std::string(filen); + BIO_free_all(x509file); + return x; + } + } + BIO_free_all( x509file ); + return x; +} + +// return 0 un success +int ssl_mkit(X509 **x509ca, X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days, bool bootstrap, std::string& msg) { + X509 *x1; + X509 *x2; + EVP_PKEY *pk; + RSA *rsa; + DH *dh; + //X509_NAME *name = NULL; + + // relative path to datadir of ssl files + const char * ssl_key_rp = (const char *)"proxysql-key.pem"; + const char * ssl_cert_rp = (const char *)"proxysql-cert.pem"; + const char * ssl_ca_rp = (const char *)"proxysql-ca.pem"; + + // how many files exists ? + int nfiles = 0; + bool ssl_key_exists = true; + bool ssl_cert_exists = true; + bool ssl_ca_exists = true; + + // check if files exists + if (bootstrap == true) { + ssl_key_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_key_rp)+8); + sprintf(ssl_key_fp,"%s/%s",GloVars.datadir,ssl_key_rp); + } + if (access(ssl_key_fp, R_OK)) { + ssl_key_exists = false; + } + + if (bootstrap == true) { + ssl_cert_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_cert_rp)+8); + sprintf(ssl_cert_fp,"%s/%s",GloVars.datadir,ssl_cert_rp); + } + if (access(ssl_cert_fp, R_OK)) { + ssl_cert_exists = false; + } + + if (bootstrap == true) { + ssl_ca_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_ca_rp)+8); + sprintf(ssl_ca_fp,"%s/%s",GloVars.datadir,ssl_ca_rp); + } + if (access(ssl_ca_fp, R_OK)) { + ssl_ca_exists = false; + } + + nfiles += (ssl_key_exists ? 1 : 0); + nfiles += (ssl_cert_exists ? 1 : 0); + nfiles += (ssl_ca_exists ? 1 : 0); + + if ( + (bootstrap == true && (nfiles != 0 && nfiles != 3)) + || + (bootstrap == false && (nfiles != 3)) + ) { + if (bootstrap == true) { + proxy_error("Only some SSL files are present. Either all files are present, or none. Exiting.\n"); + } else { + proxy_error("Aborting PROXYSQL RELOAD TLS because not all SSL files are present\n"); + } + proxy_error("%s : %s\n" , ssl_key_rp, (ssl_key_exists ? (char *)"YES" : (char *)"NO")); + proxy_error("%s : %s\n" , ssl_cert_rp, (ssl_cert_exists ? (char *)"YES" : (char *)"NO")); + proxy_error("%s : %s\n" , ssl_ca_rp, (ssl_ca_exists ? (char *)"YES" : (char *)"NO")); + + if (bootstrap == true) { + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } else { + msg = "RELOAD TLS failed: " + std::to_string(nfiles) + " TLS files are present. Expected: 3"; + return 1; + } + } + + if (bootstrap == true && nfiles == 0) { + proxy_info("No SSL keys/certificates found in datadir (%s). Generating new keys/certificates.\n", GloVars.datadir); + if ((pkeyp == NULL) || (*pkeyp == NULL)) { + if ((pk = EVP_PKEY_new()) == NULL) { + proxy_error("Unable to run EVP_PKEY_new()\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + } else + pk = *pkeyp; + + rsa = RSA_new(); + + if (!rsa) { + proxy_error("Unable to run RSA_new()\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + BIGNUM *e= BN_new(); + if (!e) { + proxy_error("Unable to run BN_new()\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + if (!BN_set_word(e, RSA_F4) || !RSA_generate_key_ex(rsa, bits, e, NULL)) { + RSA_free(rsa); + BN_free(e); + proxy_error("Unable to run BN_new()\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + BN_free(e); + + + write_rsa_key(ssl_key_fp, rsa); + + if (!EVP_PKEY_assign_RSA(pk, rsa)) { + proxy_error("Unable to run EVP_PKEY_assign_RSA()\n"); + exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted + } + time_t t = time(NULL); + x1 = generate_x509(pk, (const unsigned char *)"ProxySQL_Auto_Generated_CA_Certificate", t, 3650, NULL, NULL); + write_x509(ssl_ca_fp, x1); + x2 = generate_x509(pk, (const unsigned char *)"ProxySQL_Auto_Generated_Server_Certificate", t, 3650, x1, pk); + write_x509(ssl_cert_fp, x2); + + rsa = NULL; + } else { + proxy_info("SSL keys/certificates found in datadir (%s): loading them.\n", GloVars.datadir); + if (bootstrap == true) { + // during bootstrap we just call the reads + // if the read fails during bootstrap, proxysql immediately exists + pk = proxy_key_read(ssl_key_fp, bootstrap, msg); + x1 = proxy_read_x509(ssl_ca_fp, bootstrap, msg); + x2 = proxy_read_x509(ssl_cert_fp, bootstrap, msg); + } else { + pk = proxy_key_read(ssl_key_fp, bootstrap, msg); + if (pk) { + x1 = proxy_read_x509(ssl_ca_fp, bootstrap, msg); + if (x1) { + x2 = proxy_read_x509(ssl_cert_fp, bootstrap, msg); + } + } + // note that this is only relevant during PROXYSQL RELOAD TLS + if (pk == NULL || x1 == NULL || x2 == NULL) { + return 1; + } + } + } + *x509ca = x1; + *x509p = x2; + *pkeyp = pk; + + dh = get_dh2048(); + + if (bootstrap == true) { + if (SSL_CTX_set_tmp_dh(GloVars.global.ssl_ctx, dh) == 0) { + proxy_error("Error in SSL while initializing DH: %s . Shutting down.\n",ERR_error_string(ERR_get_error(), NULL)); + exit(EXIT_SUCCESS); // EXIT_SUCCESS to avoid a restart loop + } + } else { + SSL_METHOD *ssl_method; + ssl_method = (SSL_METHOD *)TLS_server_method(); + GloVars.global.tmp_ssl_ctx = SSL_CTX_new(ssl_method); + if (SSL_CTX_set_tmp_dh(GloVars.global.tmp_ssl_ctx, dh) == 0) { + proxy_error("Aborting PROXYSQL RELOAD TLS. Error in SSL while initializing DH: %s\n",ERR_error_string(ERR_get_error(), NULL)); + msg = "RELOAD TLS failed: Error initializing DH. "; + msg += ERR_error_string(ERR_get_error(), NULL); + return 1; + } + } + + return 0; +} + +int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg) { + BIO *bio_err; + X509 *x509 = NULL; + X509 *x509ca = NULL; + EVP_PKEY *pkey = NULL; + + int ret = 0; + + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF); + + bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); + + if (bootstrap == true) { + // this is legacy code, when keys are loaded only during bootstrap + if (ssl_mkit(&x509ca, &x509, &pkey, 2048, 0, 730, true, msg) != 0) { + proxy_error("Unable to initialize SSL. Shutting down...\n"); + exit(EXIT_SUCCESS); // we exit gracefully to not be restarted + } + + if ( SSL_CTX_use_certificate(GloVars.global.ssl_ctx, x509) <= 0 ) { + ERR_print_errors_fp(stderr); + proxy_error("Unable to use SSL certificate. Shutting down...\n"); + exit(EXIT_SUCCESS); // we exit gracefully to not be restarted + } + if ( SSL_CTX_add_extra_chain_cert(GloVars.global.ssl_ctx, x509ca) <= 0 ) { + ERR_print_errors_fp(stderr); + proxy_error("Unable to use SSL CA chain. Shutting down...\n"); + exit(EXIT_SUCCESS); // we exit gracefully to not be restarted + } + if ( SSL_CTX_use_PrivateKey(GloVars.global.ssl_ctx, pkey) <= 0 ) { + ERR_print_errors_fp(stderr); + proxy_error("Unable to use SSL key. Shutting down...\n"); + exit(EXIT_SUCCESS); // we exit gracefully to not be restarted + } + if ( !SSL_CTX_check_private_key(GloVars.global.ssl_ctx) ) { + proxy_error("Private key does not match the public certificate\n"); + exit(EXIT_SUCCESS); // we exit gracefully to not be restarted + } + GloVars.global.ssl_key_pem_mem = load_file(ssl_key_fp); + GloVars.global.ssl_cert_pem_mem = load_file(ssl_cert_fp); + + // We set the locations for the certificates to be used for + // verifications purposes. + if (!SSL_CTX_load_verify_locations(GloVars.global.ssl_ctx, ssl_ca_fp, ssl_ca_fp)) { + proxy_error("Unable to load CA certificates location for verification. Shutting down\n"); + exit(EXIT_SUCCESS); // we exit gracefully to not be restarted + } + } else { + // here we use global.tmp_ssl_ctx instead of global.ssl_ctx + // because we will try to swap at the end + if (ssl_mkit(&x509ca, &x509, &pkey, 2048, 0, 730, false, msg) == 0) { // 0 on success + if (SSL_CTX_use_certificate(GloVars.global.tmp_ssl_ctx, x509) == 1) { // 1 on success + if (SSL_CTX_add_extra_chain_cert(GloVars.global.tmp_ssl_ctx, x509ca) == 1) { // 1 on success + if (SSL_CTX_use_PrivateKey(GloVars.global.tmp_ssl_ctx, pkey) == 1) { // 1 on success + if (SSL_CTX_check_private_key(GloVars.global.tmp_ssl_ctx) == 1) { // 1 on success + if (SSL_CTX_load_verify_locations(GloVars.global.tmp_ssl_ctx, ssl_ca_fp, ssl_ca_fp) == 1) { // 1 on success + + // take the mutex + std::lock_guard lock(GloVars.global.ssl_mutex); + // note: we don't free the current SSL context, perhaps used by some connections + // swap the SSL context + GloVars.global.ssl_ctx = GloVars.global.tmp_ssl_ctx; + GloVars.global.tmp_ssl_ctx = NULL; + free(GloVars.global.ssl_key_pem_mem); + free(GloVars.global.ssl_cert_pem_mem); + GloVars.global.ssl_key_pem_mem = load_file(ssl_key_fp); + GloVars.global.ssl_cert_pem_mem = load_file(ssl_cert_fp); + + } else { + proxy_error("Failed to load location of CA certificates for verification\n"); + msg = "Unable to load CA certificates location for verification"; + ret = 1; + } + } else { + proxy_error("Private key does not match the public certificate\n"); + msg = "Private key does not match the public certificate"; + ret = 1; + } + } else { + ERR_print_errors_fp(stderr); + proxy_error("Unable to use SSL key\n"); + msg = "Unable to use SSL key"; + ret = 1; + } + } else { + ERR_print_errors_fp(stderr); + proxy_error("Unable to use SSL CA chain\n"); + msg = "Unable to use SSL CA chain"; + ret = 1; + } + } else { + ERR_print_errors_fp(stderr); + proxy_error("Unable to use SSL certificate\n"); + msg = "Unable to use SSL certificate"; + ret = 1; + } + } else { + proxy_error("Unable to initialize SSL\n"); + if (msg.length() == 0) { + msg = "Unable to initialize SSL"; + } + ret = 1; + } + } + if (ret == 0) { + SSL_CTX_set_verify(GloVars.global.ssl_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, callback_ssl_verify_peer); + } + X509_free(x509); + EVP_PKEY_free(pkey); + + + BIO_free(bio_err); + return ret; +} + diff --git a/src/proxysql_global.cpp b/src/proxysql_global.cpp index 12e868696..4273e4ece 100644 --- a/src/proxysql_global.cpp +++ b/src/proxysql_global.cpp @@ -3,3 +3,16 @@ //#include "proxysql_glovars.hpp" #include "cpp.h" //ProxySQL_GlobalVariables GloVars; + +SSL * ProxySQL_GlobalVariables::get_SSL_ctx() { + // take the mutex + std::lock_guard lock(global.ssl_mutex); + return SSL_new(GloVars.global.ssl_ctx); +} + +void ProxySQL_GlobalVariables::get_SSL_pem_mem(char **key, char **cert) { + // take the mutex + std::lock_guard lock(global.ssl_mutex); + *key = strdup(global.ssl_key_pem_mem); + *cert = strdup(global.ssl_cert_pem_mem); +}