From 05fbe44ed286484b9eb44278b94b5d3faaf869b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Fri, 5 Mar 2021 00:35:54 +0100 Subject: [PATCH 1/4] Prototype for SPIFFE - do not merge --- lib/mysql_data_stream.cpp | 26 +++++++++++++++++++++++++- src/main.cpp | 7 +++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 35b63f535..4f57af107 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -8,6 +8,8 @@ #include "MySQL_PreparedStatement.h" #include "MySQL_Data_Stream.h" +#include + /* in libssl 1.1.0 @@ -163,8 +165,30 @@ enum sslstatus MySQL_Data_Stream::do_ssl_handshake() { char buf[MY_SSL_BUFFER]; enum sslstatus status; 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("X509 client SSL certificate verify error: (%d:%s)\n" , rc, X509_verify_cert_error_string(rc)); + //proxy_error("X509 client SSL certificate verify error: (%l)\n" , rc); + } 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); + sanName = sk_GENERAL_NAME_value(san_names, 0); + str = sanName->d.dNSName; + proxy_info("%s\n" , str->data); + } else { + proxy_error("X509 error: no required certificate sent by client\n"); + } + } + } status = get_sslstatus(ssl, n); - //proxy_info("SSL status = %d\n", status); + proxy_info("SSL status = %d\n", status); /* Did SSL request to write bytes? */ if (status == SSLSTATUS_WANT_IO) { //proxy_info("SSL status is WANT_IO %d\n", status); diff --git a/src/main.cpp b/src/main.cpp index 8d11176b2..efe8cc5f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -120,6 +120,12 @@ hSypLEcUVIvzc6rtfWlYKT35wQ+AGKNADwIBBQ== */ +int callback_ssl_verify_peer(int ok, X509_STORE_CTX* ctx) { + // for now only return 1 + return 1; +} + + #ifndef HEADER_DH_H #include @@ -649,6 +655,7 @@ void ProxySQL_Main_init_SSL_module() { 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); From 7c0bde5f995d1c9b42390d0d7faeac547c56fbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 14 Mar 2021 21:46:47 +0100 Subject: [PATCH 2/4] Working prototype for SPIFFE --- include/MySQL_Authentication.hpp | 2 +- include/MySQL_Data_Stream.h | 1 + include/MySQL_Protocol.h | 2 + include/MySQL_Session.h | 1 + lib/MySQL_Authentication.cpp | 31 ++++++++++++-- lib/MySQL_Protocol.cpp | 71 ++++++++++++++++++++++++++++++-- lib/MySQL_Session.cpp | 5 +++ lib/ProxySQL_HTTP_Server.cpp | 2 +- lib/mysql_data_stream.cpp | 14 +++++-- 9 files changed, 117 insertions(+), 12 deletions(-) diff --git a/include/MySQL_Authentication.hpp b/include/MySQL_Authentication.hpp index 519b49ebe..8602fddb8 100644 --- a/include/MySQL_Authentication.hpp +++ b/include/MySQL_Authentication.hpp @@ -67,7 +67,7 @@ class MySQL_Authentication { bool reset(); void print_version(); bool exists(char *username); - char * lookup(char *username, 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 * lookup(char *username, 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); int dump_all_users(account_details_t ***, bool _complete=true); int increase_frontend_user_connections(char *username, int *mc=NULL); void decrease_frontend_user_connections(char *username); diff --git a/include/MySQL_Data_Stream.h b/include/MySQL_Data_Stream.h index 9a8047d2c..73ce29d3c 100644 --- a/include/MySQL_Data_Stream.h +++ b/include/MySQL_Data_Stream.h @@ -103,6 +103,7 @@ class MySQL_Data_Stream MySQL_Connection *myconn; MySQL_Session *sess; // pointer to the session using this data stream MySQL_Backend *mybe; // if this is a connection to a mysql server, this points to a backend structure + char *x509_subject_alt_name; SSL *ssl; BIO *rbio_ssl; BIO *wbio_ssl; diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index 723f4e56f..2ea54abf9 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -126,5 +126,7 @@ class MySQL_Protocol { stmt_execute_metadata_t * get_binds_from_pkt(void *ptr, unsigned int size, MySQL_STMT_Global_info *stmt_info, stmt_execute_metadata_t **stmt_meta); bool generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt); + + bool verify_user_attributes(int calling_line, const char *calling_func, const unsigned char *user); }; #endif /* __CLASS_MYSQL_PROTOCOL_H */ diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index daadc53b9..021629c83 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -185,6 +185,7 @@ class MySQL_Session MySQL_Data_Stream *client_myds; MySQL_Data_Stream *server_myds; char * default_schema; + char * user_attributes; //this pointer is always initialized inside handler(). // it is an attempt to start simplifying the complexing of handler() diff --git a/lib/MySQL_Authentication.cpp b/lib/MySQL_Authentication.cpp index 7e6a9313c..a786bebe1 100644 --- a/lib/MySQL_Authentication.cpp +++ b/lib/MySQL_Authentication.cpp @@ -118,7 +118,19 @@ bool MySQL_Authentication::add(char * username, char * password, enum cred_usern } if (strcasecmp(ad->attributes, attributes)) { free(ad->attributes); - ad->attributes=strdup(attributes); + if (strlen(attributes)) { + // NOTE: add() is only place where we do input validation + try { + nlohmann::json valid=nlohmann::json::parse(attributes); + ad->attributes=strdup(attributes); + } + catch(nlohmann::json::exception& e) { + ad->attributes=strdup(""); + proxy_error("Invalid attributes for user %s: %s\n", username, attributes); + } + } else { + ad->attributes=strdup(attributes); // default, empty string + } } } else { ad=(account_details_t *)malloc(sizeof(account_details_t)); @@ -126,7 +138,19 @@ bool MySQL_Authentication::add(char * username, char * password, enum cred_usern ad->default_schema=strdup(default_schema); ad->comment=strdup(comment); ad->password=strdup(password); - ad->attributes=strdup(attributes); + if (strlen(attributes)) { + // NOTE: add() is only place where we do input validation + try { + nlohmann::json valid=nlohmann::json::parse(attributes); + ad->attributes=strdup(attributes); + } + catch(nlohmann::json::exception& e) { + ad->attributes=strdup(""); + proxy_error("Invalid attributes for user %s: %s\n", username, attributes); + } + } else { + ad->attributes=strdup(attributes); // default, empty string + } new_ad=true; ad->sha1_pass=NULL; ad->num_connections_used=0; @@ -439,7 +463,7 @@ bool MySQL_Authentication::exists(char * username) { return ret; } -char * MySQL_Authentication::lookup(char * username, 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 * MySQL_Authentication::lookup(char * username, 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 *ret=NULL; uint64_t hash1, hash2; SpookyHash myhash; @@ -472,6 +496,7 @@ char * MySQL_Authentication::lookup(char * username, enum cred_username_type use memcpy(*sha1_pass,ad->sha1_pass,SHA_DIGEST_LENGTH); } } + if (attributes) *attributes=l_strdup(ad->attributes); } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index aea05d70d..e645a9620 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -346,6 +346,22 @@ int pkt_end(unsigned char *pkt, unsigned int length, MySQL_Protocol *mp) return PKT_PARSED; } +#ifdef DEBUG +void debug_spiffe_id(const unsigned char *user, const char *attributes, int __line, const char *__func) { + if (strlen(attributes)) { + json j = nlohmann::json::parse(attributes); + auto spiffe_id = j.find("spiffe_id"); + if (spiffe_id != j.end()) { + //std::stringstream ss; + std::string spiffe_val = j["spiffe_id"].get(); + //std::string spiffe_val = ss.str(); + proxy_info("%d:%s(): Attributes for user %s: %s . Spiffe_id: %s\n" , __line, __func, user, attributes, spiffe_val.c_str()); + } else { + proxy_info("%d:%s(): Attributes for user %s: %s\n" , __line, __func, user, attributes); + } + } +} +#endif MySQL_Prepared_Stmt_info::MySQL_Prepared_Stmt_info(unsigned char *pkt, unsigned int length) { @@ -1530,9 +1546,10 @@ bool MySQL_Protocol::process_pkt_auth_swich_response(unsigned char *pkt, unsigne password=GloClickHouseAuth->lookup((char *)userinfo->username, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass); #endif /* PROXYSQLCLICKHOUSE */ } else { - password=GloMyAuth->lookup((char *)userinfo->username, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass); + password=GloMyAuth->lookup((char *)userinfo->username, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass, NULL); } // FIXME: add support for default schema and fast forward , issues #255 and #256 + // FIXME: not sure if we should also handle user_attributes *here* . For now we pass NULL (no change) if (password==NULL) { ret=false; } else { @@ -1593,9 +1610,10 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in password=GloClickHouseAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass); #endif /* PROXYSQLCLICKHOUSE */ } else { - password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass); + password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass, NULL); } // FIXME: add support for default schema and fast forward, see issue #255 and #256 + // FIXME: not sure if we should also handle user_attributes . For now we pass NULL (no change) (*myds)->sess->default_hostgroup=default_hostgroup; (*myds)->sess->transaction_persistent=transaction_persistent; if (password==NULL) { @@ -1726,6 +1744,7 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned reply[SHA_DIGEST_LENGTH]='\0'; int default_hostgroup=-1; char *default_schema=NULL; + char *attributes = NULL; bool schema_locked; bool transaction_persistent = true; bool fast_forward = false; @@ -1980,7 +1999,7 @@ __do_auth: password=GloClickHouseAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); #endif /* PROXYSQLCLICKHOUSE */ } else { - password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); + password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, &attributes); } //assert(default_hostgroup>=0); if (password) { @@ -1995,6 +2014,10 @@ __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 +#ifdef DEBUG + debug_spiffe_id(user,attributes, __LINE__, __func__); +#endif (*myds)->sess->schema_locked=schema_locked; (*myds)->sess->transaction_persistent=transaction_persistent; (*myds)->sess->session_fast_forward=fast_forward; @@ -2057,6 +2080,10 @@ __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 +#ifdef DEBUG + debug_spiffe_id(user,attributes, __LINE__, __func__); +#endif (*myds)->sess->schema_locked=schema_locked; (*myds)->sess->transaction_persistent=transaction_persistent; (*myds)->sess->session_fast_forward=fast_forward; @@ -2065,10 +2092,14 @@ __do_auth: if (backend_username) { free(password); password=NULL; - password=GloMyAuth->lookup(backend_username, USERNAME_BACKEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); + password=GloMyAuth->lookup(backend_username, USERNAME_BACKEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, &attributes); if (password) { (*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 +#ifdef DEBUG + proxy_info("Attributes for user %s: %s\n" , user, attributes); +#endif (*myds)->sess->schema_locked=schema_locked; (*myds)->sess->transaction_persistent=transaction_persistent; (*myds)->sess->session_fast_forward=fast_forward; @@ -2265,6 +2296,38 @@ __exit_process_pkt_handshake_response: free(db_tmp); db_tmp=NULL; } + if (ret == true) { + ret = verify_user_attributes(__LINE__, __func__, user); + } + return ret; +} + + +bool MySQL_Protocol::verify_user_attributes(int calling_line, const char *calling_func, const unsigned char *user) { + bool ret = true; + if ((*myds)->sess->user_attributes) { + char *a = (*myds)->sess->user_attributes; // no copy, just pointer + if (strlen(a)) { + json j = nlohmann::json::parse(a); + auto spiffe_id = j.find("spiffe_id"); + if (spiffe_id != j.end()) { + // at this point, we completely ignore any password specified so far + // we assume authentication failure so far + ret = false; + std::string spiffe_val = j["spiffe_id"].get(); + if ((*myds)->x509_subject_alt_name) { + if (strncmp(spiffe_val.c_str(), "spiffe://", strlen("spiffe://"))==0) { + if (strcmp(spiffe_val.c_str(), (*myds)->x509_subject_alt_name)==0) { + ret = true; + } + } + } + if (ret == false) { + proxy_error("%d:%s(): SPIFFE Authentication error for user %s . spiffed_id expected : %s , received: %s\n", calling_line, calling_func, user, spiffe_val.c_str(), ((*myds)->x509_subject_alt_name ? (*myds)->x509_subject_alt_name : "none")); + } + } + } + } return ret; } diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 7db0ce871..32e69e04a 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -461,6 +461,7 @@ MySQL_Session::MySQL_Session() { //stats=false; client_authenticated=false; default_schema=NULL; + user_attributes=NULL; schema_locked=false; session_fast_forward=false; started_sending_data_to_client=false; @@ -583,6 +584,9 @@ MySQL_Session::~MySQL_Session() { if (default_schema) { free(default_schema); } + if (user_attributes) { + free(user_attributes); + } proxy_debug(PROXY_DEBUG_NET,1,"Thread=%p, Session=%p -- Shutdown Session %p\n" , this->thread, this, this); delete command_counters; if (session_type==PROXYSQL_SESSION_MYSQL && connections_handler==false && mirror==false) { @@ -980,6 +984,7 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { } j["client"]["DSS"] = client_myds->DSS; j["default_schema"] = ( default_schema ? default_schema : "" ); + j["user_attributes"] = ( user_attributes ? user_attributes : "" ); j["transaction_persistent"] = transaction_persistent; j["conn"]["session_track_gtids"] = ( client_myds->myconn->options.session_track_gtids ? client_myds->myconn->options.session_track_gtids : "") ; for (auto idx = 0; idx < SQL_NAME_LAST; idx++) { diff --git a/lib/ProxySQL_HTTP_Server.cpp b/lib/ProxySQL_HTTP_Server.cpp index da5d5ad5f..b11407848 100644 --- a/lib/ProxySQL_HTTP_Server.cpp +++ b/lib/ProxySQL_HTTP_Server.cpp @@ -390,7 +390,7 @@ int ProxySQL_HTTP_Server::handler(void *cls, struct MHD_Connection *connection, bool _ret_use_ssl = false; int max_connections; void *sha1_pass = NULL; - password=GloMyAuth->lookup(username, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); + password=GloMyAuth->lookup(username, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, NULL); if (default_schema) { // unused free(default_schema); } diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 4f57af107..294e6ca67 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -179,9 +179,12 @@ enum sslstatus MySQL_Data_Stream::do_ssl_handshake() { 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); - sanName = sk_GENERAL_NAME_value(san_names, 0); - str = sanName->d.dNSName; - proxy_info("%s\n" , str->data); + 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); + } } else { proxy_error("X509 error: no required certificate sent by client\n"); } @@ -259,6 +262,7 @@ MySQL_Data_Stream::MySQL_Data_Stream() { encrypted=false; switching_auth_stage = 0; switching_auth_type = 0; + x509_subject_alt_name=NULL; ssl=NULL; rbio_ssl = NULL; wbio_ssl = NULL; @@ -371,6 +375,10 @@ MySQL_Data_Stream::~MySQL_Data_Stream() { CompPktOUT.pkt.ptr=NULL; CompPktOUT.pkt.size=0; } + if (x509_subject_alt_name) { + free(x509_subject_alt_name); + x509_subject_alt_name=NULL; + } } // this function initializes a MySQL_Data_Stream From 2b8500aa3614f7019e64765d6b8816c83d6e6e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 14 Mar 2021 22:45:14 +0100 Subject: [PATCH 3/4] Do not allow CHANGE_USER with spiffe If a client connection uses spiffe, CHANGE_USER is not allowed --- include/MySQL_Protocol.h | 1 + lib/MySQL_Protocol.cpp | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index 2ea54abf9..25dc6bcc6 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -128,5 +128,6 @@ class MySQL_Protocol { bool generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt); bool verify_user_attributes(int calling_line, const char *calling_func, const unsigned char *user); + bool user_attributes_has_spiffe(int calling_line, const char *calling_func, const unsigned char *user); }; #endif /* __CLASS_MYSQL_PROTOCOL_H */ diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index e645a9620..8fee40f09 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -352,9 +352,7 @@ void debug_spiffe_id(const unsigned char *user, const char *attributes, int __li json j = nlohmann::json::parse(attributes); auto spiffe_id = j.find("spiffe_id"); if (spiffe_id != j.end()) { - //std::stringstream ss; std::string spiffe_val = j["spiffe_id"].get(); - //std::string spiffe_val = ss.str(); proxy_info("%d:%s(): Attributes for user %s: %s . Spiffe_id: %s\n" , __line, __func, user, attributes, spiffe_val.c_str()); } else { proxy_info("%d:%s(): Attributes for user %s: %s\n" , __line, __func, user, attributes); @@ -1613,7 +1611,6 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, NULL, NULL, &transaction_persistent, NULL, NULL, &sha1_pass, NULL); } // FIXME: add support for default schema and fast forward, see issue #255 and #256 - // FIXME: not sure if we should also handle user_attributes . For now we pass NULL (no change) (*myds)->sess->default_hostgroup=default_hostgroup; (*myds)->sess->transaction_persistent=transaction_persistent; if (password==NULL) { @@ -1699,6 +1696,19 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in ret = false; return ret; } + if ((*myds)->sess->user_attributes) { + if (user_attributes_has_spiffe(__LINE__, __func__, user)) { + // if SPIFFE was used, CHANGE_USER is not allowed. + // This because when SPIFFE is used, the password it is not relevant, + // as it could be a simple "none" , or "123456", or "password" + // The whole idea of using SPIFFE is that this is responsible for + // authentication, and not the password. + // Therefore CHANGE_USER is not allowed + proxy_error("Client %s:%d is trying to run CHANGE_USER , but this is disabled because it previously used SPIFFE ID. Disconnecting\n", (*myds)->addr.addr, (*myds)->addr.port); + ret = false; + return ret; + } + } assert(sess); assert(sess->client_myds); MySQL_Connection *myconn=sess->client_myds->myconn; @@ -2331,6 +2341,21 @@ bool MySQL_Protocol::verify_user_attributes(int calling_line, const char *callin return ret; } +bool MySQL_Protocol::user_attributes_has_spiffe(int calling_line, const char *calling_func, const unsigned char *user) { + bool ret = false; + if ((*myds)->sess->user_attributes) { + char *a = (*myds)->sess->user_attributes; // no copy, just pointer + if (strlen(a)) { + json j = nlohmann::json::parse(a); + auto spiffe_id = j.find("spiffe_id"); + if (spiffe_id != j.end()) { + ret = true; + } + } + } + return ret; +} + void * MySQL_Protocol::Query_String_to_packet(uint8_t sid, std::string *s, unsigned int *l) { mysql_hdr hdr; hdr.pkt_id=sid; From 68e95bee61f29c63f8d4eafbb9d81402031ea36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 15 Mar 2021 18:23:33 +0100 Subject: [PATCH 4/4] Immediately exit in case of SSL error This commmit also enhance logging --- lib/mysql_data_stream.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 294e6ca67..a8a1c63f3 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -166,11 +166,11 @@ enum sslstatus MySQL_Data_Stream::do_ssl_handshake() { enum sslstatus status; int n = SSL_do_handshake(ssl); if (n == 1) { - proxy_info("SSL handshake completed\n"); + //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("X509 client SSL certificate verify error: (%d:%s)\n" , rc, X509_verify_cert_error_string(rc)); - //proxy_error("X509 client SSL certificate verify error: (%l)\n" , rc); + 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); @@ -191,7 +191,7 @@ enum sslstatus MySQL_Data_Stream::do_ssl_handshake() { } } status = get_sslstatus(ssl, n); - proxy_info("SSL status = %d\n", status); + //proxy_info("SSL status = %d\n", status); /* Did SSL request to write bytes? */ if (status == SSLSTATUS_WANT_IO) { //proxy_info("SSL status is WANT_IO %d\n", status);