diff --git a/doc/userguide/rules/dhcp-keywords.rst b/doc/userguide/rules/dhcp-keywords.rst index 05675a947e73..14d7365aa271 100644 --- a/doc/userguide/rules/dhcp-keywords.rst +++ b/doc/userguide/rules/dhcp-keywords.rst @@ -6,6 +6,8 @@ dhcp.leasetime DHCP lease time (integer). +dhcp.leasetime uses an :ref:`unsigned 64-bits integer `. + Syntax:: dhcp.leasetime:[op] @@ -25,6 +27,8 @@ dhcp.rebinding_time DHCP rebinding time (integer). +dhcp.rebinding_time uses an :ref:`unsigned 64-bits integer `. + Syntax:: dhcp.rebinding_time:[op] @@ -44,6 +48,8 @@ dhcp.renewal_time DHCP renewal time (integer). +dhcp.renewal_time uses an :ref:`unsigned 64-bits integer `. + Syntax:: dhcp.renewal_time:[op] diff --git a/doc/userguide/rules/file-keywords.rst b/doc/userguide/rules/file-keywords.rst index c708ee746c0d..f9ef85df42dd 100644 --- a/doc/userguide/rules/file-keywords.rst +++ b/doc/userguide/rules/file-keywords.rst @@ -244,6 +244,8 @@ filesize Match on the size of the file as it is being transferred. +filesize uses an :ref:`unsigned 64-bits integer `. + Syntax:: filesize:; diff --git a/doc/userguide/rules/flow-keywords.rst b/doc/userguide/rules/flow-keywords.rst index 6d451ce82aab..a150efda62ac 100644 --- a/doc/userguide/rules/flow-keywords.rst +++ b/doc/userguide/rules/flow-keywords.rst @@ -292,6 +292,8 @@ flow.age Flow age in seconds (integer) This keyword does not wait for the end of the flow, but will be checked at each packet. +flow.age uses an :ref:`unsigned 32-bits integer `. + Syntax:: flow.age: [op] @@ -314,6 +316,8 @@ flow.pkts_toclient Flow number of packets to client (integer) This keyword does not wait for the end of the flow, but will be checked at each packet. +flow.pkts_toclient uses an :ref:`unsigned 32-bits integer `. + Syntax:: flow.pkts_toclient: [op] @@ -334,6 +338,8 @@ flow.pkts_toserver Flow number of packets to server (integer) This keyword does not wait for the end of the flow, but will be checked at each packet. +flow.pkts_toserver uses an :ref:`unsigned 32-bits integer `. + Syntax:: flow.pkts_toserver: [op] @@ -354,6 +360,8 @@ flow.bytes_toclient Flow number of bytes to client (integer) This keyword does not wait for the end of the flow, but will be checked at each packet. +flow.bytes_toclient uses an :ref:`unsigned 64-bits integer `. + Syntax:: flow.bytes_toclient: [op] @@ -374,6 +382,8 @@ flow.bytes_toserver Flow number of bytes to server (integer) This keyword does not wait for the end of the flow, but will be checked at each packet. +flow.bytes_toserver uses an :ref:`unsigned 64-bits integer `. + Syntax:: flow.bytes_toserver: [op] diff --git a/doc/userguide/rules/header-keywords.rst b/doc/userguide/rules/header-keywords.rst index 36d1437647f3..efe4a1e5ac6c 100644 --- a/doc/userguide/rules/header-keywords.rst +++ b/doc/userguide/rules/header-keywords.rst @@ -15,6 +15,8 @@ For example:: ttl:10; +ttl uses an :ref:`unsigned 8-bits integer `. + At the end of the ttl keyword you can enter the value on which you want to match. The Time-to-live value determines the maximal amount of time a packet can be in the Internet-system. If this field is set @@ -431,6 +433,8 @@ tcp.mss Match on the TCP MSS option value. Will not match if the option is not present. +tcp.mss uses an :ref:`unsigned 16-bits integer `. + The format of the keyword:: tcp.mss:-; @@ -506,6 +510,8 @@ messages. The different messages are distinct by different names, but more important by numeric values. For more information see the table with message-types and codes. +itype uses an :ref:`unsigned 8-bits integer `. + The format of the itype keyword:: itype:min<>max; @@ -565,6 +571,8 @@ code of a ICMP message clarifies the message. Together with the ICMP-type it indicates with what kind of problem you are dealing with. A code has a different purpose with every ICMP-type. +icode uses an :ref:`unsigned 8-bits integer `. + The format of the icode keyword:: icode:min<>max; @@ -719,6 +727,8 @@ icmpv6.mtu Match on the ICMPv6 MTU optional value. Will not match if the MTU is not present. +icmpv6.mtu uses an :ref:`unsigned 32-bits integer `. + The format of the keyword:: icmpv6.mtu:-; diff --git a/doc/userguide/rules/http-keywords.rst b/doc/userguide/rules/http-keywords.rst index ba0d7621f339..2a826e358b64 100644 --- a/doc/userguide/rules/http-keywords.rst +++ b/doc/userguide/rules/http-keywords.rst @@ -237,6 +237,8 @@ The ``urilen`` keyword is used to match on the length of the request URI. It is possible to use the ``<`` and ``>`` operators, which indicate respectively *smaller than* and *larger than*. +urilen uses an :ref:`unsigned 64-bits integer `. + The format of ``urilen`` is:: urilen:3; diff --git a/doc/userguide/rules/http2-keywords.rst b/doc/userguide/rules/http2-keywords.rst index 1ad83554c6ef..6fe75a33c06d 100644 --- a/doc/userguide/rules/http2-keywords.rst +++ b/doc/userguide/rules/http2-keywords.rst @@ -31,6 +31,8 @@ http2.priority Match on the value of the HTTP2 priority field present in a PRIORITY or HEADERS frame. +http2.priority uses an :ref:`unsigned 8-bits integer `. + This keyword takes a numeric argument after a colon and supports additional qualifiers, such as: * ``>`` (greater than) @@ -49,6 +51,8 @@ http2.window Match on the value of the HTTP2 value field present in a WINDOWUPDATE frame. +http2.window uses an :ref:`unsigned 32-bits integer `. + This keyword takes a numeric argument after a colon and supports additional qualifiers, such as: * ``>`` (greater than) @@ -68,6 +72,8 @@ Match on the size of the HTTP2 Dynamic Headers Table. More information on the protocol can be found here: ``_ +http2.size_update uses an :ref:`unsigned 64-bits integer `. + This keyword takes a numeric argument after a colon and supports additional qualifiers, such as: * ``>`` (greater than) diff --git a/doc/userguide/rules/ike-keywords.rst b/doc/userguide/rules/ike-keywords.rst index e0d9557bc306..f892034af63e 100644 --- a/doc/userguide/rules/ike-keywords.rst +++ b/doc/userguide/rules/ike-keywords.rst @@ -61,6 +61,8 @@ ike.exchtype Match on the value of the Exchange Type. +ike.exchtype uses an :ref:`unsigned 8-bits integer `. + This keyword takes a numeric argument after a colon and supports additional qualifiers, such as: * ``>`` (greater than) @@ -106,6 +108,8 @@ ike.key_exchange_payload_length Match against the length of the public key exchange payload (e.g. Diffie-Hellman) of the server or client. +ike.key_exchange_payload_length uses an :ref:`unsigned 32-bits integer `. + This keyword takes a numeric argument after a colon and supports additional qualifiers, such as: * ``>`` (greater than) @@ -138,6 +142,8 @@ ike.nonce_payload_length Match against the length of the nonce of the server or client. +ike.nonce_payload_length uses an :ref:`unsigned 32-bits integer `. + This keyword takes a numeric argument after a colon and supports additional qualifiers, such as: * ``>`` (greater than) diff --git a/doc/userguide/rules/index.rst b/doc/userguide/rules/index.rst index e174c6787bc5..2bd2a2d0ea48 100644 --- a/doc/userguide/rules/index.rst +++ b/doc/userguide/rules/index.rst @@ -7,6 +7,7 @@ Suricata Rules meta header-keywords payload-keywords + integer-keywords transforms prefilter-keywords flow-keywords @@ -35,6 +36,7 @@ Suricata Rules quic-keywords nfs-keywords smtp-keywords + websocket-keywords app-layer xbits thresholding diff --git a/doc/userguide/rules/integer-keywords.rst b/doc/userguide/rules/integer-keywords.rst new file mode 100644 index 000000000000..d3efc32c7761 --- /dev/null +++ b/doc/userguide/rules/integer-keywords.rst @@ -0,0 +1,74 @@ +.. _rules-integer-keywords: + +Integer Keywords +================ + +Many keywords will match on an integer value on the network traffic. +These are unsigned integers that can be 8, 16, 32 or 64 bits. + +Simple example:: + + bsize:integer value; + +The integer value can be written as base-10 like ``100`` or as +an hexadecimal value like ``0x64``. + +The most direct exemple is to match for equality, but there are +different modes. + +Comparison modes +---------------- + +Integers can be matched for +* Equality +* Inequality +* Greater than +* Less than +* Range +* Negated range +* Bitmask +* Negated Bitmask + +Comparisons are strict by default. +That means a range between 1 and 4 will match 2 and 3, but neither 1 nor 4. + +Examples:: + + bsize:integer value; # equality + bsize:=integer value; # equality + bsize:!integer value; # inequality + bsize:!=integer value; # inequality + bsize:>integer value; # greater than + bsize:>=integer value; # greater than or equal + bsize:`. + The format of the keyword:: mqtt.protocol_version:-; diff --git a/doc/userguide/rules/payload-keywords.rst b/doc/userguide/rules/payload-keywords.rst index 9a609a217f04..ae9c2b231344 100644 --- a/doc/userguide/rules/payload-keywords.rst +++ b/doc/userguide/rules/payload-keywords.rst @@ -280,6 +280,8 @@ bsize With the ``bsize`` keyword, you can match on the length of the buffer. This adds precision to the content match, previously this could have been done with ``isdataat``. +bsize uses an :ref:`unsigned 64-bits integer `. + An optional operator can be specified; if no operator is present, the operator will default to '='. When a relational operator is used, e.g., '<', '>' or '<>' (range), the bsize value will be compared using the relational operator. Ranges are inclusive. @@ -336,6 +338,8 @@ This may be convenient in detecting buffer overflows. dsize cannot be used when using app/streamlayer protocol keywords (i.e. http.uri) +dsize uses an :ref:`unsigned 16-bits integer `. + Format:: dsize:[<>!]number; || dsize:min<>max; diff --git a/doc/userguide/rules/rfb-keywords.rst b/doc/userguide/rules/rfb-keywords.rst index 628b3d85c563..756fc07432ae 100644 --- a/doc/userguide/rules/rfb-keywords.rst +++ b/doc/userguide/rules/rfb-keywords.rst @@ -36,6 +36,8 @@ rfb.sectype Match on the value of the RFB security type field, e.g. ``2`` for VNC challenge-response authentication, ``0`` for no authentication, and ``30`` for Apple's custom Remote Desktop authentication. +rfb.sectype uses an :ref:`unsigned 32-bits integer `. + This keyword takes a numeric argument after a colon and supports additional qualifiers, such as: * ``>`` (greater than) diff --git a/doc/userguide/rules/tls-keywords.rst b/doc/userguide/rules/tls-keywords.rst index dc28c97cd583..2aaa880ce297 100644 --- a/doc/userguide/rules/tls-keywords.rst +++ b/doc/userguide/rules/tls-keywords.rst @@ -284,6 +284,8 @@ tls.cert_chain_len Matches on the TLS certificate chain length. +tls.cert_chain_len uses an :ref:`unsigned 32-bits integer `. + tls.cert_chain_len supports `<, >, <>, !` and using an exact value. Example:: diff --git a/doc/userguide/rules/websocket-keywords.rst b/doc/userguide/rules/websocket-keywords.rst new file mode 100644 index 000000000000..cb03e40b5a0f --- /dev/null +++ b/doc/userguide/rules/websocket-keywords.rst @@ -0,0 +1,63 @@ +WebSocket Keywords +================== + +websocket.payload +----------------- + +A sticky buffer on the unmasked payload, +limited by suricata.yaml config value ``websocket.max-payload-size``. + +Examples:: + + websocket.payload; pcre:"/^123[0-9]*/"; + websocket.payload content:"swordfish"; + +``websocket.payload`` is a 'sticky buffer' and can be used as ``fast_pattern``. + +websocket.flags +--------------- + +Matches on the websocket flags. +It uses a 8-bit unsigned integer as value. +Only the four upper bits are used. + +The value can also be a list of strings (comma-separated), +where each string is the name of a specific bit like `fin` and `comp`, +and can be prefixed by `!` for negation. + +websocket.flags uses an :ref:`unsigned 8-bits integer ` + +Examples:: + + websocket.flags:128; + websocket.flags:&0x40=0x40; + websocket.flags:fin,!comp; + +websocket.mask +-------------- + +Matches on the websocket mask if any. +It uses a 32-bit unsigned integer as value (big-endian). + +websocket.mask uses an :ref:`unsigned 32-bits integer ` + +Examples:: + + websocket.mask:123456; + websocket.mask:>0; + +websocket.opcode +---------------- + +Matches on the websocket opcode. +It uses a 8-bit unsigned integer as value. +Only 16 values are relevant. +It can also be specified by text from the enumeration + +websocket.opcode uses an :ref:`unsigned 32-bits integer ` + +Examples:: + + websocket.opcode:1; + websocket.opcode:>8; + websocket.opcode:ping; diff --git a/etc/schema.json b/etc/schema.json index 0756acd00800..5049e6daf7d4 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -3833,6 +3833,9 @@ }, "tls": { "$ref": "#/$defs/stats_applayer_error" + }, + "websocket": { + "$ref": "#/$defs/stats_applayer_error" } }, "additionalProperties": false @@ -3950,6 +3953,9 @@ }, "tls": { "type": "integer" + }, + "websocket": { + "type": "integer" } }, "additionalProperties": false @@ -4061,6 +4067,9 @@ }, "tls": { "type": "integer" + }, + "websocket": { + "type": "integer" } }, "additionalProperties": false @@ -5501,6 +5510,27 @@ } }, "additionalProperties": false + }, + "websocket": { + "type": "object", + "properties": { + "fin": { + "type": "boolean" + }, + "mask": { + "type": "integer" + }, + "opcode": { + "type": "string" + }, + "payload_base64": { + "type": "string" + }, + "payload_printable": { + "type": "string" + } + }, + "additionalProperties": false } }, "$defs": { diff --git a/rules/Makefile.am b/rules/Makefile.am index d0ea6eda622f..cba0aa370af3 100644 --- a/rules/Makefile.am +++ b/rules/Makefile.am @@ -22,4 +22,5 @@ smb-events.rules \ smtp-events.rules \ ssh-events.rules \ stream-events.rules \ -tls-events.rules +tls-events.rules \ +websocket-events.rules diff --git a/rules/websocket-events.rules b/rules/websocket-events.rules new file mode 100644 index 000000000000..3acc21132e0a --- /dev/null +++ b/rules/websocket-events.rules @@ -0,0 +1,8 @@ +# WebSocket app-layer event rules. +# +# These SIDs fall in the 2235000+ range. See: +# http://doc.emergingthreats.net/bin/view/Main/SidAllocation and +# https://redmine.openinfosecfoundation.org/projects/suricata/wiki/AppLayer + +alert websocket any any -> any any (msg:"SURICATA Websocket skipped end of payload"; app-layer-event:websocket.skip_end_of_payload; classtype:protocol-command-decode; sid:2235000; rev:1;) +alert websocket any any -> any any (msg:"SURICATA Websocket reassembly limit reached"; app-layer-event:websocket.reassembly_limit_reached; classtype:protocol-command-decode; sid:2235001; rev:1;) diff --git a/rust/derive/src/lib.rs b/rust/derive/src/lib.rs index a2b7a6ad0442..18d7e3faf3ae 100644 --- a/rust/derive/src/lib.rs +++ b/rust/derive/src/lib.rs @@ -23,6 +23,7 @@ use proc_macro::TokenStream; mod applayerevent; mod applayerframetype; +mod stringenum; /// The `AppLayerEvent` derive macro generates a `AppLayerEvent` trait /// implementation for enums that define AppLayerEvents. @@ -50,3 +51,8 @@ pub fn derive_app_layer_event(input: TokenStream) -> TokenStream { pub fn derive_app_layer_frame_type(input: TokenStream) -> TokenStream { applayerframetype::derive_app_layer_frame_type(input) } + +#[proc_macro_derive(EnumStringU8, attributes(name))] +pub fn derive_enum_string_u8(input: TokenStream) -> TokenStream { + stringenum::derive_enum_string::(input, "u8") +} diff --git a/rust/derive/src/stringenum.rs b/rust/derive/src/stringenum.rs new file mode 100644 index 000000000000..0f0905e0116f --- /dev/null +++ b/rust/derive/src/stringenum.rs @@ -0,0 +1,96 @@ +/* Copyright (C) 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. + */ + +extern crate proc_macro; +use super::applayerevent::transform_name; +use proc_macro::TokenStream; +use quote::quote; +use syn::{self, parse_macro_input, DeriveInput}; +use std::str::FromStr; + +pub fn derive_enum_string(input: TokenStream, ustr: &str) -> TokenStream where ::Err: std::fmt::Display { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + let mut values = Vec::new(); + let mut names = Vec::new(); + let mut fields = Vec::new(); + + if let syn::Data::Enum(ref data) = input.data { + for v in (&data.variants).into_iter() { + if let Some((_, val)) = &v.discriminant { + let fname = transform_name(&v.ident.to_string()); + names.push(fname); + fields.push(v.ident.clone()); + if let syn::Expr::Lit(l) = val { + if let syn::Lit::Int(li) = &l.lit { + if let Ok(value) = li.base10_parse::() { + values.push(value); + } else { + panic!("EnumString requires explicit {}", ustr); + } + } else { + panic!("EnumString requires explicit literal integer"); + } + } else { + panic!("EnumString requires explicit literal"); + } + } else { + panic!("EnumString requires explicit values"); + } + } + } else { + panic!("EnumString can only be derived for enums"); + } + + let is_suricata = std::env::var("CARGO_PKG_NAME").map(|var| var == "suricata").unwrap_or(false); + let crate_id = if is_suricata { + syn::Ident::new("crate", proc_macro2::Span::call_site()) + } else { + syn::Ident::new("suricata", proc_macro2::Span::call_site()) + }; + + let utype_str = syn::Ident::new(ustr, proc_macro2::Span::call_site()); + + let expanded = quote! { + impl #crate_id::detect::Enum<#utype_str> for #name { + fn from_u(v: #utype_str) -> Option { + match v { + #( #values => Some(#name::#fields) ,)* + _ => None, + } + } + fn into_u(self) -> #utype_str { + match self { + #( #name::#fields => #values ,)* + } + } + fn to_str(&self) -> &'static str { + match *self { + #( #name::#fields => #names ,)* + } + } + fn from_str(s: &str) -> Option { + match s { + #( #names => Some(#name::#fields) ,)* + _ => None + } + } + } + }; + + proc_macro::TokenStream::from(expanded) +} diff --git a/rust/src/detect/mod.rs b/rust/src/detect/mod.rs index d33c9ae7fabf..5f048a8e5722 100644 --- a/rust/src/detect/mod.rs +++ b/rust/src/detect/mod.rs @@ -25,3 +25,46 @@ pub mod stream_size; pub mod uint; pub mod uri; pub mod requires; + +/// Enum trait that will be implemented on enums that +/// derive StringEnum. +pub trait Enum { + /// Return the enum variant of the given numeric value. + fn from_u(v: T) -> Option where Self: Sized; + + /// Convert the enum variant to the numeric value. + fn into_u(self) -> T; + + /// Return the string for logging the enum value. + fn to_str(&self) -> &'static str; + + /// Get an enum variant from parsing a string. + fn from_str(s: &str) -> Option where Self: Sized; +} + +#[cfg(test)] +mod test { + use super::*; + use suricata_derive::EnumStringU8; + + #[derive(Clone, Debug, PartialEq, EnumStringU8)] + #[repr(u8)] + pub enum TestEnum { + Zero = 0, + BestValueEver = 42, + } + + #[test] + fn test_enum_string_u8() { + assert_eq!(TestEnum::from_u(0), Some(TestEnum::Zero)); + assert_eq!(TestEnum::from_u(1), None); + assert_eq!(TestEnum::from_u(42), Some(TestEnum::BestValueEver)); + assert_eq!(TestEnum::Zero.into_u(), 0); + assert_eq!(TestEnum::BestValueEver.into_u(), 42); + assert_eq!(TestEnum::Zero.to_str(), "zero"); + assert_eq!(TestEnum::BestValueEver.to_str(), "best_value_ever"); + assert_eq!(TestEnum::from_str("zero"), Some(TestEnum::Zero)); + assert_eq!(TestEnum::from_str("nope"), None); + assert_eq!(TestEnum::from_str("best_value_ever"), Some(TestEnum::BestValueEver)); + } +} diff --git a/rust/src/detect/uint.rs b/rust/src/detect/uint.rs index 8c758e3a5d69..f22869d86117 100644 --- a/rust/src/detect/uint.rs +++ b/rust/src/detect/uint.rs @@ -17,12 +17,14 @@ use nom7::branch::alt; use nom7::bytes::complete::{is_a, tag, tag_no_case, take_while}; -use nom7::character::complete::digit1; +use nom7::character::complete::{char, digit1, hex_digit1}; use nom7::combinator::{all_consuming, map_opt, opt, value, verify}; use nom7::error::{make_error, ErrorKind}; use nom7::Err; use nom7::IResult; +use super::Enum; + use std::ffi::CStr; #[derive(PartialEq, Eq, Clone, Debug)] @@ -35,6 +37,9 @@ pub enum DetectUintMode { DetectUintModeGte, DetectUintModeRange, DetectUintModeNe, + DetectUintModeNegRg, + DetectUintModeBitmask, + DetectUintModeNegBitmask, } #[derive(Debug)] @@ -45,6 +50,29 @@ pub struct DetectUintData { pub mode: DetectUintMode, } +/// Parses a string for detection with integers, using enumeration strings +/// +/// Needs to specify T1 the integer type (like u8) +/// And the Enumeration for the stringer. +/// Will try to parse numerical value first, as any integer detection keyword +/// And if this fails, will resort to using the enumeration strings. +/// +/// Returns Some DetectUintData on success, None on failure +pub fn detect_parse_uint_enum>(s: &str) -> Option> { + if let Ok((_, ctx)) = detect_parse_uint::(s) { + return Some(ctx); + } + if let Some(enum_val) = T2::from_str(s) { + let ctx = DetectUintData:: { + arg1: enum_val.into_u(), + arg2: T1::min_value(), + mode: DetectUintMode::DetectUintModeEqual, + }; + return Some(ctx); + } + return None; +} + pub trait DetectIntType: std::str::FromStr + std::cmp::PartialOrd @@ -73,8 +101,25 @@ pub fn detect_parse_uint_unit(i: &str) -> IResult<&str, u64> { return Ok((i, unit)); } +pub fn detect_parse_uint_value_hex(i: &str) -> IResult<&str, T> { + let (i, _) = tag("0x")(i)?; + let (i, arg1s) = hex_digit1(i)?; + match T::from_str_radix(arg1s, 16) { + Ok(arg1) => Ok((i, arg1)), + _ => Err(Err::Error(make_error(i, ErrorKind::Verify))), + } +} + +pub fn detect_parse_uint_value(i: &str) -> IResult<&str, T> { + let (i, arg1) = alt(( + detect_parse_uint_value_hex, + map_opt(digit1, |s: &str| s.parse::().ok()), + ))(i)?; + Ok((i, arg1)) +} + pub fn detect_parse_uint_with_unit(i: &str) -> IResult<&str, T> { - let (i, arg1) = map_opt(digit1, |s: &str| s.parse::().ok())(i)?; + let (i, arg1) = detect_parse_uint_value::(i)?; let (i, unit) = opt(detect_parse_uint_unit)(i)?; if arg1 >= T::one() { if let Some(u) = unit { @@ -107,19 +152,52 @@ pub fn detect_parse_uint_start_equal( pub fn detect_parse_uint_start_interval( i: &str, ) -> IResult<&str, DetectUintData> { - let (i, arg1) = map_opt(digit1, |s: &str| s.parse::().ok())(i)?; + let (i, neg) = opt(char('!'))(i)?; + let (i, arg1) = detect_parse_uint_value(i)?; let (i, _) = opt(is_a(" "))(i)?; let (i, _) = alt((tag("-"), tag("<>")))(i)?; let (i, _) = opt(is_a(" "))(i)?; - let (i, arg2) = verify(map_opt(digit1, |s: &str| s.parse::().ok()), |x| { + let (i, arg2) = verify(detect_parse_uint_value, |x| { x > &arg1 && *x - arg1 > T::one() })(i)?; + let mode = if neg.is_some() { + DetectUintMode::DetectUintModeNegRg + } else { + DetectUintMode::DetectUintModeRange + }; + Ok(( + i, + DetectUintData { + arg1, + arg2, + mode, + }, + )) +} + +pub fn detect_parse_uint_bitmask( + i: &str, +) -> IResult<&str, DetectUintData> { + let (i, _) = opt(is_a(" "))(i)?; + let (i, _) = tag("&")(i)?; + let (i, _) = opt(is_a(" "))(i)?; + let (i, arg1) = detect_parse_uint_value(i)?; + let (i, _) = opt(is_a(" "))(i)?; + let (i, neg) = opt(tag("!"))(i)?; + let (i, _) = tag("=")(i)?; + let (i, _) = opt(is_a(" "))(i)?; + let (i, arg2) = detect_parse_uint_value(i)?; + let mode = if neg.is_none() { + DetectUintMode::DetectUintModeBitmask + } else { + DetectUintMode::DetectUintModeNegBitmask + }; Ok(( i, DetectUintData { arg1, arg2, - mode: DetectUintMode::DetectUintModeRange, + mode, }, )) } @@ -127,21 +205,27 @@ pub fn detect_parse_uint_start_interval( fn detect_parse_uint_start_interval_inclusive( i: &str, ) -> IResult<&str, DetectUintData> { - let (i, arg1) = verify(map_opt(digit1, |s: &str| s.parse::().ok()), |x| { + let (i, neg) = opt(char('!'))(i)?; + let (i, arg1) = verify(detect_parse_uint_value::, |x| { *x > T::min_value() })(i)?; let (i, _) = opt(is_a(" "))(i)?; let (i, _) = alt((tag("-"), tag("<>")))(i)?; let (i, _) = opt(is_a(" "))(i)?; - let (i, arg2) = verify(map_opt(digit1, |s: &str| s.parse::().ok()), |x| { + let (i, arg2) = verify(detect_parse_uint_value::, |x| { *x > arg1 && *x < T::max_value() })(i)?; + let mode = if neg.is_some() { + DetectUintMode::DetectUintModeNegRg + } else { + DetectUintMode::DetectUintModeRange + }; Ok(( i, DetectUintData { arg1: arg1 - T::one(), arg2: arg2 + T::one(), - mode: DetectUintMode::DetectUintModeRange, + mode, }, )) } @@ -162,7 +246,7 @@ pub fn detect_parse_uint_mode(i: &str) -> IResult<&str, DetectUintMode> { fn detect_parse_uint_start_symbol(i: &str) -> IResult<&str, DetectUintData> { let (i, mode) = detect_parse_uint_mode(i)?; let (i, _) = opt(is_a(" "))(i)?; - let (i, arg1) = map_opt(digit1, |s: &str| s.parse::().ok())(i)?; + let (i, arg1) = detect_parse_uint_value(i)?; match mode { DetectUintMode::DetectUintModeNe => {} @@ -238,6 +322,21 @@ pub fn detect_match_uint(x: &DetectUintData, val: T) -> boo return true; } } + DetectUintMode::DetectUintModeNegRg => { + if val <= x.arg1 || val >= x.arg2 { + return true; + } + } + DetectUintMode::DetectUintModeBitmask => { + if val & x.arg1 == x.arg2 { + return true; + } + } + DetectUintMode::DetectUintModeNegBitmask => { + if val & x.arg1 != x.arg2 { + return true; + } + } } return false; } @@ -245,6 +344,7 @@ pub fn detect_match_uint(x: &DetectUintData, val: T) -> boo pub fn detect_parse_uint_notending(i: &str) -> IResult<&str, DetectUintData> { let (i, _) = opt(is_a(" "))(i)?; let (i, uint) = alt(( + detect_parse_uint_bitmask, detect_parse_uint_start_interval, detect_parse_uint_start_equal, detect_parse_uint_start_symbol, @@ -407,6 +507,64 @@ pub unsafe extern "C" fn rs_detect_u16_free(ctx: &mut DetectUintData) { mod tests { use super::*; + use suricata_derive::EnumStringU8; + + #[derive(Clone, Debug, PartialEq, EnumStringU8)] + #[repr(u8)] + pub enum TestEnum { + Zero = 0, + BestValueEver = 42, + } + + #[test] + fn test_detect_parse_uint_enum() { + let ctx = detect_parse_uint_enum::("best_value_ever").unwrap(); + assert_eq!(ctx.arg1, 42); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeEqual); + + let ctx = detect_parse_uint_enum::(">1").unwrap(); + assert_eq!(ctx.arg1, 1); + assert_eq!(ctx.mode, DetectUintMode::DetectUintModeGt); + } + + #[test] + fn test_parse_uint_bitmask() { + let (_, val) = detect_parse_uint::("&0x40!=0").unwrap(); + assert_eq!(val.arg1, 0x40); + assert_eq!(val.arg2, 0); + assert_eq!(val.mode, DetectUintMode::DetectUintModeNegBitmask); + assert!(!detect_match_uint(&val, 0xBF)); + assert!(detect_match_uint(&val, 0x40)); + let (_, val) = detect_parse_uint::("&0xc0=0x80").unwrap(); + assert_eq!(val.arg1, 0xc0); + assert_eq!(val.arg2, 0x80); + assert_eq!(val.mode, DetectUintMode::DetectUintModeBitmask); + assert!(detect_match_uint(&val, 0x80)); + assert!(!detect_match_uint(&val, 0x40)); + assert!(!detect_match_uint(&val, 0xc0)); + } + #[test] + fn test_parse_uint_hex() { + let (_, val) = detect_parse_uint::("0x100").unwrap(); + assert_eq!(val.arg1, 0x100); + let (_, val) = detect_parse_uint::("0xFF").unwrap(); + assert_eq!(val.arg1, 255); + let (_, val) = detect_parse_uint::("0xff").unwrap(); + assert_eq!(val.arg1, 255); + } + + #[test] + fn test_parse_uint_negated_range() { + let (_, val) = detect_parse_uint::("!1-6").unwrap(); + assert_eq!(val.arg1, 1); + assert_eq!(val.arg2, 6); + assert_eq!(val.mode, DetectUintMode::DetectUintModeNegRg); + assert!(detect_match_uint(&val, 1)); + assert!(!detect_match_uint(&val, 2)); + assert!(!detect_match_uint(&val, 5)); + assert!(detect_match_uint(&val, 6)); + } + #[test] fn test_parse_uint_unit() { let (_, val) = detect_parse_uint::(" 2kb").unwrap(); diff --git a/rust/src/http2/parser.rs b/rust/src/http2/parser.rs index 64799460aa9a..39df368a8416 100644 --- a/rust/src/http2/parser.rs +++ b/rust/src/http2/parser.rs @@ -695,6 +695,8 @@ pub enum HTTP2SettingsId { InitialWindowSize = 4, MaxFrameSize = 5, MaxHeaderListSize = 6, + EnableConnectProtocol = 8, // rfc8441 + NoRfc7540Priorities = 9, // rfc9218 } impl fmt::Display for HTTP2SettingsId { @@ -716,6 +718,8 @@ impl std::str::FromStr for HTTP2SettingsId { "SETTINGS_INITIAL_WINDOW_SIZE" => Ok(HTTP2SettingsId::InitialWindowSize), "SETTINGS_MAX_FRAME_SIZE" => Ok(HTTP2SettingsId::MaxFrameSize), "SETTINGS_MAX_HEADER_LIST_SIZE" => Ok(HTTP2SettingsId::MaxHeaderListSize), + "SETTINGS_ENABLE_CONNECT_PROTOCOL" => Ok(HTTP2SettingsId::EnableConnectProtocol), + "SETTINGS_NO_RFC7540_PRIORITIES" => Ok(HTTP2SettingsId::NoRfc7540Priorities), _ => Err(format!("'{}' is not a valid value for HTTP2SettingsId", s)), } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 15e21c4057d1..c49fd1894dbb 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -106,6 +106,7 @@ pub mod rfb; pub mod mqtt; pub mod pgsql; pub mod telnet; +pub mod websocket; pub mod applayertemplate; pub mod rdp; pub mod x509; diff --git a/rust/src/websocket/detect.rs b/rust/src/websocket/detect.rs new file mode 100644 index 000000000000..6088bcfa2024 --- /dev/null +++ b/rust/src/websocket/detect.rs @@ -0,0 +1,133 @@ +/* Copyright (C) 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. + */ + +use super::websocket::WebSocketTransaction; +use crate::detect::uint::{detect_parse_uint, detect_parse_uint_enum, DetectUintData, DetectUintMode}; +use crate::websocket::parser::WebSocketOpcode; + +use nom7::branch::alt; +use nom7::bytes::complete::{is_a, tag}; +use nom7::combinator::{opt, value}; +use nom7::multi::many1; +use nom7::IResult; + +use std::ffi::CStr; + +#[no_mangle] +pub unsafe extern "C" fn SCWebSocketGetOpcode(tx: &mut WebSocketTransaction) -> u8 { + return tx.pdu.opcode; +} + +#[no_mangle] +pub unsafe extern "C" fn SCWebSocketGetFlags(tx: &mut WebSocketTransaction) -> u8 { + return tx.pdu.flags; +} + +#[no_mangle] +pub unsafe extern "C" fn SCWebSocketGetPayload( + tx: &WebSocketTransaction, buffer: *mut *const u8, buffer_len: *mut u32, +) -> bool { + *buffer = tx.pdu.payload.as_ptr(); + *buffer_len = tx.pdu.payload.len() as u32; + return true; +} + +#[no_mangle] +pub unsafe extern "C" fn SCWebSocketGetMask( + tx: &mut WebSocketTransaction, value: *mut u32, +) -> bool { + if let Some(xorkey) = tx.pdu.mask { + *value = xorkey; + return true; + } + return false; +} + +#[no_mangle] +pub unsafe extern "C" fn SCWebSocketParseOpcode( + 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() { + if let Some(ctx) = detect_parse_uint_enum::(s) { + let boxed = Box::new(ctx); + return Box::into_raw(boxed) as *mut _; + } + } + return std::ptr::null_mut(); +} + +struct WebSocketFlag { + neg: bool, + value: u8, +} + +fn parse_flag_list_item(s: &str) -> IResult<&str, WebSocketFlag> { + let (s, _) = opt(is_a(" "))(s)?; + let (s, neg) = opt(tag("!"))(s)?; + let neg = neg.is_some(); + let (s, value) = alt((value(0x80, tag("fin")), value(0x40, tag("comp"))))(s)?; + let (s, _) = opt(is_a(" ,"))(s)?; + Ok((s, WebSocketFlag { neg, value })) +} + +fn parse_flag_list(s: &str) -> IResult<&str, Vec> { + return many1(parse_flag_list_item)(s); +} + +fn parse_flags(s: &str) -> Option> { + // try first numerical value + if let Ok((_, ctx)) = detect_parse_uint::(s) { + return Some(ctx); + } + // otherwise, try strings for bitmask + if let Ok((_, l)) = parse_flag_list(s) { + let mut arg1 = 0; + let mut arg2 = 0; + for elem in l.iter() { + if elem.value & arg1 != 0 { + SCLogWarning!("Repeated bitflag for websocket.flags"); + return None; + } + arg1 |= elem.value; + if !elem.neg { + arg2 |= elem.value; + } + } + let ctx = DetectUintData:: { + arg1, + arg2, + mode: DetectUintMode::DetectUintModeBitmask, + }; + return Some(ctx); + } + return None; +} + +#[no_mangle] +pub unsafe extern "C" fn SCWebSocketParseFlags( + 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() { + if let Some(ctx) = parse_flags(s) { + let boxed = Box::new(ctx); + return Box::into_raw(boxed) as *mut _; + } + } + return std::ptr::null_mut(); +} diff --git a/rust/src/websocket/logger.rs b/rust/src/websocket/logger.rs new file mode 100644 index 000000000000..b6abb54a805a --- /dev/null +++ b/rust/src/websocket/logger.rs @@ -0,0 +1,59 @@ +/* Copyright (C) 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. + */ + +use super::parser::WebSocketOpcode; +use super::websocket::WebSocketTransaction; +use crate::detect::Enum; +use crate::jsonbuilder::{JsonBuilder, JsonError}; +use std; + +fn log_websocket(tx: &WebSocketTransaction, js: &mut JsonBuilder, pp: bool, pb64: bool) -> Result<(), JsonError> { + js.open_object("websocket")?; + js.set_bool("fin", tx.pdu.fin)?; + if let Some(xorkey) = tx.pdu.mask { + js.set_uint("mask", xorkey.into())?; + } + if let Some(opcode) = WebSocketOpcode::from_u(tx.pdu.opcode) { + js.set_string("opcode", opcode.to_str())?; + } else { + js.set_string("opcode", &format!("unknown-{}", tx.pdu.opcode))?; + } + if pp { + js.set_string("payload_printable", &String::from_utf8_lossy(&tx.pdu.payload))?; + } + if pb64 { + js.set_base64("payload_base64", &tx.pdu.payload)?; + } + js.close()?; + Ok(()) +} + +#[no_mangle] +pub unsafe extern "C" fn rs_websocket_logger_log( + tx: *mut std::os::raw::c_void, js: &mut JsonBuilder, +) -> bool { + let tx = cast_pointer!(tx, WebSocketTransaction); + log_websocket(tx, js, false, false).is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn SCWebSocketLogDetails( + tx: &WebSocketTransaction, js: &mut JsonBuilder, + pp: bool, pb64: bool, +) -> bool { + log_websocket(tx, js, pp, pb64).is_ok() +} diff --git a/src/output-json-modbus.h b/rust/src/websocket/mod.rs similarity index 76% rename from src/output-json-modbus.h rename to rust/src/websocket/mod.rs index 2b07e4eb2d5c..c57660f2a44b 100644 --- a/src/output-json-modbus.h +++ b/rust/src/websocket/mod.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Open Information Security Foundation +/* Copyright (C) 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 @@ -15,9 +15,9 @@ * 02110-1301, USA. */ -#ifndef __OUTPUT_JSON_MODBUS_H__ -#define __OUTPUT_JSON_MODBUS_H__ +//! Application layer websocket parser and logger module. -void JsonModbusLogRegister(void); - -#endif /* __OUTPUT_JSON_MODBUS_H__ */ +pub mod detect; +pub mod logger; +mod parser; +pub mod websocket; diff --git a/rust/src/websocket/parser.rs b/rust/src/websocket/parser.rs new file mode 100644 index 000000000000..dac2adde1fca --- /dev/null +++ b/rust/src/websocket/parser.rs @@ -0,0 +1,94 @@ +/* Copyright (C) 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. + */ + +use nom7::bytes::streaming::take; +use nom7::combinator::cond; +use nom7::number::streaming::{be_u16, be_u32, be_u64, be_u8}; +use nom7::IResult; +use suricata_derive::EnumStringU8; + +#[derive(Clone, Debug, Default, EnumStringU8)] +#[repr(u8)] +pub enum WebSocketOpcode { + #[default] + Continuation = 0, + Text = 1, + Binary = 2, + Ping = 8, + Pong = 9, +} + +#[derive(Clone, Debug, Default)] +pub struct WebSocketPdu { + pub flags: u8, + pub fin: bool, + pub compress: bool, + pub opcode: u8, + pub mask: Option, + pub payload: Vec, + pub to_skip: u64, +} + +// cf rfc6455#section-5.2 +pub fn parse_message(i: &[u8], max_pl_size: u64) -> IResult<&[u8], WebSocketPdu> { + let (i, flags_op) = be_u8(i)?; + let fin = (flags_op & 0x80) != 0; + let compress = (flags_op & 0x40) != 0; + let flags = flags_op & 0xF0; + let opcode = flags_op & 0xF; + let (i, mask_plen) = be_u8(i)?; + let mask_flag = (mask_plen & 0x80) != 0; + let (i, payload_len) = match mask_plen & 0x7F { + 126 => { + let (i, val) = be_u16(i)?; + Ok((i, val.into())) + } + 127 => be_u64(i), + _ => Ok((i, (mask_plen & 0x7F).into())), + }?; + let (i, xormask) = cond(mask_flag, take(4usize))(i)?; + let mask = if mask_flag { + let (_, m) = be_u32(xormask.unwrap())?; + Some(m) + } else { + None + }; + let (to_skip, payload_len) = if payload_len < max_pl_size { + (0, payload_len) + } else { + (payload_len - max_pl_size, max_pl_size) + }; + let (i, payload_raw) = take(payload_len)(i)?; + let mut payload = payload_raw.to_vec(); + if let Some(xorkey) = xormask { + for i in 0..payload.len() { + payload[i] ^= xorkey[i % 4]; + } + } + Ok(( + i, + WebSocketPdu { + flags, + fin, + compress, + opcode, + mask, + payload, + to_skip, + }, + )) +} diff --git a/rust/src/websocket/websocket.rs b/rust/src/websocket/websocket.rs new file mode 100644 index 000000000000..4722c953ad4b --- /dev/null +++ b/rust/src/websocket/websocket.rs @@ -0,0 +1,382 @@ +/* Copyright (C) 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. + */ + +use super::parser; +use crate::applayer::{self, *}; +use crate::conf::conf_get; +use crate::core::{AppProto, Direction, Flow, ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_TCP}; +use crate::frames::Frame; + +use nom7 as nom; +use nom7::Needed; + +use flate2::read::DeflateDecoder; + +use std; +use std::collections::VecDeque; +use std::ffi::CString; +use std::io::Read; +use std::os::raw::{c_char, c_int, c_void}; + +static mut ALPROTO_WEBSOCKET: AppProto = ALPROTO_UNKNOWN; + +static mut WEBSOCKET_MAX_PAYLOAD_SIZE: u64 = 0xFFFF; + +// app-layer-frame-documentation tag start: FrameType enum +#[derive(AppLayerFrameType)] +pub enum WebSocketFrameType { + Header, + Pdu, +} + +#[derive(AppLayerEvent)] +pub enum WebSocketEvent { + SkipEndOfPayload, + ReassemblyLimitReached, +} + +#[derive(Default)] +pub struct WebSocketTransaction { + tx_id: u64, + pub pdu: parser::WebSocketPdu, + tx_data: AppLayerTxData, +} + +impl WebSocketTransaction { + pub fn new(direction: Direction) -> WebSocketTransaction { + Self { + tx_data: AppLayerTxData::for_direction(direction), + ..Default::default() + } + } +} + +impl Transaction for WebSocketTransaction { + fn id(&self) -> u64 { + self.tx_id + } +} + +#[derive(Default)] +struct WebSocketReassemblyBuffer { + data: Vec, + compress: bool, +} + +#[derive(Default)] +pub struct WebSocketState { + state_data: AppLayerStateData, + tx_id: u64, + transactions: VecDeque, + + c2s_buf: WebSocketReassemblyBuffer, + s2c_buf: WebSocketReassemblyBuffer, + + to_skip_tc: u64, + to_skip_ts: u64, +} + +impl State for WebSocketState { + fn get_transaction_count(&self) -> usize { + self.transactions.len() + } + + fn get_transaction_by_index(&self, index: usize) -> Option<&WebSocketTransaction> { + self.transactions.get(index) + } +} + +impl WebSocketState { + pub fn new() -> Self { + Default::default() + } + + // Free a transaction by ID. + fn free_tx(&mut self, tx_id: u64) { + let len = self.transactions.len(); + let mut found = false; + let mut index = 0; + for i in 0..len { + let tx = &self.transactions[i]; + if tx.tx_id == tx_id + 1 { + found = true; + index = i; + break; + } + } + if found { + self.transactions.remove(index); + } + } + + pub fn get_tx(&mut self, tx_id: u64) -> Option<&WebSocketTransaction> { + self.transactions.iter().find(|tx| tx.tx_id == tx_id + 1) + } + + fn new_tx(&mut self, direction: Direction) -> WebSocketTransaction { + let mut tx = WebSocketTransaction::new(direction); + self.tx_id += 1; + tx.tx_id = self.tx_id; + return tx; + } + + fn parse( + &mut self, stream_slice: StreamSlice, direction: Direction, flow: *const Flow, + ) -> AppLayerResult { + let to_skip = if direction == Direction::ToClient { + &mut self.to_skip_tc + } else { + &mut self.to_skip_ts + }; + let input = stream_slice.as_slice(); + let mut start = input; + if *to_skip > 0 { + if *to_skip >= input.len() as u64 { + *to_skip -= input.len() as u64; + return AppLayerResult::ok(); + } else { + start = &input[*to_skip as usize..]; + *to_skip = 0; + } + } + + let max_pl_size = unsafe { WEBSOCKET_MAX_PAYLOAD_SIZE }; + while !start.is_empty() { + match parser::parse_message(start, max_pl_size) { + Ok((rem, pdu)) => { + let _pdu = Frame::new( + flow, + &stream_slice, + start, + (start.len() - rem.len() - pdu.payload.len()) as i64, + WebSocketFrameType::Header as u8, + ); + let _pdu = Frame::new( + flow, + &stream_slice, + start, + (start.len() - rem.len()) as i64, + WebSocketFrameType::Pdu as u8, + ); + start = rem; + let mut tx = self.new_tx(direction); + if pdu.to_skip > 0 { + if direction == Direction::ToClient { + self.to_skip_tc = pdu.to_skip; + } else { + self.to_skip_ts = pdu.to_skip; + } + tx.tx_data.set_event(WebSocketEvent::SkipEndOfPayload as u8); + } + let buf = if direction == Direction::ToClient { + &mut self.s2c_buf + } else { + &mut self.c2s_buf + }; + if !buf.data.is_empty() || !pdu.fin { + if buf.data.is_empty() { + buf.compress = pdu.compress; + } + if buf.data.len() + pdu.payload.len() < max_pl_size as usize { + buf.data.extend(&pdu.payload); + } else if buf.data.len() < max_pl_size as usize { + buf.data + .extend(&pdu.payload[..max_pl_size as usize - buf.data.len()]); + tx.tx_data + .set_event(WebSocketEvent::ReassemblyLimitReached as u8); + } + } + tx.pdu = pdu; + if tx.pdu.fin && !buf.data.is_empty() { + // the final PDU gets the full reassembled payload + std::mem::swap(&mut tx.pdu.payload, &mut buf.data); + buf.data.clear(); + } + if buf.compress && tx.pdu.fin { + buf.compress = false; + // cf RFC 7692 section-7.2.2 + tx.pdu.payload.extend_from_slice(&[0, 0, 0xFF, 0xFF]); + let mut deflater = DeflateDecoder::new(&tx.pdu.payload[..]); + let mut v = Vec::new(); + // do not check result because + // deflate with rust backend fails on good input cf https://github.com/rust-lang/flate2-rs/issues/389 + let _ = deflater.read_to_end(&mut v); + if !v.is_empty() { + std::mem::swap(&mut tx.pdu.payload, &mut v); + } + } + self.transactions.push_back(tx); + } + Err(nom::Err::Incomplete(needed)) => { + if let Needed::Size(n) = needed { + let n = usize::from(n); + // Not enough data. just ask for one more byte. + let consumed = input.len() - start.len(); + let needed = start.len() + n; + return AppLayerResult::incomplete(consumed as u32, needed as u32); + } + return AppLayerResult::err(); + } + Err(_) => { + return AppLayerResult::err(); + } + } + } + // Input was fully consumed. + return AppLayerResult::ok(); + } +} + +// C exports. + +#[no_mangle] +pub unsafe extern "C" fn rs_websocket_probing_parser( + _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, +) -> AppProto { + if !input.is_null() { + let slice = build_slice!(input, input_len as usize); + if !slice.is_empty() { + // just check reserved bits are zeroed, except RSV1 + // as RSV1 is used for compression cf RFC 7692 + if slice[0] & 0x30 == 0 { + return ALPROTO_WEBSOCKET; + } + return ALPROTO_FAILED; + } + } + return ALPROTO_UNKNOWN; +} + +extern "C" fn rs_websocket_state_new( + _orig_state: *mut c_void, _orig_proto: AppProto, +) -> *mut c_void { + let state = WebSocketState::new(); + let boxed = Box::new(state); + return Box::into_raw(boxed) as *mut c_void; +} + +unsafe extern "C" fn rs_websocket_state_free(state: *mut c_void) { + std::mem::drop(Box::from_raw(state as *mut WebSocketState)); +} + +unsafe extern "C" fn rs_websocket_state_tx_free(state: *mut c_void, tx_id: u64) { + let state = cast_pointer!(state, WebSocketState); + state.free_tx(tx_id); +} + +unsafe extern "C" fn rs_websocket_parse_request( + flow: *const Flow, state: *mut c_void, _pstate: *mut c_void, stream_slice: StreamSlice, + _data: *const c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, WebSocketState); + state.parse(stream_slice, Direction::ToServer, flow) +} + +unsafe extern "C" fn rs_websocket_parse_response( + flow: *const Flow, state: *mut c_void, _pstate: *mut c_void, stream_slice: StreamSlice, + _data: *const c_void, +) -> AppLayerResult { + let state = cast_pointer!(state, WebSocketState); + state.parse(stream_slice, Direction::ToClient, flow) +} + +unsafe extern "C" fn rs_websocket_state_get_tx(state: *mut c_void, tx_id: u64) -> *mut c_void { + let state = cast_pointer!(state, WebSocketState); + match state.get_tx(tx_id) { + Some(tx) => { + return tx as *const _ as *mut _; + } + None => { + return std::ptr::null_mut(); + } + } +} + +unsafe extern "C" fn rs_websocket_state_get_tx_count(state: *mut c_void) -> u64 { + let state = cast_pointer!(state, WebSocketState); + return state.tx_id; +} + +unsafe extern "C" fn rs_websocket_tx_get_alstate_progress( + _tx: *mut c_void, _direction: u8, +) -> c_int { + return 1; +} + +export_tx_data_get!(rs_websocket_get_tx_data, WebSocketTransaction); +export_state_data_get!(rs_websocket_get_state_data, WebSocketState); + +// Parser name as a C style string. +const PARSER_NAME: &[u8] = b"websocket\0"; + +#[no_mangle] +pub unsafe extern "C" fn rs_websocket_register_parser() { + let parser = RustParser { + name: PARSER_NAME.as_ptr() as *const c_char, + default_port: std::ptr::null(), + ipproto: IPPROTO_TCP, + probe_ts: Some(rs_websocket_probing_parser), + probe_tc: Some(rs_websocket_probing_parser), + min_depth: 0, + max_depth: 16, + state_new: rs_websocket_state_new, + state_free: rs_websocket_state_free, + tx_free: rs_websocket_state_tx_free, + parse_ts: rs_websocket_parse_request, + parse_tc: rs_websocket_parse_response, + get_tx_count: rs_websocket_state_get_tx_count, + get_tx: rs_websocket_state_get_tx, + tx_comp_st_ts: 1, + tx_comp_st_tc: 1, + tx_get_progress: rs_websocket_tx_get_alstate_progress, + get_eventinfo: Some(WebSocketEvent::get_event_info), + get_eventinfo_byid: Some(WebSocketEvent::get_event_info_by_id), + localstorage_new: None, + localstorage_free: None, + get_tx_files: None, + get_tx_iterator: Some( + applayer::state_get_tx_iterator::, + ), + get_tx_data: rs_websocket_get_tx_data, + get_state_data: rs_websocket_get_state_data, + apply_tx_config: None, + flags: 0, // do not accept gaps as there is no good way to resync + truncate: None, + get_frame_id_by_name: Some(WebSocketFrameType::ffi_id_from_name), + get_frame_name_by_id: Some(WebSocketFrameType::ffi_name_from_id), + }; + + let ip_proto_str = CString::new("tcp").unwrap(); + + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + ALPROTO_WEBSOCKET = alproto; + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + SCLogDebug!("Rust websocket parser registered."); + if let Some(val) = conf_get("app-layer.protocols.websocket.max-payload-size") { + if let Ok(v) = val.parse::() { + WEBSOCKET_MAX_PAYLOAD_SIZE = v; + } else { + SCLogError!("Invalid value for websocket.max-payload-size"); + } + } + } else { + SCLogDebug!("Protocol detector and parser disabled for WEBSOCKET."); + } +} diff --git a/scripts/setup-app-layer.py b/scripts/setup-app-layer.py index d8426634bca8..26d9892b87f7 100755 --- a/scripts/setup-app-layer.py +++ b/scripts/setup-app-layer.py @@ -200,15 +200,21 @@ def logger_patch_output_c(proto): output = io.StringIO() inlines = open(filename).readlines() for i, line in enumerate(inlines): - if line.find("ALPROTO_TEMPLATE") > -1: - new_line = line.replace("TEMPLATE", proto.upper()).replace( - "template", proto.lower()) - output.write(new_line) - if line.find("output-json-template.h") > -1: - output.write(line.replace("template", proto.lower())) if line.find("/* Template JSON logger.") > -1: output.write(inlines[i].replace("Template", proto)) output.write(inlines[i+1].replace("Template", proto)) + output.write(inlines[i+2].replace("TEMPLATE", proto.upper()).replace( + "template", proto.lower()).replace("Template", proto)) + output.write(inlines[i+3]) + if line.find("rs_template_logger_log") > -1: + output.write(inlines[i].replace("TEMPLATE", proto.upper()).replace( + "template", proto.lower())) + if line.find("OutputTemplateLogInitSub(") > -1: + output.write(inlines[i].replace("Template", proto)) + output.write(inlines[i+1]) + output.write(inlines[i+2].replace("TEMPLATE", proto.upper())) + output.write(inlines[i+3]) + output.write(inlines[i+4]) output.write(line) open(filename, "w").write(output.getvalue()) @@ -216,27 +222,12 @@ def logger_copy_templates(proto): lower = proto.lower() pairs = ( - ("src/output-json-template.h", - "src/output-json-%s.h" % (lower)), - ("src/output-json-template.c", - "src/output-json-%s.c" % (lower)), ("rust/src/applayertemplate/logger.rs", "rust/src/applayer%s/logger.rs" % (lower)), ) common_copy_templates(proto, pairs) -def logger_patch_makefile_am(protoname): - filename = "src/Makefile.am" - print("Patching %s." % (filename)) - output = io.StringIO() - with open(filename) as infile: - for line in infile: - if line.lstrip().startswith("output-json-template."): - output.write(line.replace("template", protoname.lower())) - output.write(line) - open(filename, "w").write(output.getvalue()) - def detect_copy_templates(proto, buffername): lower = proto.lower() @@ -398,7 +389,6 @@ def main(): raise SetupError("no app-layer parser exists for %s" % (proto)) logger_copy_templates(proto) patch_rust_applayer_mod_rs(proto) - logger_patch_makefile_am(proto) logger_patch_output_c(proto) logger_patch_suricata_yaml_in(proto) diff --git a/src/Makefile.am b/src/Makefile.am index 2af8b1d44cd8..93ff79ed2f6a 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -359,6 +359,7 @@ noinst_HEADERS = \ detect-urilen.h \ detect-within.h \ detect-xbits.h \ + detect-websocket.h \ device-storage.h \ feature.h \ flow-bit.h \ @@ -400,7 +401,6 @@ noinst_HEADERS = \ output.h \ output-json-alert.h \ output-json-anomaly.h \ - output-json-bittorrent-dht.h \ output-json-dcerpc.h \ output-json-dhcp.h \ output-json-dnp3.h \ @@ -413,27 +413,16 @@ noinst_HEADERS = \ output-json-frame.h \ output-json-ftp.h \ output-json.h \ - output-json-http2.h \ output-json-http.h \ output-json-ike.h \ - output-json-krb5.h \ output-json-metadata.h \ - output-json-modbus.h \ - output-json-quic.h \ output-json-mqtt.h \ output-json-netflow.h \ output-json-nfs.h \ output-json-pgsql.h \ - output-json-rdp.h \ - output-json-rfb.h \ - output-json-sip.h \ output-json-smb.h \ output-json-smtp.h \ - output-json-snmp.h \ - output-json-ssh.h \ output-json-stats.h \ - output-json-template.h \ - output-json-tftp.h \ output-json-tls.h \ output-eve-syslog.h \ output-lua.h \ @@ -975,6 +964,7 @@ libsuricata_c_a_SOURCES = \ detect-urilen.c \ detect-within.c \ detect-xbits.c \ + detect-websocket.c \ device-storage.c \ feature.c \ flow-bit.c \ @@ -1014,7 +1004,6 @@ libsuricata_c_a_SOURCES = \ output-flow.c \ output-json-alert.c \ output-json-anomaly.c \ - output-json-bittorrent-dht.c \ output-json.c \ output-json-common.c \ output-json-dcerpc.c \ @@ -1028,27 +1017,16 @@ libsuricata_c_a_SOURCES = \ output-json-flow.c \ output-json-frame.c \ output-json-ftp.c \ - output-json-http2.c \ output-json-http.c \ output-json-ike.c \ - output-json-krb5.c \ output-json-metadata.c \ - output-json-modbus.c \ - output-json-quic.c \ output-json-mqtt.c \ output-json-netflow.c \ output-json-nfs.c \ output-json-pgsql.c \ - output-json-rdp.c \ - output-json-rfb.c \ - output-json-sip.c \ output-json-smb.c \ output-json-smtp.c \ - output-json-snmp.c \ - output-json-ssh.c \ output-json-stats.c \ - output-json-template.c \ - output-json-tftp.c \ output-json-tls.c \ output-eve-syslog.c \ output-eve-null.c \ diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 690950d34e72..c47a437659fd 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -67,8 +67,6 @@ typedef struct AppLayerProtoDetectProbingParserElement_ { AppProto alproto; - /* \todo don't really need it. See if you can get rid of it */ - uint16_t port; /* \todo calculate at runtime and get rid of this var */ uint32_t alproto_mask; /* the min length of data that has to be supplied to invoke the parser */ @@ -88,6 +86,9 @@ typedef struct AppLayerProtoDetectProbingParserElement_ { typedef struct AppLayerProtoDetectProbingParserPort_ { /* the port no for which probing parser(s) are invoked */ uint16_t port; + /* wether to use this probing parser port-based */ + // WebSocket has this set to false as it only works with protocol change + bool use_ports; uint32_t alproto_mask; @@ -443,7 +444,8 @@ static AppLayerProtoDetectProbingParserPort *AppLayerProtoDetectGetProbingParser pp_port = pp->port; while (pp_port != NULL) { - if (pp_port->port == port || pp_port->port == 0) { + // always check use_ports + if ((pp_port->port == port || pp_port->port == 0) && pp_port->use_ports) { break; } pp_port = pp_port->next; @@ -579,7 +581,10 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f, const uint8_t *buf, uint3 } } - if (dir == STREAM_TOSERVER && f->alproto_tc != ALPROTO_UNKNOWN) { + if (f->alproto_expect != ALPROTO_UNKNOWN) { + // needed for websocket which does not use ports + pe0 = AppLayerProtoDetectGetProbingParser(alpd_ctx.ctx_pp, ipproto, f->alproto_expect); + } else if (dir == STREAM_TOSERVER && f->alproto_tc != ALPROTO_UNKNOWN) { pe0 = AppLayerProtoDetectGetProbingParser(alpd_ctx.ctx_pp, ipproto, f->alproto_tc); } else if (dir == STREAM_TOCLIENT && f->alproto_ts != ALPROTO_UNKNOWN) { pe0 = AppLayerProtoDetectGetProbingParser(alpd_ctx.ctx_pp, ipproto, f->alproto_ts); @@ -776,16 +781,12 @@ static void AppLayerProtoDetectProbingParserFree(AppLayerProtoDetectProbingParse SCReturn; } -static AppLayerProtoDetectProbingParserElement * -AppLayerProtoDetectProbingParserElementCreate(AppProto alproto, - uint16_t port, - uint16_t min_depth, - uint16_t max_depth) +static AppLayerProtoDetectProbingParserElement *AppLayerProtoDetectProbingParserElementCreate( + AppProto alproto, uint16_t min_depth, uint16_t max_depth) { AppLayerProtoDetectProbingParserElement *pe = AppLayerProtoDetectProbingParserElementAlloc(); pe->alproto = alproto; - pe->port = port; pe->alproto_mask = AppLayerProtoDetectProbingParserGetMask(alproto); pe->min_depth = min_depth; pe->max_depth = max_depth; @@ -817,7 +818,6 @@ AppLayerProtoDetectProbingParserElementDuplicate(AppLayerProtoDetectProbingParse AppLayerProtoDetectProbingParserElement *new_pe = AppLayerProtoDetectProbingParserElementAlloc(); new_pe->alproto = pe->alproto; - new_pe->port = pe->port; new_pe->alproto_mask = pe->alproto_mask; new_pe->min_depth = pe->min_depth; new_pe->max_depth = pe->max_depth; @@ -860,7 +860,6 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar for ( ; pp_pe != NULL; pp_pe = pp_pe->next) { printf(" alproto: %s\n", AppProtoToString(pp_pe->alproto)); - printf(" port: %"PRIu16 "\n", pp_pe->port); printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask); printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth); printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth); @@ -881,7 +880,6 @@ static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingPar for ( ; pp_pe != NULL; pp_pe = pp_pe->next) { printf(" alproto: %s\n", AppProtoToString(pp_pe->alproto)); - printf(" port: %"PRIu16 "\n", pp_pe->port); printf(" mask: %"PRIu32 "\n", pp_pe->alproto_mask); printf(" min_depth: %"PRIu32 "\n", pp_pe->min_depth); printf(" max_depth: %"PRIu32 "\n", pp_pe->max_depth); @@ -902,35 +900,14 @@ static void AppLayerProtoDetectProbingParserElementAppend(AppLayerProtoDetectPro if (*head_pe == NULL) { *head_pe = new_pe; - goto end; + SCReturn; } - if ((*head_pe)->port == 0) { - if (new_pe->port != 0) { - new_pe->next = *head_pe; - *head_pe = new_pe; - } else { - AppLayerProtoDetectProbingParserElement *temp_pe = *head_pe; - while (temp_pe->next != NULL) - temp_pe = temp_pe->next; - temp_pe->next = new_pe; - } - } else { - AppLayerProtoDetectProbingParserElement *temp_pe = *head_pe; - if (new_pe->port == 0) { - while (temp_pe->next != NULL) - temp_pe = temp_pe->next; - temp_pe->next = new_pe; - } else { - while (temp_pe->next != NULL && temp_pe->next->port != 0) - temp_pe = temp_pe->next; - new_pe->next = temp_pe->next; - temp_pe->next = new_pe; + AppLayerProtoDetectProbingParserElement *temp_pe = *head_pe; + while (temp_pe->next != NULL) + temp_pe = temp_pe->next; + temp_pe->next = new_pe; - } - } - - end: SCReturn; } @@ -963,12 +940,14 @@ static void AppLayerProtoDetectProbingParserPortAppend(AppLayerProtoDetectProbin goto end; } - if ((*head_port)->port == 0) { + // port == 0 && use_ports is special run on any ports, kept at tail + if ((*head_port)->port == 0 && (*head_port)->use_ports) { new_port->next = *head_port; *head_port = new_port; } else { AppLayerProtoDetectProbingParserPort *temp_port = *head_port; - while (temp_port->next != NULL && temp_port->next->port != 0) { + while (temp_port->next != NULL && + !(temp_port->next->port == 0 && temp_port->next->use_ports)) { temp_port = temp_port->next; } new_port->next = temp_port->next; @@ -980,13 +959,9 @@ static void AppLayerProtoDetectProbingParserPortAppend(AppLayerProtoDetectProbin } static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbingParser **pp, - uint8_t ipproto, - uint16_t port, - AppProto alproto, - uint16_t min_depth, uint16_t max_depth, - uint8_t direction, - ProbingParserFPtr ProbingParser1, - ProbingParserFPtr ProbingParser2) + uint8_t ipproto, bool use_ports, uint16_t port, AppProto alproto, uint16_t min_depth, + uint16_t max_depth, uint8_t direction, ProbingParserFPtr ProbingParser1, + ProbingParserFPtr ProbingParser2) { SCEnter(); @@ -1007,13 +982,15 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing /* get the top level port pp */ AppLayerProtoDetectProbingParserPort *curr_port = curr_pp->port; while (curr_port != NULL) { - if (curr_port->port == port) + // when not use_ports, always insert a new AppLayerProtoDetectProbingParserPort + if (curr_port->port == port && use_ports) break; curr_port = curr_port->next; } if (curr_port == NULL) { AppLayerProtoDetectProbingParserPort *new_port = AppLayerProtoDetectProbingParserPortAlloc(); new_port->port = port; + new_port->use_ports = use_ports; AppLayerProtoDetectProbingParserPortAppend(&curr_pp->port, new_port); curr_port = new_port; if (direction & STREAM_TOSERVER) { @@ -1025,7 +1002,8 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing AppLayerProtoDetectProbingParserPort *zero_port; zero_port = curr_pp->port; - while (zero_port != NULL && zero_port->port != 0) { + // get special run on any port if any, to add it to this port + while (zero_port != NULL && !(zero_port->port == 0 && zero_port->use_ports)) { zero_port = zero_port->next; } if (zero_port != NULL) { @@ -1087,9 +1065,7 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing } /* Get a new parser element */ AppLayerProtoDetectProbingParserElement *new_pe = - AppLayerProtoDetectProbingParserElementCreate(alproto, - curr_port->port, - min_depth, max_depth); + AppLayerProtoDetectProbingParserElementCreate(alproto, min_depth, max_depth); if (new_pe == NULL) goto error; curr_pe = new_pe; @@ -1123,9 +1099,10 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing } AppLayerProtoDetectProbingParserElementAppend(head_pe, new_pe); - if (curr_port->port == 0) { + // when adding special run on any port, add it on all existing ones + if (curr_port->port == 0 && curr_port->use_ports) { AppLayerProtoDetectProbingParserPort *temp_port = curr_pp->port; - while (temp_port != NULL && temp_port->port != 0) { + while (temp_port != NULL && !(temp_port->port == 0 && temp_port->use_ports)) { if (direction & STREAM_TOSERVER) { if (temp_port->dp == NULL) temp_port->dp_max_depth = curr_pe->max_depth; @@ -1153,7 +1130,7 @@ static void AppLayerProtoDetectInsertNewProbingParser(AppLayerProtoDetectProbing } temp_port = temp_port->next; } /* while */ - } /* if */ + } /* if */ error: SCReturn; @@ -1574,6 +1551,13 @@ void AppLayerProtoDetectPPRegister(uint8_t ipproto, SCEnter(); DetectPort *head = NULL; + if (portstr == NULL) { + // WebSocket has a probing parser, but no port + // as it works only on HTTP1 protocol upgrade + AppLayerProtoDetectInsertNewProbingParser(&alpd_ctx.ctx_pp, ipproto, false, 0, alproto, + min_depth, max_depth, direction, ProbingParser1, ProbingParser2); + return; + } DetectPortParse(NULL,&head, portstr); DetectPort *temp_dp = head; while (temp_dp != NULL) { @@ -1581,14 +1565,8 @@ void AppLayerProtoDetectPPRegister(uint8_t ipproto, if (port == 0 && temp_dp->port2 != 0) port++; for (;;) { - AppLayerProtoDetectInsertNewProbingParser(&alpd_ctx.ctx_pp, - ipproto, - port, - alproto, - min_depth, max_depth, - direction, - ProbingParser1, - ProbingParser2); + AppLayerProtoDetectInsertNewProbingParser(&alpd_ctx.ctx_pp, ipproto, true, port, + alproto, min_depth, max_depth, direction, ProbingParser1, ProbingParser2); if (port == temp_dp->port2) { break; } else { @@ -2914,9 +2892,6 @@ static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp, if (pp_element->alproto != ip_proto[i].port[k].toserver_element[j].alproto) { goto end; } - if (pp_element->port != ip_proto[i].port[k].toserver_element[j].port) { - goto end; - } if (pp_element->alproto_mask != ip_proto[i].port[k].toserver_element[j].alproto_mask) { goto end; } @@ -2938,9 +2913,6 @@ static int AppLayerProtoDetectPPTestData(AppLayerProtoDetectProbingParser *pp, if (pp_element->alproto != ip_proto[i].port[k].toclient_element[j].alproto) { goto end; } - if (pp_element->port != ip_proto[i].port[k].toclient_element[j].port) { - goto end; - } if (pp_element->alproto_mask != ip_proto[i].port[k].toclient_element[j].alproto_mask) { goto end; } diff --git a/src/app-layer-enip.c b/src/app-layer-enip.c index f059774b6da5..5a7bce13230e 100644 --- a/src/app-layer-enip.c +++ b/src/app-layer-enip.c @@ -599,6 +599,14 @@ void RegisterENIPTCPParsers(void) proto_name, ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), ENIPProbingParser, ENIPProbingParser)) { + SCLogDebug("no ENIP TCP config found enabling ENIP detection on port 44818."); + + AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP, 0, + sizeof(ENIPEncapHdr), STREAM_TOSERVER, ENIPProbingParser, NULL); + + AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP, 0, + sizeof(ENIPEncapHdr), STREAM_TOCLIENT, ENIPProbingParser, NULL); + return; } } diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index 5d48611812c1..5ccbac83cf1c 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -53,6 +53,7 @@ #include "app-layer-protos.h" #include "app-layer-parser.h" +#include "app-layer-expectation.h" #include "app-layer.h" #include "app-layer-detect-proto.h" @@ -975,11 +976,7 @@ static AppLayerResult HTPHandleResponseData(Flow *f, void *htp_state, AppLayerPa if (tx != NULL && tx->response_status_number == 101) { htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->response_headers, "Upgrade"); - if (h == NULL || bstr_cmp_c(h->value, "h2c") != 0) { - break; - } - if (AppLayerProtoDetectGetProtoName(ALPROTO_HTTP2) == NULL) { - // if HTTP2 is disabled, keep the HTP_STREAM_TUNNEL mode + if (h == NULL) { break; } uint16_t dp = 0; @@ -987,17 +984,39 @@ static AppLayerResult HTPHandleResponseData(Flow *f, void *htp_state, AppLayerPa dp = (uint16_t)tx->request_port_number; } consumed = htp_connp_res_data_consumed(hstate->connp); - hstate->slice = NULL; - if (!AppLayerRequestProtocolChange(hstate->f, dp, ALPROTO_HTTP2)) { - HTPSetEvent(hstate, NULL, STREAM_TOCLIENT, - HTTP_DECODER_EVENT_FAILED_PROTOCOL_CHANGE); - } - // During HTTP2 upgrade, we may consume the HTTP1 part of the data - // and we need to parser the remaining part with HTTP2 - if (consumed > 0 && consumed < input_len) { - SCReturnStruct(APP_LAYER_INCOMPLETE(consumed, input_len - consumed)); + if (bstr_cmp_c(h->value, "h2c") == 0) { + if (AppLayerProtoDetectGetProtoName(ALPROTO_HTTP2) == NULL) { + // if HTTP2 is disabled, keep the HTP_STREAM_TUNNEL mode + break; + } + hstate->slice = NULL; + if (!AppLayerRequestProtocolChange(hstate->f, dp, ALPROTO_HTTP2)) { + HTPSetEvent(hstate, NULL, STREAM_TOCLIENT, + HTTP_DECODER_EVENT_FAILED_PROTOCOL_CHANGE); + } + // During HTTP2 upgrade, we may consume the HTTP1 part of the data + // and we need to parser the remaining part with HTTP2 + if (consumed > 0 && consumed < input_len) { + SCReturnStruct(APP_LAYER_INCOMPLETE(consumed, input_len - consumed)); + } + SCReturnStruct(APP_LAYER_OK); + } else if (bstr_cmp_c_nocase(h->value, "WebSocket") == 0) { + if (AppLayerProtoDetectGetProtoName(ALPROTO_WEBSOCKET) == NULL) { + // if WS is disabled, keep the HTP_STREAM_TUNNEL mode + break; + } + hstate->slice = NULL; + if (!AppLayerRequestProtocolChange(hstate->f, dp, ALPROTO_WEBSOCKET)) { + HTPSetEvent(hstate, NULL, STREAM_TOCLIENT, + HTTP_DECODER_EVENT_FAILED_PROTOCOL_CHANGE); + } + // During WS upgrade, we may consume the HTTP1 part of the data + // and we need to parser the remaining part with WS + if (consumed > 0 && consumed < input_len) { + SCReturnStruct(APP_LAYER_INCOMPLETE(consumed, input_len - consumed)); + } + SCReturnStruct(APP_LAYER_OK); } - SCReturnStruct(APP_LAYER_OK); } break; default: diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 96fc607fd257..5cbe10978915 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -1763,6 +1763,7 @@ void AppLayerParserRegisterProtocolParsers(void) RegisterSNMPParsers(); RegisterSIPParsers(); RegisterQuicParsers(); + rs_websocket_register_parser(); rs_template_register_parser(); RegisterRFBParsers(); SCMqttRegisterParser(); diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index 368efacd88d7..b6e1b73d08d4 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -60,6 +60,7 @@ const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = { { ALPROTO_MQTT, "mqtt" }, { ALPROTO_PGSQL, "pgsql" }, { ALPROTO_TELNET, "telnet" }, + { ALPROTO_WEBSOCKET, "websocket" }, { ALPROTO_TEMPLATE, "template" }, { ALPROTO_RDP, "rdp" }, { ALPROTO_HTTP2, "http2" }, diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index e2efbec4d4b1..017c12d7c18d 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -56,6 +56,7 @@ enum AppProtoEnum { ALPROTO_MQTT, ALPROTO_PGSQL, ALPROTO_TELNET, + ALPROTO_WEBSOCKET, ALPROTO_TEMPLATE, ALPROTO_RDP, ALPROTO_HTTP2, diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 218b0d7f0cb1..2fce3c6f3110 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -237,6 +237,7 @@ #include "detect-quic-version.h" #include "detect-quic-cyu-hash.h" #include "detect-quic-cyu-string.h" +#include "detect-websocket.h" #include "detect-bypass.h" #include "detect-ftpdata.h" @@ -701,6 +702,7 @@ void SigTableSetup(void) DetectQuicVersionRegister(); DetectQuicCyuHashRegister(); DetectQuicCyuStringRegister(); + DetectWebsocketRegister(); DetectBypassRegister(); DetectConfigRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 8d4c7dfad3c1..2c2809a74a12 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -316,6 +316,10 @@ enum DetectKeywordId { DETECT_AL_QUIC_UA, DETECT_AL_QUIC_CYU_HASH, DETECT_AL_QUIC_CYU_STRING, + DETECT_WEBSOCKET_MASK, + DETECT_WEBSOCKET_OPCODE, + DETECT_WEBSOCKET_FLAGS, + DETECT_WEBSOCKET_PAYLOAD, DETECT_BYPASS, diff --git a/src/detect-websocket.c b/src/detect-websocket.c new file mode 100644 index 000000000000..91f650c85c55 --- /dev/null +++ b/src/detect-websocket.c @@ -0,0 +1,251 @@ +/* Copyright (C) 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 Philippe Antoine + */ + +#include "suricata-common.h" +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-content-inspection.h" +#include "detect-engine-uint.h" +#include "detect-engine-prefilter.h" +#include "detect-websocket.h" + +#include "rust.h" + +static int websocket_tx_id = 0; +static int websocket_payload_id = 0; + +/** + * \internal + * \brief this function will free memory associated with DetectWebSocketOpcodeData + * + * \param de pointer to DetectWebSocketOpcodeData + */ +static void DetectWebSocketOpcodeFree(DetectEngineCtx *de_ctx, void *de_ptr) +{ + rs_detect_u8_free(de_ptr); +} + +/** + * \internal + * \brief Function to match opcode of a websocket tx + * + * \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 txv Pointer to the transaction. + * \param s Pointer to the Signature. + * \param ctx Pointer to the sigmatch that we will cast into DetectWebSocketOpcodeData. + * + * \retval 0 no match. + * \retval 1 match. + */ +static int DetectWebSocketOpcodeMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, + void *state, void *txv, const Signature *s, const SigMatchCtx *ctx) +{ + const DetectU8Data *de = (const DetectU8Data *)ctx; + uint8_t opc = SCWebSocketGetOpcode(txv); + return DetectU8Match(opc, de); +} + +/** + * \internal + * \brief this function is used to add the parsed sigmatch 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 options + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectWebSocketOpcodeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +{ + if (DetectSignatureSetAppProto(s, ALPROTO_WEBSOCKET) < 0) + return -1; + + DetectU8Data *de = SCWebSocketParseOpcode(rawstr); + if (de == NULL) + return -1; + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_WEBSOCKET_OPCODE, (SigMatchCtx *)de, websocket_tx_id) == NULL) { + DetectWebSocketOpcodeFree(de_ctx, de); + return -1; + } + + return 0; +} + +/** + * \internal + * \brief this function will free memory associated with DetectWebSocketMaskData + * + * \param de pointer to DetectWebSocketMaskData + */ +static void DetectWebSocketMaskFree(DetectEngineCtx *de_ctx, void *de_ptr) +{ + rs_detect_u32_free(de_ptr); +} + +static int DetectWebSocketMaskMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, + void *state, void *txv, const Signature *s, const SigMatchCtx *ctx) +{ + uint32_t val; + const DetectU32Data *du32 = (const DetectU32Data *)ctx; + if (SCWebSocketGetMask(txv, &val)) { + return DetectU32Match(val, du32); + } + return 0; +} + +static int DetectWebSocketMaskSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +{ + if (DetectSignatureSetAppProto(s, ALPROTO_WEBSOCKET) < 0) + return -1; + + DetectU32Data *du32 = DetectU32Parse(rawstr); + if (du32 == NULL) + return -1; + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_WEBSOCKET_MASK, (SigMatchCtx *)du32, websocket_tx_id) == NULL) { + DetectWebSocketMaskFree(de_ctx, du32); + return -1; + } + + return 0; +} + +static void DetectWebSocketFlagsFree(DetectEngineCtx *de_ctx, void *de_ptr) +{ + rs_detect_u8_free(de_ptr); +} + +static int DetectWebSocketFlagsMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, + void *state, void *txv, const Signature *s, const SigMatchCtx *ctx) +{ + const DetectU8Data *de = (const DetectU8Data *)ctx; + uint8_t val = SCWebSocketGetFlags(txv); + return DetectU8Match(val, de); +} + +static int DetectWebSocketFlagsSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +{ + if (DetectSignatureSetAppProto(s, ALPROTO_WEBSOCKET) < 0) + return -1; + + DetectU8Data *de = SCWebSocketParseFlags(rawstr); + if (de == NULL) + return -1; + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_WEBSOCKET_FLAGS, (SigMatchCtx *)de, websocket_tx_id) == NULL) { + DetectWebSocketOpcodeFree(de_ctx, de); + return -1; + } + + return 0; +} + +static int DetectWebSocketPayloadSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rulestr) +{ + if (DetectBufferSetActiveList(de_ctx, s, websocket_payload_id) < 0) + return -1; + + if (DetectSignatureSetAppProto(s, ALPROTO_WEBSOCKET) != 0) + return -1; + + return 0; +} + +static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, + const int list_id) +{ + InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); + if (buffer->inspect == NULL) { + const uint8_t *b = NULL; + uint32_t b_len = 0; + + if (!SCWebSocketGetPayload(txv, &b, &b_len)) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + return buffer; +} + +/** + * \brief Registration function for websocket.opcode: keyword + */ +void DetectWebsocketRegister(void) +{ + sigmatch_table[DETECT_WEBSOCKET_OPCODE].name = "websocket.opcode"; + sigmatch_table[DETECT_WEBSOCKET_OPCODE].desc = "match WebSocket opcode"; + sigmatch_table[DETECT_WEBSOCKET_OPCODE].url = "/rules/websocket-keywords.html#websocket-opcode"; + sigmatch_table[DETECT_WEBSOCKET_OPCODE].AppLayerTxMatch = DetectWebSocketOpcodeMatch; + sigmatch_table[DETECT_WEBSOCKET_OPCODE].Setup = DetectWebSocketOpcodeSetup; + sigmatch_table[DETECT_WEBSOCKET_OPCODE].Free = DetectWebSocketOpcodeFree; + + DetectAppLayerInspectEngineRegister("websocket.tx", ALPROTO_WEBSOCKET, SIG_FLAG_TOSERVER, 1, + DetectEngineInspectGenericList, NULL); + DetectAppLayerInspectEngineRegister("websocket.tx", ALPROTO_WEBSOCKET, SIG_FLAG_TOCLIENT, 1, + DetectEngineInspectGenericList, NULL); + + websocket_tx_id = DetectBufferTypeGetByName("websocket.tx"); + + sigmatch_table[DETECT_WEBSOCKET_MASK].name = "websocket.mask"; + sigmatch_table[DETECT_WEBSOCKET_MASK].desc = "match WebSocket mask"; + sigmatch_table[DETECT_WEBSOCKET_MASK].url = "/rules/websocket-keywords.html#websocket-mask"; + sigmatch_table[DETECT_WEBSOCKET_MASK].AppLayerTxMatch = DetectWebSocketMaskMatch; + sigmatch_table[DETECT_WEBSOCKET_MASK].Setup = DetectWebSocketMaskSetup; + sigmatch_table[DETECT_WEBSOCKET_MASK].Free = DetectWebSocketMaskFree; + + sigmatch_table[DETECT_WEBSOCKET_FLAGS].name = "websocket.flags"; + sigmatch_table[DETECT_WEBSOCKET_FLAGS].desc = "match WebSocket flags"; + sigmatch_table[DETECT_WEBSOCKET_FLAGS].url = "/rules/websocket-keywords.html#websocket-flags"; + sigmatch_table[DETECT_WEBSOCKET_FLAGS].AppLayerTxMatch = DetectWebSocketFlagsMatch; + sigmatch_table[DETECT_WEBSOCKET_FLAGS].Setup = DetectWebSocketFlagsSetup; + sigmatch_table[DETECT_WEBSOCKET_FLAGS].Free = DetectWebSocketFlagsFree; + + sigmatch_table[DETECT_WEBSOCKET_PAYLOAD].name = "websocket.payload"; + sigmatch_table[DETECT_WEBSOCKET_PAYLOAD].desc = "match WebSocket payload"; + sigmatch_table[DETECT_WEBSOCKET_PAYLOAD].url = + "/rules/websocket-keywords.html#websocket-payload"; + sigmatch_table[DETECT_WEBSOCKET_PAYLOAD].Setup = DetectWebSocketPayloadSetup; + sigmatch_table[DETECT_WEBSOCKET_PAYLOAD].flags |= SIGMATCH_NOOPT; + DetectAppLayerInspectEngineRegister("websocket.payload", ALPROTO_WEBSOCKET, SIG_FLAG_TOSERVER, + 0, DetectEngineInspectBufferGeneric, GetData); + DetectAppLayerInspectEngineRegister("websocket.payload", ALPROTO_WEBSOCKET, SIG_FLAG_TOCLIENT, + 0, DetectEngineInspectBufferGeneric, GetData); + DetectAppLayerMpmRegister("websocket.payload", SIG_FLAG_TOSERVER, 2, + PrefilterGenericMpmRegister, GetData, ALPROTO_WEBSOCKET, 1); + DetectAppLayerMpmRegister("websocket.payload", SIG_FLAG_TOCLIENT, 2, + PrefilterGenericMpmRegister, GetData, ALPROTO_WEBSOCKET, 1); + websocket_payload_id = DetectBufferTypeGetByName("websocket.payload"); +} diff --git a/src/output-json-quic.h b/src/detect-websocket.h similarity index 75% rename from src/output-json-quic.h rename to src/detect-websocket.h index 48e38185f2bd..54e8a22ae4a8 100644 --- a/src/output-json-quic.h +++ b/src/detect-websocket.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Open Information Security Foundation +/* Copyright (C) 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 @@ -17,11 +17,13 @@ /** * \file + * + * \author Philippe Antoine */ -#ifndef __OUTPUT_JSON_QUIC_H__ -#define __OUTPUT_JSON_QUIC_H__ +#ifndef __DETECT_WEBSOCKET_H__ +#define __DETECT_WEBSOCKET_H__ -void JsonQuicLogRegister(void); +void DetectWebsocketRegister(void); -#endif /* __OUTPUT_JSON_QUIC_H__ */ +#endif /* __DETECT_WEBSOCKET_H__ */ diff --git a/src/output-json-alert.c b/src/output-json-alert.c index 5f511b962d29..bca1090b6d4f 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -60,13 +60,9 @@ #include "output-json-nfs.h" #include "output-json-smb.h" #include "output-json-flow.h" -#include "output-json-sip.h" -#include "output-json-rfb.h" #include "output-json-mqtt.h" #include "output-json-ike.h" -#include "output-json-modbus.h" #include "output-json-frame.h" -#include "output-json-quic.h" #include "util-print.h" #include "util-optimize.h" @@ -88,12 +84,16 @@ #define LOG_JSON_RULE_METADATA BIT_U16(8) #define LOG_JSON_RULE BIT_U16(9) #define LOG_JSON_VERDICT BIT_U16(10) +#define LOG_JSON_WEBSOCKET_PAYLOAD BIT_U16(11) +#define LOG_JSON_WEBSOCKET_PAYLOAD_BASE64 BIT_U16(12) #define METADATA_DEFAULTS ( LOG_JSON_FLOW | \ LOG_JSON_APP_LAYER | \ LOG_JSON_RULE_METADATA) -#define JSON_BODY_LOGGING (LOG_JSON_HTTP_BODY | LOG_JSON_HTTP_BODY_BASE64) +#define JSON_BODY_LOGGING \ + (LOG_JSON_HTTP_BODY | LOG_JSON_HTTP_BODY_BASE64 | LOG_JSON_WEBSOCKET_PAYLOAD | \ + LOG_JSON_WEBSOCKET_PAYLOAD_BASE64) #define JSON_STREAM_BUFFER_SIZE 4096 @@ -307,6 +307,20 @@ static void AlertAddAppLayer(const Packet *p, JsonBuilder *jb, void *tx = AppLayerParserGetTx(p->flow->proto, proto, state, tx_id); if (tx) { jb_get_mark(jb, &mark); + switch (proto) { + // first check some protocols need special options for alerts logging + case ALPROTO_WEBSOCKET: + if (option_flags & + (LOG_JSON_WEBSOCKET_PAYLOAD | LOG_JSON_WEBSOCKET_PAYLOAD_BASE64)) { + bool pp = (option_flags & LOG_JSON_WEBSOCKET_PAYLOAD) != 0; + bool pb64 = (option_flags & LOG_JSON_WEBSOCKET_PAYLOAD_BASE64) != 0; + if (!SCWebSocketLogDetails(tx, jb, pp, pb64)) { + jb_restore_mark(jb, &mark); + } + // nothing more to log or do + return; + } + } if (!al->LogTx(tx, jb)) { jb_restore_mark(jb, &mark); } @@ -854,6 +868,8 @@ static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx, SetFlag(conf, "payload-printable", LOG_JSON_PAYLOAD, &flags); SetFlag(conf, "http-body-printable", LOG_JSON_HTTP_BODY, &flags); SetFlag(conf, "http-body", LOG_JSON_HTTP_BODY_BASE64, &flags); + SetFlag(conf, "websocket-payload-printable", LOG_JSON_WEBSOCKET_PAYLOAD, &flags); + SetFlag(conf, "websocket-payload", LOG_JSON_WEBSOCKET_PAYLOAD_BASE64, &flags); SetFlag(conf, "verdict", LOG_JSON_VERDICT, &flags); /* Check for obsolete flags and warn that they have no effect. */ diff --git a/src/output-json-bittorrent-dht.c b/src/output-json-bittorrent-dht.c deleted file mode 100644 index 066df78f61fb..000000000000 --- a/src/output-json-bittorrent-dht.c +++ /dev/null @@ -1,163 +0,0 @@ -/* Copyright (C) 2021 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 - * - * Implement JSON/eve logging app-layer BitTorrent DHT. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" - -#include "output.h" -#include "output-json.h" - -#include "app-layer.h" -#include "app-layer-parser.h" - -#include "output-json-bittorrent-dht.h" -#include "rust.h" - -typedef struct LogBitTorrentDHTFileCtx_ { - uint32_t flags; - OutputJsonCtx *eve_ctx; -} LogBitTorrentDHTFileCtx; - -typedef struct LogBitTorrentDHTLogThread_ { - LogBitTorrentDHTFileCtx *bittorrent_dht_log_ctx; - OutputJsonThreadCtx *ctx; -} LogBitTorrentDHTLogThread; - -static int JsonBitTorrentDHTLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, - void *state, void *tx, uint64_t tx_id) -{ - LogBitTorrentDHTLogThread *thread = thread_data; - - JsonBuilder *js = CreateEveHeader( - p, LOG_DIR_PACKET, "bittorrent_dht", NULL, thread->bittorrent_dht_log_ctx->eve_ctx); - if (unlikely(js == NULL)) { - return TM_ECODE_FAILED; - } - - if (!rs_bittorrent_dht_logger_log(tx, js)) { - goto error; - } - - OutputJsonBuilderBuffer(js, thread->ctx); - jb_free(js); - - return TM_ECODE_OK; - -error: - jb_free(js); - return TM_ECODE_FAILED; -} - -static void OutputBitTorrentDHTLogDeInitCtxSub(OutputCtx *output_ctx) -{ - LogBitTorrentDHTFileCtx *bittorrent_dht_log_ctx = (LogBitTorrentDHTFileCtx *)output_ctx->data; - SCFree(bittorrent_dht_log_ctx); - SCFree(output_ctx); -} - -static OutputInitResult OutputBitTorrentDHTLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) -{ - OutputInitResult result = { NULL, false }; - OutputJsonCtx *ajt = parent_ctx->data; - - LogBitTorrentDHTFileCtx *bittorrent_dht_log_ctx = SCCalloc(1, sizeof(*bittorrent_dht_log_ctx)); - if (unlikely(bittorrent_dht_log_ctx == NULL)) { - return result; - } - bittorrent_dht_log_ctx->eve_ctx = ajt; - - OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx)); - if (unlikely(output_ctx == NULL)) { - SCFree(bittorrent_dht_log_ctx); - return result; - } - output_ctx->data = bittorrent_dht_log_ctx; - output_ctx->DeInit = OutputBitTorrentDHTLogDeInitCtxSub; - - AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_BITTORRENT_DHT); - - result.ctx = output_ctx; - result.ok = true; - return result; -} - -static TmEcode JsonBitTorrentDHTLogThreadInit(ThreadVars *t, const void *initdata, void **data) -{ - LogBitTorrentDHTLogThread *thread = SCCalloc(1, sizeof(*thread)); - if (unlikely(thread == NULL)) { - return TM_ECODE_FAILED; - } - - if (initdata == NULL) { - SCLogDebug("Error getting context for EveLogBitTorrentDHT. \"initdata\" is NULL."); - goto error_exit; - } - - thread->bittorrent_dht_log_ctx = ((OutputCtx *)initdata)->data; - thread->ctx = CreateEveThreadCtx(t, thread->bittorrent_dht_log_ctx->eve_ctx); - if (!thread->ctx) { - goto error_exit; - } - *data = (void *)thread; - - return TM_ECODE_OK; - -error_exit: - SCFree(thread); - return TM_ECODE_FAILED; -} - -static TmEcode JsonBitTorrentDHTLogThreadDeinit(ThreadVars *t, void *data) -{ - LogBitTorrentDHTLogThread *thread = (LogBitTorrentDHTLogThread *)data; - if (thread == NULL) { - return TM_ECODE_OK; - } - FreeEveThreadCtx(thread->ctx); - SCFree(thread); - return TM_ECODE_OK; -} - -void JsonBitTorrentDHTLogRegister(void) -{ - if (ConfGetNode("app-layer.protocols.bittorrent-dht") == NULL) { - return; - } - - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonBitTorrentDHTLog", - "eve-log.bittorrent-dht", OutputBitTorrentDHTLogInitSub, ALPROTO_BITTORRENT_DHT, - JsonBitTorrentDHTLogger, JsonBitTorrentDHTLogThreadInit, - JsonBitTorrentDHTLogThreadDeinit, NULL); -} diff --git a/src/output-json-bittorrent-dht.h b/src/output-json-bittorrent-dht.h deleted file mode 100644 index 8927f4d15996..000000000000 --- a/src/output-json-bittorrent-dht.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (C) 2021 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 - */ - -#ifndef __OUTPUT_JSON_BITTORRENT_DHT_H__ -#define __OUTPUT_JSON_BITTORRENT_DHT_H__ - -void JsonBitTorrentDHTLogRegister(void); - -#endif /* __OUTPUT_JSON_BITTORRENT_DHT_H__ */ diff --git a/src/output-json-dnp3.c b/src/output-json-dnp3.c index 4336e04e070c..1387d6213632 100644 --- a/src/output-json-dnp3.c +++ b/src/output-json-dnp3.c @@ -140,7 +140,7 @@ static void JsonDNP3LogObjects(JsonBuilder *js, DNP3ObjectList *objects) } } -void JsonDNP3LogRequest(JsonBuilder *js, DNP3Transaction *dnp3tx) +static void JsonDNP3LogRequest(JsonBuilder *js, DNP3Transaction *dnp3tx) { JB_SET_STRING(js, "type", "request"); @@ -171,7 +171,7 @@ void JsonDNP3LogRequest(JsonBuilder *js, DNP3Transaction *dnp3tx) jb_close(js); } -void JsonDNP3LogResponse(JsonBuilder *js, DNP3Transaction *dnp3tx) +static void JsonDNP3LogResponse(JsonBuilder *js, DNP3Transaction *dnp3tx) { if (dnp3tx->ah.function_code == DNP3_APP_FC_UNSOLICITED_RESP) { JB_SET_STRING(js, "type", "unsolicited_response"); diff --git a/src/output-json-dnp3.h b/src/output-json-dnp3.h index 6f81026780d9..5b5f56236abd 100644 --- a/src/output-json-dnp3.h +++ b/src/output-json-dnp3.h @@ -20,9 +20,6 @@ #include "app-layer-dnp3.h" -void JsonDNP3LogRequest(JsonBuilder *js, DNP3Transaction *); -void JsonDNP3LogResponse(JsonBuilder *js, DNP3Transaction *); - void JsonDNP3LogRegister(void); bool AlertJsonDnp3(void *vtx, JsonBuilder *js); diff --git a/src/output-json-dns.c b/src/output-json-dns.c index 3c7009b501ca..9cab2bde55c2 100644 --- a/src/output-json-dns.c +++ b/src/output-json-dns.c @@ -293,19 +293,22 @@ static JsonBuilder *JsonDNSLogAnswer(void *txptr) bool AlertJsonDns(void *txptr, JsonBuilder *js) { + bool r = false; jb_open_object(js, "dns"); JsonBuilder *qjs = JsonDNSLogQuery(txptr); if (qjs != NULL) { jb_set_object(js, "query", qjs); jb_free(qjs); + r = true; } JsonBuilder *ajs = JsonDNSLogAnswer(txptr); if (ajs != NULL) { jb_set_object(js, "answer", ajs); jb_free(ajs); + r = true; } jb_close(js); - return true; + return r; } static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data, diff --git a/src/output-json-file.c b/src/output-json-file.c index 1018be06ee80..ae8400a18d53 100644 --- a/src/output-json-file.c +++ b/src/output-json-file.c @@ -61,7 +61,6 @@ #include "output-json-email-common.h" #include "output-json-nfs.h" #include "output-json-smb.h" -#include "output-json-http2.h" #include "app-layer-htp.h" #include "app-layer-htp-xff.h" diff --git a/src/output-json-ftp.c b/src/output-json-ftp.c index 34422f72f4af..14232bdfe393 100644 --- a/src/output-json-ftp.c +++ b/src/output-json-ftp.c @@ -154,60 +154,3 @@ bool EveFTPLogCommand(void *vtx, JsonBuilder *jb) jb_close(jb); return true; } - - -static int JsonFTPLogger(ThreadVars *tv, void *thread_data, - const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id) -{ - SCEnter(); - OutputJsonThreadCtx *thread = thread_data; - - const char *event_type; - if (f->alproto == ALPROTO_FTPDATA) { - event_type = "ftp_data"; - } else { - event_type = "ftp"; - } - - JsonBuilder *jb = - CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, event_type, NULL, tx_id, thread->ctx); - if (likely(jb)) { - if (f->alproto == ALPROTO_FTPDATA) { - if (!EveFTPDataAddMetadata(vtx, jb)) { - goto fail; - } - } else { - EveFTPLogCommand(vtx, jb); - } - - OutputJsonBuilderBuffer(jb, thread); - - jb_free(jb); - } - return TM_ECODE_OK; - -fail: - jb_free(jb); - return TM_ECODE_FAILED; -} - -static OutputInitResult OutputFTPLogInitSub(ConfNode *conf, - OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_FTP); - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_FTPDATA); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonFTPLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonFTPLog", "eve-log.ftp", - OutputFTPLogInitSub, ALPROTO_FTP, JsonFTPLogger, JsonLogThreadInit, JsonLogThreadDeinit, - NULL); - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonFTPLog", "eve-log.ftp", - OutputFTPLogInitSub, ALPROTO_FTPDATA, JsonFTPLogger, JsonLogThreadInit, - JsonLogThreadDeinit, NULL); - - SCLogDebug("FTP JSON logger registered."); -} diff --git a/src/output-json-ftp.h b/src/output-json-ftp.h index 704defd9585c..61922795ce6f 100644 --- a/src/output-json-ftp.h +++ b/src/output-json-ftp.h @@ -24,7 +24,6 @@ #ifndef __OUTPUT_JSON_FTP_H__ #define __OUTPUT_JSON_FTP_H__ -void JsonFTPLogRegister(void); bool EveFTPLogCommand(void *vtx, JsonBuilder *js); #endif /* __OUTPUT_JSON_FTP_H__ */ diff --git a/src/output-json-http2.c b/src/output-json-http2.c deleted file mode 100644 index cb096f37a043..000000000000 --- a/src/output-json-http2.c +++ /dev/null @@ -1,169 +0,0 @@ -/* Copyright (C) 2020-2021 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 Philippe Antoine - * - * Implements HTTP2 JSON logging portion of the engine. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-print.h" -#include "util-unittest.h" - -#include "util-debug.h" -#include "app-layer-parser.h" -#include "output.h" -#include "app-layer-http2.h" -#include "app-layer.h" -#include "util-privs.h" -#include "util-buffer.h" - -#include "util-logopenfile.h" - -#include "output-json.h" -#include "output-json-http2.h" -#include "rust.h" - -#define MODULE_NAME "LogHttp2Log" - -typedef struct OutputHttp2Ctx_ { - OutputJsonCtx *eve_ctx; -} OutputHttp2Ctx; - - -typedef struct JsonHttp2LogThread_ { - OutputHttp2Ctx *http2log_ctx; - OutputJsonThreadCtx *ctx; -} JsonHttp2LogThread; - -static int JsonHttp2Logger(ThreadVars *tv, void *thread_data, const Packet *p, - Flow *f, void *state, void *txptr, uint64_t tx_id) -{ - JsonHttp2LogThread *aft = (JsonHttp2LogThread *)thread_data; - - if (unlikely(state == NULL)) { - return 0; - } - - JsonBuilder *js = CreateEveHeaderWithTxId( - p, LOG_DIR_FLOW, "http", NULL, tx_id, aft->http2log_ctx->eve_ctx); - if (unlikely(js == NULL)) - return 0; - - if (!rs_http2_log_json(txptr, js)) { - goto end; - } - OutputJsonBuilderBuffer(js, aft->ctx); -end: - jb_free(js); - return 0; -} - -static TmEcode JsonHttp2LogThreadInit(ThreadVars *t, const void *initdata, void **data) -{ - JsonHttp2LogThread *aft = SCCalloc(1, sizeof(JsonHttp2LogThread)); - if (unlikely(aft == NULL)) - return TM_ECODE_FAILED; - - if(initdata == NULL) - { - SCLogDebug("Error getting context for EveLogHTTP2. \"initdata\" argument NULL"); - goto error_exit; - } - - /* Use the Output Context (file pointer and mutex) */ - aft->http2log_ctx = ((OutputCtx *)initdata)->data; - aft->ctx = CreateEveThreadCtx(t, aft->http2log_ctx->eve_ctx); - if (!aft->ctx) { - goto error_exit; - } - - *data = (void *)aft; - return TM_ECODE_OK; - -error_exit: - SCFree(aft); - return TM_ECODE_FAILED; -} - -static TmEcode JsonHttp2LogThreadDeinit(ThreadVars *t, void *data) -{ - JsonHttp2LogThread *aft = (JsonHttp2LogThread *)data; - if (aft == NULL) { - return TM_ECODE_OK; - } - - FreeEveThreadCtx(aft->ctx); - /* clear memory */ - memset(aft, 0, sizeof(JsonHttp2LogThread)); - - SCFree(aft); - return TM_ECODE_OK; -} - -static void OutputHttp2LogDeinitSub(OutputCtx *output_ctx) -{ - OutputHttp2Ctx *http2_ctx = output_ctx->data; - SCFree(http2_ctx); - SCFree(output_ctx); -} - -static OutputInitResult OutputHttp2LogInitSub(ConfNode *conf, OutputCtx *parent_ctx) -{ - OutputInitResult result = { NULL, false }; - OutputJsonCtx *ojc = parent_ctx->data; - - OutputHttp2Ctx *http2_ctx = SCCalloc(1, sizeof(OutputHttp2Ctx)); - if (unlikely(http2_ctx == NULL)) - return result; - - OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); - if (unlikely(output_ctx == NULL)) { - SCFree(http2_ctx); - return result; - } - - http2_ctx->eve_ctx = ojc; - - output_ctx->data = http2_ctx; - output_ctx->DeInit = OutputHttp2LogDeinitSub; - - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP2); - - result.ctx = output_ctx; - result.ok = true; - return result; -} - -void JsonHttp2LogRegister (void) -{ - /* also register as child of eve-log */ - OutputRegisterTxSubModuleWithProgress(LOGGER_JSON_TX, "eve-log", MODULE_NAME, "eve-log.http2", - OutputHttp2LogInitSub, ALPROTO_HTTP2, JsonHttp2Logger, HTTP2StateClosed, - HTTP2StateClosed, JsonHttp2LogThreadInit, JsonHttp2LogThreadDeinit, NULL); -} diff --git a/src/output-json-http2.h b/src/output-json-http2.h deleted file mode 100644 index 88ba420ab2df..000000000000 --- a/src/output-json-http2.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 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 Philippe Antoine - */ - -#ifndef __OUTPUT_JSON_HTTP2_H__ -#define __OUTPUT_JSON_HTTP2_H__ - -void JsonHttp2LogRegister(void); - -#endif /* __OUTPUT_JSON_HTTP2_H__ */ diff --git a/src/output-json-krb5.c b/src/output-json-krb5.c deleted file mode 100644 index 9fc45c5d3c53..000000000000 --- a/src/output-json-krb5.c +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (C) 2018-2021 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 Pierre Chifflier - * - * Implement JSON/eve logging app-layer KRB5. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" - -#include "output.h" -#include "output-json.h" - -#include "app-layer.h" -#include "app-layer-parser.h" - -#include "app-layer-krb5.h" -#include "output-json-krb5.h" - -#include "rust.h" - -static int JsonKRB5Logger(ThreadVars *tv, void *thread_data, - const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) -{ - KRB5Transaction *krb5tx = tx; - OutputJsonThreadCtx *thread = thread_data; - - JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_PACKET, "krb5", NULL, thread->ctx); - if (unlikely(jb == NULL)) { - return TM_ECODE_FAILED; - } - - if (!rs_krb5_log_json_response(krb5tx, jb)) { - goto error; - } - - OutputJsonBuilderBuffer(jb, thread); - - jb_free(jb); - return TM_ECODE_OK; - -error: - jb_free(jb); - return TM_ECODE_FAILED; -} - -static OutputInitResult OutputKRB5LogInitSub(ConfNode *conf, - OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_KRB5); - AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_KRB5); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonKRB5LogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonKRB5Log", "eve-log.krb5", - OutputKRB5LogInitSub, ALPROTO_KRB5, JsonKRB5Logger, JsonLogThreadInit, - JsonLogThreadDeinit, NULL); - - SCLogDebug("KRB5 JSON logger registered."); -} diff --git a/src/output-json-krb5.h b/src/output-json-krb5.h deleted file mode 100644 index 87f5b79136cb..000000000000 --- a/src/output-json-krb5.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2015 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 Pierre Chifflier - */ - -#ifndef __OUTPUT_JSON_KRB5_H__ -#define __OUTPUT_JSON_KRB5_H__ - -void JsonKRB5LogRegister(void); - -#endif /* __OUTPUT_JSON_KRB5_H__ */ diff --git a/src/output-json-modbus.c b/src/output-json-modbus.c deleted file mode 100644 index 9e508ead9acc..000000000000 --- a/src/output-json-modbus.c +++ /dev/null @@ -1,147 +0,0 @@ -/* Copyright (C) 2019-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. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" -#include "output.h" -#include "output-json.h" -#include "app-layer.h" -#include "app-layer-parser.h" -#include "output-json-modbus.h" -#include "rust.h" - -typedef struct LogModbusFileCtx_ { - LogFileCtx *file_ctx; - OutputJsonCtx *eve_ctx; -} LogModbusFileCtx; - -typedef struct JsonModbusLogThread_ { - LogModbusFileCtx *modbuslog_ctx; - OutputJsonThreadCtx *ctx; -} JsonModbusLogThread; - -static int JsonModbusLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, - void *state, void *tx, uint64_t tx_id) -{ - JsonModbusLogThread *thread = thread_data; - - JsonBuilder *js = - CreateEveHeader(p, LOG_DIR_FLOW, "modbus", NULL, thread->modbuslog_ctx->eve_ctx); - if (unlikely(js == NULL)) { - return TM_ECODE_OK; - } - if (!rs_modbus_to_json(tx, js)) { - jb_free(js); - return TM_ECODE_FAILED; - } - OutputJsonBuilderBuffer(js, thread->ctx); - - jb_free(js); - return TM_ECODE_OK; -} - -static void OutputModbusLogDeInitCtxSub(OutputCtx *output_ctx) -{ - LogModbusFileCtx *modbuslog_ctx = (LogModbusFileCtx *)output_ctx->data; - SCFree(modbuslog_ctx); - SCFree(output_ctx); -} - -static OutputInitResult OutputModbusLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) -{ - OutputInitResult result = { NULL, false }; - OutputJsonCtx *ajt = parent_ctx->data; - - LogModbusFileCtx *modbuslog_ctx = SCCalloc(1, sizeof(*modbuslog_ctx)); - if (unlikely(modbuslog_ctx == NULL)) { - return result; - } - modbuslog_ctx->file_ctx = ajt->file_ctx; - modbuslog_ctx->eve_ctx = ajt; - - OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx)); - if (unlikely(output_ctx == NULL)) { - SCFree(modbuslog_ctx); - return result; - } - output_ctx->data = modbuslog_ctx; - output_ctx->DeInit = OutputModbusLogDeInitCtxSub; - - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_MODBUS); - - SCLogDebug("modbus log sub-module initialized."); - - result.ctx = output_ctx; - result.ok = true; - return result; -} - -static TmEcode JsonModbusLogThreadInit(ThreadVars *t, const void *initdata, void **data) -{ - if (initdata == NULL) { - SCLogDebug("Error getting context for EveLogModbus. \"initdata\" is NULL."); - return TM_ECODE_FAILED; - } - - JsonModbusLogThread *thread = SCCalloc(1, sizeof(*thread)); - if (unlikely(thread == NULL)) { - return TM_ECODE_FAILED; - } - - thread->modbuslog_ctx = ((OutputCtx *)initdata)->data; - thread->ctx = CreateEveThreadCtx(t, thread->modbuslog_ctx->eve_ctx); - if (thread->ctx == NULL) { - goto error_exit; - } - - *data = (void *)thread; - return TM_ECODE_OK; - -error_exit: - SCFree(thread); - return TM_ECODE_FAILED; -} - -static TmEcode JsonModbusLogThreadDeinit(ThreadVars *t, void *data) -{ - JsonModbusLogThread *thread = (JsonModbusLogThread *)data; - if (thread == NULL) { - return TM_ECODE_OK; - } - FreeEveThreadCtx(thread->ctx); - SCFree(thread); - return TM_ECODE_OK; -} - -void JsonModbusLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonModbusLog", "eve-log.modbus", - OutputModbusLogInitSub, ALPROTO_MODBUS, JsonModbusLogger, JsonModbusLogThreadInit, - JsonModbusLogThreadDeinit, NULL); - - SCLogDebug("modbus json logger registered."); -} diff --git a/src/output-json-quic.c b/src/output-json-quic.c deleted file mode 100644 index 830ac78fdfbb..000000000000 --- a/src/output-json-quic.c +++ /dev/null @@ -1,151 +0,0 @@ -/* Copyright (C) 2021 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 - * - * Implements JSON/eve logging for Quic app-layer. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" -#include "output.h" -#include "output-json.h" -#include "app-layer.h" -#include "app-layer-parser.h" -#include "output-json-quic.h" -#include "rust.h" - -typedef struct LogQuicFileCtx_ { - LogFileCtx *file_ctx; - OutputJsonCtx *eve_ctx; -} LogQuicFileCtx; - -typedef struct JsonQuicLogThread_ { - LogQuicFileCtx *quiclog_ctx; - OutputJsonThreadCtx *ctx; -} JsonQuicLogThread; - -static int JsonQuicLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, - void *tx, uint64_t tx_id) -{ - JsonQuicLogThread *thread = thread_data; - - JsonBuilder *js = - CreateEveHeader(p, LOG_DIR_PACKET, "quic", NULL, thread->quiclog_ctx->eve_ctx); - if (unlikely(js == NULL)) { - return TM_ECODE_OK; - } - if (!rs_quic_to_json(tx, js)) { - jb_free(js); - return TM_ECODE_FAILED; - } - OutputJsonBuilderBuffer(js, thread->ctx); - - jb_free(js); - return TM_ECODE_OK; -} - -static void OutputQuicLogDeInitCtxSub(OutputCtx *output_ctx) -{ - LogQuicFileCtx *quiclog_ctx = (LogQuicFileCtx *)output_ctx->data; - SCFree(quiclog_ctx); - SCFree(output_ctx); -} - -static OutputInitResult OutputQuicLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) -{ - OutputInitResult result = { NULL, false }; - OutputJsonCtx *ajt = parent_ctx->data; - - LogQuicFileCtx *quiclog_ctx = SCCalloc(1, sizeof(*quiclog_ctx)); - if (unlikely(quiclog_ctx == NULL)) { - return result; - } - quiclog_ctx->file_ctx = ajt->file_ctx; - quiclog_ctx->eve_ctx = ajt; - - OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx)); - if (unlikely(output_ctx == NULL)) { - SCFree(quiclog_ctx); - return result; - } - output_ctx->data = quiclog_ctx; - output_ctx->DeInit = OutputQuicLogDeInitCtxSub; - - AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_QUIC); - - result.ctx = output_ctx; - result.ok = true; - return result; -} - -static TmEcode JsonQuicLogThreadInit(ThreadVars *t, const void *initdata, void **data) -{ - if (initdata == NULL) { - SCLogDebug("Error getting context for EveLogQuic. \"initdata\" is NULL."); - return TM_ECODE_FAILED; - } - - JsonQuicLogThread *thread = SCCalloc(1, sizeof(*thread)); - if (unlikely(thread == NULL)) { - return TM_ECODE_FAILED; - } - - thread->quiclog_ctx = ((OutputCtx *)initdata)->data; - thread->ctx = CreateEveThreadCtx(t, thread->quiclog_ctx->eve_ctx); - if (thread->ctx == NULL) { - goto error_exit; - } - - *data = (void *)thread; - return TM_ECODE_OK; - -error_exit: - SCFree(thread); - return TM_ECODE_FAILED; -} - -static TmEcode JsonQuicLogThreadDeinit(ThreadVars *t, void *data) -{ - JsonQuicLogThread *thread = (JsonQuicLogThread *)data; - if (thread == NULL) { - return TM_ECODE_OK; - } - FreeEveThreadCtx(thread->ctx); - SCFree(thread); - return TM_ECODE_OK; -} - -void JsonQuicLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonQuicLog", "eve-log.quic", - OutputQuicLogInitSub, ALPROTO_QUIC, JsonQuicLogger, JsonQuicLogThreadInit, - JsonQuicLogThreadDeinit, NULL); - - SCLogDebug("quic json logger registered."); -} diff --git a/src/output-json-rdp.c b/src/output-json-rdp.c deleted file mode 100644 index bc5d9ae9df89..000000000000 --- a/src/output-json-rdp.c +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright (C) 2019-2021 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 Zach Kelly - * - * Application layer logger for RDP - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" -#include "output.h" -#include "output-json.h" -#include "app-layer.h" -#include "app-layer-parser.h" -#include "app-layer-rdp.h" -#include "output-json-rdp.h" -#include "rust.h" - -static int JsonRdpLogger(ThreadVars *tv, void *thread_data, - const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) -{ - OutputJsonThreadCtx *thread = thread_data; - - JsonBuilder *js = CreateEveHeader(p, LOG_DIR_PACKET, "rdp", NULL, thread->ctx); - if (unlikely(js == NULL)) { - return TM_ECODE_OK; - } - if (!rs_rdp_to_json(tx, js)) { - jb_free(js); - return TM_ECODE_FAILED; - } - OutputJsonBuilderBuffer(js, thread); - - jb_free(js); - return TM_ECODE_OK; -} - -static OutputInitResult OutputRdpLogInitSub(ConfNode *conf, - OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_RDP); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonRdpLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonRdpLog", "eve-log.rdp", - OutputRdpLogInitSub, ALPROTO_RDP, JsonRdpLogger, JsonLogThreadInit, JsonLogThreadDeinit, - NULL); - - SCLogDebug("rdp json logger registered."); -} diff --git a/src/output-json-rdp.h b/src/output-json-rdp.h deleted file mode 100644 index 5dc9237691ea..000000000000 --- a/src/output-json-rdp.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2019 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 Zach Kelly - */ - -#ifndef __OUTPUT_JSON_RDP_H__ -#define __OUTPUT_JSON_RDP_H__ - -void JsonRdpLogRegister(void); - -#endif /* __OUTPUT_JSON_RDP_H__ */ diff --git a/src/output-json-rfb.c b/src/output-json-rfb.c deleted file mode 100644 index e2b832bece13..000000000000 --- a/src/output-json-rfb.c +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (C) 2020-2021 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 Frank Honza - * - * Implement JSON/eve logging app-layer RFB. - */ - -#include "suricata-common.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" - -#include "output.h" -#include "output-json.h" - -#include "app-layer.h" -#include "app-layer-parser.h" - -#include "app-layer-rfb.h" -#include "output-json-rfb.h" - -#include "rust-bindings.h" - -static int JsonRFBLogger(ThreadVars *tv, void *thread_data, - const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) -{ - OutputJsonThreadCtx *thread = thread_data; - - JsonBuilder *js = CreateEveHeader(p, LOG_DIR_FLOW, "rfb", NULL, thread->ctx); - if (unlikely(js == NULL)) { - return TM_ECODE_FAILED; - } - - if (!rs_rfb_logger_log(tx, js)) { - goto error; - } - - OutputJsonBuilderBuffer(js, thread); - jb_free(js); - - return TM_ECODE_OK; - -error: - jb_free(js); - return TM_ECODE_FAILED; -} - -static OutputInitResult OutputRFBLogInitSub(ConfNode *conf, - OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_RFB); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonRFBLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonRFBLog", "eve-log.rfb", - OutputRFBLogInitSub, ALPROTO_RFB, JsonRFBLogger, JsonLogThreadInit, JsonLogThreadDeinit, - NULL); -} diff --git a/src/output-json-rfb.h b/src/output-json-rfb.h deleted file mode 100644 index 7e4e48ebd4c8..000000000000 --- a/src/output-json-rfb.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 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 Frank Honza - */ - -#ifndef __OUTPUT_JSON_RFB_H__ -#define __OUTPUT_JSON_RFB_H__ - -void JsonRFBLogRegister(void); - -#endif /* __OUTPUT_JSON_RFB_H__ */ diff --git a/src/output-json-sip.c b/src/output-json-sip.c deleted file mode 100644 index 7dd442cf6aba..000000000000 --- a/src/output-json-sip.c +++ /dev/null @@ -1,91 +0,0 @@ -/* Copyright (C) 2018-2021 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 Giuseppe Longo - * - * Implement JSON/eve logging app-layer SIP. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" - -#include "output.h" -#include "output-json.h" - -#include "app-layer.h" -#include "app-layer-parser.h" - -#include "app-layer-sip.h" -#include "output-json-sip.h" - -#include "rust.h" - -static int JsonSIPLogger(ThreadVars *tv, void *thread_data, - const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) -{ - SIPTransaction *siptx = tx; - OutputJsonThreadCtx *thread = thread_data; - - JsonBuilder *js = CreateEveHeader((Packet *)p, LOG_DIR_PACKET, "sip", NULL, thread->ctx); - if (unlikely(js == NULL)) { - return TM_ECODE_OK; - } - - if (!rs_sip_log_json(siptx, js)) { - goto error; - } - - OutputJsonBuilderBuffer(js, thread); - jb_free(js); - - return TM_ECODE_OK; - -error: - jb_free(js); - return TM_ECODE_FAILED; -} - -static OutputInitResult OutputSIPLogInitSub(ConfNode *conf, - OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_SIP); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonSIPLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonSIPLog", "eve-log.sip", - OutputSIPLogInitSub, ALPROTO_SIP, JsonSIPLogger, JsonLogThreadInit, JsonLogThreadDeinit, - NULL); - - SCLogDebug("SIP JSON logger registered."); -} diff --git a/src/output-json-sip.h b/src/output-json-sip.h deleted file mode 100644 index 0d2c53fa50df..000000000000 --- a/src/output-json-sip.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2015 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 Giuseppe Longo - */ - -#ifndef __OUTPUT_JSON_SIP_H__ -#define __OUTPUT_JSON_SIP_H__ - -void JsonSIPLogRegister(void); - -#endif /* __OUTPUT_JSON_SIP_H__ */ diff --git a/src/output-json-snmp.c b/src/output-json-snmp.c deleted file mode 100644 index cbf0a7c992e4..000000000000 --- a/src/output-json-snmp.c +++ /dev/null @@ -1,91 +0,0 @@ -/* Copyright (C) 2018-2021 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 Pierre Chifflier - * - * Implement JSON/eve logging app-layer SNMP. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" - -#include "output.h" -#include "output-json.h" - -#include "app-layer.h" -#include "app-layer-parser.h" - -#include "app-layer-snmp.h" -#include "output-json-snmp.h" - -#include "rust.h" - -static int JsonSNMPLogger(ThreadVars *tv, void *thread_data, - const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) -{ - SNMPTransaction *snmptx = tx; - OutputJsonThreadCtx *thread = thread_data; - - JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_PACKET, "snmp", NULL, thread->ctx); - if (unlikely(jb == NULL)) { - return TM_ECODE_FAILED; - } - - if (!rs_snmp_log_json_response(snmptx, jb)) { - goto error; - } - - OutputJsonBuilderBuffer(jb, thread); - - jb_free(jb); - return TM_ECODE_OK; - -error: - jb_free(jb); - return TM_ECODE_FAILED; -} - -static OutputInitResult OutputSNMPLogInitSub(ConfNode *conf, - OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_SNMP); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonSNMPLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonSNMPLog", "eve-log.snmp", - OutputSNMPLogInitSub, ALPROTO_SNMP, JsonSNMPLogger, JsonLogThreadInit, - JsonLogThreadDeinit, NULL); - - SCLogDebug("SNMP JSON logger registered."); -} diff --git a/src/output-json-snmp.h b/src/output-json-snmp.h deleted file mode 100644 index 4c88db32317d..000000000000 --- a/src/output-json-snmp.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2015-2019 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 Pierre Chifflier - */ - -#ifndef __OUTPUT_JSON_SNMP_H__ -#define __OUTPUT_JSON_SNMP_H__ - -void JsonSNMPLogRegister(void); - -#endif /* __OUTPUT_JSON_SNMP_H__ */ diff --git a/src/output-json-ssh.c b/src/output-json-ssh.c deleted file mode 100644 index 45a8d8eab333..000000000000 --- a/src/output-json-ssh.c +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright (C) 2014-2021 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 - * - * Implements SSH JSON logging portion of the engine. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-print.h" -#include "util-unittest.h" - -#include "util-debug.h" -#include "app-layer-parser.h" -#include "output.h" -#include "app-layer-ssh.h" -#include "app-layer.h" -#include "util-privs.h" -#include "util-buffer.h" - -#include "util-logopenfile.h" - -#include "output-json.h" -#include "output-json-ssh.h" -#include "rust.h" - -#define MODULE_NAME "LogSshLog" - -static int JsonSshLogger(ThreadVars *tv, void *thread_data, const Packet *p, - Flow *f, void *state, void *txptr, uint64_t tx_id) -{ - OutputJsonThreadCtx *thread = thread_data; - - if (unlikely(state == NULL)) { - return 0; - } - - JsonBuilder *js = CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, "ssh", NULL, tx_id, thread->ctx); - if (unlikely(js == NULL)) - return 0; - - if (!rs_ssh_log_json(txptr, js)) { - goto end; - } - OutputJsonBuilderBuffer(js, thread); - -end: - jb_free(js); - return 0; -} - -static OutputInitResult OutputSshLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SSH); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonSshLogRegister (void) -{ - /* register as child of eve-log */ - OutputRegisterTxSubModuleWithCondition(LOGGER_JSON_TX, "eve-log", "JsonSshLog", "eve-log.ssh", - OutputSshLogInitSub, ALPROTO_SSH, JsonSshLogger, SSHTxLogCondition, JsonLogThreadInit, - JsonLogThreadDeinit, NULL); -} diff --git a/src/output-json-ssh.h b/src/output-json-ssh.h deleted file mode 100644 index d0f9d3fc7dde..000000000000 --- a/src/output-json-ssh.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2014 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 __OUTPUT_JSON_SSH_H__ -#define __OUTPUT_JSON_SSH_H__ - -void JsonSshLogRegister(void); - -#endif /* __OUTPUT_JSON_SSH_H__ */ diff --git a/src/output-json-template.c b/src/output-json-template.c deleted file mode 100644 index 2ca48b7ae373..000000000000 --- a/src/output-json-template.c +++ /dev/null @@ -1,176 +0,0 @@ -/* Copyright (C) 2018-2022 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. - */ - -/* - * TODO: Update \author in this file and in output-json-template.h. - * TODO: Remove SCLogNotice statements, or convert to debug. - * TODO: Implement your app-layers logging. - */ - -/** - * \file - * - * \author FirstName LastName - * - * Implement JSON/eve logging app-layer Template. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" - -#include "output.h" -#include "output-json.h" - -#include "app-layer.h" -#include "app-layer-parser.h" - -#include "output-json-template.h" -#include "rust.h" - -typedef struct LogTemplateFileCtx_ { - uint32_t flags; - OutputJsonCtx *eve_ctx; -} LogTemplateFileCtx; - -typedef struct LogTemplateLogThread_ { - LogTemplateFileCtx *templatelog_ctx; - OutputJsonThreadCtx *ctx; -} LogTemplateLogThread; - -static int JsonTemplateLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, - void *state, void *tx, uint64_t tx_id) -{ - SCLogNotice("JsonTemplateLogger"); - LogTemplateLogThread *thread = thread_data; - - JsonBuilder *js = - CreateEveHeader(p, LOG_DIR_PACKET, "template", NULL, thread->templatelog_ctx->eve_ctx); - if (unlikely(js == NULL)) { - return TM_ECODE_FAILED; - } - - if (!rs_template_logger_log(tx, js)) { - goto error; - } - - OutputJsonBuilderBuffer(js, thread->ctx); - jb_free(js); - - return TM_ECODE_OK; - -error: - jb_free(js); - return TM_ECODE_FAILED; -} - -static void OutputTemplateLogDeInitCtxSub(OutputCtx *output_ctx) -{ - LogTemplateFileCtx *templatelog_ctx = (LogTemplateFileCtx *)output_ctx->data; - SCFree(templatelog_ctx); - SCFree(output_ctx); -} - -static OutputInitResult OutputTemplateLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) -{ - OutputInitResult result = { NULL, false }; - OutputJsonCtx *ajt = parent_ctx->data; - - LogTemplateFileCtx *templatelog_ctx = SCCalloc(1, sizeof(*templatelog_ctx)); - if (unlikely(templatelog_ctx == NULL)) { - return result; - } - templatelog_ctx->eve_ctx = ajt; - - OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx)); - if (unlikely(output_ctx == NULL)) { - SCFree(templatelog_ctx); - return result; - } - output_ctx->data = templatelog_ctx; - output_ctx->DeInit = OutputTemplateLogDeInitCtxSub; - - SCLogNotice("Template log sub-module initialized."); - - AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_TEMPLATE); - - result.ctx = output_ctx; - result.ok = true; - return result; -} - -static TmEcode JsonTemplateLogThreadInit(ThreadVars *t, const void *initdata, void **data) -{ - LogTemplateLogThread *thread = SCCalloc(1, sizeof(*thread)); - if (unlikely(thread == NULL)) { - return TM_ECODE_FAILED; - } - - if (initdata == NULL) { - SCLogDebug("Error getting context for EveLogTemplate. \"initdata\" is NULL."); - goto error_exit; - } - - thread->templatelog_ctx = ((OutputCtx *)initdata)->data; - thread->ctx = CreateEveThreadCtx(t, thread->templatelog_ctx->eve_ctx); - if (!thread->ctx) { - goto error_exit; - } - *data = (void *)thread; - - return TM_ECODE_OK; - -error_exit: - SCFree(thread); - return TM_ECODE_FAILED; -} - -static TmEcode JsonTemplateLogThreadDeinit(ThreadVars *t, void *data) -{ - LogTemplateLogThread *thread = (LogTemplateLogThread *)data; - if (thread == NULL) { - return TM_ECODE_OK; - } - FreeEveThreadCtx(thread->ctx); - SCFree(thread); - return TM_ECODE_OK; -} - -void JsonTemplateLogRegister(void) -{ - /* TEMPLATE_START_REMOVE */ - if (ConfGetNode("app-layer.protocols.template") == NULL) { - return; - } - /* TEMPLATE_END_REMOVE */ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTemplateLog", "eve-log.template", - OutputTemplateLogInitSub, ALPROTO_TEMPLATE, JsonTemplateLogger, - JsonTemplateLogThreadInit, JsonTemplateLogThreadDeinit, NULL); - - SCLogNotice("Template JSON logger registered."); -} diff --git a/src/output-json-template.h b/src/output-json-template.h deleted file mode 100644 index d27b8d7c0e05..000000000000 --- a/src/output-json-template.h +++ /dev/null @@ -1,29 +0,0 @@ -/* 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. - */ - -/** - * \file - * - * \author FirstName LastName - */ - -#ifndef __OUTPUT_JSON_TEMPLATE_RUST_H__ -#define __OUTPUT_JSON_TEMPLATE_RUST_H__ - -void JsonTemplateLogRegister(void); - -#endif /* __OUTPUT_JSON_TEMPLATE_RUST_H__ */ diff --git a/src/output-json-tftp.c b/src/output-json-tftp.c deleted file mode 100644 index a0bc9ee1809e..000000000000 --- a/src/output-json-tftp.c +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (C) 2020-2021 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 Clément Galland - * - * Implement JSON/eve logging app-layer TFTP. - */ - -#include "suricata-common.h" -#include "detect.h" -#include "pkt-var.h" -#include "conf.h" - -#include "threads.h" -#include "threadvars.h" -#include "tm-threads.h" - -#include "util-unittest.h" -#include "util-buffer.h" -#include "util-debug.h" -#include "util-byte.h" - -#include "output.h" -#include "output-json.h" - -#include "app-layer.h" -#include "app-layer-parser.h" - -#include "app-layer-tftp.h" -#include "output-json-tftp.h" - -#include "rust.h" - -static int JsonTFTPLogger(ThreadVars *tv, void *thread_data, - const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) -{ - OutputJsonThreadCtx *thread = thread_data; - - JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_PACKET, "tftp", NULL, thread->ctx); - if (unlikely(jb == NULL)) { - return TM_ECODE_FAILED; - } - - if (unlikely(!rs_tftp_log_json_request(tx, jb))) { - goto error; - } - - OutputJsonBuilderBuffer(jb, thread); - - jb_free(jb); - return TM_ECODE_OK; - -error: - jb_free(jb); - return TM_ECODE_FAILED; -} - -static OutputInitResult OutputTFTPLogInitSub(ConfNode *conf, - OutputCtx *parent_ctx) -{ - AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_TFTP); - return OutputJsonLogInitSub(conf, parent_ctx); -} - -void JsonTFTPLogRegister(void) -{ - /* Register as an eve sub-module. */ - OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTFTPLog", "eve-log.tftp", - OutputTFTPLogInitSub, ALPROTO_TFTP, JsonTFTPLogger, JsonLogThreadInit, - JsonLogThreadDeinit, NULL); - - SCLogDebug("TFTP JSON logger registered."); -} diff --git a/src/output-json-tftp.h b/src/output-json-tftp.h deleted file mode 100644 index 3db4ba06cd55..000000000000 --- a/src/output-json-tftp.h +++ /dev/null @@ -1,29 +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 Clément Galland - */ - -#ifndef __OUTPUT_JSON_TFTP_H__ -#define __OUTPUT_JSON_TFTP_H__ - -void JsonTFTPLogRegister(void); - -#endif /* __OUTPUT_JSON_TFTP_H__ */ diff --git a/src/output.c b/src/output.c index 149dda58c284..0bb742b961be 100644 --- a/src/output.c +++ b/src/output.c @@ -54,12 +54,12 @@ #include "log-httplog.h" #include "output-json-http.h" #include "output-json-dns.h" -#include "output-json-modbus.h" #include "log-tlslog.h" #include "log-tlsstore.h" #include "output-json-tls.h" -#include "output-json-ssh.h" #include "log-pcap.h" +// for SSHTxLogCondition +#include "app-layer-ssh.h" #include "output-json-file.h" #include "output-json-smtp.h" #include "output-json-stats.h" @@ -69,26 +69,17 @@ #include "output-json-ftp.h" // for misplaced EveFTPDataAddMetadata #include "app-layer-ftp.h" -#include "output-json-tftp.h" #include "output-json-smb.h" #include "output-json-ike.h" -#include "output-json-krb5.h" -#include "output-json-quic.h" #include "output-json-dhcp.h" -#include "output-json-snmp.h" -#include "output-json-sip.h" -#include "output-json-rfb.h" #include "output-json-mqtt.h" #include "output-json-pgsql.h" -#include "output-json-template.h" -#include "output-json-rdp.h" -#include "output-json-http2.h" #include "output-lua.h" #include "output-json-dnp3.h" #include "output-json-metadata.h" #include "output-json-dcerpc.h" #include "output-json-frame.h" -#include "output-json-bittorrent-dht.h" +#include "app-layer-parser.h" #include "output-filestore.h" typedef struct RootLogger_ { @@ -1034,6 +1025,142 @@ void OutputRegisterRootLoggers(void) OutputStreamingLoggerRegister(); } +static int JsonGenericLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, + void *state, void *tx, uint64_t tx_id) +{ + OutputJsonThreadCtx *thread = thread_data; + EveJsonSimpleAppLayerLogger *al = SCEveJsonSimpleGetLogger(f->alproto); + if (al == NULL) { + return TM_ECODE_FAILED; + } + + const char *name; + switch (al->proto) { + case ALPROTO_HTTP2: + // special case + name = "http"; + break; + case ALPROTO_FTPDATA: + // underscore instead of dash + name = "ftp_data"; + break; + case ALPROTO_BITTORRENT_DHT: + // underscore instead of dash + name = "bittorrent_dht"; + break; + default: + name = AppProtoToString(al->proto); + } + JsonBuilder *js = CreateEveHeader(p, LOG_DIR_PACKET, name, NULL, thread->ctx); + if (unlikely(js == NULL)) { + return TM_ECODE_FAILED; + } + + if (!al->LogTx(tx, js)) { + goto error; + } + + OutputJsonBuilderBuffer(js, thread); + jb_free(js); + + return TM_ECODE_OK; + +error: + jb_free(js); + return TM_ECODE_FAILED; +} + +static OutputInitResult OutputBitTorrentDHTLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_BITTORRENT_DHT); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputRdpLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_RDP); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputWebSocketLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_WEBSOCKET); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputRFBLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_RFB); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputTemplateLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_TEMPLATE); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputSIPLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_SIP); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputSNMPLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_SNMP); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputQuicLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_QUIC); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputKRB5LogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_KRB5); + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_KRB5); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputTFTPLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_TFTP); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputModbusLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_MODBUS); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputHttp2LogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP2); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputSshLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SSH); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputFTPLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_FTP); + return OutputJsonLogInitSub(conf, parent_ctx); +} + +static OutputInitResult OutputFTPDataLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) +{ + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_FTPDATA); + return OutputJsonLogInitSub(conf, parent_ctx); +} + /** * \brief Register all non-root logging modules. */ @@ -1058,13 +1185,17 @@ void OutputRegisterLoggers(void) /* http log */ LogHttpLogRegister(); JsonHttpLogRegister(); - JsonHttp2LogRegister(); + OutputRegisterTxSubModuleWithProgress(LOGGER_JSON_TX, "eve-log", "LogHttp2Log", "eve-log.http2", + OutputHttp2LogInitSub, ALPROTO_HTTP2, JsonGenericLogger, HTTP2StateClosed, + HTTP2StateClosed, JsonLogThreadInit, JsonLogThreadDeinit, NULL); /* tls log */ LogTlsLogRegister(); JsonTlsLogRegister(); LogTlsStoreRegister(); /* ssh */ - JsonSshLogRegister(); + OutputRegisterTxSubModuleWithCondition(LOGGER_JSON_TX, "eve-log", "JsonSshLog", "eve-log.ssh", + OutputSshLogInitSub, ALPROTO_SSH, JsonGenericLogger, SSHTxLogCondition, + JsonLogThreadInit, JsonLogThreadDeinit, NULL); /* pcap log */ PcapLogRegister(); /* file log */ @@ -1073,7 +1204,11 @@ void OutputRegisterLoggers(void) /* dns */ JsonDnsLogRegister(); /* modbus */ - JsonModbusLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonModbusLog", "eve-log.modbus", + OutputModbusLogInitSub, ALPROTO_MODBUS, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + + SCLogDebug("modbus json logger registered."); /* tcp streaming data */ LogTcpDataLogRegister(); /* log stats */ @@ -1094,39 +1229,82 @@ void OutputRegisterLoggers(void) /* NFS JSON logger. */ JsonNFSLogRegister(); /* TFTP JSON logger. */ - JsonTFTPLogRegister(); - /* FTP JSON logger. */ - JsonFTPLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTFTPLog", "eve-log.tftp", + OutputTFTPLogInitSub, ALPROTO_TFTP, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + + SCLogDebug("TFTP JSON logger registered."); + /* FTP and FTP-DATA JSON loggers. */ + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonFTPLog", "eve-log.ftp", + OutputFTPLogInitSub, ALPROTO_FTP, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonFTPLog", "eve-log.ftp", + OutputFTPDataLogInitSub, ALPROTO_FTPDATA, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + SCLogDebug("FTP JSON logger registered."); + /* SMB JSON logger. */ JsonSMBLogRegister(); /* IKE JSON logger. */ JsonIKELogRegister(); /* KRB5 JSON logger. */ - JsonKRB5LogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonKRB5Log", "eve-log.krb5", + OutputKRB5LogInitSub, ALPROTO_KRB5, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + + SCLogDebug("KRB5 JSON logger registered."); /* QUIC JSON logger. */ - JsonQuicLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonQuicLog", "eve-log.quic", + OutputQuicLogInitSub, ALPROTO_QUIC, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + + SCLogDebug("quic json logger registered."); /* DHCP JSON logger. */ JsonDHCPLogRegister(); /* SNMP JSON logger. */ - JsonSNMPLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonSNMPLog", "eve-log.snmp", + OutputSNMPLogInitSub, ALPROTO_SNMP, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + + SCLogDebug("SNMP JSON logger registered."); /* SIP JSON logger. */ - JsonSIPLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonSIPLog", "eve-log.sip", + OutputSIPLogInitSub, ALPROTO_SIP, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + + SCLogDebug("SIP JSON logger registered."); /* RFB JSON logger. */ - JsonRFBLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonRFBLog", "eve-log.rfb", + OutputRFBLogInitSub, ALPROTO_RFB, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); /* MQTT JSON logger. */ JsonMQTTLogRegister(); /* Pgsql JSON logger. */ JsonPgsqlLogRegister(); + /* WebSocket JSON logger. */ + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonWebSocketLog", "eve-log.websocket", + OutputWebSocketLogInitSub, ALPROTO_WEBSOCKET, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); /* Template JSON logger. */ - JsonTemplateLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTemplateLog", "eve-log.template", + OutputTemplateLogInitSub, ALPROTO_TEMPLATE, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); /* RDP JSON logger. */ - JsonRdpLogRegister(); + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonRdpLog", "eve-log.rdp", + OutputRdpLogInitSub, ALPROTO_RDP, JsonGenericLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); + SCLogDebug("rdp json logger registered."); /* DCERPC JSON logger. */ JsonDCERPCLogRegister(); /* app layer frames */ JsonFrameLogRegister(); /* BitTorrent DHT JSON logger */ - JsonBitTorrentDHTLogRegister(); + if (ConfGetNode("app-layer.protocols.bittorrent-dht") != NULL) { + /* Register as an eve sub-module. */ + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonBitTorrentDHTLog", + "eve-log.bittorrent-dht", OutputBitTorrentDHTLogInitSub, ALPROTO_BITTORRENT_DHT, + JsonGenericLogger, JsonLogThreadInit, JsonLogThreadDeinit, NULL); + } } static EveJsonSimpleAppLayerLogger simple_json_applayer_loggers[ALPROTO_MAX] = { @@ -1159,6 +1337,7 @@ static EveJsonSimpleAppLayerLogger simple_json_applayer_loggers[ALPROTO_MAX] = { { ALPROTO_MQTT, JsonMQTTAddMetadata }, { ALPROTO_PGSQL, NULL }, // TODO missing { ALPROTO_TELNET, NULL }, // no logging + { ALPROTO_WEBSOCKET, rs_websocket_logger_log }, { ALPROTO_TEMPLATE, rs_template_logger_log }, { ALPROTO_RDP, (EveJsonSimpleTxLogFunc)rs_rdp_to_json }, { ALPROTO_HTTP2, rs_http2_log_json }, diff --git a/suricata.yaml.in b/suricata.yaml.in index 630399126dbe..dbe8b065168f 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -164,6 +164,8 @@ outputs: # metadata: no # enable inclusion of app layer metadata with alert. Default yes # http-body: yes # Requires metadata; enable dumping of HTTP body in Base64 # http-body-printable: yes # Requires metadata; enable dumping of HTTP body in printable format + # websocket-payload: yes # Requires metadata; enable dumping of WebSocket Payload in Base64 + # websocket-payload-printable: yes # Requires metadata; enable dumping of WebSocket Payload in printable format # Enable the logging of tagged packets for rules using the # "tag" keyword. @@ -279,6 +281,7 @@ outputs: #md5: [body, subject] #- dnp3 + - websocket - ftp - rdp - nfs @@ -923,6 +926,10 @@ app-layer: ftp: enabled: yes # memcap: 64mb + websocket: + #enabled: yes + # Maximum used payload size, the rest is skipped + # max-payload-size: 65535 rdp: #enabled: yes ssh: