diff --git a/Makefile.am b/Makefile.am index b061fa475eb1..a5a71f710efc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,7 +54,7 @@ endif @echo "updated and managed with the suricata-update tool." @echo "" @echo "For more information please see:" - @echo " https://suricata.readthedocs.io/en/latest/rule-management/index.html" + @echo " https://docs.suricata.io/en/latest/rule-management/index.html" @echo "" install-library: diff --git a/README.md b/README.md index 436dc5ce8209..206712806bed 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ Introduction Installation ------------ -https://suricata.readthedocs.io/en/latest/install.html +https://docs.suricata.io/en/latest/install.html User Guide ---------- -You can follow the [Suricata user guide](https://suricata.readthedocs.io/en/latest/) to get started. +You can follow the [Suricata user guide](https://docs.suricata.io/en/latest/) to get started. Contributing ------------ diff --git a/doc/INSTALL b/doc/INSTALL index f7cb93311021..08b2effe8a41 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -2,4 +2,4 @@ See doc/userguide/install.rst An hosted version of this can be found at Read The Docs: -https://suricata.readthedocs.io/en/latest/install.html +https://docs.suricata.io/en/latest/install.html diff --git a/doc/Setting_up_IPSinline_for_Linux.txt b/doc/Setting_up_IPSinline_for_Linux.txt index f499a8994c78..3e2ee62beb26 100644 --- a/doc/Setting_up_IPSinline_for_Linux.txt +++ b/doc/Setting_up_IPSinline_for_Linux.txt @@ -1,5 +1,5 @@ Autogenerated on 2012-11-29 -from - https://suricata.readthedocs.io/en/latest/setting-up-ipsinline-for-linux.html +from - https://docs.suricata.io/en/latest/setting-up-ipsinline-for-linux.html Setting up IPS/inline for Linux diff --git a/doc/userguide/configuration/includes.rst b/doc/userguide/configuration/includes.rst new file mode 100644 index 000000000000..2fc62908aab5 --- /dev/null +++ b/doc/userguide/configuration/includes.rst @@ -0,0 +1,56 @@ +.. _includes: + +Includes +======== + +A Suricata configuration file (typically +``/etc/suricata/suricata.yaml``) may include other files allowing a +configuration file to be broken into multiple files. The *special* +field name ``include`` is used to include one or more files. + +The contents of the *include* file are inlined at the level of the +``include`` statement. *Include* fields may also be included at any +level within a mapping. + +Including a Single File +----------------------- + +:: + + include: filename.yaml + +Including Multiple Files +------------------------ + +:: + + include: + - filename1.yaml + - filename2.yaml + +Include Inside a Mapping +------------------------ + +:: + + vars: + address-groups: + include: address-groups.yaml + +where ``address-groups.yaml`` contains:: + + %YAML 1.1 + --- + HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]" + +is the equivalent of:: + + vars: + address-groups: + HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]" + +.. note:: Suricata versions less than 7 required multiple ``include`` + statements to be specified to include more than one file. While + Suricata 7.0 still supports this it will issue a deprecation + warning. Suricata 8.0 will not allow multiple ``include`` + statements at the same level as this is not allowed by YAML. diff --git a/doc/userguide/configuration/index.rst b/doc/userguide/configuration/index.rst index a32a83bf6eca..8a90cec474a8 100644 --- a/doc/userguide/configuration/index.rst +++ b/doc/userguide/configuration/index.rst @@ -11,3 +11,4 @@ Configuration dropping-privileges landlock systemd-notify + includes diff --git a/doc/userguide/devguide/codebase/contributing/contribution-process.rst b/doc/userguide/devguide/codebase/contributing/contribution-process.rst index 4bb7405cd8f0..198ff142afc2 100644 --- a/doc/userguide/devguide/codebase/contributing/contribution-process.rst +++ b/doc/userguide/devguide/codebase/contributing/contribution-process.rst @@ -203,7 +203,7 @@ If you are writing or updating *documentation pages*, please: * when adding diagrams or images, we prefer alternatives that can be generated automatically, if possible; * bear in mind that our documentation is published on `Read the Docs `_ and can also be + /docs.suricata.io/en/latest/#suricata-user-guide>`_ and can also be built to pdf, so it is important that it looks good in such formats. diff --git a/doc/userguide/devguide/codebase/unittests-c.rst b/doc/userguide/devguide/codebase/unittests-c.rst index bed54c87db88..0ae7bdf92e0c 100644 --- a/doc/userguide/devguide/codebase/unittests-c.rst +++ b/doc/userguide/devguide/codebase/unittests-c.rst @@ -19,7 +19,7 @@ If you would like to compile Suricata with unit tests, enter the following durin ./configure --enable-unittests -The unit tests specific command line options can be found at `Command Line Options `_. +The unit tests specific command line options can be found at `Command Line Options `_. Example: You can run tests specifically on flowbits. This is how you should do that:: @@ -42,7 +42,7 @@ This will be very verbose. You can also add the ``SC_LOG_OP_FILTER`` to limit th SC_LOG_LEVEL=Debug SC_LOG_OP_FILTER="(something|somethingelse)" suricata -u This example will show all lines (debug, info, and all other levels) that contain either something or something else. -Keep in mind the `log level `_ precedence: if you choose *Info* level, for instance, Suricata won't show messages from the other levels. +Keep in mind the `log level `_ precedence: if you choose *Info* level, for instance, Suricata won't show messages from the other levels. Writing Unit Tests - C codebase =============================== diff --git a/doc/userguide/devguide/extending/app-layer/app-layer-frames.rst b/doc/userguide/devguide/extending/app-layer/app-layer-frames.rst index ec16ea91c9b2..a810f5588dcd 100644 --- a/doc/userguide/devguide/extending/app-layer/app-layer-frames.rst +++ b/doc/userguide/devguide/extending/app-layer/app-layer-frames.rst @@ -7,7 +7,7 @@ Application Layer Frame Support Baseline ======== -- `Suricata rules format `_ +- `Suricata rules format `_ General Concepts ================ diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index f1e8b7233d03..0dd7223dfca3 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -57,6 +57,15 @@ Logging changes ``ike.ikev2.errors`` and ``ike.ikev2.notify``. - FTP DATA metadata for alerts are now logged in ``ftp_data`` instead of root. - Alert ``xff`` field is now logged as ``alert.xff`` for alerts instead of at the root. +- Protocol values and their names are built into Suricata instead of using the system's ``/etc/protocols`` file. Some names and casing may have changed + in the values ``proto`` in ``eve.json`` log entries and other logs containing protocol names and values. + See https://redmine.openinfosecfoundation.org/issues/4267 for more information. + +Deprecations +~~~~~~~~~~~~ +- Multiple "include" fields in the configuration file will now issue a + warning and in Suricata 8.0 will not be supported. See + :ref:`includes` for documentation on including multiple files. Other changes ~~~~~~~~~~~~~ @@ -66,12 +75,6 @@ Other changes - SWF decompression in http has been disabled by default. To change the default see :ref:`suricata-yaml-configure-libhtp`. Users with configurations from previous releases may want to modify their config to match the new default. See https://redmine.openinfosecfoundation.org/issues/5632 for more information. -Logging changes -~~~~~~~~~~~~~~~ -- Protocol values and their names are built into Suricata instead of using the system's ``/etc/protocols`` file. Some names and casing may have changed - in the values ``proto`` in ``eve.json`` log entries and other logs containing protocol names and values. - See https://redmine.openinfosecfoundation.org/issues/4267 for more information. - Upgrading 5.0 to 6.0 -------------------- - SIP now enabled by default diff --git a/lua/fast.lua b/lua/fast.lua index 17551900bec8..ffb3b01f6e7c 100644 --- a/lua/fast.lua +++ b/lua/fast.lua @@ -10,7 +10,7 @@ -- To learn more about all the API functions suricata provides for your lua scripts -- and the lua output extension in general see: --- http://suricata.readthedocs.io/en/latest/output/lua-output.html +-- http://docs.suricata.io/en/latest/output/lua-output.html function init() local needs = {} diff --git a/rust/src/dns/log.rs b/rust/src/dns/log.rs index 3de67c9bc75b..01dac80d0b83 100644 --- a/rust/src/dns/log.rs +++ b/rust/src/dns/log.rs @@ -399,7 +399,7 @@ pub fn dns_print_addr(addr: &Vec) -> std::string::String { /// Log SOA section fields. fn dns_log_soa(soa: &DNSRDataSOA) -> Result { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object()?; js.set_string_from_bytes("mname", &soa.mname)?; js.set_string_from_bytes("rname", &soa.rname)?; @@ -415,7 +415,7 @@ fn dns_log_soa(soa: &DNSRDataSOA) -> Result { /// Log SSHFP section fields. fn dns_log_sshfp(sshfp: &DNSRDataSSHFP) -> Result { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object()?; let mut hex = Vec::new(); for byte in &sshfp.fingerprint { @@ -432,7 +432,7 @@ fn dns_log_sshfp(sshfp: &DNSRDataSSHFP) -> Result { /// Log SRV section fields. fn dns_log_srv(srv: &DNSRDataSRV) -> Result { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object()?; js.set_uint("priority", srv.priority as u64)?; js.set_uint("weight", srv.weight as u64)?; @@ -444,7 +444,7 @@ fn dns_log_srv(srv: &DNSRDataSRV) -> Result { } fn dns_log_json_answer_detail(answer: &DNSAnswerEntry) -> Result { - let mut jsa = JsonBuilder::new_object(); + let mut jsa = JsonBuilder::try_new_object()?; jsa.set_string_from_bytes("rrname", &answer.name)?; jsa.set_string("rrtype", &dns_rrtype_string(answer.rrtype))?; @@ -516,7 +516,7 @@ fn dns_log_json_answer( js.set_string("rcode", &dns_rcode_string(header.flags))?; if !response.answers.is_empty() { - let mut js_answers = JsonBuilder::new_array(); + let mut js_answers = JsonBuilder::try_new_array()?; // For grouped answers we use a HashMap keyed by the rrtype. let mut answer_types = HashMap::new(); @@ -527,7 +527,7 @@ fn dns_log_json_answer( match &answer.data { DNSRData::A(addr) | DNSRData::AAAA(addr) => { if !answer_types.contains_key(&type_string) { - answer_types.insert(type_string.to_string(), JsonBuilder::new_array()); + answer_types.insert(type_string.to_string(), JsonBuilder::try_new_array()?); } if let Some(a) = answer_types.get_mut(&type_string) { a.append_string(&dns_print_addr(addr))?; @@ -540,7 +540,7 @@ fn dns_log_json_answer( | DNSRData::NULL(bytes) | DNSRData::PTR(bytes) => { if !answer_types.contains_key(&type_string) { - answer_types.insert(type_string.to_string(), JsonBuilder::new_array()); + answer_types.insert(type_string.to_string(), JsonBuilder::try_new_array()?); } if let Some(a) = answer_types.get_mut(&type_string) { a.append_string_from_bytes(bytes)?; @@ -548,7 +548,7 @@ fn dns_log_json_answer( } DNSRData::SOA(soa) => { if !answer_types.contains_key(&type_string) { - answer_types.insert(type_string.to_string(), JsonBuilder::new_array()); + answer_types.insert(type_string.to_string(), JsonBuilder::try_new_array()?); } if let Some(a) = answer_types.get_mut(&type_string) { a.append_object(&dns_log_soa(soa)?)?; @@ -556,7 +556,7 @@ fn dns_log_json_answer( } DNSRData::SSHFP(sshfp) => { if !answer_types.contains_key(&type_string) { - answer_types.insert(type_string.to_string(), JsonBuilder::new_array()); + answer_types.insert(type_string.to_string(), JsonBuilder::try_new_array()?); } if let Some(a) = answer_types.get_mut(&type_string) { a.append_object(&dns_log_sshfp(sshfp)?)?; @@ -564,7 +564,7 @@ fn dns_log_json_answer( } DNSRData::SRV(srv) => { if !answer_types.contains_key(&type_string) { - answer_types.insert(type_string.to_string(), JsonBuilder::new_array()); + answer_types.insert(type_string.to_string(), JsonBuilder::try_new_array()?); } if let Some(a) = answer_types.get_mut(&type_string) { a.append_object(&dns_log_srv(srv)?)?; diff --git a/rust/src/jsonbuilder.rs b/rust/src/jsonbuilder.rs index 721e90844a97..c1f466563be7 100644 --- a/rust/src/jsonbuilder.rs +++ b/rust/src/jsonbuilder.rs @@ -17,6 +17,8 @@ #![allow(clippy::missing_safety_doc)] +use std::cmp::max; +use std::collections::TryReserveError; use std::ffi::CStr; use std::os::raw::c_char; use std::str::Utf8Error; @@ -27,6 +29,7 @@ const INIT_SIZE: usize = 4096; pub enum JsonError { InvalidState, Utf8Error(Utf8Error), + Memory, } impl std::error::Error for JsonError {} @@ -36,10 +39,17 @@ impl std::fmt::Display for JsonError { match self { JsonError::InvalidState => write!(f, "invalid state"), JsonError::Utf8Error(ref e) => e.fmt(f), + JsonError::Memory => write!(f, "memory error"), } } } +impl From for JsonError { + fn from(_: TryReserveError) -> Self { + JsonError::Memory + } +} + impl From for JsonError { fn from(e: Utf8Error) -> Self { JsonError::Utf8Error(e) @@ -98,47 +108,78 @@ pub struct JsonBuilder { impl JsonBuilder { /// Returns a new JsonBuilder in object state. - pub fn new_object() -> Self { - Self::new_object_with_capacity(INIT_SIZE) + pub fn try_new_object() -> Result { + Self::try_new_object_with_capacity(INIT_SIZE) } - pub fn new_object_with_capacity(capacity: usize) -> Self { - let mut buf = String::with_capacity(capacity); + pub fn try_new_object_with_capacity(capacity: usize) -> Result { + let mut buf = String::new(); + buf.try_reserve(capacity)?; buf.push('{'); - Self { + let mut state = Vec::new(); + state.try_reserve(32)?; + state.extend_from_slice(&[State::None, State::ObjectFirst]); + Ok(Self { buf, - state: vec![State::None, State::ObjectFirst], + state, init_type: Type::Object, - } + }) } /// Returns a new JsonBuilder in array state. - pub fn new_array() -> Self { - Self::new_array_with_capacity(INIT_SIZE) + pub fn try_new_array() -> Result { + Self::try_new_array_with_capacity(INIT_SIZE) } - pub fn new_array_with_capacity(capacity: usize) -> Self { - let mut buf = String::with_capacity(capacity); + pub fn try_new_array_with_capacity(capacity: usize) -> Result { + let mut buf = String::new(); + buf.try_reserve(capacity)?; buf.push('['); - Self { + let mut state = Vec::new(); + state.try_reserve(32)?; + state.extend_from_slice(&[State::None, State::ArrayFirst]); + Ok(Self { buf, - state: vec![State::None, State::ArrayFirst], + state, init_type: Type::Array, + }) + } + + /// A wrapper around String::push that pre-allocates data return + /// an error if unable to. + pub fn push(&mut self, ch: char) -> Result<&mut Self, JsonError> { + if self.buf.capacity() == self.buf.len() { + self.buf.try_reserve(INIT_SIZE)?; + } + self.buf.push(ch); + Ok(self) + } + + /// A wrapper around String::push_str that pre-allocates data + /// return an error if unable to. + pub fn push_str(&mut self, s: &str) -> Result<&mut Self, JsonError> { + if self.buf.capacity() < self.buf.len() + s.len() { + self.buf.try_reserve(max(INIT_SIZE, s.len()))?; } + self.buf.push_str(s); + Ok(self) } // Reset the builder to its initial state, without losing // the current capacity. pub fn reset(&mut self) { self.buf.truncate(0); + self.state.clear(); match self.init_type { Type::Array => { self.buf.push('['); - self.state = vec![State::None, State::ArrayFirst]; + self.state + .extend_from_slice(&[State::None, State::ArrayFirst]); } Type::Object => { self.buf.push('{'); - self.state = vec![State::None, State::ObjectFirst]; + self.state + .extend_from_slice(&[State::None, State::ObjectFirst]); } } } @@ -147,19 +188,19 @@ impl JsonBuilder { pub fn close(&mut self) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectFirst | State::ObjectNth => { - self.buf.push('}'); + self.push('}')?; self.pop_state(); Ok(self) } State::ArrayFirst | State::ArrayNth => { - self.buf.push(']'); + self.push(']')?; self.pop_state(); Ok(self) } State::None => { debug_validate_fail!("invalid state"); Err(JsonError::InvalidState) - }, + } } } @@ -173,8 +214,12 @@ impl JsonBuilder { } /// Move to a new state. - fn push_state(&mut self, state: State) { + fn push_state(&mut self, state: State) -> Result<(), JsonError> { + if self.state.len() == self.state.capacity() { + self.state.try_reserve(32)?; + } self.state.push(state); + Ok(()) } /// Go back to the previous state. @@ -214,20 +259,20 @@ impl JsonBuilder { pub fn open_object(&mut self, key: &str) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectFirst => { - self.buf.push('"'); + self.push('"')?; self.set_state(State::ObjectNth); } State::ObjectNth => { - self.buf.push_str(",\""); + self.push_str(",\"")?; } _ => { debug_validate_fail!("invalid state"); return Err(JsonError::InvalidState); } } - self.buf.push_str(key); - self.buf.push_str("\":{"); - self.push_state(State::ObjectFirst); + self.push_str(key)?; + self.push_str("\":{")?; + self.push_state(State::ObjectFirst)?; Ok(self) } @@ -240,16 +285,16 @@ impl JsonBuilder { match self.current_state() { State::ArrayFirst => {} State::ArrayNth => { - self.buf.push(','); + self.push(',')?; } _ => { debug_validate_fail!("invalid state"); return Err(JsonError::InvalidState); } } - self.buf.push('{'); + self.push('{')?; self.set_state(State::ArrayNth); - self.push_state(State::ObjectFirst); + self.push_state(State::ObjectFirst)?; Ok(self) } @@ -262,18 +307,18 @@ impl JsonBuilder { match self.current_state() { State::ObjectFirst => {} State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } _ => { debug_validate_fail!("invalid state"); return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); - self.buf.push_str("\":["); + self.push('"')?; + self.push_str(key)?; + self.push_str("\":[")?; self.set_state(State::ObjectNth); - self.push_state(State::ArrayFirst); + self.push_state(State::ArrayFirst)?; Ok(self) } @@ -286,7 +331,7 @@ impl JsonBuilder { Ok(self) } State::ArrayNth => { - self.buf.push(','); + self.push(',')?; self.encode_string(val)?; Ok(self) } @@ -300,7 +345,7 @@ impl JsonBuilder { pub fn append_string_from_bytes(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> { match std::str::from_utf8(val) { Ok(s) => self.append_string(s), - Err(_) => self.append_string(&string_from_bytes(val)), + Err(_) => self.append_string(&try_string_from_bytes(val)?), } } @@ -308,17 +353,17 @@ impl JsonBuilder { pub fn append_base64(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> { match self.current_state() { State::ArrayFirst => { - self.buf.push('"'); - base64::encode_config_buf(val, base64::STANDARD, &mut self.buf); - self.buf.push('"'); + self.push('"')?; + self.encode_base64(val)?; + self.push('"')?; self.set_state(State::ArrayNth); Ok(self) } State::ArrayNth => { - self.buf.push(','); - self.buf.push('"'); - base64::encode_config_buf(val, base64::STANDARD, &mut self.buf); - self.buf.push('"'); + self.push(',')?; + self.push('"')?; + self.encode_base64(val)?; + self.push('"')?; Ok(self) } _ => { @@ -332,23 +377,23 @@ impl JsonBuilder { pub fn append_hex(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> { match self.current_state() { State::ArrayFirst => { - self.buf.push('"'); + self.push('"')?; for i in 0..val.len() { - self.buf.push(HEX[(val[i] >> 4) as usize] as char); - self.buf.push(HEX[(val[i] & 0xf) as usize] as char); + self.push(HEX[(val[i] >> 4) as usize] as char)?; + self.push(HEX[(val[i] & 0xf) as usize] as char)?; } - self.buf.push('"'); + self.push('"')?; self.set_state(State::ArrayNth); Ok(self) } State::ArrayNth => { - self.buf.push(','); - self.buf.push('"'); + self.push(',')?; + self.push('"')?; for i in 0..val.len() { - self.buf.push(HEX[(val[i] >> 4) as usize] as char); - self.buf.push(HEX[(val[i] & 0xf) as usize] as char); + self.push(HEX[(val[i] >> 4) as usize] as char)?; + self.push(HEX[(val[i] & 0xf) as usize] as char)?; } - self.buf.push('"'); + self.push('"')?; Ok(self) } _ => { @@ -365,15 +410,14 @@ impl JsonBuilder { self.set_state(State::ArrayNth); } State::ArrayNth => { - self.buf.push(','); + self.push(',')?; } _ => { debug_validate_fail!("invalid state"); return Err(JsonError::InvalidState); } } - self.buf.push_str(&val.to_string()); - Ok(self) + self.push_str(&val.to_string()) } pub fn append_float(&mut self, val: f64) -> Result<&mut Self, JsonError> { @@ -382,21 +426,21 @@ impl JsonBuilder { self.set_state(State::ArrayNth); } State::ArrayNth => { - self.buf.push(','); + self.push(',')?; } _ => { debug_validate_fail!("invalid state"); return Err(JsonError::InvalidState); } } - self.buf.push_str(&val.to_string()); + self.push_str(&val.to_string())?; Ok(self) } pub fn set_object(&mut self, key: &str, js: &JsonBuilder) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -406,10 +450,10 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); - self.buf.push_str("\":"); - self.buf.push_str(&js.buf); + self.push('"')?; + self.push_str(key)?; + self.push_str("\":")?; + self.push_str(&js.buf)?; Ok(self) } @@ -423,14 +467,14 @@ impl JsonBuilder { self.set_state(State::ArrayNth); } State::ArrayNth => { - self.buf.push(','); + self.push(',')?; } _ => { debug_validate_fail!("invalid state"); return Err(JsonError::InvalidState); } } - self.buf.push_str(&js.buf); + self.push_str(&js.buf)?; Ok(self) } @@ -439,7 +483,7 @@ impl JsonBuilder { pub fn set_string(&mut self, key: &str, val: &str) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -449,9 +493,9 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); - self.buf.push_str("\":"); + self.push('"')?; + self.push_str(key)?; + self.push_str("\":")?; self.encode_string(val)?; Ok(self) } @@ -459,7 +503,7 @@ impl JsonBuilder { pub fn set_formatted(&mut self, formatted: &str) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -469,7 +513,7 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push_str(formatted); + self.push_str(formatted)?; Ok(self) } @@ -477,7 +521,7 @@ impl JsonBuilder { pub fn set_string_from_bytes(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> { match std::str::from_utf8(val) { Ok(s) => self.set_string(key, s), - Err(_) => self.set_string(key, &string_from_bytes(val)), + Err(_) => self.set_string(key, &try_string_from_bytes(val)?), } } @@ -485,7 +529,7 @@ impl JsonBuilder { pub fn set_base64(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -495,11 +539,11 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); - self.buf.push_str("\":\""); - base64::encode_config_buf(val, base64::STANDARD, &mut self.buf); - self.buf.push('"'); + self.push('"')?; + self.push_str(key)?; + self.push_str("\":\"")?; + self.encode_base64(val)?; + self.push('"')?; Ok(self) } @@ -508,7 +552,7 @@ impl JsonBuilder { pub fn set_hex(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -518,14 +562,14 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); - self.buf.push_str("\":\""); + self.push('"')?; + self.push_str(key)?; + self.push_str("\":\"")?; for i in 0..val.len() { - self.buf.push(HEX[(val[i] >> 4) as usize] as char); - self.buf.push(HEX[(val[i] & 0xf) as usize] as char); + self.push(HEX[(val[i] >> 4) as usize] as char)?; + self.push(HEX[(val[i] & 0xf) as usize] as char)?; } - self.buf.push('"'); + self.push('"')?; Ok(self) } @@ -534,7 +578,7 @@ impl JsonBuilder { pub fn set_uint(&mut self, key: &str, val: u64) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -544,17 +588,17 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); - self.buf.push_str("\":"); - self.buf.push_str(&val.to_string()); + self.push('"')?; + self.push_str(key)?; + self.push_str("\":")?; + self.push_str(&val.to_string())?; Ok(self) } pub fn set_float(&mut self, key: &str, val: f64) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -564,17 +608,17 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); - self.buf.push_str("\":"); - self.buf.push_str(&val.to_string()); + self.push('"')?; + self.push_str(key)?; + self.push_str("\":")?; + self.push_str(&val.to_string())?; Ok(self) } pub fn set_bool(&mut self, key: &str, val: bool) -> Result<&mut Self, JsonError> { match self.current_state() { State::ObjectNth => { - self.buf.push(','); + self.push(',')?; } State::ObjectFirst => { self.set_state(State::ObjectNth); @@ -584,12 +628,12 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.buf.push('"'); - self.buf.push_str(key); + self.push('"')?; + self.push_str(key)?; if val { - self.buf.push_str("\":true"); + self.push_str("\":true")?; } else { - self.buf.push_str("\":false"); + self.push_str("\":false")?; } Ok(self) } @@ -602,17 +646,27 @@ impl JsonBuilder { /// /// The string is encoded into an intermediate vector as its faster /// than building onto the buffer. + /// + /// TODO: Revisit this, would be nice to build directly onto the + /// existing buffer. #[inline(always)] fn encode_string(&mut self, val: &str) -> Result<(), JsonError> { - let mut buf = vec![0; val.len() * 2 + 2]; + let mut buf = Vec::new(); + + // Start by allocating a reasonable size buffer, it will be + // grown if needed. + buf.try_reserve(val.len() * 2 + 2)?; + buf.resize(val.len() * 2 + 2, 0); + let mut offset = 0; let bytes = val.as_bytes(); buf[offset] = b'"'; offset += 1; for &x in bytes.iter() { if offset + 7 >= buf.capacity() { - let mut extend = vec![0; buf.capacity()]; - buf.append(&mut extend); + // We could be smarter, but just double the buffer size. + buf.try_reserve(buf.capacity())?; + buf.resize(buf.capacity(), 0); } let escape = ESCAPED[x as usize]; if escape == 0 { @@ -642,7 +696,7 @@ impl JsonBuilder { offset += 1; match std::str::from_utf8(&buf[0..offset]) { Ok(s) => { - self.buf.push_str(s); + self.push_str(s)?; } Err(err) => { let error = format!( @@ -651,18 +705,32 @@ impl JsonBuilder { &buf[0..offset], val.as_bytes(), ); - self.buf.push_str(&error); + self.push_str(&error)?; } } Ok(()) } + + fn encode_base64(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> { + let encoded_len = 4 * ((val.len() + 2) / 3); + if self.buf.capacity() < self.buf.len() + encoded_len { + self.buf.try_reserve(encoded_len)?; + } + base64::encode_config_buf(val, base64::STANDARD, &mut self.buf); + Ok(self) + } } /// A Suricata specific function to create a string from bytes when UTF-8 decoding fails. /// /// For bytes over 0x0f, we encode as hex like "\xf2". -fn string_from_bytes(input: &[u8]) -> String { - let mut out = String::with_capacity(input.len()); +fn try_string_from_bytes(input: &[u8]) -> Result { + let mut out = String::new(); + + // Allocate enough data to handle the worst case scenario of every + // byte needing to be presented as a byte. + out.try_reserve(input.len() * 4)?; + for b in input.iter() { if *b < 128 { out.push(*b as char); @@ -670,19 +738,29 @@ fn string_from_bytes(input: &[u8]) -> String { out.push_str(&format!("\\x{:02x}", *b)); } } - return out; + return Ok(out); } #[no_mangle] pub extern "C" fn jb_new_object() -> *mut JsonBuilder { - let boxed = Box::new(JsonBuilder::new_object()); - Box::into_raw(boxed) + match JsonBuilder::try_new_object() { + Ok(js) => { + let boxed = Box::new(js); + Box::into_raw(boxed) + } + Err(_) => std::ptr::null_mut(), + } } #[no_mangle] pub extern "C" fn jb_new_array() -> *mut JsonBuilder { - let boxed = Box::new(JsonBuilder::new_array()); - Box::into_raw(boxed) + match JsonBuilder::try_new_array() { + Ok(js) => { + let boxed = Box::new(js); + Box::into_raw(boxed) + } + Err(_) => std::ptr::null_mut(), + } } #[no_mangle] @@ -908,15 +986,27 @@ pub unsafe extern "C" fn jb_restore_mark(js: &mut JsonBuilder, mark: &mut JsonBu mod test { use super::*; + #[test] + fn test_try_reserve() { + // Just a sanity check that try_reserve works as I expect. + let mut buf = String::new(); + assert_eq!(buf.len(), 0); + assert_eq!(buf.capacity(), 0); + + buf.try_reserve(1).unwrap(); + assert_eq!(buf.len(), 0); + assert!(buf.capacity() >= 1); + } + #[test] fn test_set_bool() { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); jb.set_bool("first", true).unwrap(); assert_eq!(jb.buf, r#"{"first":true"#); jb.set_bool("second", false).unwrap(); assert_eq!(jb.buf, r#"{"first":true,"second":false"#); - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); jb.set_bool("first", false).unwrap(); assert_eq!(jb.buf, r#"{"first":false"#); jb.set_bool("second", true).unwrap(); @@ -925,7 +1015,7 @@ mod test { #[test] fn test_object_in_object() -> Result<(), JsonError> { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object().unwrap(); js.open_object("object")?; assert_eq!(js.current_state(), State::ObjectFirst); @@ -948,7 +1038,7 @@ mod test { #[test] fn test_empty_array_in_object() -> Result<(), JsonError> { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object().unwrap(); js.open_array("array")?; assert_eq!(js.current_state(), State::ArrayFirst); @@ -966,7 +1056,7 @@ mod test { #[test] #[cfg(not(feature = "debug-validate"))] fn test_array_in_object() -> Result<(), JsonError> { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object().unwrap(); // Attempt to add an item, should fail. assert_eq!( @@ -1002,7 +1092,7 @@ mod test { #[test] fn basic_test() -> Result<(), JsonError> { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object().unwrap(); assert_eq!(js.current_state(), State::ObjectFirst); assert_eq!(js.buf, "{"); @@ -1023,11 +1113,11 @@ mod test { #[test] fn test_combine() -> Result<(), JsonError> { - let mut main = JsonBuilder::new_object(); - let mut obj = JsonBuilder::new_object(); + let mut main = JsonBuilder::try_new_object().unwrap(); + let mut obj = JsonBuilder::try_new_object().unwrap(); obj.close()?; - let mut array = JsonBuilder::new_array(); + let mut array = JsonBuilder::try_new_array().unwrap(); array.append_string("one")?; array.append_uint(2)?; array.close()?; @@ -1042,7 +1132,7 @@ mod test { #[test] fn test_objects_in_array() -> Result<(), JsonError> { - let mut js = JsonBuilder::new_array(); + let mut js = JsonBuilder::try_new_array()?; assert_eq!(js.buf, r#"["#); js.start_object()?; @@ -1071,16 +1161,25 @@ mod test { #[test] fn test_grow() -> Result<(), JsonError> { - let mut jb = JsonBuilder::new_object_with_capacity(1); - assert_eq!(jb.capacity(), 1); - jb.set_string("foo", "bar")?; - assert!(jb.capacity() > 1); + let mut jb = JsonBuilder::try_new_object_with_capacity(1).unwrap(); + + // For efficiency reasons, more capacity may be allocated than + // requested. + assert!(jb.capacity() > 0); + let capacity = jb.capacity(); + + let mut value = String::new(); + for i in 0..capacity { + value.push_str((i % 10).to_string().as_str()); + } + jb.set_string("foo", &value)?; + assert!(jb.capacity() > capacity); Ok(()) } #[test] fn test_reset() -> Result<(), JsonError> { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); assert_eq!(jb.buf, "{"); jb.set_string("foo", "bar")?; let cap = jb.capacity(); @@ -1092,13 +1191,13 @@ mod test { #[test] fn test_append_string_from_bytes() -> Result<(), JsonError> { - let mut jb = JsonBuilder::new_array(); + let mut jb = JsonBuilder::try_new_array().unwrap(); let s = &[0x41, 0x41, 0x41, 0x00]; jb.append_string_from_bytes(s)?; assert_eq!(jb.buf, r#"["AAA\u0000""#); let s = &[0x00, 0x01, 0x02, 0x03]; - let mut jb = JsonBuilder::new_array(); + let mut jb = JsonBuilder::try_new_array().unwrap(); jb.append_string_from_bytes(s)?; assert_eq!(jb.buf, r#"["\u0000\u0001\u0002\u0003""#); @@ -1107,7 +1206,7 @@ mod test { #[test] fn test_set_string_from_bytes() { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); jb.set_string_from_bytes("first", &[]).unwrap(); assert_eq!(jb.buf, r#"{"first":"""#); jb.set_string_from_bytes("second", &[]).unwrap(); @@ -1117,15 +1216,13 @@ mod test { #[test] fn test_append_string_from_bytes_grow() -> Result<(), JsonError> { let s = &[0x00, 0x01, 0x02, 0x03]; - let mut jb = JsonBuilder::new_array(); + let mut jb = JsonBuilder::try_new_array().unwrap(); jb.append_string_from_bytes(s)?; for i in 1..1000 { let mut s = Vec::new(); - for _ in 0..i { - s.push(0x41); - } - let mut jb = JsonBuilder::new_array(); + s.resize(i, 0x41); + let mut jb = JsonBuilder::try_new_array().unwrap(); jb.append_string_from_bytes(&s)?; } @@ -1134,19 +1231,19 @@ mod test { #[test] fn test_invalid_utf8() { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); jb.set_string_from_bytes("invalid", &[0xf0, 0xf1, 0xf2]) .unwrap(); assert_eq!(jb.buf, r#"{"invalid":"\\xf0\\xf1\\xf2""#); - let mut jb = JsonBuilder::new_array(); + let mut jb = JsonBuilder::try_new_array().unwrap(); jb.append_string_from_bytes(&[0xf0, 0xf1, 0xf2]).unwrap(); assert_eq!(jb.buf, r#"["\\xf0\\xf1\\xf2""#); } #[test] fn test_marks() { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); jb.set_string("foo", "bar").unwrap(); assert_eq!(jb.buf, r#"{"foo":"bar""#); assert_eq!(jb.current_state(), State::ObjectNth); @@ -1169,7 +1266,7 @@ mod test { #[test] fn test_set_formatted() { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); jb.set_formatted("\"foo\":\"bar\"").unwrap(); assert_eq!(jb.buf, r#"{"foo":"bar""#); jb.set_formatted("\"bar\":\"foo\"").unwrap(); @@ -1180,7 +1277,7 @@ mod test { #[test] fn test_set_float() { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object().unwrap(); jb.set_float("one", 1.1).unwrap(); jb.set_float("two", 2.2).unwrap(); jb.close().unwrap(); @@ -1189,7 +1286,7 @@ mod test { #[test] fn test_append_float() { - let mut jb = JsonBuilder::new_array(); + let mut jb = JsonBuilder::try_new_array().unwrap(); jb.append_float(1.1).unwrap(); jb.append_float(2.2).unwrap(); jb.close().unwrap(); diff --git a/rust/src/mqtt/mqtt.rs b/rust/src/mqtt/mqtt.rs index 43bd7081512a..f1c37d83c881 100644 --- a/rust/src/mqtt/mqtt.rs +++ b/rust/src/mqtt/mqtt.rs @@ -23,6 +23,7 @@ use crate::applayer::*; use crate::applayer::{self, LoggerFlags}; use crate::conf::conf_get; use crate::core::*; +use crate::frames::*; use nom7::Err; use std; use std::collections::VecDeque; @@ -41,6 +42,13 @@ static mut MQTT_MAX_TX: usize = 1024; static mut ALPROTO_MQTT: AppProto = ALPROTO_UNKNOWN; +#[derive(AppLayerFrameType)] +pub enum MQTTFrameType { + Pdu, + Header, + Data, +} + #[derive(FromPrimitive, Debug, AppLayerEvent)] pub enum MQTTEvent { MissingConnect, @@ -162,7 +170,7 @@ impl MQTTState { } pub fn get_tx_by_pkt_id(&mut self, pkt_id: u32) -> Option<&mut MQTTTransaction> { - for tx in &mut self.transactions { + for tx in &mut self.transactions.range_mut(self.tx_index_completed..) { if !tx.complete { if let Some(mpktid) = tx.pkt_id { if mpktid == pkt_id { @@ -422,8 +430,10 @@ impl MQTTState { } } - fn parse_request(&mut self, input: &[u8]) -> AppLayerResult { + fn parse_request(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> AppLayerResult { + let input = stream_slice.as_slice(); let mut current = input; + if input.is_empty() { return AppLayerResult::ok(); } @@ -455,6 +465,13 @@ impl MQTTState { SCLogDebug!("request: handling {}", current.len()); match parse_message(current, self.protocol_version, self.max_msg_len) { Ok((rem, msg)) => { + let _pdu = Frame::new( + flow, + &stream_slice, + input, + current.len() as i64, + MQTTFrameType::Pdu as u8, + ); SCLogDebug!("request msg {:?}", msg); if let MQTTOperation::TRUNCATED(ref trunc) = msg.op { SCLogDebug!( @@ -474,6 +491,8 @@ impl MQTTState { continue; } } + + self.mqtt_hdr_and_data_frames(flow, &stream_slice, &msg); self.handle_msg(msg, false); consumed += current.len() - rem.len(); current = rem; @@ -497,8 +516,10 @@ impl MQTTState { return AppLayerResult::ok(); } - fn parse_response(&mut self, input: &[u8]) -> AppLayerResult { + fn parse_response(&mut self, flow: *const Flow, stream_slice: StreamSlice) -> AppLayerResult { + let input = stream_slice.as_slice(); let mut current = input; + if input.is_empty() { return AppLayerResult::ok(); } @@ -529,6 +550,14 @@ impl MQTTState { SCLogDebug!("response: handling {}", current.len()); match parse_message(current, self.protocol_version, self.max_msg_len) { Ok((rem, msg)) => { + let _pdu = Frame::new( + flow, + &stream_slice, + input, + input.len() as i64, + MQTTFrameType::Pdu as u8, + ); + SCLogDebug!("response msg {:?}", msg); if let MQTTOperation::TRUNCATED(ref trunc) = msg.op { SCLogDebug!( @@ -549,6 +578,8 @@ impl MQTTState { continue; } } + + self.mqtt_hdr_and_data_frames(flow, &stream_slice, &msg); self.handle_msg(msg, true); consumed += current.len() - rem.len(); current = rem; @@ -589,6 +620,25 @@ impl MQTTState { tx.tx_data.set_event(event as u8); self.transactions.push_back(tx); } + + fn mqtt_hdr_and_data_frames( + &mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &MQTTMessage, + ) { + let hdr = stream_slice.as_slice(); + //MQTT payload has a fixed header of 2 bytes + let _mqtt_hdr = Frame::new(flow, stream_slice, hdr, 2, MQTTFrameType::Header as u8); + SCLogDebug!("mqtt_hdr Frame {:?}", _mqtt_hdr); + let rem_length = input.header.remaining_length as usize; + let data = &hdr[2..rem_length + 2]; + let _mqtt_data = Frame::new( + flow, + stream_slice, + data, + rem_length as i64, + MQTTFrameType::Data as u8, + ); + SCLogDebug!("mqtt_data Frame {:?}", _mqtt_data); + } } // C exports. @@ -637,20 +687,20 @@ pub unsafe extern "C" fn rs_mqtt_state_tx_free(state: *mut std::os::raw::c_void, #[no_mangle] pub unsafe extern "C" fn rs_mqtt_parse_request( - _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, + flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, stream_slice: StreamSlice, _data: *const std::os::raw::c_void, ) -> AppLayerResult { let state = cast_pointer!(state, MQTTState); - return state.parse_request(stream_slice.as_slice()); + return state.parse_request(flow, stream_slice); } #[no_mangle] pub unsafe extern "C" fn rs_mqtt_parse_response( - _flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, + flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void, stream_slice: StreamSlice, _data: *const std::os::raw::c_void, ) -> AppLayerResult { let state = cast_pointer!(state, MQTTState); - return state.parse_response(stream_slice.as_slice()); + return state.parse_response(flow, stream_slice); } #[no_mangle] @@ -761,8 +811,8 @@ pub unsafe extern "C" fn rs_mqtt_register_parser(cfg_max_msg_len: u32) { apply_tx_config: None, flags: 0, truncate: None, - get_frame_id_by_name: None, - get_frame_name_by_id: None, + get_frame_id_by_name: Some(MQTTFrameType::ffi_id_from_name), + get_frame_name_by_id: Some(MQTTFrameType::ffi_name_from_id), }; let ip_proto_str = CString::new("tcp").unwrap(); diff --git a/rust/src/pgsql/logger.rs b/rust/src/pgsql/logger.rs index 61c13a1092b3..43b661805a6b 100644 --- a/rust/src/pgsql/logger.rs +++ b/rust/src/pgsql/logger.rs @@ -46,7 +46,7 @@ fn log_pgsql(tx: &PgsqlTransaction, flags: u32, js: &mut JsonBuilder) -> Result< } fn log_request(req: &PgsqlFEMessage, flags: u32) -> Result { - let mut js = JsonBuilder::new_object(); + let mut js = JsonBuilder::try_new_object()?; match req { PgsqlFEMessage::StartupMessage(StartupPacket { length: _, @@ -108,7 +108,7 @@ fn log_request(req: &PgsqlFEMessage, flags: u32) -> Result Result { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object()?; let mut array_open = false; for response in &tx.responses { if let PgsqlBEMessage::ParameterStatus(msg) = response { @@ -268,7 +268,7 @@ fn log_error_notice_field_types( } fn log_startup_parameters(params: &PgsqlStartupParameters) -> Result { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object()?; // User is a mandatory field in a pgsql message jb.set_string_from_bytes("user", ¶ms.user.value)?; if let Some(parameters) = ¶ms.optional_params { @@ -284,7 +284,7 @@ fn log_startup_parameters(params: &PgsqlStartupParameters) -> Result Result { - let mut jb = JsonBuilder::new_object(); + let mut jb = JsonBuilder::try_new_object()?; jb.set_string_from_bytes(param.name.to_str(), ¶m.value)?; jb.close()?; Ok(jb) diff --git a/rust/src/smb/smb1_records.rs b/rust/src/smb/smb1_records.rs index ce3f9194ec60..0d412da3d6b5 100644 --- a/rust/src/smb/smb1_records.rs +++ b/rust/src/smb/smb1_records.rs @@ -21,6 +21,8 @@ use crate::smb::smb::*; use crate::smb::smb_records::*; use nom7::bytes::streaming::{tag, take}; use nom7::combinator::{complete, cond, peek, rest, verify}; +use nom7::error::{make_error, ErrorKind}; +use nom7::Err; use nom7::multi::many1; use nom7::number::streaming::{le_u8, le_u16, le_u32, le_u64}; use nom7::IResult; @@ -94,6 +96,7 @@ pub fn parse_smb1_write_request_record(i: &[u8]) -> IResult<&[u8], Smb1WriteRequ } pub fn parse_smb1_write_andx_request_record(i : &[u8], andx_offset: usize) -> IResult<&[u8], Smb1WriteRequestRecord> { + let origin_i = i; let ax = andx_offset as u16; let (i, wct) = le_u8(i)?; let (i, _andx_command) = le_u8(i)?; @@ -106,16 +109,19 @@ pub fn parse_smb1_write_andx_request_record(i : &[u8], andx_offset: usize) -> IR let (i, _remaining) = le_u16(i)?; let (i, data_len_high) = le_u16(i)?; let (i, data_len_low) = le_u16(i)?; + let data_len = ((data_len_high as u32) << 16)|(data_len_low as u32); let (i, data_offset) = le_u16(i)?; + if data_offset < 0x3c || data_offset < ax{ + return Err(Err::Error(make_error(i, ErrorKind::LengthValue))); + } let (i, high_offset) = cond(wct == 14, le_u32)(i)?; - let (i, bcc) = le_u16(i)?; - //spec [MS-CIFS].pdf says always take one byte padding - let (i, _padding) = cond(bcc > data_len_low, |b| take(bcc - data_len_low)(b))(i)?; // TODO figure out how this works with data_len_high - let (i, _padding_evasion) = cond(data_offset > ax+4+2*(wct as u16), |b| take(data_offset - (ax+4+2*(wct as u16)))(b))(i)?; - let (i, file_data) = rest(i)?; + let (_i, _bcc) = le_u16(i)?; + let (i, _padding_data) = take(data_offset-ax)(origin_i)?; + let (i, file_data) = take(std::cmp::min(data_len, i.len() as u32))(i)?; + let record = Smb1WriteRequestRecord { - offset: high_offset.map(|ho| (ho as u64) << 32 | offset as u64).unwrap_or(0), - len: ((data_len_high as u32) << 16)|(data_len_low as u32), + offset: ((high_offset.unwrap_or(0) as u64) << 32) | offset as u64, + len: data_len, fid, data: file_data, }; @@ -862,3 +868,16 @@ pub fn parse_smb_record(i: &[u8]) -> IResult<&[u8], SmbRecord> { }; Ok((i, record)) } + +#[test] +fn test_parse_smb1_write_andx_request_record_origin() { + let data = hex::decode("0eff000000014000000000ff00000008001400000014003f000000000014004142434445464748494a4b4c4d4e4f5051520a0a").unwrap(); + let result = parse_smb1_write_andx_request_record(&data, SMB1_HEADER_SIZE); + assert!(result.is_ok()); + let record = result.unwrap().1; + assert_eq!(record.offset, 0); + assert_eq!(record.len, 20); + assert_eq!(record.fid, &[0x01, 0x40]); + assert_eq!(record.data.len(), 20); + assert_eq!(record.data, b"ABCDEFGHIJKLMNOPQR\n\n"); +} diff --git a/src/detect-app-layer-protocol.c b/src/detect-app-layer-protocol.c index 75693e6cd9ec..26a5ce6235aa 100644 --- a/src/detect-app-layer-protocol.c +++ b/src/detect-app-layer-protocol.c @@ -53,9 +53,8 @@ static int DetectAppLayerProtocolPacketMatch( const DetectAppLayerProtocolData *data = (const DetectAppLayerProtocolData *)ctx; /* if the sig is PD-only we only match when PD packet flags are set */ - if ((s->flags & SIG_FLAG_PDONLY) && - (p->flags & (PKT_PROTO_DETECT_TS_DONE|PKT_PROTO_DETECT_TC_DONE)) == 0) - { + if (s->type == SIG_TYPE_PDONLY && + (p->flags & (PKT_PROTO_DETECT_TS_DONE | PKT_PROTO_DETECT_TC_DONE)) == 0) { SCLogDebug("packet %"PRIu64": flags not set", p->pcap_cnt); SCReturnInt(0); } @@ -258,7 +257,7 @@ static int PrefilterSetupAppProto(DetectEngineCtx *de_ctx, SigGroupHead *sgh) static bool PrefilterAppProtoIsPrefilterable(const Signature *s) { - if (s->flags & SIG_FLAG_PDONLY) { + if (s->type == SIG_TYPE_PDONLY) { SCLogDebug("prefilter on PD %u", s->id); return true; } @@ -548,9 +547,9 @@ static int DetectAppLayerProtocolTest14(void) FAIL_IF(data->negated); SigGroupBuild(de_ctx); - FAIL_IF_NOT(s1->flags & SIG_FLAG_PDONLY); - FAIL_IF_NOT(s2->flags & SIG_FLAG_PDONLY); - FAIL_IF(s3->flags & SIG_FLAG_PDONLY); // failure now + FAIL_IF_NOT(s1->type == SIG_TYPE_PDONLY); + FAIL_IF_NOT(s2->type == SIG_TYPE_PDONLY); + FAIL_IF(s3->type == SIG_TYPE_PDONLY); // failure now DetectEngineCtxFree(de_ctx); PASS; diff --git a/src/detect-engine-alert.c b/src/detect-engine-alert.c index d532e6d4bebc..3a23b686025d 100644 --- a/src/detect-engine-alert.c +++ b/src/detect-engine-alert.c @@ -335,10 +335,19 @@ static inline void FlowApplySignatureActions( * - match is in applayer * - match is in stream */ if (s->action & (ACTION_DROP | ACTION_PASS)) { - if ((pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH)) || - (s->flags & (SIG_FLAG_IPONLY | SIG_FLAG_LIKE_IPONLY | SIG_FLAG_PDONLY | - SIG_FLAG_APPLAYER))) { + DEBUG_VALIDATE_BUG_ON(s->type == SIG_TYPE_NOT_SET); + DEBUG_VALIDATE_BUG_ON(s->type == SIG_TYPE_MAX); + + enum SignaturePropertyFlowAction flow_action = signature_properties[s->type].flow_action; + if (flow_action == SIG_PROP_FLOW_ACTION_FLOW) { pa->flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW; + } else if (flow_action == SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL) { + if (pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH)) { + pa->flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW; + } + } + + if (pa->flags & PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW) { SCLogDebug("packet %" PRIu64 " sid %u action %02x alert_flags %02x (set " "PACKET_ALERT_FLAG_APPLY_ACTION_TO_FLOW)", p->pcap_cnt, s->id, s->action, pa->flags); diff --git a/src/detect-engine-analyzer.c b/src/detect-engine-analyzer.c index 0ccc408ef7d5..2fc91cf9cfa1 100644 --- a/src/detect-engine-analyzer.c +++ b/src/detect-engine-analyzer.c @@ -841,6 +841,42 @@ void EngineAnalysisRules2(const DetectEngineCtx *de_ctx, const Signature *s) } jb_close(ctx.js); + switch (s->type) { + case SIG_TYPE_NOT_SET: + jb_set_string(ctx.js, "type", "unset"); + break; + case SIG_TYPE_IPONLY: + jb_set_string(ctx.js, "type", "ip_only"); + break; + case SIG_TYPE_LIKE_IPONLY: + jb_set_string(ctx.js, "type", "like_ip_only"); + break; + case SIG_TYPE_PDONLY: + jb_set_string(ctx.js, "type", "pd_only"); + break; + case SIG_TYPE_DEONLY: + jb_set_string(ctx.js, "type", "de_only"); + break; + case SIG_TYPE_PKT: + jb_set_string(ctx.js, "type", "pkt"); + break; + case SIG_TYPE_PKT_STREAM: + jb_set_string(ctx.js, "type", "pkt_stream"); + break; + case SIG_TYPE_STREAM: + jb_set_string(ctx.js, "type", "stream"); + break; + case SIG_TYPE_APPLAYER: + jb_set_string(ctx.js, "type", "app_layer"); + break; + case SIG_TYPE_APP_TX: + jb_set_string(ctx.js, "type", "app_tx"); + break; + case SIG_TYPE_MAX: + jb_set_string(ctx.js, "type", "error"); + break; + } + jb_open_array(ctx.js, "flags"); if (s->flags & SIG_FLAG_SRC_ANY) { jb_append_string(ctx.js, "src_any"); @@ -863,9 +899,6 @@ void EngineAnalysisRules2(const DetectEngineCtx *de_ctx, const Signature *s) if (s->flags & SIG_FLAG_APPLAYER) { jb_append_string(ctx.js, "applayer"); } - if (s->flags & SIG_FLAG_IPONLY) { - jb_append_string(ctx.js, "ip_only"); - } if (s->flags & SIG_FLAG_REQUIRE_PACKET) { jb_append_string(ctx.js, "need_packet"); } @@ -899,9 +932,6 @@ void EngineAnalysisRules2(const DetectEngineCtx *de_ctx, const Signature *s) if (s->flags & SIG_FLAG_PREFILTER) { jb_append_string(ctx.js, "prefilter"); } - if (s->flags & SIG_FLAG_PDONLY) { - jb_append_string(ctx.js, "proto_detect_only"); - } if (s->flags & SIG_FLAG_SRC_IS_TARGET) { jb_append_string(ctx.js, "src_is_target"); } @@ -1483,8 +1513,39 @@ void EngineAnalysisRules(const DetectEngineCtx *de_ctx, fprintf(rule_engine_analysis_FD, "== Sid: %u ==\n", s->id); fprintf(rule_engine_analysis_FD, "%s\n", line); - if (s->flags & SIG_FLAG_IPONLY) fprintf(rule_engine_analysis_FD, " Rule is ip only.\n"); - if (s->flags & SIG_FLAG_PDONLY) fprintf(rule_engine_analysis_FD, " Rule is PD only.\n"); + switch (s->type) { + case SIG_TYPE_NOT_SET: + break; + case SIG_TYPE_IPONLY: + fprintf(rule_engine_analysis_FD, " Rule is ip only.\n"); + break; + case SIG_TYPE_LIKE_IPONLY: + fprintf(rule_engine_analysis_FD, " Rule is like ip only.\n"); + break; + case SIG_TYPE_PDONLY: + fprintf(rule_engine_analysis_FD, " Rule is PD only.\n"); + break; + case SIG_TYPE_DEONLY: + fprintf(rule_engine_analysis_FD, " Rule is DE only.\n"); + break; + case SIG_TYPE_PKT: + fprintf(rule_engine_analysis_FD, " Rule is packet inspecting.\n"); + break; + case SIG_TYPE_PKT_STREAM: + fprintf(rule_engine_analysis_FD, " Rule is packet and stream inspecting.\n"); + break; + case SIG_TYPE_STREAM: + fprintf(rule_engine_analysis_FD, " Rule is stream inspecting.\n"); + break; + case SIG_TYPE_APPLAYER: + fprintf(rule_engine_analysis_FD, " Rule is app-layer inspecting.\n"); + break; + case SIG_TYPE_APP_TX: + fprintf(rule_engine_analysis_FD, " Rule is App-layer TX inspecting.\n"); + break; + case SIG_TYPE_MAX: + break; + } if (rule_ipv6_only) fprintf(rule_engine_analysis_FD, " Rule is IPv6 only.\n"); if (rule_ipv4_only) fprintf(rule_engine_analysis_FD, " Rule is IPv4 only.\n"); if (packet_buf) fprintf(rule_engine_analysis_FD, " Rule matches on packets.\n"); diff --git a/src/detect-engine-build.c b/src/detect-engine-build.c index f6e064387c29..f82567e75d7f 100644 --- a/src/detect-engine-build.c +++ b/src/detect-engine-build.c @@ -989,7 +989,7 @@ static int RulesGroupByProto(DetectEngineCtx *de_ctx) SigGroupHead *sgh_tc[256] = {NULL}; for ( ; s != NULL; s = s->next) { - if (s->flags & SIG_FLAG_IPONLY) + if (s->type == SIG_TYPE_IPONLY) continue; int p; @@ -1168,7 +1168,7 @@ static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, u DetectPort *list = NULL; while (s) { /* IP Only rules are handled separately */ - if (s->flags & SIG_FLAG_IPONLY) + if (s->type == SIG_TYPE_IPONLY) goto next; if (!(s->proto.proto[ipproto / 8] & (1<<(ipproto % 8)) || (s->proto.flags & DETECT_PROTO_ANY))) goto next; @@ -1300,21 +1300,65 @@ static DetectPort *RulesGroupByPorts(DetectEngineCtx *de_ctx, uint8_t ipproto, u void SignatureSetType(DetectEngineCtx *de_ctx, Signature *s) { + BUG_ON(s->type != SIG_TYPE_NOT_SET); int iponly = 0; /* see if the sig is dp only */ if (SignatureIsPDOnly(de_ctx, s) == 1) { - s->flags |= SIG_FLAG_PDONLY; + s->type = SIG_TYPE_PDONLY; - /* see if the sig is ip only */ + /* see if the sig is ip only */ } else if ((iponly = SignatureIsIPOnly(de_ctx, s)) > 0) { if (iponly == 1) { - s->flags |= SIG_FLAG_IPONLY; + s->type = SIG_TYPE_IPONLY; } else if (iponly == 2) { - s->flags |= SIG_FLAG_LIKE_IPONLY; + s->type = SIG_TYPE_LIKE_IPONLY; } } else if (SignatureIsDEOnly(de_ctx, s) == 1) { - s->init_data->init_flags |= SIG_FLAG_INIT_DEONLY; + s->type = SIG_TYPE_DEONLY; + + } else { + const bool has_match = s->init_data->smlists[DETECT_SM_LIST_MATCH] != NULL; + const bool has_pmatch = s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL; + bool has_buffer_frame_engine = false; + bool has_buffer_packet_engine = false; + bool has_buffer_app_engine = false; + + for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { + const uint32_t id = s->init_data->buffers[x].id; + + if (DetectEngineBufferTypeSupportsPacketGetById(de_ctx, id)) { + has_buffer_packet_engine = true; + } else if (DetectEngineBufferTypeSupportsFramesGetById(de_ctx, id)) { + has_buffer_frame_engine = true; + } else { + has_buffer_app_engine = true; + } + } + + if (has_buffer_packet_engine) { + s->type = SIG_TYPE_PKT; + } else if (has_buffer_frame_engine || has_buffer_app_engine) { + s->type = SIG_TYPE_APP_TX; + } else if (has_pmatch) { + if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) == + SIG_FLAG_REQUIRE_PACKET) { + s->type = SIG_TYPE_PKT; + } else if ((s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM)) == + SIG_FLAG_REQUIRE_STREAM) { + s->type = SIG_TYPE_STREAM; + } else { + s->type = SIG_TYPE_PKT_STREAM; + } + } else if (has_match) { + s->type = SIG_TYPE_PKT; + + /* app-layer but no inspect engines */ + } else if (s->flags & SIG_FLAG_APPLAYER) { + s->type = SIG_TYPE_APPLAYER; + } else { + s->type = SIG_TYPE_PKT; + } } } @@ -1354,15 +1398,15 @@ int SigAddressPrepareStage1(DetectEngineCtx *de_ctx) SCLogDebug("Signature %" PRIu32 ", internal id %" PRIu32 ", ptrs %p %p ", s->id, s->num, s, de_ctx->sig_array[s->num]); - if (s->flags & SIG_FLAG_PDONLY) { + if (s->type == SIG_TYPE_PDONLY) { SCLogDebug("Signature %"PRIu32" is considered \"PD only\"", s->id); - } else if (s->flags & SIG_FLAG_IPONLY) { + } else if (s->type == SIG_TYPE_IPONLY) { SCLogDebug("Signature %"PRIu32" is considered \"IP only\"", s->id); cnt_iponly++; } else if (SignatureIsInspectingPayload(de_ctx, s) == 1) { SCLogDebug("Signature %"PRIu32" is considered \"Payload inspecting\"", s->id); cnt_payload++; - } else if (s->init_data->init_flags & SIG_FLAG_INIT_DEONLY) { + } else if (s->type == SIG_TYPE_DEONLY) { SCLogDebug("Signature %"PRIu32" is considered \"Decoder Event only\"", s->id); cnt_deonly++; } else if (s->flags & SIG_FLAG_APPLAYER) { @@ -1684,11 +1728,9 @@ int SigAddressPrepareStage2(DetectEngineCtx *de_ctx) /* now for every rule add the source group to our temp lists */ for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) { SCLogDebug("s->id %"PRIu32, s->id); - if (s->flags & SIG_FLAG_IPONLY) { + if (s->type == SIG_TYPE_IPONLY) { IPOnlyAddSignature(de_ctx, &de_ctx->io_ctx, s); - } - - if (s->init_data->init_flags & SIG_FLAG_INIT_DEONLY) { + } else if (s->type == SIG_TYPE_DEONLY) { DetectEngineAddDecoderEventSig(de_ctx, s); } } @@ -1872,8 +1914,7 @@ static int SigMatchPrepare(DetectEngineCtx *de_ctx) DetectEngineAppInspectionEngine2Signature(de_ctx, s); /* built-ins */ - int type; - for (type = 0; type < DETECT_SM_LIST_MAX; type++) { + for (int type = 0; type < DETECT_SM_LIST_MAX; type++) { /* skip PMATCH if it is used in a stream 'app engine' instead */ if (type == DETECT_SM_LIST_PMATCH && (s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH)) continue; @@ -1888,8 +1929,7 @@ static int SigMatchPrepare(DetectEngineCtx *de_ctx) EngineAnalysisRules2(de_ctx, s); } /* free lists. Ctx' are xferred to sm_arrays so won't get freed */ - uint32_t i; - for (i = 0; i < DETECT_SM_LIST_MAX; i++) { + for (uint32_t i = 0; i < DETECT_SM_LIST_MAX; i++) { SigMatch *sm = s->init_data->smlists[i]; while (sm != NULL) { SigMatch *nsm = sm->next; @@ -1897,7 +1937,7 @@ static int SigMatchPrepare(DetectEngineCtx *de_ctx) sm = nsm; } } - for (i = 0; i < (uint32_t)s->init_data->transforms.cnt; i++) { + for (uint32_t i = 0; i < (uint32_t)s->init_data->transforms.cnt; i++) { if (s->init_data->transforms.transforms[i].options) { int transform = s->init_data->transforms.transforms[i].transform; sigmatch_table[transform].Free( diff --git a/src/detect-engine-iponly.c b/src/detect-engine-iponly.c index b041bb4e16c2..e2be468fcaa2 100644 --- a/src/detect-engine-iponly.c +++ b/src/detect-engine-iponly.c @@ -1524,7 +1524,7 @@ void IPOnlyPrepare(DetectEngineCtx *de_ctx) void IPOnlyAddSignature(DetectEngineCtx *de_ctx, DetectEngineIPOnlyCtx *io_ctx, Signature *s) { - if (!(s->flags & SIG_FLAG_IPONLY)) + if (!(s->type == SIG_TYPE_IPONLY)) return; SigIntId mapped_signum = IPOnlyTrackSigNum(io_ctx, s->num); diff --git a/src/detect-engine.c b/src/detect-engine.c index 4eb9acf749d3..522f1d50c16e 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -106,6 +106,21 @@ static DetectEngineAppInspectionEngine *g_app_inspect_engines = NULL; static DetectEnginePktInspectionEngine *g_pkt_inspect_engines = NULL; static DetectEngineFrameInspectionEngine *g_frame_inspect_engines = NULL; +// clang-format off +const struct SignatureProperties signature_properties[SIG_TYPE_MAX] = { + /* SIG_TYPE_NOT_SET */ { SIG_PROP_FLOW_ACTION_PACKET, }, + /* SIG_TYPE_IPONLY */ { SIG_PROP_FLOW_ACTION_FLOW, }, + /* SIG_TYPE_LIKE_IPONLY */ { SIG_PROP_FLOW_ACTION_FLOW, }, + /* SIG_TYPE_PDONLY */ { SIG_PROP_FLOW_ACTION_FLOW, }, + /* SIG_TYPE_DEONLY */ { SIG_PROP_FLOW_ACTION_PACKET, }, + /* SIG_TYPE_PKT */ { SIG_PROP_FLOW_ACTION_PACKET, }, + /* SIG_TYPE_PKT_STREAM */ { SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL, }, + /* SIG_TYPE_STREAM */ { SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL, }, + /* SIG_TYPE_APPLAYER */ { SIG_PROP_FLOW_ACTION_FLOW, }, + /* SIG_TYPE_APP_TX */ { SIG_PROP_FLOW_ACTION_FLOW, }, +}; +// clang-format on + SCEnumCharMap det_ctx_event_table[] = { #ifdef UNITTESTS { "TEST", DET_CTX_EVENT_TEST }, @@ -1314,6 +1329,15 @@ bool DetectEngineBufferTypeSupportsMpmGetById(const DetectEngineCtx *de_ctx, con return map->mpm; } +bool DetectEngineBufferTypeSupportsFramesGetById(const DetectEngineCtx *de_ctx, const int id) +{ + const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id); + if (map == NULL) + return false; + SCLogDebug("map %p id %d frame? %d", map, id, map->frame); + return map->frame; +} + void DetectBufferTypeRegisterSetupCallback(const char *name, void (*SetupCallback)(const DetectEngineCtx *, Signature *)) { diff --git a/src/detect-engine.h b/src/detect-engine.h index 9c26a60baa97..6fef8261738f 100644 --- a/src/detect-engine.h +++ b/src/detect-engine.h @@ -72,6 +72,7 @@ bool DetectEngineBufferTypeSupportsMpmGetById(const DetectEngineCtx *de_ctx, con bool DetectEngineBufferTypeSupportsPacketGetById(const DetectEngineCtx *de_ctx, const int id); bool DetectEngineBufferTypeSupportsMultiInstanceGetById( const DetectEngineCtx *de_ctx, const int id); +bool DetectEngineBufferTypeSupportsFramesGetById(const DetectEngineCtx *de_ctx, const int id); const char *DetectEngineBufferTypeGetDescriptionById(const DetectEngineCtx *de_ctx, const int id); const DetectBufferType *DetectEngineBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id); int DetectEngineBufferTypeGetByIdTransforms( diff --git a/src/detect-parse.c b/src/detect-parse.c index 8de7fba9a9de..2c7d8f2ec6fa 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -2126,7 +2126,7 @@ static Signature *SigInitHelper(DetectEngineCtx *de_ctx, const char *sigstr, /* check what the type of this sig is */ SignatureSetType(de_ctx, sig); - if (sig->flags & SIG_FLAG_IPONLY) { + if (sig->type == SIG_TYPE_IPONLY) { /* For IPOnly */ if (IPOnlySigParseAddress(de_ctx, sig, parser.src, SIG_DIREC_SRC ^ dir) < 0) goto error; diff --git a/src/detect-pcre.c b/src/detect-pcre.c index 0d3db1b2a77a..5b095bfbbc11 100644 --- a/src/detect-pcre.c +++ b/src/detect-pcre.c @@ -878,7 +878,6 @@ static int DetectPcreSetup (DetectEngineCtx *de_ctx, Signature *s, const char *r if (DetectBufferGetActiveList(de_ctx, s) == -1) goto error; - s->flags |= SIG_FLAG_APPLAYER; sm_list = s->init_data->list; } else { switch (parsed_sm_list) { diff --git a/src/detect-threshold.c b/src/detect-threshold.c index 06d64fb4419b..25bda3556bc4 100644 --- a/src/detect-threshold.c +++ b/src/detect-threshold.c @@ -512,10 +512,7 @@ static int DetectThresholdTestSig1(void) SigGroupBuild(de_ctx); - if (s->flags & SIG_FLAG_IPONLY) { - printf("signature is ip-only: "); - goto end; - } + FAIL_IF(s->type == SIG_TYPE_IPONLY); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); diff --git a/src/detect.h b/src/detect.h index 3a6c3b0e3ae9..49a920b03eb4 100644 --- a/src/detect.h +++ b/src/detect.h @@ -54,6 +54,36 @@ struct SCSigOrderFunc_; struct SCSigSignatureWrapper_; +enum SignatureType { + SIG_TYPE_NOT_SET = 0, + SIG_TYPE_IPONLY, // rule is handled by IPONLY engine + SIG_TYPE_LIKE_IPONLY, // rule is handled by pkt engine, has action effect like ip-only + /** Proto detect only signature. + * Inspected once per direction when protocol detection is done. */ + SIG_TYPE_PDONLY, // rule is handled by PDONLY engine + SIG_TYPE_DEONLY, + SIG_TYPE_PKT, + SIG_TYPE_PKT_STREAM, + SIG_TYPE_STREAM, + + SIG_TYPE_APPLAYER, // app-layer but not tx, e.g. appproto + SIG_TYPE_APP_TX, // rule is handled by TX engine + + SIG_TYPE_MAX, +}; + +enum SignaturePropertyFlowAction { + SIG_PROP_FLOW_ACTION_PACKET, + SIG_PROP_FLOW_ACTION_FLOW, + SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL, +}; + +struct SignatureProperties { + enum SignaturePropertyFlowAction flow_action; +}; + +extern const struct SignatureProperties signature_properties[SIG_TYPE_MAX]; + /* The detection engine groups similar signatures/rules together. Internally a tree of different types of data is created on initialization. This is it's @@ -206,11 +236,7 @@ typedef struct DetectPort_ { #define SIG_FLAG_NOALERT BIT_U32(4) /**< no alert flag is set */ #define SIG_FLAG_DSIZE BIT_U32(5) /**< signature has a dsize setting */ -#define SIG_FLAG_APPLAYER BIT_U32(6) /**< signature applies to app layer instead of packets */ -#define SIG_FLAG_IPONLY BIT_U32(7) /**< ip only signature */ -#define SIG_FLAG_LIKE_IPONLY \ - BIT_U32(8) /**< signature that is almost ip only, but contains negation prevening some iponly \ - optimizations */ +#define SIG_FLAG_APPLAYER BIT_U32(6) /**< signature applies to app layer instead of packets */ // vacancy @@ -236,9 +262,8 @@ typedef struct DetectPort_ { #define SIG_FLAG_PREFILTER BIT_U32(23) /**< sig is part of a prefilter engine */ -/** Proto detect only signature. - * Inspected once per direction when protocol detection is done. */ -#define SIG_FLAG_PDONLY BIT_U32(24) +// vacancy + /** Info for Source and Target identification */ #define SIG_FLAG_SRC_IS_TARGET BIT_U32(25) /** Info for Source and Target identification */ @@ -247,7 +272,7 @@ typedef struct DetectPort_ { #define SIG_FLAG_HAS_TARGET (SIG_FLAG_DEST_IS_TARGET|SIG_FLAG_SRC_IS_TARGET) /* signature init flags */ -#define SIG_FLAG_INIT_DEONLY BIT_U32(0) /**< decode event only signature */ +// available 0 #define SIG_FLAG_INIT_PACKET BIT_U32(1) /**< signature has matches against a packet (as opposed to app layer) */ #define SIG_FLAG_INIT_FLOW BIT_U32(2) /**< signature has a flow setting */ #define SIG_FLAG_INIT_BIDIREC BIT_U32(3) /**< signature has bidirectional operator */ @@ -557,6 +582,7 @@ typedef struct SignatureInitData_ { typedef struct Signature_ { uint32_t flags; /* coccinelle: Signature:flags:SIG_FLAG_ */ + enum SignatureType type; AppProto alproto; diff --git a/src/source-windivert.c b/src/source-windivert.c index a896c5757a7c..f0d956973de5 100644 --- a/src/source-windivert.c +++ b/src/source-windivert.c @@ -130,8 +130,12 @@ void *WinDivertGetQueue(int n) } // not defined in MinGW winerror.h +#ifndef ERROR_INVALID_IMAGE_HASH #define ERROR_INVALID_IMAGE_HASH 577L +#endif +#ifndef ERROR_DATA_NOT_ACCEPTED #define ERROR_DATA_NOT_ACCEPTED 592L +#endif /** * \brief return an error description for Win32 error values commonly returned diff --git a/src/suricata.h b/src/suricata.h index f4ce718f7eb3..82b049f583e2 100644 --- a/src/suricata.h +++ b/src/suricata.h @@ -82,7 +82,7 @@ #define DEFAULT_PID_BASENAME "suricata.pid" #define DEFAULT_PID_FILENAME DEFAULT_PID_DIR DEFAULT_PID_BASENAME -#define DOC_URL "https://suricata.readthedocs.io/en/" +#define DOC_URL "https://docs.suricata.io/en/" const char *GetDocURL(void); /* runtime engine control flags */ diff --git a/suricata.yaml.in b/suricata.yaml.in index 947a68268228..96d3bc4ad714 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -3,7 +3,7 @@ # Suricata configuration file. In addition to the comments describing all # options in this file, full documentation can be found at: -# https://suricata.readthedocs.io/en/latest/configuration/suricata-yaml.html +# https://docs.suricata.io/en/latest/configuration/suricata-yaml.html # This configuration file generated by Suricata @PACKAGE_VERSION@. suricata-version: "@MAJOR_MINOR@" @@ -217,7 +217,7 @@ outputs: - dns: # This configuration uses the new DNS logging format, # the old configuration is still available: - # https://suricata.readthedocs.io/en/latest/output/eve/eve-json-output.html#dns-v1-format + # https://docs.suricata.io/en/latest/output/eve/eve-json-output.html#dns-v1-format # As of Suricata 5.0, version 2 of the eve dns output # format is the default. @@ -539,7 +539,7 @@ outputs: # Lua Output Support - execute lua script to generate alert and event # output. # Documented at: - # https://suricata.readthedocs.io/en/latest/output/lua-output.html + # https://docs.suricata.io/en/latest/output/lua-output.html - lua: enabled: no #scripts-dir: /etc/suricata/lua-output/ @@ -2133,6 +2133,6 @@ reference-config-file: @e_sysconfdir@reference.config # in this configuration file. Files with relative pathnames will be # searched for in the same directory as this configuration file. You may # use absolute pathnames too. -# You can specify more than 2 configuration files, if needed. -#include: include1.yaml -#include: include2.yaml +#include: +# - include1.yaml +# - include2.yaml diff --git a/threshold.config b/threshold.config index bb992b6ee767..670a57a973a6 100644 --- a/threshold.config +++ b/threshold.config @@ -13,11 +13,11 @@ # suppress gen_id , sig_id # suppress gen_id , sig_id , track , ip # -# The options are documented at https://suricata.readthedocs.io/en/latest/configuration/global-thresholds.html +# The options are documented at https://docs.suricata.io/en/latest/configuration/global-thresholds.html # # Please note that thresholding can also be set inside a signature. The interaction between rule based thresholds # and global thresholds is documented here: -# https://suricata.readthedocs.io/en/latest/configuration/global-thresholds.html#global-thresholds-vs-rule-thresholds +# https://docs.suricata.io/en/latest/configuration/global-thresholds.html#global-thresholds-vs-rule-thresholds # Limit to 10 alerts every 10 seconds for each source host #threshold gen_id 0, sig_id 0, type limit, track by_src, count 10, seconds 10