Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/MySQL_Authentication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ class MySQL_Authentication {
void print_version();
bool exists(char *username);
account_details_t lookup(char* username, enum cred_username_type usertype, const dup_account_details_t& dup_details);
/**
* @brief Lookup backend credentials for a specific hostgroup.
* @details Searches for a backend user (backend=1) that has the specified hostgroup as its default_hostgroup.
* Only one backend user per hostgroup is allowed, ensuring unambiguous credential mapping.
* @param hostgroup_id The hostgroup ID to lookup backend credentials for
* @return account_details_t containing the backend user credentials, or an empty struct if none found
*/
account_details_t lookup_backend_for_hostgroup(int hostgroup_id);
int dump_all_users(account_details_t ***, bool _complete=true);
int increase_frontend_user_connections(char *username, PASSWORD_TYPE::E passtype, int *mc=NULL);
void decrease_frontend_user_connections(char *username, PASSWORD_TYPE::E passtype);
Expand Down
1 change: 1 addition & 0 deletions include/MySQL_Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ class MySQL_Session: public Base_Session<MySQL_Session, MySQL_Data_Stream, MySQL
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_EXECUTE(PtrSize_t& pkt);

// these functions have code that used to be inline, and split into functions for readibility
bool set_backend_credentials_for_hostgroup(MySQL_Connection *myconn, int hostgroup_id);
int handler_ProcessingQueryError_CheckBackendConnectionStatus(MySQL_Data_Stream *myds);
void SetQueryTimeout();
bool handler_rc0_PROCESSING_STMT_PREPARE(enum session_status& st, MySQL_Data_Stream *myds, bool& prepared_stmt_with_no_params);
Expand Down
14 changes: 14 additions & 0 deletions include/PgSQL_Authentication.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ class PgSQL_Authentication {
void print_version();
bool exists(char *username);
char * lookup(char *username, enum cred_username_type usertype, bool *use_ssl, int *default_hostgroup, bool *transaction_persistent, bool *fast_forward, int *max_connections, void **sha1_pass, char **attributes);
/**
* @brief Lookup backend credentials for a specific hostgroup.
* @details Searches for a backend user (backend=1) that has the specified hostgroup as its default_hostgroup.
* Only one backend user per hostgroup is allowed, ensuring unambiguous credential mapping.
* @param hostgroup_id The hostgroup ID to lookup backend credentials for
* @return Pointer to allocated pgsql_account_details_t containing the backend user credentials, or NULL if none found
* Caller is responsible for freeing the returned structure using free_account_details().
*/
pgsql_account_details_t* lookup_backend_for_hostgroup(int hostgroup_id);
/**
* @brief Free a pgsql_account_details_t structure returned by lookup_backend_for_hostgroup.
* @param acct Pointer to the account details structure to free (can be NULL)
*/
void free_account_details(pgsql_account_details_t* acct);
int dump_all_users(pgsql_account_details_t***, bool _complete=true);
int increase_frontend_user_connections(char *username, int *mc=NULL);
void decrease_frontend_user_connections(char *username);
Expand Down
8 changes: 8 additions & 0 deletions include/PgSQL_Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,14 @@ class PgSQL_Session : public Base_Session<PgSQL_Session, PgSQL_Data_Stream, PgSQ
*/
void switch_fast_forward_to_normal_mode();

/**
* @brief Set backend credentials for a specific hostgroup on a PgSQL connection.
* @param myconn The PgSQL connection to set credentials on
* @param hostgroup_id The hostgroup ID to lookup credentials for
* @return true if hostgroup-specific credentials were found and set, false if using client credentials
*/
bool set_backend_credentials_for_hostgroup(PgSQL_Connection *myconn, int hostgroup_id);

public:
inline bool is_extended_query_frame_empty() const {
return extended_query_frame.empty();
Expand Down
34 changes: 34 additions & 0 deletions lib/Admin_Bootstrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,40 @@ bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) {
check_and_build_standard_tables(configdb, tables_defs_config);
check_and_build_standard_tables(statsdb, tables_defs_stats);

// Create triggers to enforce one backend user per hostgroup constraint
// This ensures hostgroup-based backend credential mapping is unambiguous
auto create_backend_user_trigger = [](const char* table_name, const char* event) {
char buf[1024];
snprintf(buf, sizeof(buf), R"(
CREATE TRIGGER IF NOT EXISTS tr_%s_backend_hostgroup_unique_%s
BEFORE %s ON %s
WHEN NEW.backend = 1 AND EXISTS (
SELECT 1 FROM %s
WHERE backend = 1
AND default_hostgroup = NEW.default_hostgroup
AND username != NEW.username
)
BEGIN
SELECT RAISE(ABORT, 'Only one backend user allowed per hostgroup');
END;
)", table_name, event, event, table_name, table_name);
return std::string(buf);
};

std::string mysql_insert = create_backend_user_trigger("mysql_users", "INSERT");
std::string mysql_update = create_backend_user_trigger("mysql_users", "UPDATE");
std::string pgsql_insert = create_backend_user_trigger("pgsql_users", "INSERT");
std::string pgsql_update = create_backend_user_trigger("pgsql_users", "UPDATE");

admindb->execute(mysql_insert.c_str());
admindb->execute(mysql_update.c_str());
admindb->execute(pgsql_insert.c_str());
admindb->execute(pgsql_update.c_str());
configdb->execute(mysql_insert.c_str());
configdb->execute(mysql_update.c_str());
configdb->execute(pgsql_insert.c_str());
configdb->execute(pgsql_update.c_str());

__attach_db(admindb, configdb, (char *)"disk");
__attach_db(admindb, statsdb, (char *)"stats");
__attach_db(admindb, monitordb, (char *)"monitor");
Expand Down
79 changes: 72 additions & 7 deletions lib/MySQL_Authentication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
#endif

void free_account_details(account_details_t& ad) {
if (ad.username) {
free(ad.username);

Check failure on line 19 in lib/MySQL_Authentication.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "free".

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZq6AiFqxhOzSCfENCbJ&open=AZq6AiFqxhOzSCfENCbJ&pullRequest=5227
ad.username = nullptr;
}
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;
ad.sha1_pass = nullptr;
}
if (ad.clear_text_password[PASSWORD_TYPE::PRIMARY]) {
free(ad.clear_text_password[PASSWORD_TYPE::PRIMARY]);
Expand Down Expand Up @@ -129,7 +129,7 @@
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
Expand Down Expand Up @@ -691,6 +691,71 @@
return ret;
}

account_details_t MySQL_Authentication::lookup_backend_for_hostgroup(int hostgroup_id) {
account_details_t ret {};

creds_group_t &cg = creds_backends;

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_rdlock(&cg.lock);
#else
spin_rdlock(&cg.lock);
#endif

// Iterate through backend users to find the one for this hostgroup
for (const auto& pair : cg.bt_map) {
account_details_t* ad = pair.second;

if (ad->default_hostgroup == hostgroup_id) {
// Found the backend user for this hostgroup

ret.username = strdup(ad->username);
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 (ad->default_schema) {
ret.default_schema = 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 (ad->sha1_pass) {
ret.sha1_pass = malloc(SHA_DIGEST_LENGTH);

Check failure on line 737 in lib/MySQL_Authentication.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "malloc".

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZq6AiFqxhOzSCfENCbK&open=AZq6AiFqxhOzSCfENCbK&pullRequest=5227
memcpy(ret.sha1_pass, ad->sha1_pass, SHA_DIGEST_LENGTH);
}

if (ad->attributes) {
ret.attributes = strdup(ad->attributes);
}

// Only one backend user per hostgroup is allowed, so we can break
break;
}
}

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

Expand Down Expand Up @@ -800,7 +865,7 @@
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,
// - 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 {};
Expand Down
Loading