Skip to content

Commit

Permalink
mysql: fix register parser and logger bug and add document
Browse files Browse the repository at this point in the history
Task #3446
  • Loading branch information
QianKaiLin committed Oct 21, 2024
1 parent fb07e3a commit 9bfc934
Show file tree
Hide file tree
Showing 19 changed files with 679 additions and 1,656 deletions.
29 changes: 29 additions & 0 deletions doc/userguide/output/eve/eve-json-format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3057,3 +3057,32 @@ Example of ARP logging: request and response
"dest_mac": "00:1d:09:f0:92:ab",
"dest_ip": "10.10.10.1"
}

Event type: MySQL
---------------

Fields
~~~~~~

* "version": the MySQL protocol version offered by the server.
* "tls": protocol need to be upgrade to tls.
* "command": sql query statement or utility command like ping.
* "rows": zero or multi results from executing sql query statement, one row is splited by comma.

Examples
~~~~~~~~

Example of MySQL logging:

::

{
"mysql": {
"version": "8.0.32",
"tls": false,
"command": "SELECT VERSION()",
"rows": [
"8.0.32"
]
}
}
1 change: 1 addition & 0 deletions doc/userguide/rules/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Suricata Rules
nfs-keywords
smtp-keywords
websocket-keywords
mysql-keywords
app-layer
xbits
noalert
Expand Down
50 changes: 50 additions & 0 deletions doc/userguide/rules/mysql-keywords.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
MySQL Keywords
============

The MySQL keywords are implemented and can be used to match on fields in MySQL messages.

============================== ==================
Keyword Direction
============================== ==================
mysql.command Request
mysql.rows Response
============================== ==================

mysql.command
----------

This keyword matches on the query statement like `select * from xxx where yyy = zzz` found in a MySQL request.

Syntax
~~~~~~

::

mysql.command; content:<command>;

Examples
~~~~~~~~

::

mysql.commands; content:"select";

mysql.rows
-------

This keyword matches on the rows which come from query statement result found in a Mysql response.
row format: 1,foo,bar

Syntax
~~~~~~

::

mysql.rows; content:<rows>;

Examples
~~~~~~~~

::

mysql.rows; content:"foo,bar";
2 changes: 2 additions & 0 deletions doc/userguide/upgrade.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Major changes
- sip.content_length
- Napatech support has been moved to a capture plugin. See :doc:`Napatech plugin
<upgrade/8.0-napatech-plugin>`.
- MySQL parser and logger have been introduced.
- The MySQL keywords ``mysql.command`` and ``mysql.command`` have been introduced.

