diff --git a/scripts/base/protocols/mysql/consts.bro b/scripts/base/protocols/mysql/consts.bro new file mode 100644 index 0000000000..124ac0be0b --- /dev/null +++ b/scripts/base/protocols/mysql/consts.bro @@ -0,0 +1,38 @@ +module MySQL; + +export { + const commands: table[count] of string = { + [0] = "sleep", + [1] = "quit", + [2] = "init_db", + [3] = "query", + [4] = "field_list", + [5] = "create_db", + [6] = "drop_db", + [7] = "refresh", + [8] = "shutdown", + [9] = "statistics", + [10] = "process_info", + [11] = "connect", + [12] = "process_kill", + [13] = "debug", + [14] = "ping", + [15] = "time", + [16] = "delayed_insert", + [17] = "change_user", + [18] = "binlog_dump", + [19] = "table_dump", + [20] = "connect_out", + [21] = "register_slave", + [22] = "stmt_prepare", + [23] = "stmt_execute", + [24] = "stmt_send_long_data", + [25] = "stmt_close", + [26] = "stmt_reset", + [27] = "set_option", + [28] = "stmt_fetch", + [29] = "daemon", + [30] = "binlog_dump_gtid", + [31] = "reset_connection", + } &default=function(i: count): string { return fmt("unknown-%d", i); }; +} diff --git a/scripts/base/protocols/mysql/main.bro b/scripts/base/protocols/mysql/main.bro index 9605412d16..924cc09133 100644 --- a/scripts/base/protocols/mysql/main.bro +++ b/scripts/base/protocols/mysql/main.bro @@ -2,6 +2,8 @@ module MySQL; +@load ./consts + export { redef enum Log::ID += { mysql::LOG }; @@ -33,48 +35,13 @@ redef record connection += { const ports = { 1434/tcp, 3306/tcp }; -const commands: table[count] of string = { - [0] = "sleep", - [1] = "quit", - [2] = "init_db", - [3] = "query", - [4] = "field_list", - [5] = "create_db", - [6] = "drop_db", - [7] = "refresh", - [8] = "shutdown", - [9] = "statistics", - [10] = "process_info", - [11] = "connect", - [12] = "process_kill", - [13] = "debug", - [14] = "ping", - [15] = "time", - [16] = "delayed_insert", - [17] = "change_user", - [18] = "binlog_dump", - [19] = "table_dump", - [20] = "connect_out", - [21] = "register_slave", - [22] = "stmt_prepare", - [23] = "stmt_execute", - [24] = "stmt_send_long_data", - [25] = "stmt_close", - [26] = "stmt_reset", - [27] = "set_option", - [28] = "stmt_fetch", - [29] = "daemon", - [30] = "binlog_dump_gtid", - [31] = "reset_connection", -} &default=function(i: count): string { return fmt("unknown-%d", i); }; - event bro_init() &priority=5 { Log::create_stream(mysql::LOG, [$columns=Info, $ev=log_mysql]); Analyzer::register_for_ports(Analyzer::ANALYZER_MYSQL, ports); } -event mysql_handshake_response(c: connection, username: string) +event mysql_handshake(c: connection, username: string) { if ( !c?$mysql ) { @@ -88,7 +55,7 @@ event mysql_handshake_response(c: connection, username: string) } } -event mysql_command_request(c: connection, command: count, arg: string) +event mysql_command_request(c: connection, command: count, arg: string) &priority=5 { if ( !c?$mysql ) { @@ -99,6 +66,13 @@ event mysql_command_request(c: connection, command: count, arg: string) info$cmd = commands[command]; info$arg = sub(arg, /\0$/, ""); c$mysql = info; + } + } + +event mysql_command_request(c: connection, command: count, arg: string) &priority=-5 + { + if ( !c?$mysql ) + { if ( command == 1 ) { # We get no response for quits, so let's just log it now. @@ -108,34 +82,37 @@ event mysql_command_request(c: connection, command: count, arg: string) } } -event mysql_command_response(c: connection, response: count) - { - if ( c?$mysql ) - { - c$mysql$result = "ok"; - c$mysql$response = fmt("Affected rows: %d", response); - Log::write(mysql::LOG, c$mysql); - delete c$mysql; - } - } - -event mysql_error(c: connection, code: count, msg: string) +event mysql_error(c: connection, code: count, msg: string) &priority=5 { if ( c?$mysql ) { c$mysql$result = "error"; c$mysql$response = msg; + } + } + +event mysql_error(c: connection, code: count, msg: string) &priority=-5 + { + if ( c?$mysql ) + { Log::write(mysql::LOG, c$mysql); delete c$mysql; } } -event mysql_ok(c: connection, affected_rows: count) +event mysql_ok(c: connection, affected_rows: count) &priority=5 { if ( c?$mysql ) { c$mysql$result = "ok"; c$mysql$response = fmt("Affected rows: %d", affected_rows); + } + } + +event mysql_ok(c: connection, affected_rows: count) &priority=-5 + { + if ( c?$mysql ) + { Log::write(mysql::LOG, c$mysql); delete c$mysql; } diff --git a/src/analyzer/protocol/mysql/MySQL.h b/src/analyzer/protocol/mysql/MySQL.h index 6249b817c4..1b9e923aaa 100644 --- a/src/analyzer/protocol/mysql/MySQL.h +++ b/src/analyzer/protocol/mysql/MySQL.h @@ -33,7 +33,7 @@ public: static bool Available() { - return ( mysql_command_response || mysql_server_version || mysql_debug || mysql_handshake_response || mysql_login || mysql_command_request ); + return ( mysql_command_request || mysql_error || mysql_ok || mysql_server_version || mysql_handshake ); } protected: diff --git a/src/analyzer/protocol/mysql/events.bif b/src/analyzer/protocol/mysql/events.bif index 0565c7afd4..d7160c1ac6 100644 --- a/src/analyzer/protocol/mysql/events.bif +++ b/src/analyzer/protocol/mysql/events.bif @@ -1,10 +1,65 @@ -event mysql_command_response%(c: connection, response: count%); -event mysql_server_version%(c: connection, ver: string%); -event mysql_debug%(c: connection, ver: count%); -event mysql_handshake_response%(c: connection, username: string%); - -event mysql_login%(c: connection, username: string, success: bool%); +## Generated for a command request from a MySQL client. +## +## See the MySQL `documentation `__ +## for more information about the MySQL protocol. +## +## c: The connection. +## +## command: The numerical code of the command issued. +## +## arg: The argument for the command (empty string if not provided). +## +## .. bro:see:: mysql_error mysql_ok mysql_server_version mysql_handshake_response event mysql_command_request%(c: connection, command: count, arg: string%); +## Generated for an unsuccessful MySQL response. +## +## See the MySQL `documentation `__ +## for more information about the MySQL protocol. +## +## c: The connection. +## +## code: The error code. +## +## msg: Any extra details about the error (empty string if not provided). +## +## .. bro:see:: mysql_command_request mysql_ok mysql_server_version mysql_handshake_response event mysql_error%(c: connection, code: count, msg: string%); + +## Generated for a successful MySQL response. +## +## See the MySQL `documentation `__ +## for more information about the MySQL protocol. +## +## c: The connection. +## +## affected_rows: The number of rows that were affected. +## +## .. bro:see:: mysql_command_request mysql_error mysql_server_version mysql_handshake_response event mysql_ok%(c: connection, affected_rows: count%); + +## Generated for the initial server handshake packet, which includes the MySQL server version. +## +## See the MySQL `documentation `__ +## for more information about the MySQL protocol. +## +## c: The connection. +## +## ver: The server version string. +## +## .. bro:see:: mysql_command_request mysql_error mysql_ok mysql_handshake_response +event mysql_server_version%(c: connection, ver: string%); + +## Generated for a client handshake response packet, which includes the username the client is attempting +## to connect as. +## +## See the MySQL `documentation `__ +## for more information about the MySQL protocol. +## +## c: The connection. +## +## username: The username supplied by the client +## +## .. bro:see:: mysql_command_request mysql_error mysql_ok mysql_server_version +event mysql_handshake%(c: connection, username: string%); + diff --git a/src/analyzer/protocol/mysql/mysql-analyzer.pac b/src/analyzer/protocol/mysql/mysql-analyzer.pac index 7695a4559c..789c93e307 100644 --- a/src/analyzer/protocol/mysql/mysql-analyzer.pac +++ b/src/analyzer/protocol/mysql/mysql-analyzer.pac @@ -1,25 +1,32 @@ refine flow MySQL_Flow += { - function proc_mysql_handshakev10(msg: Handshake_v10): bool + function proc_mysql_initial_handshake_packet(msg: Initial_Handshake_Packet): bool %{ - if ( mysql_server_version ) - BifEvent::generate_mysql_server_version(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), - bytestring_to_val(${msg.server_version})); - connection()->bro_analyzer()->ProtocolConfirmation(); + if ( mysql_server_version ) + { + if ( ${msg.version} == 10 ) + BifEvent::generate_mysql_server_version(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.handshake10.server_version})); + if ( ${msg.version} == 9 ) + BifEvent::generate_mysql_server_version(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.handshake9.server_version})); + } return true; %} function proc_mysql_handshake_response_packet(msg: Handshake_Response_Packet): bool %{ - if ( mysql_handshake_response ) + if ( mysql_handshake ) { if ( ${msg.version} == 10 ) - BifEvent::generate_mysql_handshake_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${msg.v10_response.username})); + BifEvent::generate_mysql_handshake(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.v10_response.username})); if ( ${msg.version} == 9 ) - BifEvent::generate_mysql_handshake_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - bytestring_to_val(${msg.v9_response.username})); + BifEvent::generate_mysql_handshake(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + bytestring_to_val(${msg.v9_response.username})); } return true; %} @@ -27,37 +34,45 @@ refine flow MySQL_Flow += { function proc_mysql_command_request_packet(msg: Command_Request_Packet): bool %{ if ( mysql_command_request ) - BifEvent::generate_mysql_command_request(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), - ${msg.command}, bytestring_to_val(${msg.arg})); + BifEvent::generate_mysql_command_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${msg.command}, + bytestring_to_val(${msg.arg})); return true; %} function proc_err_packet(msg: ERR_Packet): bool %{ if ( mysql_error ) - BifEvent::generate_mysql_error(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), - ${msg.code}, bytestring_to_val(${msg.msg})); + BifEvent::generate_mysql_error(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${msg.code}, + bytestring_to_val(${msg.msg})); return true; %} function proc_ok_packet(msg: OK_Packet): bool %{ if ( mysql_ok ) - BifEvent::generate_mysql_ok(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${msg.rows}); + BifEvent::generate_mysql_ok(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${msg.rows}); return true; %} function proc_resultset(msg: Resultset): bool %{ - if ( mysql_command_response ) - BifEvent::generate_mysql_command_response(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(), ${msg.rows}->size()); + if ( mysql_ok ) + BifEvent::generate_mysql_ok(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${msg.rows}->size()); return true; %} }; -refine typeattr Handshake_v10 += &let { - proc = $context.flow.proc_mysql_handshakev10(this); +refine typeattr Initial_Handshake_Packet += &let { + proc = $context.flow.proc_mysql_initial_handshake_packet(this); }; refine typeattr Handshake_Response_Packet += &let { @@ -77,5 +92,5 @@ refine typeattr OK_Packet += &let { }; refine typeattr Resultset += &let { - debug = $context.flow.proc_resultset(this); + proc = $context.flow.proc_resultset(this); }; diff --git a/src/analyzer/protocol/mysql/mysql-protocol.pac b/src/analyzer/protocol/mysql/mysql-protocol.pac index 336f4ff178..b3d23e416d 100644 --- a/src/analyzer/protocol/mysql/mysql-protocol.pac +++ b/src/analyzer/protocol/mysql/mysql-protocol.pac @@ -159,14 +159,14 @@ type Client_Message(state: int) = case state of { # Handshake Request type Initial_Handshake_Packet = record { - protocol_version: uint8; - pkt : case protocol_version of { + version : uint8; + pkt : case version of { 10 -> handshake10 : Handshake_v10; 9 -> handshake9 : Handshake_v9; default -> error : ERR_Packet; }; } &let { - set_version : bool = $context.connection.set_version(protocol_version); + set_version : bool = $context.connection.set_version(version); }; type Handshake_v10 = record {