From 9cb618c71819911b6c3aadf0a5b1448751a6ec1d Mon Sep 17 00:00:00 2001 From: Fupeng Zhao Date: Sun, 30 Jun 2024 15:59:13 +0800 Subject: [PATCH 01/15] Add support for parsing the "caching_sha2_password" auth plugin --- .../protocol/mysql/mysql-analyzer.pac | 6 +- .../protocol/mysql/mysql-protocol.pac | 97 +++++++++++++++++- .../mysql.log | 23 +++++ .../out | 39 +++++++ .../Traces/mysql/caching_sha2_password.trace | Bin 0 -> 7561 bytes .../mysql/caching_sha2_password.test | 35 +++++++ 6 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out create mode 100644 testing/btest/Traces/mysql/caching_sha2_password.trace create mode 100644 testing/btest/scripts/base/protocols/mysql/caching_sha2_password.test diff --git a/src/analyzer/protocol/mysql/mysql-analyzer.pac b/src/analyzer/protocol/mysql/mysql-analyzer.pac index 31addd2518..ebc964a793 100644 --- a/src/analyzer/protocol/mysql/mysql-analyzer.pac +++ b/src/analyzer/protocol/mysql/mysql-analyzer.pac @@ -23,7 +23,7 @@ refine flow MySQL_Flow += { connection()->zeek_analyzer()->AnalyzerConfirmation(); // If the client requested SSL and didn't provide credentials, switch to SSL - if ( ${msg.version} == 10 && ( ${msg.v10_response.cap_flags} & CLIENT_SSL ) && ${msg.v10_response.credentials}->empty() ) + if ( ${msg.version} == 10 && ( ${msg.v10_response.cap_flags} & CLIENT_SSL )) { connection()->zeek_analyzer()->StartTLS(); return true; @@ -31,10 +31,10 @@ refine flow MySQL_Flow += { if ( mysql_handshake ) { - if ( ${msg.version} == 10 && ${msg.v10_response.credentials}->size() > 0 ) + if ( ${msg.version} == 10 ) zeek::BifEvent::enqueue_mysql_handshake(connection()->zeek_analyzer(), connection()->zeek_analyzer()->Conn(), - zeek::make_intrusive(c_str(${msg.v10_response.credentials[0].username}))); + zeek::make_intrusive(c_str(${msg.v10_response.plain.credentials.username}))); if ( ${msg.version} == 9 ) zeek::BifEvent::enqueue_mysql_handshake(connection()->zeek_analyzer(), connection()->zeek_analyzer()->Conn(), diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index e8415e3de0..ffc0c1fbc5 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -96,6 +96,11 @@ type LengthEncodedStringArg(first_byte: uint8) = record { }; %} +%code{ + const char* PLUGIN_CACHING_SHA2_PASSWORD = "caching_sha2_password"; +%} + +extern type PLUGIN_CACHING_SHA2_PASSWORD; extern type to_int; # Enums @@ -138,6 +143,9 @@ enum command_consts { enum state { CONNECTION_PHASE = 0, COMMAND_PHASE = 1, + SHA2_AUTH_PHASE = 2, + PUB_KEY_PHASE = 3, + SHA2_AUTH_RESP_PHASE = 4, }; enum Expected { @@ -158,12 +166,23 @@ enum EOFType { }; enum Client_Capabilities { + CLIENT_CONNECT_WITH_DB = 0x00000008, CLIENT_SSL = 0x00000800, + CLIENT_PLUGIN_AUTH = 0x00080000, + CLIENT_CONNECT_ATTRS = 0x00100000, # Expects an OK (instead of EOF) after the resultset rows of a Text Resultset. CLIENT_DEPRECATE_EOF = 0x01000000, + CLIENT_ZSTD_COMPRESSION_ALGORITHM = 0x04000000, +}; + +enum SHA2_Atuh_State { + REQUEST_PUBLIC_KEY = 2, + FAST_AUTH_SUCCESS = 3, + PERFORM_FULL_AUTHENTICATION = 4, }; type NUL_String = RE/[^\0]*\0/; +type EmptyOrNUL_String = RE/([^\0]*\0)?/; # MySQL PDU @@ -195,6 +214,9 @@ type Server_Message(seq_id: uint8, pkt_len: uint32) = case is_initial of { type Client_Message(state: int) = case state of { CONNECTION_PHASE -> connection_phase: Handshake_Response_Packet; COMMAND_PHASE -> command_phase : Command_Request_Packet; + SHA2_AUTH_PHASE -> sha2_auth_phase : SHA2_Auth_Packet; + PUB_KEY_PHASE -> pub_key_phase : Public_Key_Packet; + SHA2_AUTH_RESP_PHASE -> sha2_auth_resp_phase : SHA2_Auth_Response_Packet; }; # Handshake Request @@ -220,7 +242,12 @@ type Handshake_v10 = record { status_flags : uint16; capability_flags_2 : uint16; auth_plugin_data_len : uint8; - auth_plugin_name : NUL_String; + reserved : padding[10]; + auth_plugin_data_part_2: bytestring &length=13; + have_plugin : case ( ( capability_flags_2 << 4 ) & CLIENT_PLUGIN_AUTH ) of { + CLIENT_PLUGIN_AUTH -> auth_plugin_name: NUL_String; + 0x0 -> none : empty; + }; }; type Handshake_v9 = record { @@ -240,7 +267,40 @@ type Handshake_Response_Packet = case $context.connection.get_version() of { type Handshake_Credentials_v10 = record { username : NUL_String; - password : bytestring &restofdata; + password : LengthEncodedString; +}; + +type Connection_Attribute = record { + name : LengthEncodedString; + value : LengthEncodedString; +}; + +type Handshake_Connection_Attributes = record { + length : uint8; + attrs : Connection_Attribute[] &until($input.length() == 0); +} &length = length+1; + +type Handshake_Plain_v10(cap_flags: uint32) = record { + credentials: Handshake_Credentials_v10; + have_db : case ( cap_flags & CLIENT_CONNECT_WITH_DB ) of { + CLIENT_CONNECT_WITH_DB -> database: NUL_String; + 0x0 -> none_1 : empty; + }; + have_plugin : case ( cap_flags & CLIENT_PLUGIN_AUTH ) of { + CLIENT_PLUGIN_AUTH -> auth_plugin_name: EmptyOrNUL_String; + 0x0 -> none_2 : empty; + }; + have_attrs : case ( cap_flags & CLIENT_CONNECT_ATTRS ) of { + CLIENT_CONNECT_ATTRS -> conn_attrs: Handshake_Connection_Attributes; + 0x0 -> none_3 : empty; + }; + have_zstd : case ( cap_flags & CLIENT_ZSTD_COMPRESSION_ALGORITHM ) of { + CLIENT_ZSTD_COMPRESSION_ALGORITHM -> zstd_compression_level: uint8; + 0x0 -> none_4 : empty; + }; +} &let { + update_state: bool = $context.connection.update_state(SHA2_AUTH_PHASE) + &if(( cap_flags & CLIENT_PLUGIN_AUTH ) && auth_plugin_name==PLUGIN_CACHING_SHA2_PASSWORD); }; type Handshake_Response_Packet_v10 = record { @@ -248,7 +308,10 @@ type Handshake_Response_Packet_v10 = record { max_pkt_size: uint32; char_set : uint8; pad : padding[23]; - credentials : Handshake_Credentials_v10[] &until($input.length() == 0); + use_ssl : case ( cap_flags & CLIENT_SSL ) of { + CLIENT_SSL -> none : empty; + default -> plain: Handshake_Plain_v10(cap_flags); + }; } &let { deprecate_eof: bool = $context.connection.set_deprecate_eof(cap_flags & CLIENT_DEPRECATE_EOF); }; @@ -258,13 +321,37 @@ type Handshake_Response_Packet_v9 = record { max_pkt_size : uint24le; username : NUL_String; auth_response: NUL_String; - have_db : case ( cap_flags & 0x8 ) of { - 0x8 -> database: NUL_String; + have_db : case ( cap_flags & CLIENT_CONNECT_WITH_DB ) of { + CLIENT_CONNECT_WITH_DB -> database: NUL_String; 0x0 -> none : empty; }; password : bytestring &restofdata; }; +# SHA2 Auth + +type SHA2_Auth_Packet = record { + state: bytestring &restofdata; +} &let { + update_state_1: bool = $context.connection.update_state(COMMAND_PHASE) + &if(state[0] == FAST_AUTH_SUCCESS); + update_state_2: bool = $context.connection.update_state(PUB_KEY_PHASE) + &if(state[1] == REQUEST_PUBLIC_KEY); +}; + +type Public_Key_Packet = record { + pad : uint8; + pub_key: bytestring &restofdata; +} &let { + update_state: bool = $context.connection.update_state(SHA2_AUTH_RESP_PHASE); +}; + +type SHA2_Auth_Response_Packet = record { + data: bytestring &restofdata; +} &let { + update_state: bool = $context.connection.update_state(COMMAND_PHASE); +}; + # Command Request type Command_Request_Packet = record { diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log new file mode 100644 index 0000000000..53fb4143f2 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log @@ -0,0 +1,23 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path mysql +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response +#types time string addr port addr port string string bool count string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 56494 127.0.0.1 3306 login root F - Got an error reading communication packets +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 login root T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query \x00\x01show databases T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query \x00\x01show tables T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 field_list t T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query \x00\x01select @@version_comment limit 1 T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 quit (empty) - - - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 login root T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query \x00\x01show databases T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query \x00\x01show tables T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 field_list t T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query \x00\x01select @@version_comment limit 1 T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 quit (empty) - - - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out new file mode 100644 index 0000000000..6dd3801eba --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out @@ -0,0 +1,39 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql handshake, root +mysql error, 1158, Got an error reading communication packets +mysql handshake, root +mysql ok, 0 +mysql request, 3, \x00\x01show databases +mysql result row, [information_schema] +mysql result row, [mysql] +mysql result row, [performance_schema] +mysql result row, [sys] +mysql result row, [test] +mysql ok, 0 +mysql request, 3, \x00\x01show tables +mysql result row, [t] +mysql ok, 0 +mysql request, 4, t\x00 +mysql ok, 0 +mysql request, 3, \x00\x01select @@version_comment limit 1 +mysql result row, [MySQL Community Server - GPL] +mysql ok, 0 +mysql request, 1, +mysql handshake, root +mysql ok, 0 +mysql request, 3, \x00\x01show databases +mysql result row, [information_schema] +mysql result row, [mysql] +mysql result row, [performance_schema] +mysql result row, [sys] +mysql result row, [test] +mysql ok, 0 +mysql request, 3, \x00\x01show tables +mysql result row, [t] +mysql ok, 0 +mysql request, 4, t\x00 +mysql ok, 0 +mysql request, 3, \x00\x01select @@version_comment limit 1 +mysql result row, [MySQL Community Server - GPL] +mysql ok, 0 +mysql request, 1, diff --git a/testing/btest/Traces/mysql/caching_sha2_password.trace b/testing/btest/Traces/mysql/caching_sha2_password.trace new file mode 100644 index 0000000000000000000000000000000000000000..184ed83254a02fee3dd5614307debefdc7faa93b GIT binary patch literal 7561 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 H5!L?z3qPk) literal 0 HcmV?d00001 diff --git a/testing/btest/scripts/base/protocols/mysql/caching_sha2_password.test b/testing/btest/scripts/base/protocols/mysql/caching_sha2_password.test new file mode 100644 index 0000000000..ba5c8a90b0 --- /dev/null +++ b/testing/btest/scripts/base/protocols/mysql/caching_sha2_password.test @@ -0,0 +1,35 @@ +# @TEST-EXEC: zeek -b -C -r $TRACES/mysql/caching_sha2_password.trace %INPUT >out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff mysql.log + +@load base/protocols/mysql + +event mysql_ok(c: connection, affected_rows: count) + { + print "mysql ok", affected_rows; + } + +event mysql_eof(c: connection, is_intermediate: bool) + { + print "mysql eof", is_intermediate; + } + +event mysql_result_row(c: connection, row: string_vec) + { + print "mysql result row", row; + } + +event mysql_error(c: connection, code: count, msg: string) + { + print "mysql error", code, msg; + } + +event mysql_command_request(c: connection, command: count, arg: string) + { + print "mysql request", command, arg; + } + +event mysql_handshake(c: connection, username: string) + { + print "mysql handshake", username; + } From e8bdf149f22099a9a9cc84faa1964c23ea795fd3 Mon Sep 17 00:00:00 2001 From: Fupeng Zhao Date: Sun, 30 Jun 2024 21:52:31 +0800 Subject: [PATCH 02/15] Add support for "auth switch" and "query attrs" Also fix the issue where Resultset could not correctly distinguish between EOF_Packet and OK_Packet. --- .../protocol/mysql/mysql-analyzer.pac | 4 +- .../protocol/mysql/mysql-protocol.pac | 164 ++++++++++++++---- .../mysql.log | 16 ++ .../out | 14 ++ .../mysql.log | 12 +- .../out | 12 +- .../mysql.log | 14 ++ .../out | 12 ++ ...ing_sha2_password-after-auth-switch.pcapng | Bin 0 -> 4412 bytes testing/btest/Traces/mysql/query-attr.pcapng | Bin 0 -> 7696 bytes ...ching_sha2_password-after-auth-switch.test | 35 ++++ .../base/protocols/mysql/query-attr.test | 35 ++++ 12 files changed, 272 insertions(+), 46 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/mysql.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/mysql.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/out create mode 100644 testing/btest/Traces/mysql/caching_sha2_password-after-auth-switch.pcapng create mode 100644 testing/btest/Traces/mysql/query-attr.pcapng create mode 100644 testing/btest/scripts/base/protocols/mysql/caching_sha2_password-after-auth-switch.test create mode 100644 testing/btest/scripts/base/protocols/mysql/query-attr.test diff --git a/src/analyzer/protocol/mysql/mysql-analyzer.pac b/src/analyzer/protocol/mysql/mysql-analyzer.pac index ebc964a793..5f6782c4ff 100644 --- a/src/analyzer/protocol/mysql/mysql-analyzer.pac +++ b/src/analyzer/protocol/mysql/mysql-analyzer.pac @@ -83,8 +83,8 @@ refine flow MySQL_Flow += { function proc_resultset(msg: Resultset): bool %{ - if ( ${msg.is_eof} ) - return true; // Raised through proc_eof_packet() + if ( ${msg.is_eof_or_ok} ) + return true; // Raised through proc_eof_packet() or proc_ok_packet() if ( ! mysql_result_row ) return true; diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index ffc0c1fbc5..d482d23b10 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -96,11 +96,6 @@ type LengthEncodedStringArg(first_byte: uint8) = record { }; %} -%code{ - const char* PLUGIN_CACHING_SHA2_PASSWORD = "caching_sha2_password"; -%} - -extern type PLUGIN_CACHING_SHA2_PASSWORD; extern type to_int; # Enums @@ -141,11 +136,12 @@ enum command_consts { }; enum state { - CONNECTION_PHASE = 0, - COMMAND_PHASE = 1, - SHA2_AUTH_PHASE = 2, - PUB_KEY_PHASE = 3, - SHA2_AUTH_RESP_PHASE = 4, + CONNECTION_PHASE = 0, + COMMAND_PHASE = 1, + SHA2_AUTH_PHASE = 2, + PUB_KEY_PHASE = 3, + SHA2_AUTH_RESP_PHASE = 4, + AUTH_SWITCH_RESP_PHASE = 5, }; enum Expected { @@ -173,9 +169,10 @@ enum Client_Capabilities { # Expects an OK (instead of EOF) after the resultset rows of a Text Resultset. CLIENT_DEPRECATE_EOF = 0x01000000, CLIENT_ZSTD_COMPRESSION_ALGORITHM = 0x04000000, + CLIENT_QUERY_ATTRIBUTES = 0x08000000, }; -enum SHA2_Atuh_State { +enum SHA2_Auth_State { REQUEST_PUBLIC_KEY = 2, FAST_AUTH_SUCCESS = 3, PERFORM_FULL_AUTHENTICATION = 4, @@ -217,6 +214,7 @@ type Client_Message(state: int) = case state of { SHA2_AUTH_PHASE -> sha2_auth_phase : SHA2_Auth_Packet; PUB_KEY_PHASE -> pub_key_phase : Public_Key_Packet; SHA2_AUTH_RESP_PHASE -> sha2_auth_resp_phase : SHA2_Auth_Response_Packet; + AUTH_SWITCH_RESP_PHASE -> auth_switch_resp_phase : Auth_Switch_Response_Packet; }; # Handshake Request @@ -244,10 +242,14 @@ type Handshake_v10 = record { auth_plugin_data_len : uint8; reserved : padding[10]; auth_plugin_data_part_2: bytestring &length=13; - have_plugin : case ( ( capability_flags_2 << 4 ) & CLIENT_PLUGIN_AUTH ) of { - CLIENT_PLUGIN_AUTH -> auth_plugin_name: NUL_String; + have_plugin : case ( ( capability_flags_2 << 16 ) & CLIENT_PLUGIN_AUTH ) of { + CLIENT_PLUGIN_AUTH -> auth_plugin: NUL_String; 0x0 -> none : empty; }; +} &let { + update_auth_plugin: bool = $context.connection.set_auth_plugin(auth_plugin) + &if( ( capability_flags_2 << 16 ) & CLIENT_PLUGIN_AUTH ); + server_query_attrs: bool = $context.connection.set_server_query_attrs(( capability_flags_2 << 16 ) & CLIENT_QUERY_ATTRIBUTES); }; type Handshake_v9 = record { @@ -287,7 +289,7 @@ type Handshake_Plain_v10(cap_flags: uint32) = record { 0x0 -> none_1 : empty; }; have_plugin : case ( cap_flags & CLIENT_PLUGIN_AUTH ) of { - CLIENT_PLUGIN_AUTH -> auth_plugin_name: EmptyOrNUL_String; + CLIENT_PLUGIN_AUTH -> auth_plugin: EmptyOrNUL_String; 0x0 -> none_2 : empty; }; have_attrs : case ( cap_flags & CLIENT_CONNECT_ATTRS ) of { @@ -299,8 +301,10 @@ type Handshake_Plain_v10(cap_flags: uint32) = record { 0x0 -> none_4 : empty; }; } &let { - update_state: bool = $context.connection.update_state(SHA2_AUTH_PHASE) - &if(( cap_flags & CLIENT_PLUGIN_AUTH ) && auth_plugin_name==PLUGIN_CACHING_SHA2_PASSWORD); + update_auth_plugin: bool = $context.connection.set_auth_plugin(auth_plugin) + &if( cap_flags & CLIENT_PLUGIN_AUTH ); + update_state: bool = $context.connection.update_state_from_auth() + &if( cap_flags & CLIENT_PLUGIN_AUTH ); }; type Handshake_Response_Packet_v10 = record { @@ -314,6 +318,7 @@ type Handshake_Response_Packet_v10 = record { }; } &let { deprecate_eof: bool = $context.connection.set_deprecate_eof(cap_flags & CLIENT_DEPRECATE_EOF); + client_query_attrs: bool = $context.connection.set_client_query_attrs(cap_flags & CLIENT_QUERY_ATTRIBUTES); }; type Handshake_Response_Packet_v9 = record { @@ -352,10 +357,43 @@ type SHA2_Auth_Response_Packet = record { update_state: bool = $context.connection.update_state(COMMAND_PHASE); }; +# Auth Switch + +type Auth_Switch_Response_Packet = record { + data : bytestring &restofdata; +} &let { + update_state: bool = $context.connection.update_state_from_auth(); +}; + # Command Request +type AttributeTypeAndName = record { + type: uint16; + name: LengthEncodedString; +}; + +type Attributes(count: uint8) = record { + unused : uint8; + send_types_to_server: uint8; # Always 1. + names : AttributeTypeAndName[count]; + values : LengthEncodedString[count]; +}; + +type Query_Attributes = record { + count : uint8; + set_coun : uint8; + have_attr : case ( count > 0 ) of { + true -> attrs: Attributes(count); + false -> none: empty; + }; +}; + type Command_Request_Packet = record { command: uint8; + attrs : case ( command == COM_QUERY && $context.connection.get_client_query_attrs() && $context.connection.get_server_query_attrs() ) of { + true -> query_attrs: Query_Attributes; + false -> none: empty; + }; arg : bytestring &restofdata; } &let { update_expectation: bool = $context.connection.set_next_expected_from_command(command); @@ -413,22 +451,22 @@ type ColumnDefinition = record { }; # Only used to indicate the end of a result, no intermediate eofs here. -type EOFOrOK = case $context.connection.get_deprecate_eof() of { +# MySQL spec says "You must check whether the packet length is less than 9 +# to make sure that it is a EOF_Packet packet" so the value of 13 here +# comes from that 9, plus a 4-byte header. +type EOFOrOK(pkt_len: uint32) = case ( $context.connection.get_deprecate_eof() || pkt_len > 13 ) of { false -> eof: EOF_Packet(EOF_END); true -> ok: OK_Packet; }; type ColumnDefinitionOrEOF(pkt_len: uint32) = record { marker : uint8; - def_or_eof: case is_eof of { - true -> eof: EOFOrOK; + def_or_eof: case is_eof_or_ok of { + true -> eof: EOFOrOK(pkt_len); false -> def: ColumnDefinition41(marker); - } &requires(is_eof); + } &requires(is_eof_or_ok); } &let { - # MySQL spec says "You must check whether the packet length is less than 9 - # to make sure that it is a EOF_Packet packet" so the value of 13 here - # comes from that 9, plus a 4-byte header. - is_eof: bool = (marker == 0xfe && pkt_len < 13); + is_eof_or_ok: bool = (marker == 0xfe); }; @@ -442,17 +480,14 @@ type EOFIfLegacyThenResultset(pkt_len: uint32) = case $context.connection.get_de type Resultset(pkt_len: uint32) = record { marker : uint8; - row_or_eof: case is_eof of { - true -> eof: EOFOrOK; + row_or_eof: case is_eof_or_ok of { + true -> eof: EOFOrOK(pkt_len); false -> row: ResultsetRow(marker); - } &requires(is_eof); + } &requires(is_eof_or_ok); } &let { - # MySQL spec says "You must check whether the packet length is less than 9 - # to make sure that it is a EOF_Packet packet" so the value of 13 here - # comes from that 9, plus a 4-byte header. - is_eof : bool = (marker == 0xfe && pkt_len < 13); + is_eof_or_ok : bool = (marker == 0xfe); update_result_seen: bool = $context.connection.inc_results_seen(); - update_expectation: bool = $context.connection.set_next_expected(is_eof ? NO_EXPECTATION : EXPECT_RESULTSET); + update_expectation: bool = $context.connection.set_next_expected(is_eof_or_ok ? NO_EXPECTATION : EXPECT_RESULTSET); }; type ResultsetRow(first_byte: uint8) = record { @@ -480,6 +515,9 @@ type AuthSwitchRequest = record { status: uint8; name : NUL_String; data : bytestring &restofdata; +} &let { + update_auth_plugin: bool = $context.connection.set_auth_plugin(name); + update_state: bool = $context.connection.update_state(AUTH_SWITCH_RESP_PHASE); }; type ColumnDefinition320 = record { @@ -531,6 +569,9 @@ refine connection MySQL_Conn += { uint32 remaining_cols_; uint32 results_seen_; bool deprecate_eof_; + bool server_query_attrs_; + bool client_query_attrs_; + bytestring auth_plugin_; %} %init{ @@ -542,6 +583,13 @@ refine connection MySQL_Conn += { remaining_cols_ = 0; results_seen_ = 0; deprecate_eof_ = false; + server_query_attrs_ = false; + client_query_attrs_ = false; + auth_plugin_ = bytestring(); + %} + + %cleanup{ + auth_plugin_.free(); %} function get_version(): uint8 @@ -577,6 +625,18 @@ refine connection MySQL_Conn += { return true; %} + function update_state_from_auth(): bool + %{ + if ( auth_plugin_ == "caching_sha2_password" ) + { + state_ = SHA2_AUTH_PHASE; + if ( expected_ == EXPECT_AUTH_SWITCH ) + expected_ = EXPECT_STATUS; + } + + return true; + %} + function get_deprecate_eof(): bool %{ return deprecate_eof_; @@ -588,6 +648,46 @@ refine connection MySQL_Conn += { return true; %} + function get_server_query_attrs(): bool + %{ + return server_query_attrs_; + %} + + function set_server_query_attrs(q: bool): bool + %{ + server_query_attrs_ = q; + return true; + %} + + function get_client_query_attrs(): bool + %{ + return client_query_attrs_; + %} + + function set_client_query_attrs(q: bool): bool + %{ + client_query_attrs_ = q; + return true; + %} + + function get_auth_plugin(): bytestring + %{ + return auth_plugin_; + %} + + function set_auth_plugin(a: bytestring): bool + %{ + if ( auth_plugin_.length() > 0 && + strncmp(c_str(auth_plugin_), c_str(a), auth_plugin_.length()) != 0 ) + { + expected_ = EXPECT_AUTH_SWITCH; + } + + auth_plugin_.free(); + auth_plugin_.init(a.data(), a.length()); + return true; + %} + function get_expectation(): Expected %{ return expected_; diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/mysql.log new file mode 100644 index 0000000000..26e609fe2a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/mysql.log @@ -0,0 +1,16 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path mysql +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response +#types time string addr port addr port string string bool count string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 35928 127.0.0.1 3306 login root T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 35928 127.0.0.1 3306 query select @@version_comment limit 1 T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 35928 127.0.0.1 3306 query select DATABASE(), USER() limit 1 T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 35928 127.0.0.1 3306 query select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1 T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 35928 127.0.0.1 3306 statistics (empty) - - - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 35928 127.0.0.1 3306 quit (empty) - - - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out new file mode 100644 index 0000000000..e8ead41d58 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out @@ -0,0 +1,14 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql handshake, root +mysql ok, 0 +mysql request, 3, select @@version_comment limit 1 +mysql result row, [MySQL Community Server - GPL] +mysql ok, 0 +mysql request, 3, select DATABASE(), USER() limit 1 +mysql result row, [, root@localhost] +mysql ok, 0 +mysql request, 3, select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1 +mysql result row, [utf8mb4, utf8mb4, utf8mb4, utf8mb4] +mysql ok, 0 +mysql request, 9, +mysql request, 1, diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log index 53fb4143f2..bb46a96482 100644 --- a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/mysql.log @@ -9,15 +9,15 @@ #types time string addr port addr port string string bool count string XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 56494 127.0.0.1 3306 login root F - Got an error reading communication packets XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 login root T 0 - -XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query \x00\x01show databases T 0 - -XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query \x00\x01show tables T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query show databases T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query show tables T 0 - XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 field_list t T 0 - -XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query \x00\x01select @@version_comment limit 1 T 0 - +XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 query select @@version_comment limit 1 T 0 - XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 127.0.0.1 49352 127.0.0.1 3306 quit (empty) - - - XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 login root T 0 - -XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query \x00\x01show databases T 0 - -XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query \x00\x01show tables T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query show databases T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query show tables T 0 - XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 field_list t T 0 - -XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query \x00\x01select @@version_comment limit 1 T 0 - +XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 query select @@version_comment limit 1 T 0 - XXXXXXXXXX.XXXXXX C4J4Th3PJpwUYZZ6gc 127.0.0.1 40950 127.0.0.1 3306 quit (empty) - - - #close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out index 6dd3801eba..f8855b38fc 100644 --- a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out @@ -3,37 +3,37 @@ mysql handshake, root mysql error, 1158, Got an error reading communication packets mysql handshake, root mysql ok, 0 -mysql request, 3, \x00\x01show databases +mysql request, 3, show databases mysql result row, [information_schema] mysql result row, [mysql] mysql result row, [performance_schema] mysql result row, [sys] mysql result row, [test] mysql ok, 0 -mysql request, 3, \x00\x01show tables +mysql request, 3, show tables mysql result row, [t] mysql ok, 0 mysql request, 4, t\x00 mysql ok, 0 -mysql request, 3, \x00\x01select @@version_comment limit 1 +mysql request, 3, select @@version_comment limit 1 mysql result row, [MySQL Community Server - GPL] mysql ok, 0 mysql request, 1, mysql handshake, root mysql ok, 0 -mysql request, 3, \x00\x01show databases +mysql request, 3, show databases mysql result row, [information_schema] mysql result row, [mysql] mysql result row, [performance_schema] mysql result row, [sys] mysql result row, [test] mysql ok, 0 -mysql request, 3, \x00\x01show tables +mysql request, 3, show tables mysql result row, [t] mysql ok, 0 mysql request, 4, t\x00 mysql ok, 0 -mysql request, 3, \x00\x01select @@version_comment limit 1 +mysql request, 3, select @@version_comment limit 1 mysql result row, [MySQL Community Server - GPL] mysql ok, 0 mysql request, 1, diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/mysql.log new file mode 100644 index 0000000000..3fd06ec55f --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/mysql.log @@ -0,0 +1,14 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path mysql +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response +#types time string addr port addr port string string bool count string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 51682 127.0.0.1 3306 login ykg T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 51682 127.0.0.1 3306 query select @@version_comment limit 1 T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 51682 127.0.0.1 3306 query select now() T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 51682 127.0.0.1 3306 query select now() T 0 - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/out b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/out new file mode 100644 index 0000000000..dce5524739 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr/out @@ -0,0 +1,12 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql handshake, ykg +mysql ok, 0 +mysql request, 3, select @@version_comment limit 1 +mysql result row, [Source distribution] +mysql ok, 0 +mysql request, 3, select now() +mysql result row, [2022-07-13 10:45:41] +mysql ok, 0 +mysql request, 3, select now() +mysql result row, [2022-07-13 10:45:43] +mysql ok, 0 diff --git a/testing/btest/Traces/mysql/caching_sha2_password-after-auth-switch.pcapng b/testing/btest/Traces/mysql/caching_sha2_password-after-auth-switch.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..3f4407eb49c93a38b29b4fc7fcbcf2235bd912ec GIT binary patch literal 4412 zcmbVQZ)_Xo8Gp}q{*tmbf;P}rdj*qlNNk;*i~mGb<|Yj-EFpiKuxQ127vH5muzlw4 z?2t}lUZqLZln?tbBoI*8R<)`q7){j#6+#GYn$%7Ea#ESlM7IgbD9hLfq;0UqJkRU9 z_|7p#;)&ke``*3x{GR9cd*1gwxp&vD-q#2rGlz!mRr{0oD0Qq^St z;>;jbqJa=U8seAebOA3b)G`qvaAOOwedAs8McUb3G|D}~Y%G!hI4QG0BV3c*m2r~Wd#Y8gHd4)LMDiIq@{ zk3@u(2tN`Xgb)eFi?K@9m4HVN{N+>v*xh7WRf{WP=CK}_)dO>GA-#kYRn4FyBYcFA zzORT6USYmF;hMt%*oXU<_U#?|EcyWSu{o`qOc8P@_3Z>nbY468BM1)L#&X|{_~t8J zIP*nM?VT;25Rb3+j?dHGd$l^{!TX%o3(paw8SA+x+hGxcScfj#SbaA(md|e9elOs4 zzt~^r-tSssASZu!bMhM){^VTk z4)Da)obDWbO;rs-^6UEJxpYA^l(O7lVcnN5DpF@?Bsvo1d(y?6XsoJQzO%k)y=hhN zV2iphoyjS3!2kz&xjUzh61XOjg~! z4QufNTPM@*m?&(B|B;esmgV1Gy>Om>K6$QKz4rXX@rla$$|rqKO-#n`K2^uZIIe>dwrI|a{t>5QIYgYoxF?o^F*c_$ zoLO^%?F*8fuZ2_H#+ls4@#5aIkB|=V;z_{2TRVEX)0X+zs;j_V#CNW9q2!58z&&hE zWB5Yq2;=*QHonF8b>RC-(>iDPjdNqea)voR^{ywTz!;m;7{>9`6vJ3*iGg)C>(23R zaN#AKF6ZQoK@$mUd8IRIJ`c;1=9Ii*&=AbR7%<;|LPNt?9{jy8_DASra~g}~O_SAE z_ES3+i|;G(&FU_u>#tq0c!5@qy9WsOO7beW+p{Aa(pMvkOB}gL$TtZ&2%#UCSx?SS z!#71fUn(fZI!($Ntc;aw!%0DbvBOLdT2x2-bOem>S;iz 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 +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff mysql.log + +@load base/protocols/mysql + +event mysql_ok(c: connection, affected_rows: count) + { + print "mysql ok", affected_rows; + } + +event mysql_eof(c: connection, is_intermediate: bool) + { + print "mysql eof", is_intermediate; + } + +event mysql_result_row(c: connection, row: string_vec) + { + print "mysql result row", row; + } + +event mysql_error(c: connection, code: count, msg: string) + { + print "mysql error", code, msg; + } + +event mysql_command_request(c: connection, command: count, arg: string) + { + print "mysql request", command, arg; + } + +event mysql_handshake(c: connection, username: string) + { + print "mysql handshake", username; + } diff --git a/testing/btest/scripts/base/protocols/mysql/query-attr.test b/testing/btest/scripts/base/protocols/mysql/query-attr.test new file mode 100644 index 0000000000..d827ea97c0 --- /dev/null +++ b/testing/btest/scripts/base/protocols/mysql/query-attr.test @@ -0,0 +1,35 @@ +# @TEST-EXEC: zeek -b -C -r $TRACES/mysql/query-attr.pcapng %INPUT >out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff mysql.log + +@load base/protocols/mysql + +event mysql_ok(c: connection, affected_rows: count) + { + print "mysql ok", affected_rows; + } + +event mysql_eof(c: connection, is_intermediate: bool) + { + print "mysql eof", is_intermediate; + } + +event mysql_result_row(c: connection, row: string_vec) + { + print "mysql result row", row; + } + +event mysql_error(c: connection, code: count, msg: string) + { + print "mysql error", code, msg; + } + +event mysql_command_request(c: connection, command: count, arg: string) + { + print "mysql request", command, arg; + } + +event mysql_handshake(c: connection, username: string) + { + print "mysql handshake", username; + } From c82756bda473e6c79b582773c45d66aef0ced787 Mon Sep 17 00:00:00 2001 From: Fupeng Zhao Date: Mon, 1 Jul 2024 14:55:47 +0800 Subject: [PATCH 03/15] Refactored connection phase state handling Added `ConnectionExpected` enum for expected packet types during the connection phase. --- .../protocol/mysql/mysql-protocol.pac | 68 +++++++++++++------ 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index d482d23b10..76bdb5676f 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -136,12 +136,15 @@ enum command_consts { }; enum state { - CONNECTION_PHASE = 0, - COMMAND_PHASE = 1, - SHA2_AUTH_PHASE = 2, - PUB_KEY_PHASE = 3, - SHA2_AUTH_RESP_PHASE = 4, - AUTH_SWITCH_RESP_PHASE = 5, + CONNECTION_PHASE = 0, + COMMAND_PHASE = 1, +}; + +enum ConnectionExpected { + EXPECT_HANDSHAKE, + EXPECT_SHA2_AUTH, + EXPECT_PUB_KEY, + EXPECT_AUTH_SWITCH_RESP, }; enum Expected { @@ -154,6 +157,7 @@ enum Expected { EXPECT_RESULTSET, EXPECT_REST_OF_PACKET, EXPECT_AUTH_SWITCH, + EXPECT_SHA2_AUTH_RESP, }; enum EOFType { @@ -209,12 +213,8 @@ type Server_Message(seq_id: uint8, pkt_len: uint32) = case is_initial of { }; type Client_Message(state: int) = case state of { - CONNECTION_PHASE -> connection_phase: Handshake_Response_Packet; + CONNECTION_PHASE -> connection_phase: Connection_Phase_Packets; COMMAND_PHASE -> command_phase : Command_Request_Packet; - SHA2_AUTH_PHASE -> sha2_auth_phase : SHA2_Auth_Packet; - PUB_KEY_PHASE -> pub_key_phase : Public_Key_Packet; - SHA2_AUTH_RESP_PHASE -> sha2_auth_resp_phase : SHA2_Auth_Response_Packet; - AUTH_SWITCH_RESP_PHASE -> auth_switch_resp_phase : Auth_Switch_Response_Packet; }; # Handshake Request @@ -303,7 +303,7 @@ type Handshake_Plain_v10(cap_flags: uint32) = record { } &let { update_auth_plugin: bool = $context.connection.set_auth_plugin(auth_plugin) &if( cap_flags & CLIENT_PLUGIN_AUTH ); - update_state: bool = $context.connection.update_state_from_auth() + update_expected: bool = $context.connection.update_expected_from_auth() &if( cap_flags & CLIENT_PLUGIN_AUTH ); }; @@ -338,9 +338,9 @@ type Handshake_Response_Packet_v9 = record { type SHA2_Auth_Packet = record { state: bytestring &restofdata; } &let { - update_state_1: bool = $context.connection.update_state(COMMAND_PHASE) + update_state : bool = $context.connection.update_state(COMMAND_PHASE) &if(state[0] == FAST_AUTH_SUCCESS); - update_state_2: bool = $context.connection.update_state(PUB_KEY_PHASE) + update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_PUB_KEY) &if(state[1] == REQUEST_PUBLIC_KEY); }; @@ -348,7 +348,7 @@ type Public_Key_Packet = record { pad : uint8; pub_key: bytestring &restofdata; } &let { - update_state: bool = $context.connection.update_state(SHA2_AUTH_RESP_PHASE); + update_expectation: bool = $context.connection.set_next_expected(EXPECT_SHA2_AUTH_RESP); }; type SHA2_Auth_Response_Packet = record { @@ -362,7 +362,17 @@ type SHA2_Auth_Response_Packet = record { type Auth_Switch_Response_Packet = record { data : bytestring &restofdata; } &let { - update_state: bool = $context.connection.update_state_from_auth(); + update_expected: bool = $context.connection.update_expected_from_auth(); +}; + +# Connection Phase + +type Connection_Phase_Packets = case $context.connection.get_conn_expectation() of { + EXPECT_HANDSHAKE -> handshake_resp : Handshake_Response_Packet; + EXPECT_SHA2_AUTH -> sha2_auth : SHA2_Auth_Packet; + EXPECT_PUB_KEY -> pub_key : Public_Key_Packet; + EXPECT_AUTH_SWITCH_RESP -> atuh_switch_resp : Auth_Switch_Response_Packet; + default -> unknown : empty; }; # Command Request @@ -409,6 +419,7 @@ type Command_Response(pkt_len: uint32) = case $context.connection.get_expectatio EXPECT_REST_OF_PACKET -> rest : bytestring &restofdata; EXPECT_STATUS -> status : Command_Response_Status; EXPECT_AUTH_SWITCH -> auth_switch : AuthSwitchRequest; + EXPECT_SHA2_AUTH_RESP -> sha2_auth_resp: SHA2_Auth_Response_Packet; EXPECT_EOF_THEN_RESULTSET -> eof : EOFIfLegacyThenResultset(pkt_len); default -> unknown : empty; }; @@ -516,8 +527,8 @@ type AuthSwitchRequest = record { name : NUL_String; data : bytestring &restofdata; } &let { - update_auth_plugin: bool = $context.connection.set_auth_plugin(name); - update_state: bool = $context.connection.update_state(AUTH_SWITCH_RESP_PHASE); + update_auth_plugin : bool = $context.connection.set_auth_plugin(name); + update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_AUTH_SWITCH_RESP); }; type ColumnDefinition320 = record { @@ -565,6 +576,7 @@ refine connection MySQL_Conn += { uint8 previous_seq_id_; int state_; Expected expected_; + ConnectionExpected conn_expected_; uint32 col_count_; uint32 remaining_cols_; uint32 results_seen_; @@ -579,6 +591,7 @@ refine connection MySQL_Conn += { previous_seq_id_ = 0; state_ = CONNECTION_PHASE; expected_ = EXPECT_STATUS; + conn_expected_ = EXPECT_HANDSHAKE; col_count_ = 0; remaining_cols_ = 0; results_seen_ = 0; @@ -622,14 +635,18 @@ refine connection MySQL_Conn += { function update_state(s: state): bool %{ state_ = s; + + if ( s == COMMAND_PHASE ) + conn_expected_ = EXPECT_HANDSHAKE; // Reset connection phase expectation + return true; %} - function update_state_from_auth(): bool + function update_expected_from_auth(): bool %{ if ( auth_plugin_ == "caching_sha2_password" ) { - state_ = SHA2_AUTH_PHASE; + conn_expected_ = EXPECT_SHA2_AUTH; if ( expected_ == EXPECT_AUTH_SWITCH ) expected_ = EXPECT_STATUS; } @@ -699,6 +716,17 @@ refine connection MySQL_Conn += { return true; %} + function get_conn_expectation(): ConnectionExpected + %{ + return conn_expected_; + %} + + function set_next_conn_expected(c: ConnectionExpected): bool + %{ + conn_expected_ = c; + return true; + %} + function set_next_expected_from_command(cmd: uint8): bool %{ switch ( cmd ) { From e98b80d14073e1f060f8b8b0ba6703f3fc5f7df8 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Thu, 4 Jul 2024 10:24:20 +0200 Subject: [PATCH 04/15] mysql: Fix auth_plugin_data_part2 length computation --- src/analyzer/protocol/mysql/mysql-protocol.pac | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index 76bdb5676f..ea81bb3398 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -240,13 +240,14 @@ type Handshake_v10 = record { status_flags : uint16; capability_flags_2 : uint16; auth_plugin_data_len : uint8; - reserved : padding[10]; - auth_plugin_data_part_2: bytestring &length=13; + reserved : padding[10]; + auth_plugin_data_part_2: bytestring &length=auth_plugin_data_part_2_len; have_plugin : case ( ( capability_flags_2 << 16 ) & CLIENT_PLUGIN_AUTH ) of { CLIENT_PLUGIN_AUTH -> auth_plugin: NUL_String; 0x0 -> none : empty; }; } &let { + auth_plugin_data_part_2_len = (auth_plugin_data_len > 8 && (auth_plugin_data_len - 8) > 13) ? auth_plugin_data_len - 8 : 13; update_auth_plugin: bool = $context.connection.set_auth_plugin(auth_plugin) &if( ( capability_flags_2 << 16 ) & CLIENT_PLUGIN_AUTH ); server_query_attrs: bool = $context.connection.set_server_query_attrs(( capability_flags_2 << 16 ) & CLIENT_QUERY_ATTRIBUTES); From 48e76f38cb3085785af24a45d6cd22d9e2762755 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Thu, 4 Jul 2024 10:25:56 +0200 Subject: [PATCH 05/15] mysql: Make auth_plugin_ a std::string --- .../protocol/mysql/mysql-protocol.pac | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index ea81bb3398..2ea734cd56 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -584,7 +584,7 @@ refine connection MySQL_Conn += { bool deprecate_eof_; bool server_query_attrs_; bool client_query_attrs_; - bytestring auth_plugin_; + std::string auth_plugin_; %} %init{ @@ -599,11 +599,6 @@ refine connection MySQL_Conn += { deprecate_eof_ = false; server_query_attrs_ = false; client_query_attrs_ = false; - auth_plugin_ = bytestring(); - %} - - %cleanup{ - auth_plugin_.free(); %} function get_version(): uint8 @@ -688,21 +683,16 @@ refine connection MySQL_Conn += { return true; %} - function get_auth_plugin(): bytestring - %{ - return auth_plugin_; - %} - function set_auth_plugin(a: bytestring): bool %{ - if ( auth_plugin_.length() > 0 && - strncmp(c_str(auth_plugin_), c_str(a), auth_plugin_.length()) != 0 ) + // binpac::std_str() includes trailing \0 from parsing. + auto new_auth_plugin = std::string(binpac::c_str(a)); + if ( ! auth_plugin_.empty() && new_auth_plugin != auth_plugin_ ) { expected_ = EXPECT_AUTH_SWITCH; } - auth_plugin_.free(); - auth_plugin_.init(a.data(), a.length()); + auth_plugin_ = std::move(new_auth_plugin); return true; %} From 8a92945b0639fc1a814c519d9dbbf21f67b91053 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Fri, 5 Jul 2024 10:25:25 +0200 Subject: [PATCH 06/15] mysql: AuthSwitchRequest: &enforce a 0xfe / 254 status --- src/analyzer/protocol/mysql/mysql-protocol.pac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index 2ea734cd56..7d427460ef 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -524,7 +524,7 @@ type ColumnDefinition41(first_byte: uint8) = record { }; type AuthSwitchRequest = record { - status: uint8; + status: uint8 &enforce(status==254); name : NUL_String; data : bytestring &restofdata; } &let { From 40f1c2cb6dbc2d3be9977ea8db3eb443f09515dc Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Fri, 5 Jul 2024 10:24:40 +0200 Subject: [PATCH 07/15] mysql: Add mysql_auth_plugin, mysql_auth_more_data and mysql_auth_switch_request events Remove caching_sha2_password parsing/state from the analyzer and implement the generic events. If we actually want to peak into the authentication mechanism, we could write a separate analyzer for it. For now, treat it as opaque values that are exposed to script land. The added tests show the --get-server-public-key in use where mysql_auth_more_data contains an RSA public key. --- src/analyzer/protocol/mysql/events.bif | 36 +++++++++ .../protocol/mysql/mysql-analyzer.pac | 49 +++++++++++ .../protocol/mysql/mysql-protocol.pac | 81 +++++-------------- .../out | 5 ++ .../out | 12 +++ ...ching_sha2_password-after-auth-switch.test | 15 ++++ .../mysql/caching_sha2_password.test | 15 ++++ 7 files changed, 151 insertions(+), 62 deletions(-) diff --git a/src/analyzer/protocol/mysql/events.bif b/src/analyzer/protocol/mysql/events.bif index ec5fa61ae6..68ff0e5720 100644 --- a/src/analyzer/protocol/mysql/events.bif +++ b/src/analyzer/protocol/mysql/events.bif @@ -87,3 +87,39 @@ event mysql_server_version%(c: connection, ver: string%); ## .. zeek:see:: mysql_command_request mysql_error mysql_ok mysql_server_version event mysql_handshake%(c: connection, username: string%); +## Generated for information about plugin authentication within handshake packets. +## +## c: The connection. +## +## is_orig: True if this is from the client, false if from the server. +## +## name: Name of the authentication plugin. +## +## .. zeek:see:: mysql_handshake mysql_auth_switch_request mysql_auth_more_data +event mysql_auth_plugin%(c: connection, is_orig: bool, name: string%); + +## Generated for a server packet with an auth switch request. +## +## c: The connection. +## +## name: The plugin name. +## +## data: Initial authentication data for the plugin. +## +## .. zeek:see:: mysql_handshake mysql_auth_more_data +event mysql_auth_switch_request%(c: connection, name: string, data: string%); + +## Generated for opaque authentication data exchanged between client and server +## after the client's handshake packet, but before the server replied with +## an OK_Packet +## +## Data is specific to the plugin auth mechanism used by client and server. +## +## c: The connection. +## +## is_orig: True if this is from the client, false if from the server. +## +## data: More authentication data. +## +## .. zeek:see:: mysql_handshake mysql_auth_switch_request +event mysql_auth_more_data%(c: connection, is_orig: bool, data: string%); diff --git a/src/analyzer/protocol/mysql/mysql-analyzer.pac b/src/analyzer/protocol/mysql/mysql-analyzer.pac index 5f6782c4ff..738e063fbb 100644 --- a/src/analyzer/protocol/mysql/mysql-analyzer.pac +++ b/src/analyzer/protocol/mysql/mysql-analyzer.pac @@ -14,6 +14,17 @@ refine flow MySQL_Flow += { connection()->zeek_analyzer()->Conn(), zeek::make_intrusive(c_str(${msg.handshake9.server_version}))); } + + if ( mysql_auth_plugin ) + { + if ( ${msg.version} == 10 && (${msg.handshake10.capability_flags_2} << 16) & CLIENT_PLUGIN_AUTH ) + { + zeek::BifEvent::enqueue_mysql_auth_plugin(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn(), + false /*is_orig*/, + zeek::make_intrusive(c_str(${msg.handshake10.auth_plugin}))); + } + } return true; %} @@ -40,6 +51,18 @@ refine flow MySQL_Flow += { connection()->zeek_analyzer()->Conn(), zeek::make_intrusive(c_str(${msg.v9_response.username}))); } + + if ( mysql_auth_plugin ) + { + if ( ${msg.version} == 10 && ${msg.v10_response.plain.cap_flags} & CLIENT_PLUGIN_AUTH ) + { + zeek::BifEvent::enqueue_mysql_auth_plugin(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn(), + true /*is_orig*/, + zeek::make_intrusive(c_str(${msg.v10_response.plain.auth_plugin}))); + } + } + return true; %} @@ -112,6 +135,24 @@ refine flow MySQL_Flow += { return true; %} + function proc_auth_switch_request(msg: AuthSwitchRequest): bool + %{ + zeek::BifEvent::enqueue_mysql_auth_switch_request(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn(), + zeek::make_intrusive(c_str(${msg.name})), + to_stringval(${msg.data})); + return true; + %} + + function proc_auth_more_data(msg: AuthMoreData): bool + %{ + zeek::BifEvent::enqueue_mysql_auth_more_data(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn(), + ${is_orig}, + to_stringval(${msg.data})); + return true; + %} + }; refine typeattr Initial_Handshake_Packet += &let { @@ -141,3 +182,11 @@ refine typeattr EOF_Packet += &let { refine typeattr Resultset += &let { proc = $context.flow.proc_resultset(this); }; + +refine typeattr AuthSwitchRequest += &let { + proc = $context.flow.proc_auth_switch_request(this); +}; + +refine typeattr AuthMoreData += &let { + proc = $context.flow.proc_auth_more_data(this); +}; diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index 7d427460ef..d415735fec 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -142,9 +142,7 @@ enum state { enum ConnectionExpected { EXPECT_HANDSHAKE, - EXPECT_SHA2_AUTH, - EXPECT_PUB_KEY, - EXPECT_AUTH_SWITCH_RESP, + EXPECT_AUTH_DATA, }; enum Expected { @@ -157,7 +155,6 @@ enum Expected { EXPECT_RESULTSET, EXPECT_REST_OF_PACKET, EXPECT_AUTH_SWITCH, - EXPECT_SHA2_AUTH_RESP, }; enum EOFType { @@ -176,12 +173,6 @@ enum Client_Capabilities { CLIENT_QUERY_ATTRIBUTES = 0x08000000, }; -enum SHA2_Auth_State { - REQUEST_PUBLIC_KEY = 2, - FAST_AUTH_SUCCESS = 3, - PERFORM_FULL_AUTHENTICATION = 4, -}; - type NUL_String = RE/[^\0]*\0/; type EmptyOrNUL_String = RE/([^\0]*\0)?/; @@ -304,7 +295,10 @@ type Handshake_Plain_v10(cap_flags: uint32) = record { } &let { update_auth_plugin: bool = $context.connection.set_auth_plugin(auth_plugin) &if( cap_flags & CLIENT_PLUGIN_AUTH ); - update_expected: bool = $context.connection.update_expected_from_auth() + + # Switch client state into expecting more auth data. If the server responds + # with an OK_Packet before, will switch into COMMAND_PHASE. + update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_AUTH_DATA) &if( cap_flags & CLIENT_PLUGIN_AUTH ); }; @@ -334,46 +328,11 @@ type Handshake_Response_Packet_v9 = record { password : bytestring &restofdata; }; -# SHA2 Auth - -type SHA2_Auth_Packet = record { - state: bytestring &restofdata; -} &let { - update_state : bool = $context.connection.update_state(COMMAND_PHASE) - &if(state[0] == FAST_AUTH_SUCCESS); - update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_PUB_KEY) - &if(state[1] == REQUEST_PUBLIC_KEY); -}; - -type Public_Key_Packet = record { - pad : uint8; - pub_key: bytestring &restofdata; -} &let { - update_expectation: bool = $context.connection.set_next_expected(EXPECT_SHA2_AUTH_RESP); -}; - -type SHA2_Auth_Response_Packet = record { - data: bytestring &restofdata; -} &let { - update_state: bool = $context.connection.update_state(COMMAND_PHASE); -}; - -# Auth Switch - -type Auth_Switch_Response_Packet = record { - data : bytestring &restofdata; -} &let { - update_expected: bool = $context.connection.update_expected_from_auth(); -}; - # Connection Phase type Connection_Phase_Packets = case $context.connection.get_conn_expectation() of { - EXPECT_HANDSHAKE -> handshake_resp : Handshake_Response_Packet; - EXPECT_SHA2_AUTH -> sha2_auth : SHA2_Auth_Packet; - EXPECT_PUB_KEY -> pub_key : Public_Key_Packet; - EXPECT_AUTH_SWITCH_RESP -> atuh_switch_resp : Auth_Switch_Response_Packet; - default -> unknown : empty; + EXPECT_HANDSHAKE -> handshake_resp: Handshake_Response_Packet; + EXPECT_AUTH_DATA -> auth_data: AuthMoreData(true); }; # Command Request @@ -420,7 +379,6 @@ type Command_Response(pkt_len: uint32) = case $context.connection.get_expectatio EXPECT_REST_OF_PACKET -> rest : bytestring &restofdata; EXPECT_STATUS -> status : Command_Response_Status; EXPECT_AUTH_SWITCH -> auth_switch : AuthSwitchRequest; - EXPECT_SHA2_AUTH_RESP -> sha2_auth_resp: SHA2_Auth_Response_Packet; EXPECT_EOF_THEN_RESULTSET -> eof : EOFIfLegacyThenResultset(pkt_len); default -> unknown : empty; }; @@ -429,6 +387,10 @@ type Command_Response_Status = record { pkt_type: uint8; response: case pkt_type of { 0x00 -> data_ok: OK_Packet; + # When still in the CONNECTION_PHASE, the server can reply + # with AuthMoreData which is 0x01 stuffed opaque payload. + # https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_auth_more_data.html + 0x01 -> auth_more_data: AuthMoreData(false); 0xfe -> data_eof: EOF_Packet(EOF_END); 0xff -> data_err: ERR_Packet; default -> unknown: empty; @@ -523,13 +485,20 @@ type ColumnDefinition41(first_byte: uint8) = record { filler : padding[2]; }; +# Opaque auth data exchanged during the connection phase between client and server. +type AuthMoreData(is_orig: bool) = record { + data : bytestring &restofdata; +}; + type AuthSwitchRequest = record { status: uint8 &enforce(status==254); name : NUL_String; data : bytestring &restofdata; } &let { update_auth_plugin : bool = $context.connection.set_auth_plugin(name); - update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_AUTH_SWITCH_RESP); + update_conn_expectation: bool = $context.connection.set_next_conn_expected(EXPECT_AUTH_DATA); + # After an AuthSwitchRequest, server replies with OK_Packet, ERR_Packet or AuthMoreData. + update_expectation: bool = $context.connection.set_next_expected(EXPECT_STATUS); }; type ColumnDefinition320 = record { @@ -638,18 +607,6 @@ refine connection MySQL_Conn += { return true; %} - function update_expected_from_auth(): bool - %{ - if ( auth_plugin_ == "caching_sha2_password" ) - { - conn_expected_ = EXPECT_SHA2_AUTH; - if ( expected_ == EXPECT_AUTH_SWITCH ) - expected_ = EXPECT_STATUS; - } - - return true; - %} - function get_deprecate_eof(): bool %{ return deprecate_eof_; diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out index e8ead41d58..947e3add03 100644 --- a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out @@ -1,5 +1,10 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql auth plugin, F, caching_sha2_password, Vz\x08w+^\x04p\x02Tv\x01"~\x114\x14RP6\x00, 21 mysql handshake, root +mysql auth plugin, T, mysql_native_password, , 0 +mysql auth switch request, caching_sha2_password, Vz\x08w+^\x04p\x02Tv\x01"~\x114\x14RP6\x00, 21 +mysql auth more data, T, \xf7dS\x9eXe\xc4\xd6\xa9\xa7 \xfbC\xa6p\xaf\xdf\x9dB[B\x80\xa7\x80\xef\x0c\x95BC9#\x82, 32 +mysql auth more data, F, \x03, 1 mysql ok, 0 mysql request, 3, select @@version_comment limit 1 mysql result row, [MySQL Community Server - GPL] diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out index f8855b38fc..a9dd402e6c 100644 --- a/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out @@ -1,7 +1,16 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql auth plugin, F, caching_sha2_password, s.\x13\x01>\x05m\x04~Lq)%\x0fLL\x01\x08Xj\x00, 21 mysql handshake, root +mysql auth plugin, T, caching_sha2_password, \x98\xa0Ex\x8a\xeb`\xf3\xc7)\xa6\xaf\xf1\xa4]-\xa0\xdf\x959\xa1\xc5\xd6\xb8\xf3\xd6}\xb2\xa8\x033~, 32 +mysql auth more data, F, \x04, 1 mysql error, 1158, Got an error reading communication packets +mysql auth plugin, F, caching_sha2_password, 4x`?e\x04i'k&-P%LID\x17/\x0f{\x00, 21 mysql handshake, root +mysql auth plugin, T, caching_sha2_password, y.\x91:\x11\x87i\x17\xdfI_\xd2\xec\x9a"\xc2%sB\x10\x90\xbd\x15C\xf4w\xc0\x09p}\x8eE, 32 +mysql auth more data, F, \x04, 1 +mysql auth more data, T, \x02, 1 +mysql auth more data, F, -----BEGIN PUBLIC KEY-----\x0aMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0VACy/bY60MRuPW6aCxZ\x0abi+o0EgCgxzFObbyzDfnTnVJegOXbrdcbu1qIlEjPyn7UMBfjQr+VueiJvPjz2M8\x0ad/6GX1h4fYuwW4bEXBVo4HGxM8N0IyO1BYjafOaoUeL/NI+bLifH70KorIcSUR+h\x0a879DAQ0zlKz5vwpDYN2LVxidjFvy5baSPi/csDMqi2jitBAzbNW992O/v9CPnh5f\x0akdRMa2lMPKxRaPeqAw9U7CAmRqAaHZAfdI5kYnj3vsOFvKL2dkE+ckY8sh5H2uto\x0a37+mg6oll5PsydMbSuvFHLc0JZm++oem5z2WsZBdxmohqJ8Foc43W8IOtxs+YAOw\x0avwIDAQAB\x0a-----END PUBLIC KEY-----\x0a, 451 +mysql auth more data, T, \xca3\x89.M\x9d\xc0\xcb\xd6'2Zo*\xda8\xd2\xba\xb1\xabI\xcb\x1es%R\x1fo\xd0\xa6\xb8\x90\xf56\x0e\xd9\xd8p\x9eX\x84K\xb5\x1a\xe5\xfa\x18\xc1*\xfc\xa9W\xd6p\x1a\xcfv\xe8%\xe0\xb9\xfe\x98\x1b\xb3\x938\x85\xf4O\xf0c2b\xae\x81F\x1e\xb9\x1f\xbd\xdf\x16C\x91\xd5\x08\xa6\x82\xb6y\xf7\xa3u Date: Fri, 5 Jul 2024 12:17:48 +0200 Subject: [PATCH 08/15] mysql: Add data parameter to mysql_auth_plugin This may contain salt from the server or a hashed password from the client. --- src/analyzer/protocol/mysql/events.bif | 6 +++++- src/analyzer/protocol/mysql/mysql-analyzer.pac | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/analyzer/protocol/mysql/events.bif b/src/analyzer/protocol/mysql/events.bif index 68ff0e5720..8f5596e655 100644 --- a/src/analyzer/protocol/mysql/events.bif +++ b/src/analyzer/protocol/mysql/events.bif @@ -95,8 +95,12 @@ event mysql_handshake%(c: connection, username: string%); ## ## name: Name of the authentication plugin. ## +## data: The initial auth data. From the server, it is the concatenation of +## auth_plugin_data_part_1 and auth_plugin_data_part_2 in the handshake. +## For the client it is the auth_response in the handshake response. +## ## .. zeek:see:: mysql_handshake mysql_auth_switch_request mysql_auth_more_data -event mysql_auth_plugin%(c: connection, is_orig: bool, name: string%); +event mysql_auth_plugin%(c: connection, is_orig: bool, name: string, data: string%); ## Generated for a server packet with an auth switch request. ## diff --git a/src/analyzer/protocol/mysql/mysql-analyzer.pac b/src/analyzer/protocol/mysql/mysql-analyzer.pac index 738e063fbb..48cd0b6bd5 100644 --- a/src/analyzer/protocol/mysql/mysql-analyzer.pac +++ b/src/analyzer/protocol/mysql/mysql-analyzer.pac @@ -19,10 +19,20 @@ refine flow MySQL_Flow += { { if ( ${msg.version} == 10 && (${msg.handshake10.capability_flags_2} << 16) & CLIENT_PLUGIN_AUTH ) { + auto auth_plugin = zeek::make_intrusive(c_str(${msg.handshake10.auth_plugin})); + auto data_part_1 = ${msg.handshake10.auth_plugin_data_part_1}; + auto data_part_2 = ${msg.handshake10.auth_plugin_data_part_2}; + std::vector data_parts = { + zeek::data_chunk_t{data_part_1.length(), reinterpret_cast(data_part_1.begin())}, + zeek::data_chunk_t{data_part_2.length(), reinterpret_cast(data_part_2.begin())}, + }; + auto data = zeek::make_intrusive(zeek::concatenate(data_parts)); + zeek::BifEvent::enqueue_mysql_auth_plugin(connection()->zeek_analyzer(), connection()->zeek_analyzer()->Conn(), false /*is_orig*/, - zeek::make_intrusive(c_str(${msg.handshake10.auth_plugin}))); + std::move(auth_plugin), + std::move(data)); } } return true; @@ -56,10 +66,13 @@ refine flow MySQL_Flow += { { if ( ${msg.version} == 10 && ${msg.v10_response.plain.cap_flags} & CLIENT_PLUGIN_AUTH ) { + auto auth_plugin = zeek::make_intrusive(c_str(${msg.v10_response.plain.auth_plugin})); + auto data = to_stringval(${msg.v10_response.plain.credentials.password.val}); zeek::BifEvent::enqueue_mysql_auth_plugin(connection()->zeek_analyzer(), connection()->zeek_analyzer()->Conn(), true /*is_orig*/, - zeek::make_intrusive(c_str(${msg.v10_response.plain.auth_plugin}))); + std::move(auth_plugin), + std::move(data)); } } From 6ea1045245f2f07ac1c6262c00f75c20a52c6395 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Fri, 5 Jul 2024 13:56:44 +0200 Subject: [PATCH 09/15] mysql: Fix EOFIfLegacyThenResultSet Only expect a result next if an EOF was consumed. --- src/analyzer/protocol/mysql/mysql-protocol.pac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index d415735fec..c992927b37 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -449,7 +449,7 @@ type EOFIfLegacyThenResultset(pkt_len: uint32) = case $context.connection.get_de true -> resultset: Resultset(pkt_len); } &let { update_result_seen: bool = $context.connection.set_results_seen(0); - update_expectation: bool = $context.connection.set_next_expected(EXPECT_RESULTSET); + update_expectation: bool = $context.connection.set_next_expected(EXPECT_RESULTSET) &if( ! $context.connection.get_deprecate_eof() ); }; type Resultset(pkt_len: uint32) = record { From 0a1568f1a10bb5d995dc573bad46987b91ec122b Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Tue, 9 Jul 2024 11:43:18 +0200 Subject: [PATCH 10/15] mysql: Introduce mysql_ssl_request event This should've been added with fa48c885 for completion. Do it now. The MySQL spec calls it SSLRequest packet, so keep SSL in the name for consistency. --- src/analyzer/protocol/mysql/events.bif | 13 ++++++++++++- src/analyzer/protocol/mysql/mysql-analyzer.pac | 4 ++++ .../tls-12.out | 2 ++ .../tls-13.out | 2 ++ .../scripts.base.protocols.mysql.encrypted/out | 2 ++ .../base/protocols/mysql/encrypted-aws-rds.test | 15 +++++++++++---- .../scripts/base/protocols/mysql/encrypted.test | 8 +++++++- 7 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-12.out create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-13.out create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.encrypted/out diff --git a/src/analyzer/protocol/mysql/events.bif b/src/analyzer/protocol/mysql/events.bif index 8f5596e655..a102842e05 100644 --- a/src/analyzer/protocol/mysql/events.bif +++ b/src/analyzer/protocol/mysql/events.bif @@ -84,9 +84,20 @@ event mysql_server_version%(c: connection, ver: string%); ## ## username: The username supplied by the client ## -## .. zeek:see:: mysql_command_request mysql_error mysql_ok mysql_server_version +## .. zeek:see:: mysql_command_request mysql_error mysql_ok mysql_server_version mysql_ssl_request event mysql_handshake%(c: connection, username: string%); +## Generated for a short client handshake response packet with the CLIENT_SSL +## flag set. Usually the client will initiate a TLS handshake afterwards. +# +## See the MySQL `documentation `__ +## for more information about the MySQL protocol. +## +## c: The connection. +## +## .. zeek:see:: mysql_handshake +event mysql_ssl_request%(c: connection%); + ## Generated for information about plugin authentication within handshake packets. ## ## c: The connection. diff --git a/src/analyzer/protocol/mysql/mysql-analyzer.pac b/src/analyzer/protocol/mysql/mysql-analyzer.pac index 48cd0b6bd5..28ce1e7d9e 100644 --- a/src/analyzer/protocol/mysql/mysql-analyzer.pac +++ b/src/analyzer/protocol/mysql/mysql-analyzer.pac @@ -47,6 +47,10 @@ refine flow MySQL_Flow += { if ( ${msg.version} == 10 && ( ${msg.v10_response.cap_flags} & CLIENT_SSL )) { connection()->zeek_analyzer()->StartTLS(); + + if ( mysql_ssl_request ) + zeek::BifEvent::enqueue_mysql_ssl_request(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn()); return true; } diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-12.out b/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-12.out new file mode 100644 index 0000000000..dd294ea217 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-12.out @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql ssl request, CHhAvVGS1DHFjwGM9 diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-13.out b/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-13.out new file mode 100644 index 0000000000..dd294ea217 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted-aws-rds/tls-13.out @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql ssl request, CHhAvVGS1DHFjwGM9 diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted/out b/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted/out new file mode 100644 index 0000000000..dd294ea217 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.encrypted/out @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql ssl request, CHhAvVGS1DHFjwGM9 diff --git a/testing/btest/scripts/base/protocols/mysql/encrypted-aws-rds.test b/testing/btest/scripts/base/protocols/mysql/encrypted-aws-rds.test index d653608aa4..7f336edf80 100644 --- a/testing/btest/scripts/base/protocols/mysql/encrypted-aws-rds.test +++ b/testing/btest/scripts/base/protocols/mysql/encrypted-aws-rds.test @@ -1,15 +1,17 @@ # Just two traces with MySQL running in Amazon RDS tls1.3 and tls1.2 -# @TEST-EXEC: zeek -b -r $TRACES/mysql/tls-12-amazon-rds.trace %INPUT -# @TEST-EXEC: mkdir tls-12 && mv *log tls-12 +# @TEST-EXEC: zeek -b -r $TRACES/mysql/tls-12-amazon-rds.trace %INPUT >out +# @TEST-EXEC: mkdir tls-12 && mv *log out tls-12 # -# @TEST-EXEC: zeek -b -r $TRACES/mysql/tls-13-amazon-rds.trace %INPUT -# @TEST-EXEC: mkdir tls-13 && mv *log tls-13 +# @TEST-EXEC: zeek -b -r $TRACES/mysql/tls-13-amazon-rds.trace %INPUT >out +# @TEST-EXEC: mkdir tls-13 && mv *log out tls-13 # +# @TEST-EXEC: btest-diff tls-12/out # @TEST-EXEC: btest-diff tls-12/conn.log # @TEST-EXEC: btest-diff tls-12/ssl.log # @TEST-EXEC: btest-diff tls-12/x509.log # +# @TEST-EXEC: btest-diff tls-13/out # @TEST-EXEC: btest-diff tls-13/conn.log # @TEST-EXEC: btest-diff tls-13/ssl.log # @TEST-EXEC: ! test -f tls-13/x509.log @@ -17,3 +19,8 @@ @load base/protocols/conn @load base/protocols/mysql @load base/protocols/ssl + +event mysql_ssl_request(c: connection) + { + print "mysql ssl request", c$uid; + } diff --git a/testing/btest/scripts/base/protocols/mysql/encrypted.test b/testing/btest/scripts/base/protocols/mysql/encrypted.test index 1f43ec7da6..808bed3cfb 100644 --- a/testing/btest/scripts/base/protocols/mysql/encrypted.test +++ b/testing/btest/scripts/base/protocols/mysql/encrypted.test @@ -3,8 +3,9 @@ # empty mysql.log file. # @TEST-EXEC: touch mysql.log -# @TEST-EXEC: zeek -b -r $TRACES/mysql/encrypted.trace %INPUT +# @TEST-EXEC: zeek -b -r $TRACES/mysql/encrypted.trace %INPUT >out # @TEST-EXEC: btest-diff mysql.log +# @TEST-EXEC: btest-diff out # # Ensure the connection was handed off by peaking into some other logs. # @TEST-EXEC: btest-diff conn.log @@ -14,3 +15,8 @@ @load base/protocols/conn @load base/protocols/mysql @load base/protocols/ssl + +event mysql_ssl_request(c: connection) + { + print "mysql ssl request", c$uid; + } From 93f5813be365bd6de27d741f8aeb757de7a9dafe Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Tue, 9 Jul 2024 13:28:43 +0200 Subject: [PATCH 11/15] btest/mysql: Add pcap with non-string query attributes Pcap was generated as follows. Doesn't seem wireshark even parses this properly right now. with common.get_connection() as c: with c.cursor() as cur: date1 = datetime.date(1987, 10, 18) datetime1 = datetime.datetime(1990, 9, 26, 12, 13, 14) cur.add_attribute("number1", 42) cur.add_attribute("string1", "a string") cur.add_attribute("date1", date1) cur.add_attribute("datetime1", datetime1) cur.execute("SELECT version()") result = cur.fetchall() print("result", result) --- .../mysql/mysql-9.0.0-query-attributes.pcap | Bin 0 -> 2034 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testing/btest/Traces/mysql/mysql-9.0.0-query-attributes.pcap diff --git a/testing/btest/Traces/mysql/mysql-9.0.0-query-attributes.pcap b/testing/btest/Traces/mysql/mysql-9.0.0-query-attributes.pcap new file mode 100644 index 0000000000000000000000000000000000000000..684dbcad3811dba656df87c10d9ee67e5a5d2168 GIT binary patch literal 2034 zcmaKtZ)g*D7{{MqE|+U-;?(`IbD$YAvASeQ*40L|7LU?ST6NCS?$wcAuO>6OOYbh) z!q_5$R1nG(^P+p>Ht@BM3F4b|W(;8t#u$tho!jV(;TR0Yirw(C=lLbqUP7A(9`5dU z$LGG!_j!JIKa@ZF*av=C4L>lvSl&7_(eo37BX~~fjTYzwI3TBcp*OJn)&~HL^87;Z zacJ(#Z|KQuzJLF6VgNqB__^QL(sn!X6@LX21c}H)P00X6K5)k+2Opnb7+XB}1=8F4Rf3~=}(l`q~LdX!6UA`)q;CM>lJ+C0fQvY z0HrsmV76C2O~fb4n#6XtRM}Bq@ZWeD`sAf{B=uIjIM2Ch7&e4o-Z?A$_Ivi$xAuIu z{@t+`FMYacbn(V}H~&$G4?Xz)$B))A;i_NBXT}5ForyQP_O&YcY|5T6%v@lqyG!Zn z3@VmUFjY-S8sgp-b_I;tl;oiLAMK$1U}VClXC}+T)#D?y?k;?u94Nr z=?R7A?v}ANPuH5o%MTe0pmCJm;Ip{D^eQLLu1Rc1Vsxc7B2BjsHz*TGY46Vrh7d^U z4bDAOig5152JW+5DREz~=SlzqQV`E4^a{e)w?WPle0kV~S10std2F#~d&7j1Xp!Uw z9x>=gI;A(b+@zf2#A9m`aYCI)om`Q|d?R5;5!=LtD0gAT#n$?@9bLGGJH62b5nUj% z>%t!hpTlcPZ*a|*WS!to{&V3q7w(}89SC1?iuIaLB0}&p{8$wwy^tHz%(w%RWt;es zh_`__p0c%g5L)SIXL4G6PfIFXz3necZCj+TrPgi1(8jm>2K(fb;o2&m+zSb^toz2NEXBBeAG$(o_T|vNUB%6HyPk*vB91gA%9C8 Date: Tue, 9 Jul 2024 13:23:44 +0200 Subject: [PATCH 12/15] mysql: Support non-string query attributes The query attributes aren't exposed to script layer right now, but this should at least parse over them once encountered and some fixups. --- .../protocol/mysql/mysql-protocol.pac | 144 ++++++++++++++++-- .../mysql.log | 54 +++++++ .../out | 132 ++++++++++++++++ .../mysql.log | 14 ++ .../out | 10 ++ .../btest/Traces/mysql/many-query-attrs.pcap | Bin 0 -> 37064 bytes .../base/protocols/mysql/many-query-attr.test | 35 +++++ .../mysql/query-attr-non-string.test | 35 +++++ 8 files changed, 412 insertions(+), 12 deletions(-) create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/mysql.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/out create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/mysql.log create mode 100644 testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/out create mode 100644 testing/btest/Traces/mysql/many-query-attrs.pcap create mode 100644 testing/btest/scripts/base/protocols/mysql/many-query-attr.test create mode 100644 testing/btest/scripts/base/protocols/mysql/query-attr-non-string.test diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index c992927b37..bc350c747c 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -173,6 +173,85 @@ enum Client_Capabilities { CLIENT_QUERY_ATTRIBUTES = 0x08000000, }; +# Binary Protocol Resultset encoding. +# +# https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_binary_resultset.html +# +# Values taken from here: https://dev.mysql.com/doc/dev/mysql-server/latest/namespaceclassic__protocol_1_1field__type.html +enum field_types { + TYPE_DECIMAL = 0x00, + TYPE_TINY = 0x01, + TYPE_SHORT = 0x02, + TYPE_LONG = 0x03, + TYPE_FLOAT = 0x04, + TYPE_DOUBLE = 0x05, + TYPE_NULL = 0x06, + TYPE_TIMESTAMP = 0x07, + TYPE_LONGLONG = 0x08, + TYPE_INT24 = 0x09, + TYPE_DATE = 0x0a, + TYPE_TIME = 0x0b, + TYPE_DATETIME = 0x0c, + TYPE_YEAR = 0x0d, + TYPE_VARCHAR = 0x0f, + TYPE_BIT = 0x10, + TYPE_TIMESTAMP2 = 0x11, + TYPE_JSON = 0xf5, + TYPE_NEWDECIMAL = 0xf6, + TYPE_ENUM = 0xf7, + TYPE_SET = 0xf8, + TYPE_TINYBLOB = 0xf9, + TYPE_MEDIUMBLOB = 0xfa, + TYPE_LONGBLOB = 0xfb, + TYPE_BLOB = 0xfc, + TYPE_VARSTRING = 0xfd, + TYPE_STRING = 0xfe, + TYPE_GEOMETRY = 0xff, +}; + +type BinaryDate = record { + len: uint8 &enforce(len == 0 || len == 4 || len == 7 || len == 11); + not_implemented: bytestring &length=len; +}; + +type BinaryTime = record { + len: uint8 &enforce(len == 0 || len == 8 || len == 12); + not_implemented: bytestring &length=len; +}; + +type BinaryValue(type: uint16) = record { + value: case ( type ) of { + TYPE_DECIMAL -> decimal_val: LengthEncodedInteger; + TYPE_TINY -> tiny_val: int8; + TYPE_SHORT -> short_val: int16; + TYPE_LONG -> long_val: int32; + TYPE_FLOAT -> float_val: bytestring &length=4; + TYPE_DOUBLE -> double_val: bytestring &length=8; + TYPE_NULL -> null_val: empty; # in null_bitmap + TYPE_TIMESTAMP -> timestamp_val: BinaryDate; + TYPE_LONGLONG -> longlong_val: int64; + TYPE_INT24 -> int24_val: int32; + TYPE_DATE -> date_val: BinaryDate; + TYPE_TIME -> time_val: BinaryTime; + TYPE_DATETIME -> datetime_val: BinaryDate; + TYPE_YEAR -> year_val: int16; + TYPE_VARCHAR -> varchar_val: LengthEncodedString; + TYPE_BIT -> bit_val: LengthEncodedString; + TYPE_TIMESTAMP2 -> timestamp2_val: BinaryDate; + TYPE_JSON -> json_val: LengthEncodedString; + TYPE_NEWDECIMAL -> newdecimal_val: LengthEncodedString; + TYPE_ENUM -> enum_val: LengthEncodedString; + TYPE_SET -> set_val: LengthEncodedString; + TYPE_TINYBLOB -> tinyblob_val: LengthEncodedString; + TYPE_MEDIUMBLOB -> mediumblob_val: LengthEncodedString; + TYPE_LONGBLOB -> longblob_val: LengthEncodedString; + TYPE_BLOB -> blob_val: LengthEncodedString; + TYPE_VARSTRING -> varstring_val: LengthEncodedString; + TYPE_STRING -> string_val: LengthEncodedString; + TYPE_GEOMETRY -> geometry_val: LengthEncodedString; + }; +}; + type NUL_String = RE/[^\0]*\0/; type EmptyOrNUL_String = RE/([^\0]*\0)?/; @@ -335,29 +414,51 @@ type Connection_Phase_Packets = case $context.connection.get_conn_expectation() EXPECT_AUTH_DATA -> auth_data: AuthMoreData(true); }; -# Command Request - +# Query attribute handling for COM_QUERY +# type AttributeTypeAndName = record { - type: uint16; + type: uint8; + unsigned_flag: uint8; name: LengthEncodedString; }; -type Attributes(count: uint8) = record { - unused : uint8; - send_types_to_server: uint8; # Always 1. +type AttributeValue(is_null: bool, type: uint8) = record { + null: case is_null of { + false -> val: BinaryValue(type); + true -> null_val: empty; + }; +} &let { + # Move parsing the next query attribute. + done = $context.connection.next_query_attr(); +}; + +type Attributes(count: int) = record { + null_bitmap : bytestring &length=(count + 7) / 8; + send_types_to_server: uint8 &enforce(send_types_to_server == 1); names : AttributeTypeAndName[count]; - values : LengthEncodedString[count]; + values : AttributeValue( + # Check if null_bitmap contains this attribute index. This + # will pass true if the attribute value is NULL and parsing + # skipped in AttributeValue above. + (null_bitmap[$context.connection.query_attr_idx() / 8] >> ($context.connection.query_attr_idx() % 8)) & 0x01, + names[$context.connection.query_attr_idx()].type + )[] &until($context.connection.query_attr_idx() >= count); }; type Query_Attributes = record { - count : uint8; - set_coun : uint8; - have_attr : case ( count > 0 ) of { - true -> attrs: Attributes(count); + count : LengthEncodedInteger; + set_count: LengthEncodedInteger; + have_attr: case ( attr_count > 0 ) of { + true -> attrs: Attributes(attr_count); false -> none: empty; - }; + } &requires(new_query_attrs); +} &let { + attr_count: int = to_int()(count); + new_query_attrs = $context.connection.new_query_attrs(); }; +# Command Request + type Command_Request_Packet = record { command: uint8; attrs : case ( command == COM_QUERY && $context.connection.get_client_query_attrs() && $context.connection.get_server_query_attrs() ) of { @@ -554,6 +655,7 @@ refine connection MySQL_Conn += { bool server_query_attrs_; bool client_query_attrs_; std::string auth_plugin_; + int query_attr_idx_; %} %init{ @@ -568,6 +670,7 @@ refine connection MySQL_Conn += { deprecate_eof_ = false; server_query_attrs_ = false; client_query_attrs_ = false; + query_attr_idx_ = 0; %} function get_version(): uint8 @@ -825,4 +928,21 @@ refine connection MySQL_Conn += { ++results_seen_; return true; %} + + function query_attr_idx(): int + %{ + return query_attr_idx_; + %} + + function new_query_attrs(): bool + %{ + query_attr_idx_ = 0; + return true; + %} + + function next_query_attr(): bool + %{ + query_attr_idx_++; + return true; + %} }; diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/mysql.log new file mode 100644 index 0000000000..1f6d2899e8 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/mysql.log @@ -0,0 +1,54 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path mysql +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response +#types time string addr port addr port string string bool count string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 login root T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 query show databases T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 query show tables T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list columns_priv T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list component T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list db T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list default_roles T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list engine_cost T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list func T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list general_log T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list global_grants T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list gtid_executed T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list help_category T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list help_keyword T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list help_relation T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list help_topic T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list innodb_index_stats T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list innodb_table_stats T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list ndb_binlog_index T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list password_history T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list plugin T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list procs_priv T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list proxies_priv T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list replication_asynchronous_connection_failover T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list replication_asynchronous_connection_failover_managed T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list replication_group_configuration_version T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list replication_group_member_actions T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list role_edges T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list server_cost T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list servers T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list slave_master_info T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list slave_relay_log_info T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list slave_worker_info T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list slow_log T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list tables_priv T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list time_zone T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list time_zone_leap_second T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list time_zone_name T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list time_zone_transition T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list time_zone_transition_type T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 field_list user T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 query select @@version_comment limit 1 T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 query SELECT mysql_query_attribute_string('n1'), mysql_query_attribute_string('n2') T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33592 127.0.0.1 3306 quit (empty) - - - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/out b/testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/out new file mode 100644 index 0000000000..a064e5f357 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.many-query-attr/out @@ -0,0 +1,132 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql handshake, root +mysql ok, 0 +mysql request, 3, show databases +mysql result row, [information_schema] +mysql result row, [mysql] +mysql result row, [performance_schema] +mysql result row, [sys] +mysql ok, 0 +mysql request, 3, show tables +mysql result row, [columns_priv] +mysql result row, [component] +mysql result row, [db] +mysql result row, [default_roles] +mysql result row, [engine_cost] +mysql result row, [func] +mysql result row, [general_log] +mysql result row, [global_grants] +mysql result row, [gtid_executed] +mysql result row, [help_category] +mysql result row, [help_keyword] +mysql result row, [help_relation] +mysql result row, [help_topic] +mysql result row, [innodb_index_stats] +mysql result row, [innodb_table_stats] +mysql result row, [ndb_binlog_index] +mysql result row, [password_history] +mysql result row, [plugin] +mysql result row, [procs_priv] +mysql result row, [proxies_priv] +mysql result row, [replication_asynchronous_connection_failover] +mysql result row, [replication_asynchronous_connection_failover_managed] +mysql result row, [replication_group_configuration_version] +mysql result row, [replication_group_member_actions] +mysql result row, [role_edges] +mysql result row, [server_cost] +mysql result row, [servers] +mysql result row, [slave_master_info] +mysql result row, [slave_relay_log_info] +mysql result row, [slave_worker_info] +mysql result row, [slow_log] +mysql result row, [tables_priv] +mysql result row, [time_zone] +mysql result row, [time_zone_leap_second] +mysql result row, [time_zone_name] +mysql result row, [time_zone_transition] +mysql result row, [time_zone_transition_type] +mysql result row, [user] +mysql ok, 0 +mysql request, 4, columns_priv\x00 +mysql ok, 0 +mysql request, 4, component\x00 +mysql ok, 0 +mysql request, 4, db\x00 +mysql ok, 0 +mysql request, 4, default_roles\x00 +mysql ok, 0 +mysql request, 4, engine_cost\x00 +mysql ok, 0 +mysql request, 4, func\x00 +mysql ok, 0 +mysql request, 4, general_log\x00 +mysql ok, 0 +mysql request, 4, global_grants\x00 +mysql ok, 0 +mysql request, 4, gtid_executed\x00 +mysql ok, 0 +mysql request, 4, help_category\x00 +mysql ok, 0 +mysql request, 4, help_keyword\x00 +mysql ok, 0 +mysql request, 4, help_relation\x00 +mysql ok, 0 +mysql request, 4, help_topic\x00 +mysql ok, 0 +mysql request, 4, innodb_index_stats\x00 +mysql ok, 0 +mysql request, 4, innodb_table_stats\x00 +mysql ok, 0 +mysql request, 4, ndb_binlog_index\x00 +mysql ok, 0 +mysql request, 4, password_history\x00 +mysql ok, 0 +mysql request, 4, plugin\x00 +mysql ok, 0 +mysql request, 4, procs_priv\x00 +mysql ok, 0 +mysql request, 4, proxies_priv\x00 +mysql ok, 0 +mysql request, 4, replication_asynchronous_connection_failover\x00 +mysql ok, 0 +mysql request, 4, replication_asynchronous_connection_failover_managed\x00 +mysql ok, 0 +mysql request, 4, replication_group_configuration_version\x00 +mysql ok, 0 +mysql request, 4, replication_group_member_actions\x00 +mysql ok, 0 +mysql request, 4, role_edges\x00 +mysql ok, 0 +mysql request, 4, server_cost\x00 +mysql ok, 0 +mysql request, 4, servers\x00 +mysql ok, 0 +mysql request, 4, slave_master_info\x00 +mysql ok, 0 +mysql request, 4, slave_relay_log_info\x00 +mysql ok, 0 +mysql request, 4, slave_worker_info\x00 +mysql ok, 0 +mysql request, 4, slow_log\x00 +mysql ok, 0 +mysql request, 4, tables_priv\x00 +mysql ok, 0 +mysql request, 4, time_zone\x00 +mysql ok, 0 +mysql request, 4, time_zone_leap_second\x00 +mysql ok, 0 +mysql request, 4, time_zone_name\x00 +mysql ok, 0 +mysql request, 4, time_zone_transition\x00 +mysql ok, 0 +mysql request, 4, time_zone_transition_type\x00 +mysql ok, 0 +mysql request, 4, user\x00 +mysql ok, 0 +mysql request, 3, select @@version_comment limit 1 +mysql result row, [MySQL Community Server - GPL] +mysql ok, 0 +mysql request, 3, SELECT mysql_query_attribute_string('n1'), mysql_query_attribute_string('n2') +mysql result row, [42, v2] +mysql ok, 0 +mysql request, 1, diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/mysql.log b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/mysql.log new file mode 100644 index 0000000000..8ae14a6dc2 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/mysql.log @@ -0,0 +1,14 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path mysql +#open XXXX-XX-XX-XX-XX-XX +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response +#types time string addr port addr port string string bool count string +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33754 127.0.0.1 3306 login root T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33754 127.0.0.1 3306 ping (empty) T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33754 127.0.0.1 3306 query SELECT version() T 0 - +XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 127.0.0.1 33754 127.0.0.1 3306 quit (empty) - - - +#close XXXX-XX-XX-XX-XX-XX diff --git a/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/out b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/out new file mode 100644 index 0000000000..0924f49140 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.query-attr-non-string/out @@ -0,0 +1,10 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +mysql handshake, root +mysql ok, 0 +mysql request, 14, +mysql ok, 0 +mysql request, 3, SELECT version() +mysql eof, T +mysql result row, [9.0.0] +mysql eof, F +mysql request, 1, diff --git a/testing/btest/Traces/mysql/many-query-attrs.pcap b/testing/btest/Traces/mysql/many-query-attrs.pcap new file mode 100644 index 0000000000000000000000000000000000000000..175e5a9644a21ad189cec7b03f14385249267f02 GIT binary patch literal 37064 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≈0df` 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 H`0D=vR>j^i literal 0 HcmV?d00001 diff --git a/testing/btest/scripts/base/protocols/mysql/many-query-attr.test b/testing/btest/scripts/base/protocols/mysql/many-query-attr.test new file mode 100644 index 0000000000..5ff2e4cb92 --- /dev/null +++ b/testing/btest/scripts/base/protocols/mysql/many-query-attr.test @@ -0,0 +1,35 @@ +# @TEST-EXEC: zeek -b -C -r $TRACES/mysql/many-query-attrs.pcap %INPUT >out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff mysql.log + +@load base/protocols/mysql + +event mysql_ok(c: connection, affected_rows: count) + { + print "mysql ok", affected_rows; + } + +event mysql_eof(c: connection, is_intermediate: bool) + { + print "mysql eof", is_intermediate; + } + +event mysql_result_row(c: connection, row: string_vec) + { + print "mysql result row", row; + } + +event mysql_error(c: connection, code: count, msg: string) + { + print "mysql error", code, msg; + } + +event mysql_command_request(c: connection, command: count, arg: string) + { + print "mysql request", command, arg; + } + +event mysql_handshake(c: connection, username: string) + { + print "mysql handshake", username; + } diff --git a/testing/btest/scripts/base/protocols/mysql/query-attr-non-string.test b/testing/btest/scripts/base/protocols/mysql/query-attr-non-string.test new file mode 100644 index 0000000000..ef6ef4d8b7 --- /dev/null +++ b/testing/btest/scripts/base/protocols/mysql/query-attr-non-string.test @@ -0,0 +1,35 @@ +# @TEST-EXEC: zeek -b -C -r $TRACES/mysql/mysql-9.0.0-query-attributes.pcap %INPUT >out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff mysql.log + +@load base/protocols/mysql + +event mysql_ok(c: connection, affected_rows: count) + { + print "mysql ok", affected_rows; + } + +event mysql_eof(c: connection, is_intermediate: bool) + { + print "mysql eof", is_intermediate; + } + +event mysql_result_row(c: connection, row: string_vec) + { + print "mysql result row", row; + } + +event mysql_error(c: connection, code: count, msg: string) + { + print "mysql error", code, msg; + } + +event mysql_command_request(c: connection, command: count, arg: string) + { + print "mysql request", command, arg; + } + +event mysql_handshake(c: connection, username: string) + { + print "mysql handshake", username; + } From 7b8bbc6d001ae6e478257406ef001d41cb75237a Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Tue, 9 Jul 2024 16:32:21 +0200 Subject: [PATCH 13/15] btest/mysql: Clean query-attr.pcapng It contained some unrelated IntelliJ traffic. --- testing/btest/Traces/mysql/query-attr.pcap | Bin 0 -> 2557 bytes testing/btest/Traces/mysql/query-attr.pcapng | Bin 7696 -> 0 bytes .../scripts/base/protocols/mysql/query-attr.test | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 testing/btest/Traces/mysql/query-attr.pcap delete mode 100644 testing/btest/Traces/mysql/query-attr.pcapng diff --git a/testing/btest/Traces/mysql/query-attr.pcap b/testing/btest/Traces/mysql/query-attr.pcap new file mode 100644 index 0000000000000000000000000000000000000000..144134b89c42149c9eb828ead53b9b093ea19f8f GIT binary patch literal 2557 zcmbuBOKcle6o$_{>^Nbf2EwZ}5=JbV2ol*&;-pEXScs}Xf*1i=G%PGXCeEm1k2_Q-{nPeu|-f*N7 z*;f4B|3Ck^ll<`Y#rJp+VW&jk@XfX8a{Agm5*%hd9Z)F zYv-Uy_jv2EK)EOJ(4o_iNo8i#LM~~}q+-cp$~2GbMwVsu4ytV8TV%dJcU5|PR~z~6 zyHA2UaTTb&MZUS993bLv>vf6bd(-cG8Q;FRXzxW5XM1s;*C&tWAUvA>^RV#RcjC`? zzxt~_xzO{$D_yU5J^j~5Z9iTbo40!Zs6PTjOcvE_;J|P!HrSdhYN^t+Zsda}hU3Zj zfRNP9z=T>TpOBInO;rk|WFeJTLYkV+pEQqYExMU3n~EWzfIaSS%qoVd>V;q}E2(Jc zIU|*4UktEs(QXXlj2ch9!n57@(w&jB8`bdn___HG&aSs@my!4w&V<@qY}-aEY=5n# zxv;YW5LBVXDI}qM@uyPBNyZwOh$i2`=?lclrq*Hr~$$d@^ zG2&TQu8&A^pYqFn5D@}!rlKjCQaF)twk4U-^LgB_u%_nKQaDO;(wbVSuzO^E<{s?K z=>pDarID3)yCirM1yFm7S$V0#vb1LE63NObzZJaR9Je1wFJzTz#NGZMHz3*Y>?8;O z0Njty19aZ`j9xY}N;s>UB|}Y@OL$W-L2+v*!77Sul3**F_x2(?^Io~Jq>1%xuizlx z_&5ML6iDqYX8VZTRyV;Kk!<6h)*kIsbF{(_AVT0d;I@D-MCnJ2pN;afF(=c4e!RPf zj{OjiZEU2%R?*sC#ae!vi1}^$jnJ`Eb3`;Q2jFEqp4wZ?$f*ijMa%H#=Mc$=?lm$s z$MV~^lV5--2WbpC5iY$zC$P+7kyxxZGSnOG4@V;-1A`+2QBP(hYypKewkqGoCVa&e z=47^snT^-8x{AcnZv>b`0o2}NRu9V(BfejkNLKN0V=c4PG7WDJB6e;a^GSys!9yC$ zuCP_x{mj(xzlouCzic`rwM@i?Z-r(Fu~oB*u52gs8qt?f>N*ksM?(Fcgh<%iD6Fwn Xxt-1Pk}Is1&`>)jbat0j5_bJxw0wPw literal 0 HcmV?d00001 diff --git a/testing/btest/Traces/mysql/query-attr.pcapng b/testing/btest/Traces/mysql/query-attr.pcapng deleted file mode 100644 index 15b6d479c14aae8bd679678e1ea0bdb06cabdadb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7696 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 +# @TEST-EXEC: zeek -b -C -r $TRACES/mysql/query-attr.pcap %INPUT >out # @TEST-EXEC: btest-diff out # @TEST-EXEC: btest-diff mysql.log From 4adea7978c14204bbe9c5edd7374cd2ee20e70e8 Mon Sep 17 00:00:00 2001 From: Fupeng Zhao Date: Fri, 12 Jul 2024 11:21:44 +0200 Subject: [PATCH 14/15] mysql: Improve date and time parsing --- .../protocol/mysql/mysql-protocol.pac | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index bc350c747c..3ab7830d8f 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -209,14 +209,50 @@ enum field_types { TYPE_GEOMETRY = 0xff, }; +type Date = record { + year : int16; + month: int8; + day : int8; +}; + +type Time = record { + hour : int8; + minute: int8; + second: int8; +}; + type BinaryDate = record { len: uint8 &enforce(len == 0 || len == 4 || len == 7 || len == 11); - not_implemented: bytestring &length=len; + have_date: case ( len > 0 ) of { + true -> date : Date; + false -> none_1: empty; + }; + have_time: case ( len > 4 ) of { + true -> time : Time; + false -> none_2: empty; + }; + have_micros: case ( len > 7 ) of { + true -> micros: int32; + false -> none_3: empty; + }; +}; + +type DurationTime = record { + is_negative: int8 &enforce(is_negative == 0 || is_negative == 1); + days : int32; + time : Time; }; type BinaryTime = record { len: uint8 &enforce(len == 0 || len == 8 || len == 12); - not_implemented: bytestring &length=len; + have_time: case ( len > 0 ) of { + true -> time : DurationTime; + false -> none_1: empty; + }; + have_micros: case ( len > 8 ) of { + true -> micros: int32; + false -> none_2: empty; + }; }; type BinaryValue(type: uint16) = record { From 2e1e57033d6ee42275ec1cf3cb71857a25d32776 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Thu, 25 Jul 2024 11:45:13 +0200 Subject: [PATCH 15/15] mysql: Simplify length computation Thanks Tim! --- src/analyzer/protocol/mysql/mysql-protocol.pac | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index 3ab7830d8f..aeee595887 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -345,7 +345,7 @@ type Handshake_v10 = record { character_set : uint8; status_flags : uint16; capability_flags_2 : uint16; - auth_plugin_data_len : uint8; + auth_plugin_data_len : uint8 &enforce( auth_plugin_data_len==0 || auth_plugin_data_len >= 21); reserved : padding[10]; auth_plugin_data_part_2: bytestring &length=auth_plugin_data_part_2_len; have_plugin : case ( ( capability_flags_2 << 16 ) & CLIENT_PLUGIN_AUTH ) of { @@ -353,7 +353,13 @@ type Handshake_v10 = record { 0x0 -> none : empty; }; } &let { - auth_plugin_data_part_2_len = (auth_plugin_data_len > 8 && (auth_plugin_data_len - 8) > 13) ? auth_plugin_data_len - 8 : 13; + # The length of auth_plugin_data_part_2 is at least 13 bytes, + # or auth_plugin_data_len - 8 if that is larger, check for + # auth_plugin_data_len > 21 (8 + 13) to prevent underflow for + # when subtracting 8. + # + # https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_v10.html + auth_plugin_data_part_2_len = auth_plugin_data_len > 21 ? auth_plugin_data_len - 8 : 13; update_auth_plugin: bool = $context.connection.set_auth_plugin(auth_plugin) &if( ( capability_flags_2 << 16 ) & CLIENT_PLUGIN_AUTH ); server_query_attrs: bool = $context.connection.set_server_query_attrs(( capability_flags_2 << 16 ) & CLIENT_QUERY_ATTRIBUTES);