Removals
~~~~~~~~
Expand Down
33 changes: 26 additions & 7 deletions etc/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2387,23 +2387,30 @@
"type": "object",
"optional": true,
"properties": {
"tx_id": {
"type": "integer"
},
"version": {
"type": "string"
"type": "string",
"description": "Mysql server version"
},
"tls": {
"type": "boolean"
},
"command": {
"type": "string"
"type": "string",
"description": "sql query statement or some utility commands like ping."
},
"affected_rows": {
"type": "integer"
},
},
"additionalProperties": false
"rows": {
"type": "array",
"optional": true,
"minItems": 1,
"items": {
"type": "string"
},
"description": "Comma separated result from sql statement"
}
}
},
"ldap": {
"type": "object",
Expand Down Expand Up @@ -4685,6 +4692,10 @@
"description": "Errors encountered parsing MQTT protocol",
"$ref": "#/$defs/stats_applayer_error"
},
"mysql": {
"description": "Errors encountered parsing MySQL protocol",
"$ref": "#/$defs/stats_applayer_error"
},
"nfs_tcp": {
"description": "Errors encountered parsing NFS/TCP protocol",
"$ref": "#/$defs/stats_applayer_error"
Expand Down Expand Up @@ -4860,6 +4871,10 @@
"description": "Number of flows for MQTT protocol",
"type": "integer"
},
"mysql": {
"description": "Number of flows for MySQL protocol",
"type": "integer"
},
"nfs_tcp": {
"description": "Number of flows for NFS/TCP protocol",
"type": "integer"
Expand Down Expand Up @@ -5030,6 +5045,10 @@
"description": "Number of transactions for MQTT protocol",
"type": "integer"
},
"mysql": {
"description": "Number of flows for MySQL protocol",
"type": "integer"
},
"nfs_tcp": {
"description": "Number of transactions for NFS/TCP protocol",
"type": "integer"
Expand Down
166 changes: 166 additions & 0 deletions rust/src/mysql/detect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/* Copyright (C) 2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

// Author: QianKaiLin <[email protected]>

/// Detect
/// Get the mysql query
use super::mysql::{MysqlTransaction, ALPROTO_MYSQL};
use crate::detect::{
DetectBufferSetActiveList, DetectHelperBufferMpmRegister, DetectHelperGetData,
DetectHelperGetMultiData, DetectHelperKeywordRegister, DetectHelperMultiBufferMpmRegister,
DetectSignatureSetAppProto, SCSigTableElmt, SIGMATCH_NOOPT,
};
use std::os::raw::{c_int, c_void};

static mut G_MYSQL_COMMAND_BUFFER_ID: c_int = 0;
static mut G_MYSQL_ROWS_BUFFER_ID: c_int = 0;

#[no_mangle]
unsafe extern "C" fn SCMysqlCommandSetup(
de: *mut c_void, s: *mut c_void, _raw: *const std::os::raw::c_char,
) -> c_int {
if DetectSignatureSetAppProto(s, ALPROTO_MYSQL) != 0 {
return -1;
}
if DetectBufferSetActiveList(de, s, G_MYSQL_COMMAND_BUFFER_ID) < 0 {
return -1;
}
return 0;
}

#[no_mangle]
unsafe extern "C" fn SCMysqlGetCommand(
de: *mut c_void, transforms: *const c_void, flow: *const c_void, flow_flags: u8,
tx: *const c_void, list_id: c_int,
) -> *mut c_void {
return DetectHelperGetData(
de,
transforms,
flow,
flow_flags,
tx,
list_id,
SCMysqlGetCommandData,
);
}

#[no_mangle]
unsafe extern "C" fn SCMysqlGetCommandData(
tx: *const c_void, _flags: u8, buf: *mut *const u8, len: *mut u32,
) -> bool {
let tx = cast_pointer!(tx, MysqlTransaction);
if let Some(command) = &tx.command {
if !command.is_empty() {
*buf = command.as_ptr();
*len = command.len() as u32;
return true;
}
}

false
}

#[no_mangle]
unsafe extern "C" fn SCMysqlRowsSetup(
de: *mut c_void, s: *mut c_void, _raw: *const std::os::raw::c_char,
) -> c_int {
if DetectSignatureSetAppProto(s, ALPROTO_MYSQL) != 0 {
return -1;
}
if DetectBufferSetActiveList(de, s, G_MYSQL_ROWS_BUFFER_ID) < 0 {
return -1;
}
return 0;
}

#[no_mangle]
unsafe extern "C" fn SCMysqlGetRows(
de: *mut c_void, transforms: *const c_void, flow: *const c_void, flow_flags: u8,
tx: *const c_void, list_id: c_int, local_id: u32,
) -> *mut c_void {
return DetectHelperGetMultiData(
de,
transforms,
flow,
flow_flags,
tx,
list_id,
local_id,
SCMysqlGetRowsData,
);
}

/// Get the mysql rows at index i
#[no_mangle]
pub unsafe extern "C" fn SCMysqlGetRowsData(
tx: *const c_void, _flow_flags: u8, local_id: u32, buf: *mut *const u8, len: *mut u32,
) -> bool {
let tx = cast_pointer!(tx, MysqlTransaction);
if let Some(rows) = &tx.rows {
if !rows.is_empty() {
let index = local_id as usize;
if let Some(row) = rows.get(index) {
*buf = row.as_ptr();
*len = row.len() as u32;
return true;
}
}
}

false
}

#[no_mangle]
pub unsafe extern "C" fn ScDetectMysqlRegister() {
let kw = SCSigTableElmt {
name: b"mysql.command\0".as_ptr() as *const libc::c_char,
desc: b"sticky buffer to match on the MySQL command\0".as_ptr() as *const libc::c_char,
url: b"/rules/mysql-keywords.html#mysql-command\0".as_ptr() as *const libc::c_char,
Setup: SCMysqlCommandSetup,
flags: SIGMATCH_NOOPT,
AppLayerTxMatch: None,
Free: None,
};
let _g_mysql_command_kw_id = DetectHelperKeywordRegister(&kw);
G_MYSQL_COMMAND_BUFFER_ID = DetectHelperBufferMpmRegister(
b"mysql.command\0".as_ptr() as *const libc::c_char,
b"mysql.command\0".as_ptr() as *const libc::c_char,
ALPROTO_MYSQL,
false,
true,
SCMysqlGetCommand,
);
let kw = SCSigTableElmt {
name: b"mysql.rows\0".as_ptr() as *const libc::c_char,
desc: b"sticky buffer to match on the MySQL Rows\0".as_ptr() as *const libc::c_char,
url: b"/rules/mysql-keywords.html#mysql-rows\0".as_ptr() as *const libc::c_char,
Setup: SCMysqlRowsSetup,
flags: SIGMATCH_NOOPT,
AppLayerTxMatch: None,
Free: None,
};
let _g_mysql_rows_kw_id = DetectHelperKeywordRegister(&kw);
G_MYSQL_ROWS_BUFFER_ID = DetectHelperMultiBufferMpmRegister(
b"mysql.rows\0".as_ptr() as *const libc::c_char,
b"mysql select statement resultset\0".as_ptr() as *const libc::c_char,
ALPROTO_MYSQL,
true,
false,
SCMysqlGetRows,
);
}
30 changes: 10 additions & 20 deletions rust/src/mysql/logger.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2022 Open Information Security Foundation
/* Copyright (C) 2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
Expand All @@ -15,21 +15,15 @@
* 02110-1301, USA.
*/

// written by linqiankai <linqiankai@geweian.com>
//
// Author: QianKaiLin <linqiankai666@outlook.com>

use crate::jsonbuilder::{JsonBuilder, JsonError};
use crate::mysql::mysql::*;

fn log_mysql(tx: &MysqlTransaction, _flags: u32, js: &mut JsonBuilder) -> Result<(), JsonError> {
fn log_mysql(tx: &MysqlTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> {
js.open_object("mysql")?;
if let Some(version) = &tx.version {
js.set_string("version", version)?;
}
if let Some(tls) = &tx.tls {
js.set_bool("tls", *tls)?;
} else {
js.set_bool("tls", false)?;
}
js.set_string("version", tx.version.as_str())?;
js.set_bool("tls", tx.tls)?;

if let Some(command) = &tx.command {
js.set_string("command", command)?;
Expand All @@ -54,16 +48,12 @@ fn log_mysql(tx: &MysqlTransaction, _flags: u32, js: &mut JsonBuilder) -> Result

#[no_mangle]
pub unsafe extern "C" fn SCMysqlLogger(
tx: *mut std::os::raw::c_void, flags: u32, js: &mut JsonBuilder,
tx: *mut std::os::raw::c_void, js: &mut JsonBuilder,
) -> bool {
let tx_mysql = cast_pointer!(tx, MysqlTransaction);
SCLogDebug!(
"----------- MySQL rs_mysql_logger call. Tx is {:?}",
tx_mysql
);
let result = log_mysql(tx_mysql, flags, js);
if let Err(ref err) = result {
SCLogError!("----------- MySQL rs_mysql_logger failed. err is {:?}", err);
let result = log_mysql(tx_mysql, js);
if let Err(ref _err) = result {
return false;
}
return result.is_ok();
}
Loading

0 comments on commit 9bfc934

Please sign in to comment.