MySQL: Decode caching_sha2_password packets

This commit is contained in:
Daniël van Eeden 2023-01-24 09:51:12 +00:00 committed by Alexis La Goutte
parent 06fc6483b3
commit 8ec198c272
1 changed files with 120 additions and 3 deletions

View File

@ -168,6 +168,8 @@ void proto_reg_handoff_mysql(void);
#define MYSQL_DAEMON 29
#define MYSQL_BINLOG_DUMP_GTID 30 /* replication */
#define MYSQL_RESET_CONNECTION 31
#define MYSQL_CLONE 32
#define MYSQL_SUBSCRIBE_GROUP_REPLICATION_STREAM 33
/* MariaDB specific commands */
#define MARIADB_STMT_BULK_EXECUTE 250
@ -251,6 +253,8 @@ static const value_string mysql_command_vals[] = {
{MYSQL_DAEMON, "Daemon"},
{MYSQL_BINLOG_DUMP_GTID, "Send Binlog GTID"},
{MYSQL_RESET_CONNECTION, "Reset Connection"},
{MYSQL_CLONE, "Native cloning"},
{MYSQL_SUBSCRIBE_GROUP_REPLICATION_STREAM, "Subscribe Group Repliction Stream"},
{MARIADB_STMT_BULK_EXECUTE, "Execute Bulk Statement"},
{0, NULL}
};
@ -1139,6 +1143,9 @@ static int hf_mysql_auth_switch_request_status = -1;
static int hf_mysql_auth_switch_request_name = -1;
static int hf_mysql_auth_switch_request_data = -1;
static int hf_mysql_auth_switch_response_data = -1;
static int hf_mysql_sha2_auth = -1;
static int hf_mysql_sha2_response = -1;
static int hf_mysql_pubkey = -1;
static int hf_mysql_compressed_packet_length = -1;
static int hf_mysql_compressed_packet_length_uncompressed = -1;
static int hf_mysql_compressed_packet_number = -1;
@ -1230,6 +1237,9 @@ typedef enum mysql_state {
PREPARED_FIELDS,
AUTH_SWITCH_REQUEST,
AUTH_SWITCH_RESPONSE,
AUTH_SHA2,
AUTH_PUBKEY,
AUTH_SHA2_RESPONSE,
BINLOG_DUMP
} mysql_state_t;
@ -1252,6 +1262,9 @@ static const value_string state_vals[] = {
{PREPARED_FIELDS, "fields in response to PREPARE"},
{AUTH_SWITCH_REQUEST, "authentication switch request"},
{AUTH_SWITCH_RESPONSE, "authentication switch response"},
{AUTH_SHA2, "caching_sha2_password"},
{AUTH_PUBKEY, "public key request"},
{AUTH_SHA2_RESPONSE, "caching_sha2_password response"},
{BINLOG_DUMP, "binlog event"},
{0, NULL}
};
@ -1319,6 +1332,7 @@ static int mysql_dissect_response_prepare(tvbuff_t *tvb, packet_info *pinfo, int
static int mysql_dissect_auth_switch_request(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data);
static int mysql_dissect_eof(tvbuff_t *tvb, packet_info *pinfo, proto_item *pi, int offset, proto_tree *tree, mysql_conn_data_t *conn_data);
static int mysql_dissect_auth_switch_response(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data);
static int mysql_dissect_auth_sha2(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data);
static void mysql_dissect_exec_string(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_datetime(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_tiny(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
@ -1999,6 +2013,9 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
if(current_state == AUTH_SWITCH_RESPONSE){
return mysql_dissect_auth_switch_response(tvb, pinfo, offset, tree, conn_data);
}
if(current_state == AUTH_SHA2){
return mysql_dissect_auth_sha2(tvb, pinfo, offset, tree, conn_data);
}
request_item = proto_tree_add_item(tree, hf_mysql_request, tvb, offset, -1, ENC_NA);
req_tree = proto_item_add_subtree(request_item, ett_request);
@ -2522,7 +2539,6 @@ mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_EOF, state_vals, "Unknown (%u)"));
mysql_set_conn_state(pinfo, conn_data, REQUEST);
}
} else if (tvb_reported_length_remaining(tvb, offset) < 0xffffff) {
// not an EOF
if (current_state == AUTH_SWITCH_REQUEST) {
@ -2623,8 +2639,13 @@ mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset,
break;
case AUTH_SWITCH_REQUEST:
proto_item_append_text(pi, " - %s", val_to_str(AUTH_SWITCH_REQUEST, state_vals, "Unknown (%u)"));
offset = mysql_dissect_auth_switch_request(tvb, pinfo, offset, tree, conn_data);
if (tvb_reported_length_remaining(tvb,offset) == 2) {
proto_item_append_text(pi, " - %s", val_to_str(AUTH_SHA2, state_vals, "Unknown (%u)"));
offset = mysql_dissect_auth_sha2(tvb, pinfo, offset, tree, conn_data);
} else {
proto_item_append_text(pi, " - %s", val_to_str(AUTH_SWITCH_REQUEST, state_vals, "Unknown (%u)"));
offset = mysql_dissect_auth_switch_request(tvb, pinfo, offset, tree, conn_data);
}
break;
default:
@ -3562,6 +3583,81 @@ mysql_dissect_auth_switch_response(tvbuff_t *tvb, packet_info *pinfo, int offset
return offset + tvb_reported_length_remaining(tvb, offset);
}
/*
caching_sha2_password authentication state
Doc: https://dev.mysql.com/doc/dev/mysql-server/latest/page_caching_sha2_authentication_exchanges.html
*/
static int
mysql_dissect_auth_sha2(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_)
{
col_set_str(pinfo->cinfo, COL_INFO, "Caching_sha2_password " );
col_set_fence(pinfo->cinfo, COL_INFO);
if (tvb_reported_length_remaining(tvb,offset) == 2)
offset++;
char *auth2_state;
guint8 c = tvb_get_guint8(tvb, offset);
switch (c) {
case 2:
auth2_state = "request_public_key";
mysql_set_conn_state(pinfo, conn_data, AUTH_PUBKEY);
break;
case 3:
auth2_state = "fast_auth_success";
break;
case 4:
auth2_state = "perform_full_authentication";
mysql_set_conn_state(pinfo, conn_data, AUTH_SHA2);
break;
default:
auth2_state = "unknown";
}
col_append_str(pinfo->cinfo, COL_INFO, auth2_state);
proto_tree_add_string(tree, hf_mysql_sha2_auth, tvb, offset, 1, auth2_state);
offset++;
return offset + tvb_reported_length_remaining(tvb, offset);
}
/*
Public key as requested during caching_sha2_password authentication
*/
static int
mysql_dissect_pubkey(tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_tree *tree, mysql_conn_data_t *conn_data _U_, proto_item *pi _U_, mysql_state_t current_state _U_)
{
tvbuff_t *next_tvb;
col_set_str(pinfo->cinfo, COL_INFO, "Public key " );
col_set_fence(pinfo->cinfo, COL_INFO);
mysql_set_conn_state(pinfo, conn_data, AUTH_SHA2_RESPONSE);
offset++;
gint len = tvb_reported_length_remaining(tvb, offset) - 1;
next_tvb = tvb_new_subset_length(tvb, offset, len);
add_new_data_source(pinfo, next_tvb, "public key");
proto_tree_add_item(tree, hf_mysql_pubkey, tvb, offset, len, ENC_ASCII);
offset += len;
return offset + tvb_reported_length_remaining(tvb,offset);
}
/*
If caching_sha2_password authentication is used over a non-secure channel
then the authentication response (password) is encrypted with a RSA public key.
This means that we can't dissect this response without access to the RSA private key.
*/
static int
mysql_dissect_sha2_response(tvbuff_t *tvb, packet_info *pinfo _U_, int offset,
proto_tree *tree _U_, mysql_conn_data_t *conn_data _U_, proto_item *pi _U_, mysql_state_t current_state _U_)
{
gint len = tvb_reported_length_remaining(tvb, offset);
proto_tree_add_item(tree, hf_mysql_sha2_response, tvb, offset, len, ENC_NA);
return offset + tvb_reported_length_remaining(tvb, offset);
}
/*
get length of string in packet buffer
@ -3773,6 +3869,9 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
if (packet_number == 0 && mysql_frame_data_p->state == UNDEFINED) {
col_set_str(pinfo->cinfo, COL_INFO, "Server Greeting ");
offset = mysql_dissect_greeting(tvb, pinfo, offset, mysql_tree, conn_data);
} else if (mysql_frame_data_p->state == AUTH_PUBKEY) {
col_set_str(pinfo->cinfo, COL_INFO, "Public key ");
offset = mysql_dissect_pubkey(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p->state);
} else {
col_set_str(pinfo->cinfo, COL_INFO, "Response ");
offset = mysql_dissect_response(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p->state);
@ -3787,6 +3886,9 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
conn_data->compressed_state = MYSQL_COMPRESS_INIT;
}
}
} else if (mysql_frame_data_p->state == AUTH_SHA2_RESPONSE) {
col_set_str(pinfo->cinfo, COL_INFO, "Caching_sha2_password response");
offset = mysql_dissect_sha2_response(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p->state);
} else {
col_set_str(pinfo->cinfo, COL_INFO, "Request");
offset = mysql_dissect_request(tvb, pinfo, offset, mysql_tree, conn_data, mysql_frame_data_p->state);
@ -4957,6 +5059,21 @@ void proto_register_mysql(void)
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_mysql_sha2_auth,
{ "SHA2 Auth State", "mysql.hf_mysql_sha2_auth.name",
FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_mysql_pubkey,
{ "Public Key", "mysql.hf_mysql_pubkey",
FT_STRINGZ, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_mysql_sha2_response,
{ "SHA2 Auth Response", "mysql.hf_mysql_sha2_response",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_mysql_compressed_packet_length,
{ "Compressed Packet Length", "mysql.compressed_packet_length",
FT_UINT24, BASE_DEC, NULL, 0x0,