diff --git a/CHANGES b/CHANGES index d23022d832..7a5c649435 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,77 @@ +7.1.0-dev.61 | 2024-07-25 12:02:51 +0200 + + * mysql: Simplify length computation (Arne Welzel, Corelight) + + Thanks Tim! + + * mysql: Improve date and time parsing (Fupeng Zhao) + + * btest/mysql: Clean query-attr.pcapng (Arne Welzel, Corelight) + + It contained some unrelated IntelliJ traffic. + + * mysql: Support non-string query attributes (Arne Welzel, Corelight) + + The query attributes aren't exposed to script layer right now, but this + should at least parse over them once encountered and some fixups. + + * btest/mysql: Add pcap with non-string query attributes (Arne Welzel, Corelight) + + 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: Introduce mysql_ssl_request event (Arne Welzel, Corelight) + + 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. + + * mysql: Fix EOFIfLegacyThenResultSet (Arne Welzel, Corelight) + + Only expect a result next if an EOF was consumed. + + * mysql: Add data parameter to mysql_auth_plugin (Arne Welzel, Corelight) + + This may contain salt from the server or a hashed password from the client. + + * mysql: Add mysql_auth_plugin, mysql_auth_more_data and mysql_auth_switch_request events (Arne Welzel, Corelight) + + 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. + + * mysql: AuthSwitchRequest: &enforce a 0xfe / 254 status (Arne Welzel, Corelight) + + * mysql: Make auth_plugin_ a std::string (Arne Welzel, Corelight) + + * mysql: Fix auth_plugin_data_part2 length computation (Arne Welzel, Corelight) + + * Refactored connection phase state handling (Fupeng Zhao) + + Added `ConnectionExpected` enum for expected packet types during the connection phase. + + * Add support for "auth switch" and "query attrs" (Fupeng Zhao) + + Also fix the issue where Resultset could not correctly distinguish between EOF_Packet and OK_Packet. + + * Add support for parsing the "caching_sha2_password" auth plugin (Fupeng Zhao) + 7.1.0-dev.45 | 2024-07-24 15:28:59 -0700 * Update binpac submodule to better format output code [nomail] (Tim Wojtulewicz, Corelight) diff --git a/NEWS b/NEWS index ab80cd160c..a4506b1577 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,10 @@ Changed Functionality made more strict and predictable. Please provide input if this results in less visibility in your environment. +* The MySQL analyzer has been improved to better support plugin authentication + mechanisms, like caching_sha2_password, as well as recognizing MySQL query + attributes. + Removed Functionality --------------------- diff --git a/VERSION b/VERSION index 2680b53ded..fdfcdd022a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0-dev.45 +7.1.0-dev.61 diff --git a/src/analyzer/protocol/mysql/events.bif b/src/analyzer/protocol/mysql/events.bif index ec5fa61ae6..a102842e05 100644 --- a/src/analyzer/protocol/mysql/events.bif +++ b/src/analyzer/protocol/mysql/events.bif @@ -84,6 +84,57 @@ 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. +## +## is_orig: True if this is from the client, false if from the server. +## +## 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, data: 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 31addd2518..afb75a44f3 100644 --- a/src/analyzer/protocol/mysql/mysql-analyzer.pac +++ b/src/analyzer/protocol/mysql/mysql-analyzer.pac @@ -14,6 +14,28 @@ 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 ) + { + 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*/, + std::move(auth_plugin), + std::move(data)); + } + } + return true; %} @@ -23,23 +45,42 @@ 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(); + + if ( mysql_ssl_request ) + zeek::BifEvent::enqueue_mysql_ssl_request(connection()->zeek_analyzer(), + connection()->zeek_analyzer()->Conn()); return true; } 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(), 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 ) + { + 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*/, + std::move(auth_plugin), + std::move(data)); + } + } + return true; %} @@ -83,8 +124,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; @@ -112,6 +153,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 +200,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 e8415e3de0..aeee595887 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -140,6 +140,11 @@ enum state { COMMAND_PHASE = 1, }; +enum ConnectionExpected { + EXPECT_HANDSHAKE, + EXPECT_AUTH_DATA, +}; + enum Expected { NO_EXPECTATION, EXPECT_STATUS, @@ -158,12 +163,133 @@ 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, + 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 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); + 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); + 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 { + 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)?/; # MySQL PDU @@ -193,7 +319,7 @@ 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; }; @@ -219,8 +345,24 @@ type Handshake_v10 = record { character_set : uint8; status_flags : uint16; capability_flags_2 : uint16; - auth_plugin_data_len : uint8; - auth_plugin_name : NUL_String; + 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 { + CLIENT_PLUGIN_AUTH -> auth_plugin: NUL_String; + 0x0 -> none : empty; + }; +} &let { + # 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); }; type Handshake_v9 = record { @@ -240,7 +382,45 @@ 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: 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_auth_plugin: bool = $context.connection.set_auth_plugin(auth_plugin) + &if( cap_flags & CLIENT_PLUGIN_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 ); }; type Handshake_Response_Packet_v10 = record { @@ -248,9 +428,13 @@ 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); + client_query_attrs: bool = $context.connection.set_client_query_attrs(cap_flags & CLIENT_QUERY_ATTRIBUTES); }; type Handshake_Response_Packet_v9 = record { @@ -258,17 +442,71 @@ 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; }; +# Connection Phase + +type Connection_Phase_Packets = case $context.connection.get_conn_expectation() of { + EXPECT_HANDSHAKE -> handshake_resp: Handshake_Response_Packet; + EXPECT_AUTH_DATA -> auth_data: AuthMoreData(true); +}; + +# Query attribute handling for COM_QUERY +# +type AttributeTypeAndName = record { + type: uint8; + unsigned_flag: uint8; + name: LengthEncodedString; +}; + +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 : 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 : 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 { + true -> query_attrs: Query_Attributes; + false -> none: empty; + }; arg : bytestring &restofdata; } &let { update_expectation: bool = $context.connection.set_next_expected_from_command(command); @@ -292,6 +530,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; @@ -326,22 +568,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); }; @@ -350,22 +592,19 @@ 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 { 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 { @@ -389,10 +628,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; + 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_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 { @@ -440,10 +689,15 @@ refine connection MySQL_Conn += { uint8 previous_seq_id_; int state_; Expected expected_; + ConnectionExpected conn_expected_; uint32 col_count_; uint32 remaining_cols_; uint32 results_seen_; bool deprecate_eof_; + bool server_query_attrs_; + bool client_query_attrs_; + std::string auth_plugin_; + int query_attr_idx_; %} %init{ @@ -451,10 +705,14 @@ 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; deprecate_eof_ = false; + server_query_attrs_ = false; + client_query_attrs_ = false; + query_attr_idx_ = 0; %} function get_version(): uint8 @@ -487,6 +745,10 @@ 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; %} @@ -501,6 +763,41 @@ 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 set_auth_plugin(a: bytestring): bool + %{ + // 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_ = std::move(new_auth_plugin); + return true; + %} + function get_expectation(): Expected %{ return expected_; @@ -512,6 +809,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 ) { @@ -662,4 +970,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.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..947e3add03 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password-after-auth-switch/out @@ -0,0 +1,19 @@ +### 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] +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 new file mode 100644 index 0000000000..bb46a96482 --- /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 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 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 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 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 new file mode 100644 index 0000000000..a9dd402e6c --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.mysql.caching_sha2_password/out @@ -0,0 +1,51 @@ +### 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= 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/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/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 0000000000..3f4407eb49 Binary files /dev/null and b/testing/btest/Traces/mysql/caching_sha2_password-after-auth-switch.pcapng differ 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 0000000000..184ed83254 Binary files /dev/null and b/testing/btest/Traces/mysql/caching_sha2_password.trace differ 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 0000000000..175e5a9644 Binary files /dev/null and b/testing/btest/Traces/mysql/many-query-attrs.pcap differ 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 0000000000..684dbcad38 Binary files /dev/null and b/testing/btest/Traces/mysql/mysql-9.0.0-query-attributes.pcap differ diff --git a/testing/btest/Traces/mysql/query-attr.pcap b/testing/btest/Traces/mysql/query-attr.pcap new file mode 100644 index 0000000000..144134b89c Binary files /dev/null and b/testing/btest/Traces/mysql/query-attr.pcap differ diff --git a/testing/btest/scripts/base/protocols/mysql/caching_sha2_password-after-auth-switch.test b/testing/btest/scripts/base/protocols/mysql/caching_sha2_password-after-auth-switch.test new file mode 100644 index 0000000000..bcc4a5a870 --- /dev/null +++ b/testing/btest/scripts/base/protocols/mysql/caching_sha2_password-after-auth-switch.test @@ -0,0 +1,50 @@ +# @TEST-EXEC: zeek -b -C -r $TRACES/mysql/caching_sha2_password-after-auth-switch.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; + } + +event mysql_auth_plugin(c: connection, is_orig: bool, name: string, data: string) + { + print "mysql auth plugin", is_orig, name, data, |data|; + } + +event mysql_auth_switch_request(c: connection, name: string, data: string) + { + print "mysql auth switch request", name, data, |data|; + } + +event mysql_auth_more_data(c: connection, is_orig: bool, data: string) + { + print "mysql auth more data", is_orig, data, |data|; + } 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..414d0d2c30 --- /dev/null +++ b/testing/btest/scripts/base/protocols/mysql/caching_sha2_password.test @@ -0,0 +1,50 @@ +# @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; + } + +event mysql_auth_plugin(c: connection, is_orig: bool, name: string, data: string) + { + print "mysql auth plugin", is_orig, name, data, |data|; + } + +event mysql_auth_switch_request(c: connection, name: string, data: string) + { + print "mysql auth switch request", name, data, |data|; + } + +event mysql_auth_more_data(c: connection, is_orig: bool, data: string) + { + print "mysql auth more data", is_orig, data, |data|; + } 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; + } 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; + } 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..0f9d33d70b --- /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.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; + }