Merge pull request #3343 from sysown/v2.1.1-spiffe

V2.1.1 spiffe
pull/3364/head
René Cannaò 5 years ago committed by GitHub
commit 3266cfc506
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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);

@ -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;

@ -126,5 +126,8 @@ 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);
bool user_attributes_has_spiffe(int calling_line, const char *calling_func, const unsigned char *user);
};
#endif /* __CLASS_MYSQL_PROTOCOL_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()

@ -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);

@ -346,6 +346,20 @@ 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::string spiffe_val = j["spiffe_id"].get<std::string>();
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 +1544,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,7 +1608,7 @@ 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
(*myds)->sess->default_hostgroup=default_hostgroup;
@ -1681,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;
@ -1726,6 +1754,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 +2009,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 +2024,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 +2090,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 +2102,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 +2306,53 @@ __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<std::string>();
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;
}
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;
}

@ -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++) {

@ -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);
}

@ -8,6 +8,8 @@
#include "MySQL_PreparedStatement.h"
#include "MySQL_Data_Stream.h"
#include <openssl/x509v3.h>
/*
in libssl 1.1.0
@ -163,6 +165,31 @@ 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("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);
}
} else {
proxy_error("X509 error: no required certificate sent by client\n");
}
}
}
status = get_sslstatus(ssl, n);
//proxy_info("SSL status = %d\n", status);
/* Did SSL request to write bytes? */
@ -235,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;
@ -347,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

@ -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 <openssl/dh.h>
@ -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);

Loading…
Cancel
Save