diff --git a/rust/src/smb/detect.rs b/rust/src/smb/detect.rs index c85a6f59ce33..3445274bbcaf 100644 --- a/rust/src/smb/detect.rs +++ b/rust/src/smb/detect.rs @@ -21,6 +21,9 @@ use crate::smb::smb::*; use crate::dcerpc::detect::{DCEIfaceData, DCEOpnumData, DETECT_DCE_OPNUM_RANGE_UNINITIALIZED}; use crate::dcerpc::dcerpc::DCERPC_TYPE_REQUEST; use crate::detect::uint::detect_match_uint; +use std::ffi::CStr; +use std::os::raw::{c_char, c_void}; +use crate::smb::smb::SMBTransaction; #[no_mangle] pub unsafe extern "C" fn rs_smb_tx_get_share(tx: &mut SMBTransaction, @@ -192,3 +195,70 @@ pub unsafe extern "C" fn rs_smb_tx_get_ntlmssp_domain(tx: &mut SMBTransaction, *buffer_len = 0; return 0; } + +#[no_mangle] +pub unsafe extern "C" fn rs_smb_version_match( + tx: &mut SMBTransaction, version_data: &mut u8, +) -> u8 { + + let version = tx.vercmd.get_version(); + if version == *version_data { + return 1; + } + + return 0; +} + + +#[no_mangle] +pub unsafe extern "C" fn rs_smb_version_parse(carg: *const c_char) -> *mut c_void { + if carg.is_null() { + return std::ptr::null_mut(); + } + + if let Ok(arg) = CStr::from_ptr(carg).to_str() { + if let Ok(detect) = parse_version_data(arg) { + return Box::into_raw(Box::new(detect)) as *mut _; + } + } + + return std::ptr::null_mut(); +} + +#[no_mangle] +pub unsafe extern "C" fn rs_smb_version_free(ptr: *mut c_void) { + if ptr != std::ptr::null_mut() { + std::mem::drop(Box::from_raw(ptr as *mut u8)); + } +} + +fn parse_version_data(arg: &str) -> Result { + let arg = arg.trim(); + let version = u8::from_str_radix(&arg, 10).map_err(|_| ())?; + + if version != 1 && version != 2 { + return Err(()); + } + + return Ok(version); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_cmd_data() { + assert_eq!(Err(()), parse_version_data("0")); + assert_eq!(1u8, parse_version_data("1").unwrap()); + assert_eq!(2u8, parse_version_data("2").unwrap()); + assert_eq!(Err(()), parse_version_data("3")); + } + + #[test] + fn test_parse_cmd_data_with_spaces() { + assert_eq!(1u8, parse_version_data(" 1").unwrap()); + assert_eq!(2u8, parse_version_data(" 2 ").unwrap()); + } + +} diff --git a/src/Makefile.am b/src/Makefile.am index 4695c2d35f51..6f9f0a4ef032 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -294,6 +294,7 @@ noinst_HEADERS = \ detect-sip-uri.h \ detect-smb-ntlmssp.h \ detect-smb-share.h \ + detect-smb-version.h \ detect-snmp-community.h \ detect-snmp-pdu_type.h \ detect-snmp-usm.h \ @@ -907,6 +908,7 @@ libsuricata_c_a_SOURCES = \ detect-sip-uri.c \ detect-smb-ntlmssp.c \ detect-smb-share.c \ + detect-smb-version.c \ detect-snmp-community.c \ detect-snmp-pdu_type.c \ detect-snmp-usm.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 0f459eccb67b..ab6d26743271 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -76,6 +76,7 @@ #include "detect-config.h" #include "detect-smb-share.h" +#include "detect-smb-version.h" #include "detect-base64-decode.h" #include "detect-base64-data.h" @@ -609,6 +610,8 @@ void SigTableSetup(void) DetectSmbShareRegister(); DetectSmbNtlmsspUserRegister(); DetectSmbNtlmsspDomainRegister(); + DetectSmbVersionRegister(); + DetectTlsRegister(); DetectTlsValidityRegister(); DetectTlsVersionRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 273aa10d7c9b..733a331059b4 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -201,6 +201,7 @@ enum DetectKeywordId { DETECT_SMB_SHARE, DETECT_SMB_NTLMSSP_USER, DETECT_SMB_NTLMSSP_DOMAIN, + DETECT_SMB_VERSION, DETECT_ASN1, diff --git a/src/detect-smb-version.c b/src/detect-smb-version.c new file mode 100644 index 000000000000..529289753560 --- /dev/null +++ b/src/detect-smb-version.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2022-2023 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 Eloy PĂ©rez + * \author Jason Taylor + * + * Implements the smb.version keyword + */ + +#include "suricata-common.h" + +#include "detect.h" +#include "detect-parse.h" + +#include "detect-engine.h" +#include "detect-engine-mpm.h" +#include "detect-engine-state.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-content-inspection.h" + +#include "detect-smb-version.h" +#include "rust.h" + +#define BUFFER_NAME "smb_version" +#define KEYWORD_NAME "smb.version" +#define KEYWORD_ID DETECT_SMB_VERSION + +static int g_smb_version_list_id = 0; + +static void DetectSmbVersionFree(DetectEngineCtx *de_ctx, void *ptr) +{ + + SCLogDebug("smb_version: DetectSmbVersionFree"); + rs_smb_version_free(ptr); +} + +/** + * \brief Creates a SigMatch for the "smb.version" keyword being sent as argument, + * and appends it to the rs_smb_version_match Signature(s). + * + * \param de_ctx Pointer to the detection engine context. + * \param s Pointer to signature for the current Signature being parsed + * from the rules. + * \param arg Pointer to the string holding the keyword value. + * + * \retval 0 on success, -1 on failure + */ + +static int DetectSmbVersionSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) +{ + SCLogDebug("smb_version: DetectSmbVersionSetup"); + + if (DetectSignatureSetAppProto(s, ALPROTO_SMB) < 0) + return -1; + + if (arg == NULL) { + SCLogError("Error parsing smb.version option in signature, it needs a value"); + return -1; + } + + if (DetectGetLastSMFromLists(s, DETECT_SMB_VERSION, -1)) { + SCLogError("Can't use 2 or more smb.version declarations in " + "the same sig. Invalidating signature."); + return -1; + } + + void *dod = rs_smb_version_parse(arg); + + if (dod == NULL) { + SCLogError("Error parsing smb.version option in signature"); + return -1; + } + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_SMB_VERSION, (SigMatchCtx *)dod, g_smb_version_list_id) == NULL) { + DetectSmbVersionFree(de_ctx, dod); + return -1; + } + + return 0; +} + +/** + * \brief App layer match function for the "smb.version" keyword. + * + * \param t Pointer to the ThreadVars instance. + * \param det_ctx Pointer to the DetectEngineThreadCtx. + * \param f Pointer to the flow. + * \param flags Pointer to the flags indicating the flow direction. + * \param state Pointer to the app layer state data. + * \param s Pointer to the Signature instance. + * \param m Pointer to the SigMatch. + * + * \retval 1 On Match. + * \retval 0 On no match. + */ + +static int DetectSmbVersionMatchRust(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, + void *state, void *txv, const Signature *s, const SigMatchCtx *m) +{ + + SCLogDebug("smb_version: DetectSmbVersionMatchRust"); + + int matchvalue = rs_smb_version_match(txv, (void *)m); + + if (matchvalue != 1) { + SCLogDebug("rs_smb_version_match: didn't match"); + SCReturnInt(0); + } else { + SCLogDebug("rs_smb_version_match: matched!"); + return matchvalue; + } +} + +/** + * \brief Registers the keyword handlers for the "smb_version" keyword. + */ + +void DetectSmbVersionRegister(void) +{ + sigmatch_table[DETECT_SMB_VERSION].name = KEYWORD_NAME; + sigmatch_table[DETECT_SMB_VERSION].Setup = DetectSmbVersionSetup; + sigmatch_table[DETECT_SMB_VERSION].Match = NULL; + sigmatch_table[DETECT_SMB_VERSION].AppLayerTxMatch = DetectSmbVersionMatchRust; + sigmatch_table[DETECT_SMB_VERSION].Free = DetectSmbVersionFree; + sigmatch_table[DETECT_SMB_VERSION].desc = "smb keyword to match on SMB version"; + sigmatch_table[DETECT_FLOW_AGE].url = "/rules/smb-keywords.html#smb-version"; + + DetectAppLayerInspectEngineRegister2( + BUFFER_NAME, ALPROTO_SMB, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL); + + DetectAppLayerInspectEngineRegister2( + BUFFER_NAME, ALPROTO_SMB, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL); + + g_smb_version_list_id = DetectBufferTypeRegister(BUFFER_NAME); + + SCLogDebug("registering " BUFFER_NAME " rule option"); +} \ No newline at end of file diff --git a/src/detect-smb-version.h b/src/detect-smb-version.h new file mode 100644 index 000000000000..11bdb1feae26 --- /dev/null +++ b/src/detect-smb-version.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2022-2023 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. + */ + +#ifndef __DETECT_SMB_VERSION_H__ +#define __DETECT_SMB_VERSION_H__ + +/** \brief registers the keyword into the engine. Called from + * detect.c::SigTableSetup() */ +void DetectSmbVersionRegister(void); + +#endif /* __DETECT_SMB_VERSION_H__ */ \ No newline at end of file