-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
plugin: add in-tree app-layer template plugin for testing
Ticket: 7151 Ticket: 7152 Ticket: 7154
- Loading branch information
1 parent
355b687
commit 956a028
Showing
12 changed files
with
853 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[build] | ||
# custom flags to pass to all compiler invocations | ||
rustflags = ["-Clink-args=-Wl,-undefined,dynamic_lookup"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "suricata-altemplate" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
crate-type = ["cdylib"] | ||
|
||
[dependencies] | ||
nom7 = { version="7.0", package="nom" } | ||
libc = "~0.2.82" | ||
suricata = { path = "../../../rust/" } | ||
|
||
[features] | ||
default = ["suricata8"] | ||
suricata8 = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
alert altemplate any any -> any any (msg:"TEST"; altemplate.buffer; content:"Hello"; flow:established,to_server; sid:1; rev:1;) | ||
alert altemplate any any -> any any (msg:"TEST"; altemplate.buffer; content:"Bye"; flow:established,to_client; sid:2; rev:1;) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
%YAML 1.1 | ||
--- | ||
|
||
outputs: | ||
- eve-log: | ||
enabled: yes | ||
types: | ||
- altemplate | ||
- alert | ||
- flow | ||
|
||
app-layer: | ||
protocols: | ||
altemplate: | ||
enabled: yes | ||
detection-ports: | ||
dp: 7000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* 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. | ||
*/ | ||
|
||
// same file as rust/src/applayertemplate/detect.rs except | ||
// TEMPLATE_START_REMOVE removed | ||
// different paths for use statements | ||
// keywords prefixed with altemplate instead of just template | ||
|
||
use super::template::{TemplateTransaction, ALPROTO_TEMPLATE}; | ||
use std::os::raw::{c_int, c_void}; | ||
use suricata::cast_pointer; | ||
use suricata::detect::{ | ||
DetectBufferSetActiveList, DetectHelperBufferMpmRegister, DetectHelperGetData, | ||
DetectHelperKeywordRegister, DetectSignatureSetAppProto, SCSigTableElmt, | ||
SIGMATCH_INFO_STICKY_BUFFER, SIGMATCH_NOOPT, | ||
}; | ||
use suricata::direction::Direction; | ||
|
||
static mut G_TEMPLATE_BUFFER_BUFFER_ID: c_int = 0; | ||
|
||
unsafe extern "C" fn template_buffer_setup( | ||
de: *mut c_void, s: *mut c_void, _raw: *const std::os::raw::c_char, | ||
) -> c_int { | ||
if DetectSignatureSetAppProto(s, ALPROTO_TEMPLATE) != 0 { | ||
return -1; | ||
} | ||
if DetectBufferSetActiveList(de, s, G_TEMPLATE_BUFFER_BUFFER_ID) < 0 { | ||
return -1; | ||
} | ||
return 0; | ||
} | ||
|
||
/// Get the request/response buffer for a transaction from C. | ||
unsafe extern "C" fn template_buffer_get_data( | ||
tx: *const c_void, flags: u8, buf: *mut *const u8, len: *mut u32, | ||
) -> bool { | ||
let tx = cast_pointer!(tx, TemplateTransaction); | ||
if flags & Direction::ToClient as u8 != 0 { | ||
if let Some(ref response) = tx.response { | ||
*len = response.len() as u32; | ||
*buf = response.as_ptr(); | ||
return true; | ||
} | ||
} else if let Some(ref request) = tx.request { | ||
*len = request.len() as u32; | ||
*buf = request.as_ptr(); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
unsafe extern "C" fn template_buffer_get( | ||
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, | ||
template_buffer_get_data, | ||
); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn ScDetectTemplateRegister() { | ||
// TODO create a suricata-verify test | ||
// Setup a keyword structure and register it | ||
let kw = SCSigTableElmt { | ||
name: b"altemplate.buffer\0".as_ptr() as *const libc::c_char, | ||
desc: b"Template content modifier to match on the template buffer\0".as_ptr() | ||
as *const libc::c_char, | ||
// TODO use the right anchor for url and write doc | ||
url: b"/rules/template-keywords.html#buffer\0".as_ptr() as *const libc::c_char, | ||
Setup: template_buffer_setup, | ||
flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER, | ||
AppLayerTxMatch: None, | ||
Free: None, | ||
}; | ||
let _g_template_buffer_kw_id = DetectHelperKeywordRegister(&kw); | ||
G_TEMPLATE_BUFFER_BUFFER_ID = DetectHelperBufferMpmRegister( | ||
b"altemplate.buffer\0".as_ptr() as *const libc::c_char, | ||
b"template.buffer intern description\0".as_ptr() as *const libc::c_char, | ||
ALPROTO_TEMPLATE, | ||
true, //toclient | ||
true, //toserver | ||
template_buffer_get, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
mod detect; | ||
mod log; | ||
mod parser; | ||
pub mod plugin; | ||
mod template; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* Copyright (C) 2018 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. | ||
*/ | ||
|
||
// same file as rust/src/applayertemplate/logger.rs except | ||
// different paths for use statements | ||
// open_object using altemplate instead of just template | ||
// Jsonbuilder using C API due to opaque implementation | ||
|
||
use super::template::TemplateTransaction; | ||
use std::ffi::{c_char, CString}; | ||
use suricata::cast_pointer; | ||
use suricata::jsonbuilder::JsonError; | ||
|
||
use std; | ||
|
||
// Jsonbuilder opaque with implementation using C API to feel like usual | ||
#[repr(C)] | ||
pub struct JsonBuilder { | ||
_data: [u8; 0], | ||
} | ||
|
||
extern "C" { | ||
pub fn jb_set_string(jb: &mut JsonBuilder, key: *const c_char, val: *const c_char) -> bool; | ||
pub fn jb_close(jb: &mut JsonBuilder) -> bool; | ||
pub fn jb_open_object(jb: &mut JsonBuilder, key: *const c_char) -> bool; | ||
} | ||
|
||
impl JsonBuilder { | ||
pub fn close(&mut self) -> Result<(), JsonError> { | ||
if unsafe { !jb_close(self) } { | ||
return Err(JsonError::Memory); | ||
} | ||
Ok(()) | ||
} | ||
pub fn open_object(&mut self, key: &str) -> Result<(), JsonError> { | ||
let keyc = CString::new(key).unwrap(); | ||
if unsafe { !jb_open_object(self, keyc.as_ptr()) } { | ||
return Err(JsonError::Memory); | ||
} | ||
Ok(()) | ||
} | ||
pub fn set_string(&mut self, key: &str, val: &str) -> Result<(), JsonError> { | ||
let keyc = CString::new(key).unwrap(); | ||
let valc = CString::new(val.escape_default().to_string()).unwrap(); | ||
if unsafe { !jb_set_string(self, keyc.as_ptr(), valc.as_ptr()) } { | ||
return Err(JsonError::Memory); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
fn log_template(tx: &TemplateTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { | ||
js.open_object("altemplate")?; | ||
if let Some(ref request) = tx.request { | ||
js.set_string("request", request)?; | ||
} | ||
if let Some(ref response) = tx.response { | ||
js.set_string("response", response)?; | ||
} | ||
js.close()?; | ||
Ok(()) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn rs_template_logger_log( | ||
tx: *const std::os::raw::c_void, js: *mut std::os::raw::c_void, | ||
) -> bool { | ||
let tx = cast_pointer!(tx, TemplateTransaction); | ||
let js = cast_pointer!(js, JsonBuilder); | ||
log_template(tx, js).is_ok() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* Copyright (C) 2018 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. | ||
*/ | ||
|
||
// same file as rust/src/applayertemplate/parser.rs except this comment | ||
|
||
use nom7::{ | ||
bytes::streaming::{take, take_until}, | ||
combinator::map_res, | ||
IResult, | ||
}; | ||
use std; | ||
|
||
fn parse_len(input: &str) -> Result<u32, std::num::ParseIntError> { | ||
input.parse::<u32>() | ||
} | ||
|
||
pub fn parse_message(i: &[u8]) -> IResult<&[u8], String> { | ||
let (i, len) = map_res(map_res(take_until(":"), std::str::from_utf8), parse_len)(i)?; | ||
let (i, _sep) = take(1_usize)(i)?; | ||
let (i, msg) = map_res(take(len as usize), std::str::from_utf8)(i)?; | ||
let result = msg.to_string(); | ||
Ok((i, result)) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use nom7::Err; | ||
|
||
/// Simple test of some valid data. | ||
#[test] | ||
fn test_parse_valid() { | ||
let buf = b"12:Hello World!4:Bye."; | ||
|
||
let result = parse_message(buf); | ||
match result { | ||
Ok((remainder, message)) => { | ||
// Check the first message. | ||
assert_eq!(message, "Hello World!"); | ||
|
||
// And we should have 6 bytes left. | ||
assert_eq!(remainder.len(), 6); | ||
} | ||
Err(Err::Incomplete(_)) => { | ||
panic!("Result should not have been incomplete."); | ||
} | ||
Err(Err::Error(err)) | Err(Err::Failure(err)) => { | ||
panic!("Result should not be an error: {:?}.", err); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
use super::template::rs_template_register_parser; | ||
use crate::detect::ScDetectTemplateRegister; | ||
use crate::log::rs_template_logger_log; | ||
use suricata::plugin::{ | ||
SCAppLayerPlugin, SCPlugin, SCPluginRegisterAppLayer, SC_PLUGIN_API_VERSION, | ||
}; | ||
use suricata::{SCLogError, SCLogNotice}; | ||
|
||
extern "C" fn altemplate_plugin_init() { | ||
suricata::plugin::init(); | ||
SCLogNotice!("Initializing altemplate plugin"); | ||
let plugin = SCAppLayerPlugin { | ||
version: SC_PLUGIN_API_VERSION, // api version for suricata compatibility | ||
name: b"altemplate\0".as_ptr() as *const libc::c_char, | ||
logname: b"JsonaltemplateLog\0".as_ptr() as *const libc::c_char, | ||
confname: b"eve-log.altemplate\0".as_ptr() as *const libc::c_char, | ||
Register: rs_template_register_parser, | ||
Logger: rs_template_logger_log, | ||
KeywordsRegister: ScDetectTemplateRegister, | ||
}; | ||
unsafe { | ||
if SCPluginRegisterAppLayer(Box::into_raw(Box::new(plugin))) != 0 { | ||
SCLogError!("Failed to register altemplate plugin"); | ||
} | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
extern "C" fn SCPluginRegister() -> *const SCPlugin { | ||
let plugin = SCPlugin { | ||
name: b"altemplate\0".as_ptr() as *const libc::c_char, | ||
license: b"MIT\0".as_ptr() as *const libc::c_char, | ||
author: b"Philippe Antoine\0".as_ptr() as *const libc::c_char, | ||
Init: altemplate_plugin_init, | ||
}; | ||
Box::into_raw(Box::new(plugin)) | ||
} |
Oops, something went wrong.