From 3641b4eda18478a7f281a127eaa0ddfd866bb9d7 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 9 Sep 2025 22:16:27 +0200 Subject: [PATCH] detect/nfs: move nfs_procedure to rust Make it able to use strings on the way Ticket: 6723 --- doc/userguide/rules/nfs-keywords.rst | 19 +- rust/src/nfs/detect.rs | 258 ++++++++++++++++ rust/src/nfs/log.rs | 16 +- rust/src/nfs/mod.rs | 1 + rust/src/nfs/nfs.rs | 29 -- rust/src/nfs/types.rs | 157 +++++----- src/Makefile.am | 2 - src/detect-engine-register.c | 3 +- src/detect-engine-register.h | 1 - src/detect-nfs-procedure.c | 441 --------------------------- src/detect-nfs-procedure.h | 30 -- src/detect-nfs-version.c | 2 + 12 files changed, 371 insertions(+), 588 deletions(-) create mode 100644 rust/src/nfs/detect.rs delete mode 100644 src/detect-nfs-procedure.c delete mode 100644 src/detect-nfs-procedure.h diff --git a/doc/userguide/rules/nfs-keywords.rst b/doc/userguide/rules/nfs-keywords.rst index 22c3304f63..eb8a3190c8 100644 --- a/doc/userguide/rules/nfs-keywords.rst +++ b/doc/userguide/rules/nfs-keywords.rst @@ -16,4 +16,21 @@ Signature Example: :example-rule-options:`file.name; content:"file.txt";` \ classtype:bad-unknown; sid:1; rev:1;) -For additional information on the ``file.name`` keyword, see :doc:`file-keywords`. \ No newline at end of file +For additional information on the ``file.name`` keyword, see :doc:`file-keywords`. + +nfs_procedure +------------- + +This keyword allows to match the nfs procedure by its type (integer). + +nfs_procedure uses :ref:`unsigned 32-bit integer `. + +It is also possible to specify the string values for NFSv3 or NFSv4 procedures. +``nfs_procedure: getattr`` will match like ``nfs_procedure: 1; nfs.version: <4;`` +or ``nfs_procedure: 9; nfs.version: >=4;`` + +Unlike the other keywords, the usage of range is inclusive. + +Syntax:: + + nfs_procedure:(mode) diff --git a/rust/src/nfs/detect.rs b/rust/src/nfs/detect.rs new file mode 100644 index 0000000000..137726c85a --- /dev/null +++ b/rust/src/nfs/detect.rs @@ -0,0 +1,258 @@ +/* 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. + */ + +// written by Pierre Chifflier + +use suricata_sys::sys::AppProtoEnum::ALPROTO_NFS; +use suricata_sys::sys::{ + AppProto, DetectEngineCtx, DetectEngineThreadCtx, Flow, SCDetectHelperBufferRegister, + SCDetectHelperKeywordRegister, SCDetectSignatureSetAppProto, SCSigMatchAppendSMToList, + SCSigTableAppLiteElmt, SigMatchCtx, Signature, +}; + +use super::nfs::{NFSTransaction, NFSTransactionTypeData}; +use super::types::{NfsProc3, NfsProc4}; +use crate::core::STREAM_TOSERVER; +use crate::detect::uint::{ + detect_match_uint, detect_parse_uint_enum, detect_parse_uint_inclusive, DetectUintData, + SCDetectU32Free, +}; +use crate::detect::{SIGMATCH_INFO_ENUM_UINT, SIGMATCH_INFO_MULTI_UINT, SIGMATCH_INFO_UINT32}; + +use std::ffi::{c_int, CStr}; +use std::os::raw::c_void; + +static mut G_NFS_PROCEDURE_KW_ID: u16 = 0; +static mut G_NFS_PROCEDURE_BUFFER_ID: c_int = 0; + +struct DetectNfsProcedureDataVersion { + v3: Option>, + v4: Option>, +} + +enum DetectNfsProcedureData { + VersionLiteral(DetectNfsProcedureDataVersion), + Num(DetectUintData), +} + +fn nfs_procedure_parse_aux(s: &str) -> Option { + if let Ok((_, ctx)) = detect_parse_uint_inclusive::(s) { + return Some(DetectNfsProcedureData::Num(ctx)); + } + let v3 = detect_parse_uint_enum::(s); + let v4 = detect_parse_uint_enum::(s); + if v3.is_none() && v4.is_none() { + return None; + } + return Some(DetectNfsProcedureData::VersionLiteral( + DetectNfsProcedureDataVersion { v3, v4 }, + )); +} + +unsafe extern "C" fn nfs_procedure_parse( + ustr: *const std::os::raw::c_char, +) -> *mut DetectUintData { + let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe + if let Ok(s) = ft_name.to_str() { + // TODO big composite type + if let Some(ctx) = nfs_procedure_parse_aux(s) { + let boxed = Box::new(ctx); + return Box::into_raw(boxed) as *mut _; + } + } + return std::ptr::null_mut(); +} + +unsafe extern "C" fn nfs_procedure_setup( + de: *mut DetectEngineCtx, s: *mut Signature, raw: *const libc::c_char, +) -> c_int { + if SCDetectSignatureSetAppProto(s, ALPROTO_NFS as AppProto) != 0 { + return -1; + } + let ctx = nfs_procedure_parse(raw) as *mut c_void; + if ctx.is_null() { + return -1; + } + if SCSigMatchAppendSMToList( + de, + s, + G_NFS_PROCEDURE_KW_ID, + ctx as *mut SigMatchCtx, + G_NFS_PROCEDURE_BUFFER_ID, + ) + .is_null() + { + nfs_procedure_free(std::ptr::null_mut(), ctx); + return -1; + } + return 0; +} + +fn nfs_procedure_match_val(proc: u32, nfs_version: u16, ctx: &DetectNfsProcedureData) -> bool { + match ctx { + DetectNfsProcedureData::VersionLiteral(ver) => { + if nfs_version < 4 { + if let Some(du32v3) = &ver.v3 { + return detect_match_uint(du32v3, proc); + } + } else if let Some(du32v4) = &ver.v4 { + return detect_match_uint(du32v4, proc); + } + return false; + } + DetectNfsProcedureData::Num(du32) => { + return detect_match_uint(du32, proc); + } + } +} + +fn nfs_procedure_match_aux(tx: &NFSTransaction, ctx: &DetectNfsProcedureData) -> c_int { + // first try tx.procedure + if nfs_procedure_match_val(tx.procedure, tx.nfs_version, ctx) { + return 1; + } + + if !tx.is_file_tx { + return 0; + } + + /* file tx handling follows */ + if let Some(NFSTransactionTypeData::FILE(ref tdf)) = tx.type_data { + for proc in &tdf.file_additional_procs { + if nfs_procedure_match_val(*proc, tx.nfs_version, ctx) { + return 1; + } + } + } + return 0; +} + +unsafe extern "C" fn nfs_procedure_match( + _de: *mut DetectEngineThreadCtx, _f: *mut Flow, _flags: u8, _state: *mut c_void, + tx: *mut c_void, _sig: *const Signature, ctx: *const SigMatchCtx, +) -> c_int { + let tx = cast_pointer!(tx, NFSTransaction); + let ctx = cast_pointer!(ctx, DetectNfsProcedureData); + return nfs_procedure_match_aux(tx, ctx); +} + +unsafe extern "C" fn nfs_procedure_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { + let ctx = cast_pointer!(ctx, DetectUintData); + SCDetectU32Free(ctx); +} + +#[no_mangle] +pub unsafe extern "C" fn SCDetectNfsProcedureRegister() { + let kw = SCSigTableAppLiteElmt { + name: b"nfs_procedure\0".as_ptr() as *const libc::c_char, + desc: b"match NFS procedure\0".as_ptr() as *const libc::c_char, + url: b"/rules/nfs-keywords.html#procedure\0".as_ptr() as *const libc::c_char, + AppLayerTxMatch: Some(nfs_procedure_match), + Setup: Some(nfs_procedure_setup), + Free: Some(nfs_procedure_free), + flags: SIGMATCH_INFO_UINT32 | SIGMATCH_INFO_MULTI_UINT | SIGMATCH_INFO_ENUM_UINT, + }; + G_NFS_PROCEDURE_KW_ID = SCDetectHelperKeywordRegister(&kw); + G_NFS_PROCEDURE_BUFFER_ID = SCDetectHelperBufferRegister( + b"nfs_procedure\0".as_ptr() as *const libc::c_char, + ALPROTO_NFS as AppProto, + STREAM_TOSERVER, + ); +} + +#[cfg(test)] +mod test { + use super::*; + use crate::detect::uint::DetectUintMode; + + #[test] + fn nfs_procedure_parse_test() { + let ctx = nfs_procedure_parse_aux("1430000000").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1430000000); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeEqual); + } else { + panic!("not right enum"); + } + + let ctx = nfs_procedure_parse_aux(">1430000000").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1430000000); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeGt); + } else { + panic!("not right enum"); + } + + let ctx = nfs_procedure_parse_aux("<1430000000").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1430000000); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeLt); + } else { + panic!("not right enum"); + } + + let ctx = nfs_procedure_parse_aux("1430000001<>1470000000").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1430000000); + assert_eq!(ctx.arg2, 1470000001); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeRange); + } else { + panic!("not right enum"); + } + + assert!(nfs_procedure_parse_aux("A").is_none()); + assert!(nfs_procedure_parse_aux(">1430000000<>1470000000").is_none()); + assert!(nfs_procedure_parse_aux("1430000000<>").is_none()); + assert!(nfs_procedure_parse_aux("<>1430000000").is_none()); + assert!(nfs_procedure_parse_aux("").is_none()); + assert!(nfs_procedure_parse_aux(" ").is_none()); + assert!(nfs_procedure_parse_aux("1490000000<>1430000000").is_none()); + + let ctx = nfs_procedure_parse_aux("1430000001 <> 1490000000").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1430000000); + assert_eq!(ctx.arg2, 1490000001); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeRange); + } else { + panic!("not right enum"); + } + + let ctx = nfs_procedure_parse_aux("> 1430000000 ").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1430000000); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeGt); + } else { + panic!("not right enum"); + } + + let ctx = nfs_procedure_parse_aux("< 1490000000 ").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1490000000); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeLt); + } else { + panic!("not right enum"); + } + + let ctx = nfs_procedure_parse_aux(" 1490000000 ").unwrap(); + if let DetectNfsProcedureData::Num(ctx) = ctx { + assert_eq!(ctx.arg1, 1490000000); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeEqual); + } else { + panic!("not right enum"); + } + } +} diff --git a/rust/src/nfs/log.rs b/rust/src/nfs/log.rs index e2b63f2c57..291a519c01 100644 --- a/rust/src/nfs/log.rs +++ b/rust/src/nfs/log.rs @@ -20,6 +20,7 @@ use crate::nfs::nfs::*; use crate::nfs::types::*; use crc::crc32; use std::string::String; +use crate::detect::EnumString; #[no_mangle] pub extern "C" fn SCNfsTxLoggingIsFiltered(state: &mut NFSState, tx: &NFSTransaction) -> u8 { @@ -81,12 +82,17 @@ fn nfs_common_header( state: &NFSState, tx: &NFSTransaction, js: &mut JsonBuilder, ) -> Result<(), JsonError> { js.set_uint("version", state.nfs_version as u64)?; - let proc_string = if state.nfs_version < 4 { - nfs3_procedure_string(tx.procedure) + if state.nfs_version < 4 { + if let Some(proc) = NfsProc3::from_u(tx.procedure) { + js.set_string("procedure", &proc.to_str().to_uppercase())?; + } else { + js.set_string("procedure", &format!("{}", tx.procedure))?; + } + } else if let Some(proc) = NfsProc4::from_u(tx.procedure) { + js.set_string("procedure", &proc.to_str().to_uppercase())?; } else { - nfs4_procedure_string(tx.procedure) - }; - js.set_string("procedure", &proc_string)?; + js.set_string("procedure", &format!("{}", tx.procedure))?; + } let file_name = String::from_utf8_lossy(&tx.file_name); js.set_string("filename", &file_name)?; diff --git a/rust/src/nfs/mod.rs b/rust/src/nfs/mod.rs index 72b6af922a..554f0420f4 100644 --- a/rust/src/nfs/mod.rs +++ b/rust/src/nfs/mod.rs @@ -17,6 +17,7 @@ //! NFS application layer, parser, logger module. +pub mod detect; pub mod log; pub mod nfs; pub mod nfs2; diff --git a/rust/src/nfs/nfs.rs b/rust/src/nfs/nfs.rs index f70553d0c4..47c1326663 100644 --- a/rust/src/nfs/nfs.rs +++ b/rust/src/nfs/nfs.rs @@ -2045,35 +2045,6 @@ unsafe extern "C" fn nfs_get_tx_data(tx: *mut std::os::raw::c_void) -> *mut AppL export_state_data_get!(nfs_get_state_data, NFSState); -/// return procedure(s) in the tx. At 0 return the main proc, -/// otherwise get procs from the 'file_additional_procs'. -/// Keep calling until 0 is returned. -#[no_mangle] -pub unsafe extern "C" fn SCNfsTxGetProcedures( - tx: &mut NFSTransaction, i: u16, procedure: *mut u32, -) -> u8 { - if i == 0 { - *procedure = tx.procedure; - return 1; - } - - if !tx.is_file_tx { - return 0; - } - - /* file tx handling follows */ - - if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { - let idx = i as usize - 1; - if idx < tdf.file_additional_procs.len() { - let p = tdf.file_additional_procs[idx]; - *procedure = p; - return 1; - } - } - return 0; -} - #[no_mangle] pub unsafe extern "C" fn SCNfsTxGetVersion(tx: &mut NFSTransaction, version: *mut u32) { *version = tx.nfs_version as u32; diff --git a/rust/src/nfs/types.rs b/rust/src/nfs/types.rs index 609d02be43..6315a6de76 100644 --- a/rust/src/nfs/types.rs +++ b/rust/src/nfs/types.rs @@ -39,35 +39,32 @@ pub const NFSPROC3_FSINFO: u32 = 19; pub const NFSPROC3_PATHCONF: u32 = 20; pub const NFSPROC3_COMMIT: u32 = 21; -pub fn nfs3_procedure_string(procedure: u32) -> String { - match procedure { - NFSPROC3_NULL => "NULL", - NFSPROC3_GETATTR => "GETATTR", - NFSPROC3_SETATTR => "SETATTR", - NFSPROC3_LOOKUP => "LOOKUP", - NFSPROC3_ACCESS => "ACCESS", - NFSPROC3_READLINK => "READLINK", - NFSPROC3_READ => "READ", - NFSPROC3_WRITE => "WRITE", - NFSPROC3_CREATE => "CREATE", - NFSPROC3_MKDIR => "MKDIR", - NFSPROC3_SYMLINK => "SYMLINK", - NFSPROC3_MKNOD => "MKNOD", - NFSPROC3_REMOVE => "REMOVE", - NFSPROC3_RMDIR => "RMDIR", - NFSPROC3_RENAME => "RENAME", - NFSPROC3_LINK => "LINK", - NFSPROC3_READDIR => "READDIR", - NFSPROC3_READDIRPLUS => "READDIRPLUS", - NFSPROC3_FSSTAT => "FSSTAT", - NFSPROC3_FSINFO => "FSINFO", - NFSPROC3_PATHCONF => "PATHCONF", - NFSPROC3_COMMIT => "COMMIT", - _ => { - return (procedure).to_string(); - } - } - .to_string() +#[repr(u32)] +#[derive(EnumStringU32)] +#[allow(non_camel_case_types)] +pub enum NfsProc3 { + NULL = 0, + GETATTR = 1, + SETATTR = 2, + LOOKUP = 3, + ACCESS = 4, + READLINK = 5, + READ = 6, + WRITE = 7, + CREATE = 8, + MKDIR = 9, + SYMLINK = 10, + MKNOD = 11, + REMOVE = 12, + RMDIR = 13, + RENAME = 14, + LINK = 15, + READDIR = 16, + READDIRPLUS = 17, + FSSTAT = 18, + FSINFO = 19, + PATHCONF = 20, + COMMIT = 21, } /* RFC 1813, section '2.6 Defined Error Numbers' */ @@ -242,54 +239,60 @@ pub const NFSPROC4_RECLAIM_COMPLETE: u32 = 58; pub const NFSPROC4_ILLEGAL: u32 = 10044; -pub fn nfs4_procedure_string(procedure: u32) -> String { - match procedure { - NFSPROC4_COMPOUND => "COMPOUND", - NFSPROC4_NULL => "NULL", - // ops - NFSPROC4_ACCESS => "ACCESS", - NFSPROC4_CLOSE => "CLOSE", - NFSPROC4_COMMIT => "COMMIT", - NFSPROC4_CREATE => "CREATE", - NFSPROC4_DELEGPURGE => "DELEGPURGE", - NFSPROC4_DELEGRETURN => "DELEGRETURN", - NFSPROC4_GETATTR => "GETATTR", - NFSPROC4_GETFH => "GETFH", - NFSPROC4_LINK => "LINK", - NFSPROC4_LOCK => "LOCK", - NFSPROC4_LOCKT => "LOCKT", - NFSPROC4_LOCKU => "LOCKU", - NFSPROC4_LOOKUP => "LOOKUP", - NFSPROC4_LOOKUPP => "LOOKUPP", - NFSPROC4_NVERIFY => "NVERIFY", - NFSPROC4_OPEN => "OPEN", - NFSPROC4_OPENATTR => "OPENATTR", - NFSPROC4_OPEN_CONFIRM => "OPEN_CONFIRM", - NFSPROC4_OPEN_DOWNGRADE => "OPEN_DOWNGRADE", - NFSPROC4_PUTFH => "PUTFH", - NFSPROC4_PUTPUBFH => "PUTPUBFH", - NFSPROC4_PUTROOTFH => "PUTROOTFH", - NFSPROC4_READ => "READ", - NFSPROC4_READDIR => "READDIR", - NFSPROC4_READLINK => "READLINK", - NFSPROC4_REMOVE => "REMOVE", - NFSPROC4_RENAME => "RENAME", - NFSPROC4_RENEW => "RENEW", - NFSPROC4_RESTOREFH => "RESTOREFH", - NFSPROC4_SAVEFH => "SAVEFH", - NFSPROC4_SECINFO => "SECINFO", - NFSPROC4_SETATTR => "SETATTR", - NFSPROC4_SETCLIENTID => "SETCLIENTID", - NFSPROC4_SETCLIENTID_CONFIRM => "SETCLIENTID_CONFIRM", - NFSPROC4_VERIFY => "VERIFY", - NFSPROC4_WRITE => "WRITE", - NFSPROC4_RELEASE_LOCKOWNER => "RELEASE_LOCKOWNER", - NFSPROC4_ILLEGAL => "ILLEGAL", - _ => { - return (procedure).to_string(); - } - } - .to_string() +#[repr(u32)] +#[derive(EnumStringU32)] +#[allow(non_camel_case_types)] +pub enum NfsProc4 { + NULL = 0, + COMPOUND = 1, + ACCESS = 3, + CLOSE = 4, + COMMIT = 5, + CREATE = 6, + DELEGPURGE = 7, + DELEGRETURN = 8, + GETATTR = 9, + GETFH = 10, + LINK = 11, + LOCK = 12, + LOCKT = 13, + LOCKU = 14, + LOOKUP = 15, + LOOKUPP = 16, + NVERIFY = 17, + OPEN = 18, + OPENATTR = 19, + OPEN_CONFIRM = 20, + OPEN_DOWNGRADE = 21, + PUTFH = 22, + PUTPUBFH = 23, + PUTROOTFH = 24, + READ = 25, + READDIR = 26, + READLINK = 27, + REMOVE = 28, + RENAME = 29, + RENEW = 30, + RESTOREFH = 31, + SAVEFH = 32, + SECINFO = 33, + SETATTR = 34, + SETCLIENTID = 35, + SETCLIENTID_CONFIRM = 36, + VERIFY = 37, + WRITE = 38, + RELEASE_LOCKOWNER = 39, + EXCHANGE_ID = 42, + CREATE_SESSION = 43, + DESTROY_SESSION = 44, + GETDEVINFO = 47, + LAYOUTGET = 50, + LAYOUTRETURN = 51, + SECINFO_NO_NAME = 52, + SEQUENCE = 53, + DESTROY_CLIENTID = 57, + RECLAIM_COMPLETE = 58, + ILLEGAL = 10044, } pub const NFS4_OK: u32 = 0; diff --git a/src/Makefile.am b/src/Makefile.am index 91105a8a4f..66da2d1df0 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -253,7 +253,6 @@ noinst_HEADERS = \ detect-metadata.h \ detect-modbus.h \ detect-msg.h \ - detect-nfs-procedure.h \ detect-nfs-version.h \ detect-noalert.h \ detect-nocase.h \ @@ -853,7 +852,6 @@ libsuricata_c_a_SOURCES = \ detect-metadata.c \ detect-modbus.c \ detect-msg.c \ - detect-nfs-procedure.c \ detect-nfs-version.c \ detect-noalert.c \ detect-nocase.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 74e00c1595..8a490d75b4 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -71,7 +71,6 @@ #include "detect-http-host.h" #include "detect-mark.h" -#include "detect-nfs-procedure.h" #include "detect-nfs-version.h" #include "detect-engine-event.h" @@ -704,7 +703,7 @@ void SigTableSetup(void) DetectTlsRegister(); DetectTlsValidityRegister(); DetectTlsVersionRegister(); - DetectNfsProcedureRegister(); + SCDetectNfsProcedureRegister(); DetectNfsVersionRegister(); DetectUrilenRegister(); DetectBsizeRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index e37adfd010..cdfe9e22f1 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -187,7 +187,6 @@ enum DetectKeywordId { DETECT_HTTP_HOST_RAW, DETECT_HTTP_REQUEST_LINE, DETECT_HTTP_RESPONSE_LINE, - DETECT_NFS_PROCEDURE, DETECT_NFS_VERSION, DETECT_SSH_PROTOCOL, DETECT_SSH_PROTOVERSION, diff --git a/src/detect-nfs-procedure.c b/src/detect-nfs-procedure.c deleted file mode 100644 index 6d91946868..0000000000 --- a/src/detect-nfs-procedure.c +++ /dev/null @@ -1,441 +0,0 @@ -/* Copyright (C) 2017-2020 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. - */ - -/** - * \file - * - * \author Victor Julien - */ - -#include "suricata-common.h" -#include "threads.h" -#include "decode.h" -#include "detect.h" - -#include "detect-parse.h" -#include "detect-engine.h" -#include "detect-engine-mpm.h" -#include "detect-content.h" -#include "detect-pcre.h" -#include "detect-nfs-procedure.h" -#include "detect-engine-uint.h" - -#include "app-layer-parser.h" - -#include "flow.h" -#include "flow-util.h" -#include "flow-var.h" - -#include "util-unittest.h" -#include "util-unittest-helper.h" -#include "util-byte.h" - -#include "app-layer-nfs-tcp.h" -#include "rust.h" - -static int DetectNfsProcedureSetup (DetectEngineCtx *, Signature *s, const char *str); -static void DetectNfsProcedureFree(DetectEngineCtx *, void *); -#ifdef UNITTESTS -static void DetectNfsProcedureRegisterTests(void); -#endif -static int g_nfs_request_buffer_id = 0; - -static int DetectNfsProcedureMatch (DetectEngineThreadCtx *, Flow *, - uint8_t, void *, void *, const Signature *, - const SigMatchCtx *); - -/** - * \brief Registration function for nfs_procedure keyword. - */ -void DetectNfsProcedureRegister (void) -{ - sigmatch_table[DETECT_NFS_PROCEDURE].name = "nfs_procedure"; - sigmatch_table[DETECT_NFS_PROCEDURE].desc = "match NFS procedure"; - sigmatch_table[DETECT_NFS_PROCEDURE].url = "/rules/nfs-keywords.html#procedure"; - sigmatch_table[DETECT_NFS_PROCEDURE].Match = NULL; - sigmatch_table[DETECT_NFS_PROCEDURE].AppLayerTxMatch = DetectNfsProcedureMatch; - sigmatch_table[DETECT_NFS_PROCEDURE].Setup = DetectNfsProcedureSetup; - sigmatch_table[DETECT_NFS_PROCEDURE].Free = DetectNfsProcedureFree; - sigmatch_table[DETECT_NFS_PROCEDURE].flags = SIGMATCH_INFO_UINT32 | SIGMATCH_INFO_MULTI_UINT; -#ifdef UNITTESTS - sigmatch_table[DETECT_NFS_PROCEDURE].RegisterTests = DetectNfsProcedureRegisterTests; -#endif - - DetectAppLayerInspectEngineRegister( - "nfs_request", ALPROTO_NFS, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL); - - g_nfs_request_buffer_id = DetectBufferTypeGetByName("nfs_request"); - - SCLogDebug("g_nfs_request_buffer_id %d", g_nfs_request_buffer_id); -} - -/** - * \internal - * \brief Function to match procedure of a TX - * - * For 'file txs' - * - * \param t Pointer to thread vars. - * \param det_ctx Pointer to the pattern matcher thread. - * \param f Pointer to the current flow. - * \param flags Flags. - * \param state App layer state. - * \param s Pointer to the Signature. - * \param m Pointer to the sigmatch that we will cast into - * DetectU32Data. - * - * \retval 0 no match. - * \retval 1 match. - */ -static int DetectNfsProcedureMatch (DetectEngineThreadCtx *det_ctx, - Flow *f, uint8_t flags, void *state, - void *txv, const Signature *s, - const SigMatchCtx *ctx) -{ - SCEnter(); - - const DetectU32Data *dd = (const DetectU32Data *)ctx; - uint16_t i; - for (i = 0; i < 256; i++) { - uint32_t procedure; - if (SCNfsTxGetProcedures(txv, i, &procedure) == 1) { - SCLogDebug("proc %u mode %u lo %u hi %u", procedure, dd->mode, dd->arg1, dd->arg2); - if (DetectU32Match(procedure, dd)) - SCReturnInt(1); - continue; - } - break; - } - SCReturnInt(0); -} - -/** - * \internal - * \brief Function to parse options passed via tls validity keywords. - * - * \param rawstr Pointer to the user provided options. - * - * \retval dd pointer to DetectU32Data on success. - * \retval NULL on failure. - */ -static DetectU32Data *DetectNfsProcedureParse(const char *rawstr) -{ - return SCDetectU32ParseInclusive(rawstr); -} - - - -/** - * \brief Function to add the parsed tls validity field into the current signature. - * - * \param de_ctx Pointer to the Detection Engine Context. - * \param s Pointer to the Current Signature. - * \param rawstr Pointer to the user provided flags options. - * \param type Defines if this is notBefore or notAfter. - * - * \retval 0 on Success. - * \retval -1 on Failure. - */ -static int DetectNfsProcedureSetup (DetectEngineCtx *de_ctx, Signature *s, - const char *rawstr) -{ - DetectU32Data *dd = NULL; - - SCLogDebug("\'%s\'", rawstr); - - if (SCDetectSignatureSetAppProto(s, ALPROTO_NFS) != 0) - return -1; - - dd = DetectNfsProcedureParse(rawstr); - if (dd == NULL) { - SCLogError("Parsing \'%s\' failed", rawstr); - return -1; - } - - /* okay so far so good, lets get this into a SigMatch - * and put it in the Signature. */ - - SCLogDebug("low %u hi %u", dd->arg1, dd->arg2); - if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_NFS_PROCEDURE, (SigMatchCtx *)dd, - g_nfs_request_buffer_id) == NULL) { - DetectNfsProcedureFree(de_ctx, dd); - return -1; - } - return 0; -} - -/** - * \internal - * \brief Function to free memory associated with DetectU32Data. - * - * \param de_ptr Pointer to DetectU32Data. - */ -void DetectNfsProcedureFree(DetectEngineCtx *de_ctx, void *ptr) -{ - SCDetectU32Free(ptr); -} - -#ifdef UNITTESTS - -/** - * \test This is a test for a valid value 1430000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse01 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("1430000000"); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1430000000 && dd->mode == DETECT_UINT_EQ); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \test This is a test for a valid value >1430000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse02 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse(">1430000000"); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1430000000 && dd->mode == DETECT_UINT_GT); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \test This is a test for a valid value <1430000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse03 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("<1430000000"); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1430000000 && dd->mode == DETECT_UINT_LT); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \test This is a test for a valid value 1430000000<>1470000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse04 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("1430000001<>1470000000"); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1430000000 && dd->arg2 == 1470000001 && dd->mode == DETECT_UINT_RA); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \test This is a test for a invalid value A. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse05 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("A"); - FAIL_IF_NOT_NULL(dd); - PASS; -} - -/** - * \test This is a test for a invalid value >1430000000<>1470000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse06 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse(">1430000000<>1470000000"); - FAIL_IF_NOT_NULL(dd); - PASS; -} - -/** - * \test This is a test for a invalid value 1430000000<>. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse07 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("1430000000<>"); - FAIL_IF_NOT_NULL(dd); - PASS; -} - -/** - * \test This is a test for a invalid value <>1430000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse08 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("<>1430000000"); - FAIL_IF_NOT_NULL(dd); - PASS; -} - -/** - * \test This is a test for a invalid value "". - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse09 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse(""); - FAIL_IF_NOT_NULL(dd); - PASS; -} - -/** - * \test This is a test for a invalid value " ". - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse10 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse(" "); - FAIL_IF_NOT_NULL(dd); - PASS; -} - -/** - * \test This is a test for a invalid value 1490000000<>1430000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse11 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("1490000000<>1430000000"); - FAIL_IF_NOT_NULL(dd); - PASS; -} - -/** - * \test This is a test for a valid value 1430000000 <> 1490000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse12 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("1430000001 <> 1490000000"); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1430000000 && dd->arg2 == 1490000001 && dd->mode == DETECT_UINT_RA); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \test This is a test for a valid value > 1430000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse13 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("> 1430000000 "); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1430000000 && dd->mode == DETECT_UINT_GT); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \test This is a test for a valid value < 1490000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse14 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse("< 1490000000 "); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1490000000 && dd->mode == DETECT_UINT_LT); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \test This is a test for a valid value 1490000000. - * - * \retval 1 on success. - * \retval 0 on failure. - */ -static int ValidityTestParse15 (void) -{ - DetectU32Data *dd = NULL; - dd = DetectNfsProcedureParse(" 1490000000 "); - FAIL_IF_NULL(dd); - FAIL_IF_NOT(dd->arg1 == 1490000000 && dd->mode == DETECT_UINT_EQ); - DetectNfsProcedureFree(NULL, dd); - PASS; -} - -/** - * \brief Register unit tests for nfs_procedure. - */ -void DetectNfsProcedureRegisterTests(void) -{ - UtRegisterTest("ValidityTestParse01", ValidityTestParse01); - UtRegisterTest("ValidityTestParse02", ValidityTestParse02); - UtRegisterTest("ValidityTestParse03", ValidityTestParse03); - UtRegisterTest("ValidityTestParse04", ValidityTestParse04); - UtRegisterTest("ValidityTestParse05", ValidityTestParse05); - UtRegisterTest("ValidityTestParse06", ValidityTestParse06); - UtRegisterTest("ValidityTestParse07", ValidityTestParse07); - UtRegisterTest("ValidityTestParse08", ValidityTestParse08); - UtRegisterTest("ValidityTestParse09", ValidityTestParse09); - UtRegisterTest("ValidityTestParse10", ValidityTestParse10); - UtRegisterTest("ValidityTestParse11", ValidityTestParse11); - UtRegisterTest("ValidityTestParse12", ValidityTestParse12); - UtRegisterTest("ValidityTestParse13", ValidityTestParse13); - UtRegisterTest("ValidityTestParse14", ValidityTestParse14); - UtRegisterTest("ValidityTestParse15", ValidityTestParse15); -} -#endif /* UNITTESTS */ diff --git a/src/detect-nfs-procedure.h b/src/detect-nfs-procedure.h deleted file mode 100644 index a823e1d1ae..0000000000 --- a/src/detect-nfs-procedure.h +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (C) 2017 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. - */ - -/** - * \file - * - * \author Victor Julien - */ - -#ifndef SURICATA_DETECT_NFS_PROCEDURE_H -#define SURICATA_DETECT_NFS_PROCEDURE_H - -/* prototypes */ -void DetectNfsProcedureRegister (void); - -#endif /* SURICATA_DETECT_NFS_PROCEDURE_H */ diff --git a/src/detect-nfs-version.c b/src/detect-nfs-version.c index 8dccff6811..1d267ee249 100644 --- a/src/detect-nfs-version.c +++ b/src/detect-nfs-version.c @@ -71,6 +71,8 @@ void DetectNfsVersionRegister (void) sigmatch_table[DETECT_NFS_VERSION].flags = SIGMATCH_INFO_UINT32; // unit tests were the same as DetectNfsProcedureRegisterTests + DetectAppLayerInspectEngineRegister( + "nfs_request", ALPROTO_NFS, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL); g_nfs_request_buffer_id = DetectBufferTypeGetByName("nfs_request"); SCLogDebug("g_nfs_request_buffer_id %d", g_nfs_request_buffer_id);