Working prototype of caching_sha2_password

Now mysql_users.password can store the authentication_string as
in MySQL's mysql.user table.

Note: this commit also changes SALT_LEN_MAX in SHA-crypt code from
16 to 20 because MySQL caching_sha2_password uses 20 bytes salt
v2.x_sha2pass_draft2-TEST
René Cannaò 3 years ago
parent e6e6bb0f7c
commit cf6f4f765e

@ -194,10 +194,13 @@ class MySQL_Protocol {
void PPHR_5passwordFalse_0(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1);
void PPHR_5passwordFalse_auth2(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1 , void *& sha1_pass);
void PPHR_6auth2(bool& ret, MyProt_tmp_auth_vars& vars1);
void PPHR_sha2full(bool& ret, MyProt_tmp_auth_vars& vars1, enum proxysql_auth_plugins passformat);
void PPHR_7auth1(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1 , void *& sha1_pass);
void PPHR_7auth2(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1 , void *& sha1_pass);
void PPHR_SetConnAttrs(MyProt_tmp_auth_vars& vars1, MyProt_tmp_auth_attrs& attr1);
void generate_one_byte_pkt(unsigned char b);
bool process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned int len);
void * Query_String_to_packet(uint8_t sid, std::string *s, unsigned int *l);
/**

@ -35,6 +35,9 @@ extern ClickHouse_Authentication *GloClickHouseAuth;
extern const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr);
MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char *name);
char * sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen);
static const char *plugins[3] = {
"mysql_native_password",
"mysql_clear_password",
@ -464,6 +467,23 @@ bool MySQL_Protocol::generate_pkt_ERR(bool send, void **ptr, unsigned int *len,
return true;
}
void MySQL_Protocol::generate_one_byte_pkt(unsigned char b) {
assert((*myds) != NULL);
uint8_t sequence_id;
sequence_id = (*myds)->pkt_sid;
sequence_id++;
mysql_hdr myhdr;
myhdr.pkt_id=sequence_id;
myhdr.pkt_length=1;
unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr);
unsigned char *_ptr=(unsigned char *)l_alloc(size);
memcpy(_ptr, &myhdr, sizeof(mysql_hdr));
int l=sizeof(mysql_hdr);
_ptr[l]=b;
(*myds)->PSarrayOUT->add((void *)_ptr,size);
(*myds)->pkt_sid=sequence_id;
}
bool MySQL_Protocol::generate_pkt_OK(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, unsigned int affected_rows, uint64_t last_insert_id, uint16_t status, uint16_t warnings, char *msg, bool eof_identifier) {
if ((*myds)->sess->mirror==true) {
return true;
@ -1678,7 +1698,8 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in
// this function was inline in process_pkt_handshake_response() , split for readibility
int MySQL_Protocol::PPHR_1(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1) { // process_pkt_handshake_response inner 1
(*myds)->switching_auth_stage=2;
if ((*myds)->switching_auth_stage == 1) (*myds)->switching_auth_stage=2;
if ((*myds)->switching_auth_stage == 4) (*myds)->switching_auth_stage=5;
(*myds)->auth_in_progress = 0;
if (len==5) {
ret = false;
@ -1689,7 +1710,7 @@ int MySQL_Protocol::PPHR_1(unsigned char *pkt, unsigned int len, bool& ret, MyPr
return 1;
}
auth_plugin_id = (*myds)->switching_auth_type;
if (auth_plugin_id==0) {
if (auth_plugin_id == AUTH_MYSQL_NATIVE_PASSWORD) {
vars1.pass_len = len - sizeof(mysql_hdr);
} else {
vars1.pass_len=strlen((char *)pkt);
@ -2151,6 +2172,53 @@ void MySQL_Protocol::PPHR_7auth2(
}
}
void MySQL_Protocol::PPHR_sha2full(
bool& ret,
MyProt_tmp_auth_vars& vars1,
enum proxysql_auth_plugins passformat
) {
if ((*myds)->switching_auth_stage == 0) {
const unsigned char perform_full_authentication = '\4';
generate_one_byte_pkt(perform_full_authentication);
(*myds)->switching_auth_type = auth_plugin_id;
(*myds)->switching_auth_stage = 4;
(*myds)->auth_in_progress = 1;
} else if ((*myds)->switching_auth_stage == 5) {
if (passformat == AUTH_MYSQL_NATIVE_PASSWORD) {
unsigned char md1_buf[SHA_DIGEST_LENGTH];
unsigned char md2_buf[SHA_DIGEST_LENGTH];
SHA1(vars1.pass, vars1.pass_len, md1_buf);
SHA1(md1_buf,SHA_DIGEST_LENGTH,md2_buf);
char *double_hashed_password = sha1_pass_hex((char *)md2_buf); // note that sha1_pass_hex() returns a new buffer
if (strcasecmp(double_hashed_password,vars1.password)==0) {
ret = true;
}
free(double_hashed_password);
} else if (passformat == AUTH_MYSQL_CACHING_SHA2_PASSWORD) {
assert(strlen(vars1.password) == 70);
string sp = string(vars1.password);
long rounds = stol(sp.substr(3,3));
string salt = sp.substr(7,20);
string sha256hash = sp.substr(27,43);
//char * sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen);
char buf[100];
salt = "$5$rounds=" + to_string(rounds*1000) + "$" + salt;
sha256_crypt_r((const char*)vars1.pass, salt.c_str(), buf, sizeof(buf));
string sbuf = string(buf);
std::size_t found = sbuf.find_last_of("$");
assert(found != string::npos);
sbuf = sbuf.substr(found+1);
if (strcmp(sbuf.c_str(),vars1.password+27)==0) {
ret = true;
}
} else {
assert(0);
}
} else {
assert(0);
}
}
void MySQL_Protocol::PPHR_SetConnAttrs(MyProt_tmp_auth_vars& vars1, MyProt_tmp_auth_attrs& attr1) {
MySQL_Connection *myconn = NULL;
myconn=sess->client_myds->myconn;
@ -2312,9 +2380,14 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned
case AUTH_MYSQL_CLEAR_PASSWORD:
break;
case AUTH_MYSQL_CACHING_SHA2_PASSWORD:
if ((*myds)->auth_in_progress != 0) {
assert(0);
}
if ((*myds)->switching_auth_stage != 0) {
assert(0);
}
break;
default:
assert(0);
break;
}
} else {
@ -2378,7 +2451,16 @@ __do_auth:
proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , password='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, vars1.user, tmp_pass, auth_plugin_id);
free(tmp_pass);
#endif // debug
if (vars1.password[0]!='*') { // clear text password
if (
auth_plugin_id == AUTH_MYSQL_CACHING_SHA2_PASSWORD
&&
strlen(vars1.password) > 60
&&
strncasecmp(vars1.password,"$A$0",4)==0
) {
// we have a hashed caching_sha2_password
PPHR_sha2full(ret, vars1, AUTH_MYSQL_CACHING_SHA2_PASSWORD);
} else if (vars1.password[0]!='*') { // clear text password
if (auth_plugin_id == AUTH_MYSQL_NATIVE_PASSWORD) { // mysql_native_password
proxy_scramble(reply, (*myds)->myconn->scramble_buff, vars1.password);
if (vars1.pass_len != 0 && memcmp(reply, vars1.pass, SHA_DIGEST_LENGTH)==0) {
@ -2390,16 +2472,22 @@ __do_auth:
}
} else if (auth_plugin_id == AUTH_MYSQL_CACHING_SHA2_PASSWORD) { // caching_sha2_password
PPHR_6auth2(ret, vars1);
if (ret == true) {
if ((*myds)->switching_auth_stage == 0) {
const unsigned char fast_auth_success = '\3';
generate_one_byte_pkt(fast_auth_success);
}
}
} else {
assert(0);
}
} else {
} else { // password hashed with SHA1 , mysql_native_password format
if (auth_plugin_id == AUTH_MYSQL_NATIVE_PASSWORD) { // mysql_native_password
PPHR_7auth1(pkt, len, ret, vars1, reply, attr1, sha1_pass);
} else if (auth_plugin_id == AUTH_MYSQL_CLEAR_PASSWORD) { // mysql_clear_password
PPHR_7auth2(pkt, len, ret, vars1, reply, attr1, sha1_pass);
} else if (auth_plugin_id == AUTH_MYSQL_CACHING_SHA2_PASSWORD) { // caching_sha2_password
assert(0);
PPHR_sha2full(ret, vars1, AUTH_MYSQL_NATIVE_PASSWORD);
} else {
assert(0);
}
@ -2476,7 +2564,6 @@ __exit_process_pkt_handshake_response:
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) {

@ -5454,8 +5454,9 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
client_authenticated=false;
*wrong_pass=true;
client_myds->setDSS_STATE_QUERY_SENT_NET();
uint8_t _pid = 2;
if (client_myds->switching_auth_stage) _pid+=2;
//uint8_t _pid = 2;
//if (client_myds->switching_auth_stage) _pid+=2;
uint8_t _pid = client_myds->pkt_sid; _pid++;
if (max_connections_reached==true) {
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p , Too many connections\n", this, client_myds);
client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,_pid,1040,(char *)"08004", (char *)"Too many connections", true);
@ -5515,9 +5516,10 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
} else {
client_addr = strdup((char *)"");
}
uint8_t _pid = 2;
if (client_myds->switching_auth_stage) _pid+=2;
if (is_encrypted) _pid++;
//uint8_t _pid = 2;
//if (client_myds->switching_auth_stage) _pid+=2;
//if (is_encrypted) _pid++;
uint8_t _pid = client_myds->pkt_sid; _pid++;
if (
(strcmp(client_addr,(char *)"127.0.0.1")==0)
||
@ -5543,8 +5545,9 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
free(client_addr);
} else {
uint8_t _pid = 2;
if (client_myds->switching_auth_stage) _pid+=2;
if (is_encrypted) _pid++;
//if (client_myds->switching_auth_stage) _pid+=2;
//if (is_encrypted) _pid++;
_pid = client_myds->pkt_sid; _pid++;
// If this condition is met, it means that the
// 'STATE_SERVER_HANDSHAKE' being performed isn't from the start of a
// connection, but as a consequence of a 'COM_USER_CHANGE' which
@ -5614,9 +5617,10 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
}
if (client_myds->myconn->userinfo->username) {
char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100+strlen(client_addr));
uint8_t _pid = 2;
if (client_myds->switching_auth_stage) _pid+=2;
if (is_encrypted) _pid++;
//uint8_t _pid = 2;
//if (client_myds->switching_auth_stage) _pid+=2;
//if (is_encrypted) _pid++;
uint8_t _pid = client_myds->pkt_sid; _pid++;
#ifdef DEBUG
if (client_myds->myconn->userinfo->password) {
char *tmp_pass=strdup(client_myds->myconn->userinfo->password);

@ -292,7 +292,7 @@ static const char sha256_salt_prefix[] = "$5$";
static const char sha256_rounds_prefix[] = "rounds=";
/* Maximum salt string length. */
#define SALT_LEN_MAX 16
#define SALT_LEN_MAX 20
/* Default number of rounds if not explicitly specified. */
#define ROUNDS_DEFAULT 5000
/* Minimum number of rounds. */
@ -305,8 +305,7 @@ static const char b64t[65] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static char *
sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
char * sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
{
unsigned char alt_result[32]
__attribute__ ((__aligned__ (__alignof__ (uint32_t))));

Loading…
Cancel
Save