#include "../deps/json/json.hpp" //using json = nlohmann::json; #define PROXYJSON //#include "btree_map.h" #include "proxysql.h" #include "cpp.h" #include "proxysql_atomic.h" #include "MySQL_Authentication.hpp" #ifndef SPOOKYV2 #include "SpookyV2.h" #define SPOOKYV2 #endif void free_account_details(account_details_t& ad) { if (ad.password) { free(ad.password); ad.password = nullptr; } if (ad.sha1_pass) { free(ad.sha1_pass); ad.sha1_pass=NULL; } if (ad.password) { free(ad.password); ad.password = nullptr; } if (ad.clear_text_password[PASSWORD_TYPE::PRIMARY]) { free(ad.clear_text_password[PASSWORD_TYPE::PRIMARY]); ad.clear_text_password[PASSWORD_TYPE::PRIMARY] = nullptr; } if (ad.clear_text_password[PASSWORD_TYPE::ADDITIONAL]) { free(ad.clear_text_password[PASSWORD_TYPE::ADDITIONAL]); ad.clear_text_password[PASSWORD_TYPE::ADDITIONAL] = nullptr; } if (ad.default_schema) { free(ad.default_schema); ad.default_schema = nullptr; } if (ad.attributes) { free(ad.attributes); ad.attributes = nullptr; } if (ad.comment) { free(ad.comment); ad.comment = nullptr; } } MySQL_Authentication::MySQL_Authentication() { #ifdef DEBUG if (glovars.has_debug==false) { #else if (glovars.has_debug==true) { #endif /* DEBUG */ perror("Incompatible debugging version"); exit(EXIT_FAILURE); } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_init(&creds_backends.lock, NULL); pthread_rwlock_init(&creds_frontends.lock, NULL); #else spinlock_rwlock_init(&creds_backends.lock); spinlock_rwlock_init(&creds_frontends.lock); #endif creds_backends.cred_array = new PtrArray(); creds_frontends.cred_array = new PtrArray(); }; MySQL_Authentication::~MySQL_Authentication() { reset(); delete creds_backends.cred_array; delete creds_frontends.cred_array; }; void MySQL_Authentication::print_version() { fprintf(stderr,"Standard MySQL Authentication rev. %s -- %s -- %s\n", MYSQL_AUTHENTICATION_VERSION, __FILE__, __TIMESTAMP__); }; void MySQL_Authentication::set_all_inactive(enum cred_username_type usertype) { creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif unsigned int i; for (i=0; ilen; i++) { account_details_t *ado=(account_details_t *)cg.cred_array->index(i); ado->__active=false; } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif } void MySQL_Authentication::remove_inactives(enum cred_username_type usertype) { creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif unsigned int i; __loop_remove_inactives: for (i=0; ilen; i++) { account_details_t *ado=(account_details_t *)cg.cred_array->index(i); if (ado->__active==false) { del(ado->username,usertype,false); goto __loop_remove_inactives; // we aren't sure how the underlying structure changes, so we jump back to 0 } } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif } bool MySQL_Authentication::add(char * 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, char* attributes, char *comment) { uint64_t hash1, hash2; SpookyHash myhash; myhash.Init(1,2); myhash.Update(username,strlen(username)); myhash.Final(&hash1,&hash2); creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif std::map::iterator lookup; lookup = cg.bt_map.find(hash1); // few changes will follow, due to issue #802 account_details_t *ad=NULL; bool new_ad=false; if (lookup != cg.bt_map.end()) { ad=lookup->second; if (strcmp(ad->password,password)) { // the password has changed free(ad->password); ad->password=strdup(password); if (ad->sha1_pass) { free(ad->sha1_pass); ad->sha1_pass=NULL; } if (ad->clear_text_password[0]) { free(ad->clear_text_password[0]); ad->clear_text_password[0]=NULL; } if (ad->clear_text_password[1]) { free(ad->clear_text_password[1]); ad->clear_text_password[1]=NULL; } // FIXME: if the password is a clear text password, automatically generate sha1_pass and clear_text_password } if (strcmp(ad->default_schema,default_schema)) { free(ad->default_schema); ad->default_schema=strdup(default_schema); } if (strcmp(ad->comment,comment)) { free(ad->comment); ad->comment=strdup(comment); } if (strcasecmp(ad->attributes, attributes)) { free(ad->attributes); if (strlen(attributes)) { // NOTE: add() is only place where we do input validation try { nlohmann::json valid=nlohmann::json::parse(attributes); // we do further input validation here, and possibly transforming the JSON itself bool json_rewritten = false; auto default_transaction_isolation = valid.find("default-transaction_isolation"); if (default_transaction_isolation != valid.end()) { std::string dti = valid["default-transaction_isolation"].get(); for (unsigned int i = 0; i < dti.length(); ++i) { if (dti[i] == '-') { dti[i] = ' '; json_rewritten = true; // the json needs to be rewritten } } // input validation if ( (strcasecmp(dti.c_str(), "READ UNCOMMITTED")==0 ) || (strcasecmp(dti.c_str(), "READ COMMITTED")==0 ) || (strcasecmp(dti.c_str(), "REPEATABLE READ")==0 ) || (strcasecmp(dti.c_str(), "SERIALIZABLE")==0 ) ) { if (json_rewritten) { valid["default-transaction_isolation"]=dti; } } else { std::string dti_orig = valid["default-transaction_isolation"].get(); proxy_error("Invalid default-transaction_isolation for user %s : %s . Removing it from runtime\n", username, dti_orig.c_str()); valid.erase("default-transaction_isolation"); json_rewritten = true; // the json was rewritten } } if (json_rewritten) { std::string d = valid.dump(); if (d.length()==2) { // empty json ad->attributes=strdup(""); // empty string } else { ad->attributes=strdup(d.c_str()); } } else { // the JSON wasn't rewritten for the purpose of input validation, therefore we copy the original value 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)); ad->username=strdup(username); ad->default_schema=strdup(default_schema); ad->comment=strdup(comment); ad->password=strdup(password); 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; ad->clear_text_password[0] = NULL; ad->clear_text_password[1] = NULL; // FIXME: if the password is a clear text password, automatically generate sha1_pass and clear_text_password } ad->use_ssl=use_ssl; ad->default_hostgroup=default_hostgroup; ad->schema_locked=schema_locked; ad->transaction_persistent=transaction_persistent; ad->fast_forward=fast_forward; ad->max_connections=max_connections; ad->__active=true; if (new_ad) { cg.bt_map.insert(std::make_pair(hash1,ad)); cg.cred_array->add(ad); } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif return true; }; unsigned int MySQL_Authentication::memory_usage() { unsigned int ret=0; #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_rdlock(&creds_frontends.lock); pthread_rwlock_rdlock(&creds_backends.lock); #else spin_rdlock(&creds_frontends.lock); spin_rdlock(&creds_backends.lock); #endif unsigned i=0; for (i=0; ilen; i++) { account_details_t *ado=(account_details_t *)creds_frontends.cred_array->index(i); ret += sizeof(account_details_t); if (ado->username) ret += strlen(ado->username) + 1; if (ado->password) ret += strlen(ado->password) + 1; if (ado->sha1_pass) ret += SHA_DIGEST_LENGTH; if (ado->clear_text_password[0]) ret += strlen(ado->clear_text_password[0]) + 1; if (ado->clear_text_password[1]) ret += strlen(ado->clear_text_password[1]) + 1; if (ado->default_schema) ret += strlen(ado->default_schema) + 1; if (ado->comment) ret += strlen(ado->comment) + 1; if (ado->attributes) ret += strlen(ado->attributes) + 1; } ret += sizeof(creds_group_t); ret += sizeof(PtrArray); ret += (creds_frontends.cred_array->size * sizeof(void *)); for (i=0; ilen; i++) { account_details_t *ado=(account_details_t *)creds_backends.cred_array->index(i); ret += sizeof(account_details_t); if (ado->username) ret += strlen(ado->username) + 1; if (ado->password) ret += strlen(ado->password) + 1; if (ado->sha1_pass) ret += SHA_DIGEST_LENGTH; if (ado->clear_text_password[0]) ret += strlen(ado->clear_text_password[0]) + 1; if (ado->clear_text_password[1]) ret += strlen(ado->clear_text_password[1]) + 1; if (ado->default_schema) ret += strlen(ado->default_schema) + 1; if (ado->comment) ret += strlen(ado->comment) + 1; if (ado->attributes) ret += strlen(ado->attributes) + 1; } ret += sizeof(creds_group_t); ret += sizeof(PtrArray); ret += (creds_backends.cred_array->size * sizeof(void *)); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&creds_frontends.lock); pthread_rwlock_unlock(&creds_backends.lock); #else spin_rdunlock(&creds_frontends.lock); spin_rdunlock(&creds_backends.lock); #endif return ret; } int MySQL_Authentication::dump_all_users(account_details_t ***ads, bool _complete) { #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_rdlock(&creds_frontends.lock); pthread_rwlock_rdlock(&creds_backends.lock); #else spin_rdlock(&creds_frontends.lock); spin_rdlock(&creds_backends.lock); #endif int total_size; int idx_=0; unsigned i=0; account_details_t **_ads; total_size=creds_frontends.cred_array->len; if (_complete) { total_size+=creds_backends.cred_array->len; } if (!total_size) goto __exit_dump_all_users; _ads=(account_details_t **)malloc(sizeof(account_details_t *)*total_size); for (i=0; ilen; i++) { account_details_t *ad=(account_details_t *)malloc(sizeof(account_details_t)); account_details_t *ado=(account_details_t *)creds_frontends.cred_array->index(i); ad->username=strdup(ado->username); ad->max_connections=ado->max_connections; ad->default_hostgroup=ado->default_hostgroup; if (_complete==false) { ad->password=NULL; ad->default_schema=NULL; ad->attributes=NULL; ad->comment=NULL; ad->num_connections_used=ado->num_connections_used; } else { ad->num_connections_used=ado->num_connections_used; ad->password=strdup(ado->password); ad->sha1_pass=NULL; ad->clear_text_password[0] = NULL; ad->clear_text_password[1] = NULL; ad->use_ssl=ado->use_ssl; ad->default_schema=strdup(ado->default_schema); ad->attributes=strdup(ado->attributes); ad->comment=strdup(ado->comment); ad->schema_locked=ado->schema_locked; ad->transaction_persistent=ado->transaction_persistent; ad->fast_forward=ado->fast_forward; ad->__frontend=1; ad->__backend=0; } _ads[idx_]=ad; idx_++; } if (_complete==true) { for (i=0; ilen; i++) { account_details_t *ad=(account_details_t *)malloc(sizeof(account_details_t)); account_details_t *ado=(account_details_t *)creds_backends.cred_array->index(i); ad->num_connections_used=0; ad->username=strdup(ado->username); ad->password=strdup(ado->password); ad->sha1_pass=NULL; ad->clear_text_password[0] = NULL; ad->clear_text_password[1] = NULL; ad->use_ssl=ado->use_ssl; ad->default_hostgroup=ado->default_hostgroup; ad->default_schema=strdup(ado->default_schema); ad->attributes=strdup(ado->attributes); ad->comment=strdup(ado->comment); ad->schema_locked=ado->schema_locked; ad->transaction_persistent=ado->transaction_persistent; ad->fast_forward=ado->fast_forward; ad->max_connections=ado->max_connections; ad->__frontend=0; ad->__backend=1; _ads[idx_]=ad; idx_++; } } *ads=_ads; __exit_dump_all_users: #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&creds_frontends.lock); pthread_rwlock_unlock(&creds_backends.lock); #else spin_rdunlock(&creds_frontends.lock); spin_rdunlock(&creds_backends.lock); #endif return total_size; } int MySQL_Authentication::increase_frontend_user_connections( char *username, PASSWORD_TYPE::E passtype, int *mc ) { uint64_t hash1, hash2; SpookyHash *myhash=new SpookyHash(); myhash->Init(1,2); myhash->Update(username,strlen(username)); myhash->Final(&hash1,&hash2); delete myhash; creds_group_t &cg=creds_frontends; int ret=0; #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif std::map::iterator it; it = cg.bt_map.find(hash1); if (it != cg.bt_map.end()) { account_details_t *ad=it->second; if (ad->max_connections > ad->num_connections_used) { ret=ad->max_connections-ad->num_connections_used; ad->num_connections_used++; if (passtype == PASSWORD_TYPE::ADDITIONAL) { ad->num_connections_used_addl_pass += 1; } } if (mc) { *mc=ad->max_connections; } } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif return ret; } void MySQL_Authentication::decrease_frontend_user_connections(char *username, PASSWORD_TYPE::E passtype) { uint64_t hash1, hash2; SpookyHash *myhash=new SpookyHash(); myhash->Init(1,2); myhash->Update(username,strlen(username)); myhash->Final(&hash1,&hash2); delete myhash; creds_group_t &cg=creds_frontends; #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif std::map::iterator it; it = cg.bt_map.find(hash1); if (it != cg.bt_map.end()) { account_details_t *ad=it->second; if (ad->num_connections_used > 0) { ad->num_connections_used--; if (passtype == PASSWORD_TYPE::ADDITIONAL) { ad->num_connections_used_addl_pass -= 1; } } } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif } bool MySQL_Authentication::del(char * username, enum cred_username_type usertype, bool set_lock) { bool ret=false; uint64_t hash1, hash2; SpookyHash *myhash=new SpookyHash(); myhash->Init(1,2); myhash->Update(username,strlen(username)); myhash->Final(&hash1,&hash2); delete myhash; creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); if (set_lock) #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif std::map::iterator lookup; lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { account_details_t *ad=lookup->second; cg.cred_array->remove_fast(ad); cg.bt_map.erase(lookup); free(ad->username); free(ad->password); if (ad->sha1_pass) { free(ad->sha1_pass); ad->sha1_pass=NULL; } if (ad->clear_text_password[0]) { free(ad->clear_text_password[0]); ad->clear_text_password[0]=NULL; } if (ad->clear_text_password[1]) { free(ad->clear_text_password[1]); ad->clear_text_password[1]=NULL; } free(ad->default_schema); free(ad->attributes); free(ad->comment); free(ad); ret=true; } if (set_lock) #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif return ret; }; bool MySQL_Authentication::set_SHA1(char * username, enum cred_username_type usertype, void *sha_pass) { bool ret=false; uint64_t hash1, hash2; SpookyHash *myhash=new SpookyHash(); myhash->Init(1,2); myhash->Update(username,strlen(username)); myhash->Final(&hash1,&hash2); delete myhash; creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif std::map::iterator lookup; lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { account_details_t *ad=lookup->second; if (ad->sha1_pass) { free(ad->sha1_pass); ad->sha1_pass=NULL; } if (sha_pass) { ad->sha1_pass=malloc(SHA_DIGEST_LENGTH); memcpy(ad->sha1_pass,sha_pass,SHA_DIGEST_LENGTH); } ret=true; } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif return ret; }; bool MySQL_Authentication::set_clear_text_password( char* username, enum cred_username_type usertype, const char* clear_text_password, PASSWORD_TYPE::E passtype ) { bool ret=false; uint64_t hash1, hash2; SpookyHash *myhash=new SpookyHash(); myhash->Init(1,2); myhash->Update(username,strlen(username)); myhash->Final(&hash1,&hash2); delete myhash; creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif std::map::iterator lookup; lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { account_details_t *ad=lookup->second; if (passtype == PASSWORD_TYPE::PRIMARY) { if (ad->clear_text_password[0]) { free(ad->clear_text_password[0]); ad->clear_text_password[0]=NULL; } if (clear_text_password) { ad->clear_text_password[0] = strdup(clear_text_password); } } else { if (ad->clear_text_password[1]) { free(ad->clear_text_password[1]); ad->clear_text_password[1]=NULL; } if (clear_text_password) { ad->clear_text_password[1] = strdup(clear_text_password); } } ret=true; } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif return ret; }; bool MySQL_Authentication::exists(char * username) { bool ret = false; uint64_t hash1, hash2; SpookyHash myhash; myhash.Init(1,2); myhash.Update(username,strlen(username)); myhash.Final(&hash1,&hash2); creds_group_t &cg = creds_frontends ; pthread_rwlock_rdlock(&cg.lock); std::map::iterator lookup; lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { ret = true; } pthread_rwlock_unlock(&cg.lock); return ret; } account_details_t MySQL_Authentication::lookup( char* username, enum cred_username_type usertype, const dup_account_details_t& dup_details ) { account_details_t ret {}; uint64_t hash1, hash2; SpookyHash myhash; myhash.Init(1,2); myhash.Update(username,strlen(username)); myhash.Final(&hash1,&hash2); creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_rdlock(&cg.lock); #else spin_rdlock(&cg.lock); #endif std::map::iterator lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { account_details_t* ad = lookup->second; ret.password = strdup(ad->password); if (ad->clear_text_password[PASSWORD_TYPE::PRIMARY]) { ret.clear_text_password[PASSWORD_TYPE::PRIMARY] = strdup(ad->clear_text_password[PASSWORD_TYPE::PRIMARY]); } if (ad->clear_text_password[PASSWORD_TYPE::ADDITIONAL]) { ret.clear_text_password[PASSWORD_TYPE::ADDITIONAL] = strdup(ad->clear_text_password[PASSWORD_TYPE::ADDITIONAL]); } ret.use_ssl = ad->use_ssl; ret.default_hostgroup = ad->default_hostgroup; if (dup_details.default_schema) { ret.default_schema = l_strdup(ad->default_schema); } ret.schema_locked = ad->schema_locked; ret.transaction_persistent = ad->transaction_persistent; ret.fast_forward = ad->fast_forward; ret.max_connections = ad->max_connections; if (dup_details.sha1_pass) { if (ad->sha1_pass) { ret.sha1_pass = malloc(SHA_DIGEST_LENGTH); memcpy(ret.sha1_pass,ad->sha1_pass,SHA_DIGEST_LENGTH); } } if (dup_details.attributes) { ret.attributes = l_strdup(ad->attributes); } } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_rdunlock(&cg.lock); #endif return ret; } bool MySQL_Authentication::_reset(enum cred_username_type usertype) { creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_wrlock(&cg.lock); #else spin_wrlock(&cg.lock); #endif std::map::iterator lookup; while (cg.bt_map.size()) { lookup = cg.bt_map.begin(); if ( lookup != cg.bt_map.end() ) { account_details_t *ad=lookup->second; cg.bt_map.erase(lookup); free(ad->username); free(ad->password); if (ad->sha1_pass) { free(ad->sha1_pass); ad->sha1_pass=NULL; } if (ad->clear_text_password[0]) { free(ad->clear_text_password[0]); ad->clear_text_password[0] = NULL; } if (ad->clear_text_password[1]) { free(ad->clear_text_password[1]); ad->clear_text_password[1] = NULL; } free(ad->default_schema); free(ad->comment); free(ad->attributes); free(ad); } } while (cg.cred_array->len) { cg.cred_array->remove_index_fast(0); } #ifdef PROXYSQL_AUTH_PTHREAD_MUTEX pthread_rwlock_unlock(&cg.lock); #else spin_wrunlock(&cg.lock); #endif return true; }; bool MySQL_Authentication::reset() { _reset(USERNAME_BACKEND); _reset(USERNAME_FRONTEND); return true; } using std::map; static uint64_t compute_accounts_hash(const umap_auth& accs_map) { if (accs_map.size() == 0) { return 0; } bool foundany = false; SpookyHash acc_map_hash; acc_map_hash.Init(13,4); for (const pair& map_entry : accs_map) { const account_details_t* ad = map_entry.second; if (ad->default_hostgroup >= 0) { foundany = true; acc_map_hash.Update(&ad->use_ssl,sizeof(ad->use_ssl)); acc_map_hash.Update(&ad->default_hostgroup,sizeof(ad->default_hostgroup)); acc_map_hash.Update(&ad->schema_locked,sizeof(ad->schema_locked)); acc_map_hash.Update(&ad->transaction_persistent,sizeof(ad->transaction_persistent)); acc_map_hash.Update(&ad->fast_forward,sizeof(ad->fast_forward)); acc_map_hash.Update(&ad->max_connections,sizeof(ad->max_connections)); acc_map_hash.Update(ad->username,strlen(ad->username)); acc_map_hash.Update(ad->password,strlen(ad->password)); if (ad->default_schema) acc_map_hash.Update(ad->default_schema,strlen(ad->default_schema)); if (ad->comment) acc_map_hash.Update(ad->comment,strlen(ad->comment)); if (ad->attributes) { acc_map_hash.Update(ad->attributes,strlen(ad->attributes)); } } } if (foundany == false) { return 0; } else { uint64_t hash1 = 0, hash2 = 0; acc_map_hash.Final(&hash1, &hash2); return hash1; } } uint64_t MySQL_Authentication::_get_runtime_checksum(enum cred_username_type usertype) { creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); uint64_t accs_hash = compute_accounts_hash(cg.bt_map); return accs_hash; } uint64_t MySQL_Authentication::get_runtime_checksum() { uint64_t hashB = _get_runtime_checksum(USERNAME_BACKEND); uint64_t hashF = _get_runtime_checksum(USERNAME_FRONTEND); return hashB+hashF; } static pair extract_accounts_details(MYSQL_RES* resultset, unique_ptr& all_users) { if (resultset == nullptr) { return { umap_auth {}, umap_auth {} }; } // The following order is assumed for the resulset received fields: // - username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked, // transaction_persistent, fast_forward, backend, frontend, max_connections, attributes, comment. umap_auth f_accs_map {}; umap_auth b_accs_map {}; // Create the SQLite3 resultsets for 'frontend' and 'backend' users uint32_t num_fields = mysql_num_fields(resultset); MYSQL_FIELD* fields = mysql_fetch_fields(resultset); SQLite3_result* _all_users { new SQLite3_result(num_fields) }; for (uint32_t i = 0; i < num_fields; i++) { _all_users->add_column_definition(SQLITE_TEXT, fields[i].name); } const auto create_account_details = [] (MYSQL_ROW row) -> account_details_t* { account_details_t* acc_details { new account_details_t {} }; acc_details->username = row[0]; acc_details->password = row[1] ? row[1] : const_cast(""); acc_details->__active = true; acc_details->use_ssl = strcmp(row[2], "1") == 0 ? true : false; acc_details->default_hostgroup = atoi(row[3]); acc_details->default_schema = row[4] ? row[4] : const_cast(""); acc_details->schema_locked = strcmp(row[5], "1") == 0 ? true : false; acc_details->transaction_persistent = strcmp(row[6], "1") == 0 ? true : false; acc_details->fast_forward = strcmp(row[7], "1") == 0 ? true : false; acc_details->__backend = strcmp(row[8], "1") == 0 ? true : false; acc_details->__frontend = strcmp(row[9], "1") == 0 ? true : false; acc_details->max_connections = atoi(row[10]); acc_details->attributes = row[11] ? row[11] : const_cast(""); acc_details->comment = row[12] ? row[12] : const_cast(""); return acc_details; }; vector pta(static_cast(num_fields)); while (MYSQL_ROW row = mysql_fetch_row(resultset)) { // compute the 'username' hash for the map uint64_t u_hash = 0, _u_hash2 = 0; SpookyHash myhash {}; myhash.Init(1,2); myhash.Update(row[0], strlen(row[0])); myhash.Final(&u_hash, &_u_hash2); // is backend if (strcmp(row[8], "1") == 0) { account_details_t* acc_details = create_account_details(row); b_accs_map.insert({u_hash, acc_details}); } // is frontend if (strcmp(row[9], "1") == 0) { account_details_t* acc_details = create_account_details(row); f_accs_map.insert({u_hash, acc_details}); } // Update the contents of the row for the SQLite3 resultset for (uint32_t i = 0; i < num_fields; i++) { pta[i] = row[i]; } _all_users->add_row(&pta[0]); } mysql_data_seek(resultset, 0); // Update the supplied 'unique_ptr' with the target resultsets all_users.reset(_all_users); return { b_accs_map, f_accs_map }; } uint64_t MySQL_Authentication::get_runtime_checksum(MYSQL_RES* resultset, unique_ptr& all_users) { if (resultset == NULL) { return 0; } pair acc_maps { extract_accounts_details(resultset, all_users) }; uint64_t b_acc_hash = compute_accounts_hash(acc_maps.first); uint64_t f_acc_hash = compute_accounts_hash(acc_maps.second); for (pair& map_entry : acc_maps.first) { delete map_entry.second; } for (pair& map_entry : acc_maps.second) { delete map_entry.second; } return b_acc_hash + f_acc_hash; } void MySQL_Authentication::save_mysql_users(unique_ptr&& users) { this->mysql_users_resultset = std::move(users); } SQLite3_result* MySQL_Authentication::get_current_mysql_users() { return this->mysql_users_resultset.get(); }