diff --git a/doc/userguide/rules/base64-keywords.rst b/doc/userguide/rules/base64-keywords.rst index 81db203448a2..9e4f942e46d8 100644 --- a/doc/userguide/rules/base64-keywords.rst +++ b/doc/userguide/rules/base64-keywords.rst @@ -10,6 +10,8 @@ base64_decode Decodes base64 data from a buffer and makes it available for the base64_data function. +We recommend using the base64 transform instead -- see :ref:`from_base64 `. + Syntax:: base64_decode:bytes , offset , relative; diff --git a/doc/userguide/rules/transforms.rst b/doc/userguide/rules/transforms.rst index f730f0d2dc71..e536757f29f7 100644 --- a/doc/userguide/rules/transforms.rst +++ b/doc/userguide/rules/transforms.rst @@ -96,7 +96,7 @@ to_uppercase Converts the buffer to uppercase and passes the value on. -This example alerts if ``http.uri`` contains ``THIS TEXT HAS BEEN CONVERTED TO LOWERCASE`` +This example alerts if ``http.uri`` contains ``THIS TEXT HAS BEEN CONVERTED TO UPPERCASE`` Example:: @@ -188,3 +188,58 @@ Example:: alert http any any -> any any (msg:"HTTP ua only"; http.header_names; \ bsize:16; content:"|0d 0a|User-Agent|0d 0a 0d 0a|"; nocase; sid:1;) + +.. _from_base64: + +from_base64 +----------- + +This transform is similar to the keyword ``base64_decode``: the buffer is decoded using +the optional values for ``mode``, ``offset`` and ``bytes`` and is available for matching +on the decoded data. + +After this transform completes, the buffer will contain only bytes that could be bases64-decoded. +If the decoding process encountered invalid bytes, those will not be included in the buffer. + +The option values must be ``,`` separated and can appear in any order. + +.. note:: ``from_base64`` follows RFC 4648 by default i.e. encounter with any character + that is not found in the base64 alphabet leads to rejection of that character and the + rest of the string. + +Format:: + + from_base64: [[bytes ] [, offset [, mode: strict|rfc4648|rfc2045]]] + +There are defaults for each of the options: +- ``bytes`` defaults to the length of the input buffer +- ``offset`` defaults to ``0`` and must be less than ``65536`` +- ``mode`` defaults to ``rfc4648`` + +Note that both ``bytes`` and ``offset`` may be variables from `byte_extract` and/or `byte_math` in +later versions of Suricata. They are not supported yet. + +Mode ``rfc4648`` applies RFC 4648 decoding logic which is suitable for encoding binary +data that can be safely sent by email, used in a URL, or included with HTTP POST requests. + +Mode ``rfc2045`` applies RFC 2045 decoding logic which supports strings, including those with embedded spaces, +line breaks, and any non base64 alphabet. + +Mode ``strict`` will fail if an invalid character is found in the encoded bytes. + +The following examples will alert when the buffer contents match (see the +last ``content`` value for the expected strings). + +This example uses the defaults and transforms `"VGhpcyBpcyBTdXJpY2F0YQ=="` to `"This is Suricata"`:: + + content: "VGhpcyBpcyBTdXJpY2F0YQ=="; from_base64; content:"This is Suricata"; + +This example transforms `"dGhpc2lzYXRlc3QK"` to `"thisisatest"`:: + + content:"/?arg=dGhpc2lzYXRlc3QK"; from_base64: offset 6, mode rfc4648; \ + content:"thisisatest"; + +This example transforms `"Zm 9v Ym Fy"` to `"foobar"`:: + + content:"/?arg=Zm 9v Ym Fy"; from_base64: offset 6, mode rfc2045; \ + content:"foobar"; diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index 4de3971c94c3..bafecb804cca 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -60,6 +60,8 @@ Major changes - It is possible to see an increase of alerts, for the same rule-sets, if you use many stream/payload rules, due to Suricata triggering TCP stream reassembly earlier. +- New transform ``from_base64`` that base64 decodes a buffer and passes the + decoded buffer. It's recommended that ``from_base64`` be used instead of ``base64_decode`` Upgrading 6.0 to 7.0 -------------------- diff --git a/rust/src/detect/byte_math.rs b/rust/src/detect/byte_math.rs index c362e37b48ad..833adfa90a2c 100644 --- a/rust/src/detect/byte_math.rs +++ b/rust/src/detect/byte_math.rs @@ -18,7 +18,7 @@ // Author: Jeff Lucovsky use crate::detect::error::RuleParseError; -use crate::detect::parser::{parse_token, take_until_whitespace}; +use crate::detect::parser::{parse_var, take_until_whitespace, ResultValue}; use std::ffi::{CStr, CString}; use std::os::raw::c_char; @@ -88,12 +88,6 @@ pub const DETECT_BYTEMATH_FIXED_PARAM_COUNT: usize = 5; // Optional parameters: endian, relative, string, dce, bitmask pub const DETECT_BYTEMATH_MAX_PARAM_COUNT: usize = 10; -#[derive(Debug)] -enum ResultValue { - Numeric(u64), - String(String), -} - #[repr(C)] #[derive(Debug)] pub struct DetectByteMathData { @@ -194,17 +188,6 @@ fn get_endian_value(value: &str) -> Result { Ok(res) } -// Parsed as a u64 for validation with u32 {min,max} so values greater than uint32 -// are not treated as a string value. -fn parse_var(input: &str) -> IResult<&str, ResultValue, RuleParseError<&str>> { - let (input, value) = parse_token(input)?; - if let Ok(val) = value.parse::() { - Ok((input, ResultValue::Numeric(val))) - } else { - Ok((input, ResultValue::String(value.to_string()))) - } -} - fn parse_bytemath(input: &str) -> IResult<&str, DetectByteMathData, RuleParseError<&str>> { // Inner utility function for easy error creation. fn make_error(reason: String) -> nom7::Err> { diff --git a/rust/src/detect/error.rs b/rust/src/detect/error.rs index 4959e2c883a4..ec395d0682f2 100644 --- a/rust/src/detect/error.rs +++ b/rust/src/detect/error.rs @@ -20,9 +20,12 @@ use nom7::error::{ErrorKind, ParseError}; /// Custom rule parse errors. /// /// Implemented based on the Nom example for implementing custom errors. +/// The string is an error message provided by the parsing logic, e.g., +/// Incorrect usage because of "x", "y" and "z" #[derive(Debug, PartialEq, Eq)] pub enum RuleParseError { InvalidByteMath(String), + InvalidTransformBase64(String), Nom(I, ErrorKind), } diff --git a/rust/src/detect/mod.rs b/rust/src/detect/mod.rs index 2b1fd0e464b0..94a60eff63fd 100644 --- a/rust/src/detect/mod.rs +++ b/rust/src/detect/mod.rs @@ -22,6 +22,7 @@ pub mod error; pub mod iprep; pub mod parser; pub mod stream_size; +pub mod transform_base64; pub mod uint; pub mod uri; pub mod requires; diff --git a/rust/src/detect/parser.rs b/rust/src/detect/parser.rs index 0ac584634d74..d75e010e676a 100644 --- a/rust/src/detect/parser.rs +++ b/rust/src/detect/parser.rs @@ -22,12 +22,27 @@ use nom7::character::complete::multispace0; use nom7::sequence::preceded; use nom7::IResult; +#[derive(Debug)] +pub enum ResultValue { + Numeric(u64), + String(String), +} + static WHITESPACE: &str = " \t\r\n"; /// Parse all characters up until the next whitespace character. pub fn take_until_whitespace(input: &str) -> IResult<&str, &str, RuleParseError<&str>> { nom7::bytes::complete::is_not(WHITESPACE)(input) } +// Parsed as a u64 so the value can be validated against a u32 min/max if needed. +pub fn parse_var(input: &str) -> IResult<&str, ResultValue, RuleParseError<&str>> { + let (input, value) = parse_token(input)?; + if let Ok(val) = value.parse::() { + Ok((input, ResultValue::Numeric(val))) + } else { + Ok((input, ResultValue::String(value.to_string()))) + } +} /// Parse the next token ignoring leading whitespace. /// /// A token is the next sequence of chars until a terminating character. Leading whitespace diff --git a/rust/src/detect/transform_base64.rs b/rust/src/detect/transform_base64.rs new file mode 100644 index 000000000000..1fae2915a404 --- /dev/null +++ b/rust/src/detect/transform_base64.rs @@ -0,0 +1,441 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// Author: Jeff Lucovsky + +use crate::detect::error::RuleParseError; +use crate::detect::parser::{parse_var, take_until_whitespace, ResultValue}; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +use nom7::bytes::complete::tag; +use nom7::character::complete::multispace0; +use nom7::sequence::preceded; +use nom7::{Err, IResult}; +use std::str; + +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DetectBase64Mode { + Base64ModeRelax = 0, + /* If the following strings were to be passed to the decoder with RFC2045 mode, + * the results would be as follows. See the unittest B64TestVectorsRFC2045 in + * src/util-base64.c + * + * BASE64("") = "" + * BASE64("f") = "Zg==" + * BASE64("fo") = "Zm8=" + * BASE64("foo") = "Zm9v" + * BASE64("foob") = "Zm9vYg==" + * BASE64("fooba") = "Zm9vYmE=" + * BASE64("foobar") = "Zm9vYmFy" + * BASE64("foobar") = "Zm 9v Ym Fy" <-- Notice how the spaces are ignored + * BASE64("foobar") = "Zm$9vYm.Fy" # According to RFC 2045, All line breaks or *other + * characters* not found in base64 alphabet must be ignored by decoding software + * */ + Base64ModeRFC2045, /* SPs are allowed during transfer but must be skipped by Decoder */ + Base64ModeStrict, + /* If the following strings were to be passed to the decoder with RFC4648 mode, + * the results would be as follows. See the unittest B64TestVectorsRFC4648 in + * src/util-base64.c + * + * BASE64("") = "" + * BASE64("f") = "Zg==" + * BASE64("fo") = "Zm8=" + * BASE64("foo") = "Zm9v" + * BASE64("foob") = "Zm9vYg==" + * BASE64("fooba") = "Zm9vYmE=" + * BASE64("foobar") = "Zm9vYmFy" + * BASE64("f") = "Zm 9v Ym Fy" <-- Notice how the processing stops once space is encountered + * BASE64("f") = "Zm$9vYm.Fy" <-- Notice how the processing stops once an invalid char is + * encountered + * */ + Base64ModeRFC4648, /* reject the encoded data if it contains characters outside the base alphabet */ +} + +pub const TRANSFORM_FROM_BASE64_MODE_DEFAULT: DetectBase64Mode = DetectBase64Mode::Base64ModeRFC4648; + +const DETECT_TRANSFORM_BASE64_MAX_PARAM_COUNT: usize = 3; +pub const DETECT_TRANSFORM_BASE64_FLAG_MODE: u8 = 0x01; +pub const DETECT_TRANSFORM_BASE64_FLAG_NBYTES: u8 = 0x02; +pub const DETECT_TRANSFORM_BASE64_FLAG_OFFSET: u8 = 0x04; +pub const DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR: u8 = 0x08; +pub const DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR: u8 = 0x10; + +#[repr(C)] +#[derive(Debug)] +pub struct SCDetectTransformFromBase64Data { + flags: u8, + nbytes: u32, + nbytes_str: *const c_char, + offset: u32, + offset_str: *const c_char, + mode: DetectBase64Mode, +} + +impl Drop for SCDetectTransformFromBase64Data { + fn drop(&mut self) { + unsafe { + if !self.offset_str.is_null() { + let _ = CString::from_raw(self.offset_str as *mut c_char); + } + if !self.nbytes_str.is_null() { + let _ = CString::from_raw(self.nbytes_str as *mut c_char); + } + } + } +} +impl Default for SCDetectTransformFromBase64Data { + fn default() -> Self { + SCDetectTransformFromBase64Data { + flags: 0, + nbytes: 0, + nbytes_str: std::ptr::null_mut(), + offset: 0, + offset_str: std::ptr::null_mut(), + mode: TRANSFORM_FROM_BASE64_MODE_DEFAULT, + } + } +} + +impl SCDetectTransformFromBase64Data { + pub fn new() -> Self { + Self { + ..Default::default() + } + } +} + +fn get_mode_value(value: &str) -> Option { + let res = match value { + "rfc4648" => Some(DetectBase64Mode::Base64ModeRFC4648), + "rfc2045" => Some(DetectBase64Mode::Base64ModeRFC2045), + "strict" => Some(DetectBase64Mode::Base64ModeStrict), + _ => None, + }; + + res +} + +fn parse_transform_base64( + input: &str, +) -> IResult<&str, SCDetectTransformFromBase64Data, RuleParseError<&str>> { + // Inner utility function for easy error creation. + fn make_error(reason: String) -> nom7::Err> { + Err::Error(RuleParseError::InvalidTransformBase64(reason)) + } + let mut transform_base64 = SCDetectTransformFromBase64Data::new(); + + // No options so return defaults + if input.is_empty() { + return Ok((input, transform_base64)); + } + let (_, values) = nom7::multi::separated_list1( + tag(","), + preceded(multispace0, nom7::bytes::complete::is_not(",")), + )(input)?; + + // Too many options? + if values.len() > DETECT_TRANSFORM_BASE64_MAX_PARAM_COUNT { + return Err(make_error(format!("Incorrect argument string; at least 1 value must be specified but no more than {}: {:?}", + DETECT_TRANSFORM_BASE64_MAX_PARAM_COUNT, input))); + } + + for value in values { + let (mut val, mut name) = take_until_whitespace(value)?; + val = val.trim(); + name = name.trim(); + match name { + "mode" => { + if 0 != (transform_base64.flags & DETECT_TRANSFORM_BASE64_FLAG_MODE) { + return Err(make_error("mode already set".to_string())); + } + if let Some(mode) = get_mode_value(val) { + transform_base64.mode = mode; + } else { + return Err(make_error(format!("invalid mode value: {}", val))); + } + transform_base64.flags |= DETECT_TRANSFORM_BASE64_FLAG_MODE; + } + + "offset" => { + if 0 != (transform_base64.flags & DETECT_TRANSFORM_BASE64_FLAG_OFFSET) { + return Err(make_error("offset already set".to_string())); + } + + let (_, res) = parse_var(val)?; + match res { + ResultValue::Numeric(val) => { + if val <= u16::MAX.into() { + transform_base64.offset = val as u32 + } else { + return Err(make_error(format!( + "invalid offset value: must be between 0 and {}: {}", + u16::MAX, val + ))); + } + } + ResultValue::String(val) => match CString::new(val) { + Ok(newval) => { + transform_base64.offset_str = newval.into_raw(); + transform_base64.flags |= DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR; + } + _ => { + return Err(make_error( + "parse string not safely convertible to C".to_string(), + )) + } + }, + } + + transform_base64.flags |= DETECT_TRANSFORM_BASE64_FLAG_OFFSET; + } + + "bytes" => { + if 0 != (transform_base64.flags & DETECT_TRANSFORM_BASE64_FLAG_NBYTES) { + return Err(make_error("bytes already set".to_string())); + } + let (_, res) = parse_var(val)?; + match res { + ResultValue::Numeric(val) => { + if val as u32 <= u16::MAX.into() { + transform_base64.nbytes = val as u32 + } else { + return Err(make_error(format!( + "invalid bytes value: must be between {} and {}: {}", + 0, u16::MAX, val + ))); + } + } + ResultValue::String(val) => match CString::new(val) { + Ok(newval) => { + transform_base64.nbytes_str = newval.into_raw(); + transform_base64.flags |= DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR; + } + _ => { + return Err(make_error( + "parse string not safely convertible to C".to_string(), + )) + } + }, + } + transform_base64.flags |= DETECT_TRANSFORM_BASE64_FLAG_NBYTES; + } + _ => { + return Err(make_error(format!("unknown base64 keyword: {}", name))); + } + }; + } + + Ok((input, transform_base64)) +} + +/// Intermediary function between the C code and the parsing functions. +#[no_mangle] +pub unsafe extern "C" fn SCTransformBase64Parse( + c_arg: *const c_char, +) -> *mut SCDetectTransformFromBase64Data { + if c_arg.is_null() { + return std::ptr::null_mut(); + } + + let arg = CStr::from_ptr(c_arg) + .to_str() + .unwrap_or(""); + + match parse_transform_base64(arg) { + Ok((_, detect)) => return Box::into_raw(Box::new(detect)), + Err(_) => return std::ptr::null_mut(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn SCTransformBase64Free(ptr: *mut SCDetectTransformFromBase64Data) { + if !ptr.is_null() { + let _ = Box::from_raw(ptr); + } +} + +#[cfg(test)] +mod tests { + use super::*; + // structure equality only used by test cases + impl PartialEq for SCDetectTransformFromBase64Data { + fn eq(&self, other: &Self) -> bool { + let mut res: bool = true; + + if !self.nbytes_str.is_null() && !other.nbytes_str.is_null() { + let s_val = unsafe { CStr::from_ptr(self.nbytes_str) }; + let o_val = unsafe { CStr::from_ptr(other.nbytes_str) }; + res = s_val == o_val; + } else if !self.nbytes_str.is_null() || !other.nbytes_str.is_null() { + return false; + } + + if !self.offset_str.is_null() && !other.offset_str.is_null() { + let s_val = unsafe { CStr::from_ptr(self.offset_str) }; + let o_val = unsafe { CStr::from_ptr(other.offset_str) }; + res = s_val == o_val; + } else if !self.offset_str.is_null() || !other.offset_str.is_null() { + return false; + } + + res && self.nbytes == other.nbytes + && self.flags == other.flags + && self.offset == other.offset + && self.mode == other.mode + } + } + + fn valid_test( + args: &str, + nbytes: u32, + nbytes_str: &str, + offset: u32, + offset_str: &str, + mode: DetectBase64Mode, + flags: u8, + ) { + let tbd = SCDetectTransformFromBase64Data { + flags, + nbytes, + nbytes_str: if !nbytes_str.is_empty() { + CString::new(nbytes_str).unwrap().into_raw() + } else { + std::ptr::null_mut() + }, + offset, + offset_str: if !offset_str.is_empty() { + CString::new(offset_str).unwrap().into_raw() + } else { + std::ptr::null_mut() + }, + mode, + }; + + let (_, val) = parse_transform_base64(args).unwrap(); + assert_eq!(val, tbd); + } + + #[test] + fn test_parser_invalid() { + assert!(parse_transform_base64("bytes 4, offset 3933, mode unknown").is_err()); + assert!(parse_transform_base64("bytes 4, offset 70000, mode strict").is_err()); + assert!( + parse_transform_base64("bytes 4, offset 70000, mode strict, mode rfc2045").is_err() + ); + } + + #[test] + fn test_parser_parse_partial_valid() { + let mut tbd = SCDetectTransformFromBase64Data { + nbytes: 4, + offset: 0, + mode: TRANSFORM_FROM_BASE64_MODE_DEFAULT, + flags: 0, + ..Default::default() + }; + + tbd.mode = TRANSFORM_FROM_BASE64_MODE_DEFAULT; + tbd.flags = DETECT_TRANSFORM_BASE64_FLAG_NBYTES; + let (_, val) = parse_transform_base64("bytes 4").unwrap(); + assert_eq!(val, tbd); + + tbd.offset = 3933; + tbd.flags = DETECT_TRANSFORM_BASE64_FLAG_NBYTES | DETECT_TRANSFORM_BASE64_FLAG_OFFSET; + let (_, val) = parse_transform_base64("bytes 4, offset 3933").unwrap(); + assert_eq!(val, tbd); + + tbd.flags = DETECT_TRANSFORM_BASE64_FLAG_NBYTES | DETECT_TRANSFORM_BASE64_FLAG_OFFSET; + let (_, val) = parse_transform_base64("offset 3933, bytes 4").unwrap(); + assert_eq!(val, tbd); + + tbd.flags = DETECT_TRANSFORM_BASE64_FLAG_MODE; + tbd.mode = DetectBase64Mode::Base64ModeRFC2045; + tbd.offset = 0; + tbd.nbytes = 0; + let (_, val) = parse_transform_base64("mode rfc2045").unwrap(); + assert_eq!(val, tbd); + } + + #[test] + fn test_parser_parse_valid() { + valid_test("", 0, "", 0, "", TRANSFORM_FROM_BASE64_MODE_DEFAULT, 0); + + valid_test( + "bytes 4, offset 3933, mode strict", + 4, + "", + 3933, + "", + DetectBase64Mode::Base64ModeStrict, + DETECT_TRANSFORM_BASE64_FLAG_NBYTES + | DETECT_TRANSFORM_BASE64_FLAG_OFFSET + | DETECT_TRANSFORM_BASE64_FLAG_MODE, + ); + + valid_test( + "bytes 4, offset 3933, mode rfc2045", + 4, + "", + 3933, + "", + DetectBase64Mode::Base64ModeRFC2045, + DETECT_TRANSFORM_BASE64_FLAG_NBYTES + | DETECT_TRANSFORM_BASE64_FLAG_OFFSET + | DETECT_TRANSFORM_BASE64_FLAG_MODE, + ); + + valid_test( + "bytes 4, offset 3933, mode rfc4648", + 4, + "", + 3933, + "", + DetectBase64Mode::Base64ModeRFC4648, + DETECT_TRANSFORM_BASE64_FLAG_NBYTES + | DETECT_TRANSFORM_BASE64_FLAG_OFFSET + | DETECT_TRANSFORM_BASE64_FLAG_MODE, + ); + + valid_test( + "bytes 4, offset var, mode rfc4648", + 4, + "", + 0, + "var", + DetectBase64Mode::Base64ModeRFC4648, + DETECT_TRANSFORM_BASE64_FLAG_NBYTES + | DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR + | DETECT_TRANSFORM_BASE64_FLAG_OFFSET + | DETECT_TRANSFORM_BASE64_FLAG_MODE, + ); + + valid_test( + "bytes var, offset 3933, mode rfc4648", + 0, + "var", + 3933, + "", + DetectBase64Mode::Base64ModeRFC4648, + DETECT_TRANSFORM_BASE64_FLAG_NBYTES + | DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR + | DETECT_TRANSFORM_BASE64_FLAG_OFFSET + | DETECT_TRANSFORM_BASE64_FLAG_MODE, + ); + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 4ccc691802d2..6904c33d455c 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -355,6 +355,7 @@ noinst_HEADERS = \ detect-tls-version.h \ detect-tls-random.h \ detect-tos.h \ + detect-transform-base64.h \ detect-transform-casechange.h \ detect-transform-compress-whitespace.h \ detect-transform-dotprefix.h \ @@ -980,6 +981,7 @@ libsuricata_c_a_SOURCES = \ detect-tls-version.c \ detect-tls-random.c \ detect-tos.c \ + detect-transform-base64.c \ detect-transform-casechange.c \ detect-transform-compress-whitespace.c \ detect-transform-dotprefix.c \ diff --git a/src/datasets.c b/src/datasets.c index 01ef5bb47c90..31fa6b398ca2 100644 --- a/src/datasets.c +++ b/src/datasets.c @@ -23,6 +23,7 @@ #include "suricata-common.h" #include "suricata.h" +#include "rust.h" #include "conf.h" #include "datasets.h" #include "datasets-string.h" @@ -522,7 +523,7 @@ static int DatasetLoadString(Dataset *set) uint8_t decoded[strlen(line)]; uint32_t consumed = 0, num_decoded = 0; Base64Ecode code = DecodeBase64(decoded, strlen(line), (const uint8_t *)line, - strlen(line), &consumed, &num_decoded, BASE64_MODE_STRICT); + strlen(line), &consumed, &num_decoded, Base64ModeStrict); if (code == BASE64_ECODE_ERR) { FatalErrorOnInit("bad base64 encoding %s/%s", set->name, set->load); continue; @@ -543,7 +544,7 @@ static int DatasetLoadString(Dataset *set) uint8_t decoded[strlen(line)]; uint32_t consumed = 0, num_decoded = 0; Base64Ecode code = DecodeBase64(decoded, strlen(line), (const uint8_t *)line, - strlen(line), &consumed, &num_decoded, BASE64_MODE_STRICT); + strlen(line), &consumed, &num_decoded, Base64ModeStrict); if (code == BASE64_ECODE_ERR) { FatalErrorOnInit("bad base64 encoding %s/%s", set->name, set->load); continue; @@ -1606,7 +1607,7 @@ static int DatasetOpSerialized(Dataset *set, const char *string, DatasetOpFunc D uint8_t decoded[strlen(string)]; uint32_t consumed = 0, num_decoded = 0; Base64Ecode code = DecodeBase64(decoded, strlen(string), (const uint8_t *)string, - strlen(string), &consumed, &num_decoded, BASE64_MODE_STRICT); + strlen(string), &consumed, &num_decoded, Base64ModeStrict); if (code == BASE64_ECODE_ERR) { return -2; } diff --git a/src/detect-base64-decode.c b/src/detect-base64-decode.c index 2794509a430f..2e4a16e68a8d 100644 --- a/src/detect-base64-decode.c +++ b/src/detect-base64-decode.c @@ -96,7 +96,7 @@ int DetectBase64DecodeDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s uint32_t consumed = 0, num_decoded = 0; (void)DecodeBase64(det_ctx->base64_decoded, det_ctx->base64_decoded_len_max, payload, - decode_len, &consumed, &num_decoded, BASE64_MODE_RFC4648); + decode_len, &consumed, &num_decoded, Base64ModeRFC4648); det_ctx->base64_decoded_len = num_decoded; SCLogDebug("Decoded %d bytes from base64 data.", det_ctx->base64_decoded_len); diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 7b7e5442aad1..37fd04588c00 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -260,6 +260,7 @@ #include "detect-transform-xor.h" #include "detect-transform-casechange.h" #include "detect-transform-header-lowercase.h" +#include "detect-transform-base64.h" #include "util-rule-vars.h" @@ -765,6 +766,7 @@ void SigTableSetup(void) DetectTransformToLowerRegister(); DetectTransformToUpperRegister(); DetectTransformHeaderLowercaseRegister(); + DetectTransformFromBase64DecodeRegister(); DetectFileHandlerRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 58908c05d756..365165bd1404 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -360,6 +360,7 @@ enum DetectKeywordId { DETECT_TRANSFORM_TOLOWER, DETECT_TRANSFORM_TOUPPER, DETECT_TRANSFORM_HEADER_LOWERCASE, + DETECT_TRANSFORM_FROM_BASE64, DETECT_AL_IKE_EXCH_TYPE, DETECT_AL_IKE_SPI_INITIATOR, diff --git a/src/detect-transform-base64.c b/src/detect-transform-base64.c new file mode 100644 index 000000000000..47204db0cb67 --- /dev/null +++ b/src/detect-transform-base64.c @@ -0,0 +1,381 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Jeff Lucovsky + * + * Implements the from_base64 transformation keyword + */ + +#include "suricata-common.h" + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-byte.h" + +#include "rust.h" + +#include "detect-transform-base64.h" + +#include "util-base64.h" +#include "util-unittest.h" +#include "util-print.h" + +static int DetectTransformFromBase64DecodeSetup(DetectEngineCtx *, Signature *, const char *); +static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *, void *); +#ifdef UNITTESTS +static void DetectTransformFromBase64DecodeRegisterTests(void); +#endif +static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options); + +#define DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT (uint8_t) Base64ModeRFC4648 + +void DetectTransformFromBase64DecodeRegister(void) +{ + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].name = "from_base64"; + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].desc = "convert the base64 decode of the buffer"; + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].url = "/rules/transforms.html#from_base64"; + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Setup = DetectTransformFromBase64DecodeSetup; + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Transform = TransformFromBase64Decode; + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Free = DetectTransformFromBase64DecodeFree; +#ifdef UNITTESTS + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].RegisterTests = + DetectTransformFromBase64DecodeRegisterTests; +#endif + sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].flags |= SIGMATCH_OPTIONAL_OPT; +} + +static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *de_ctx, void *ptr) +{ + SCTransformBase64Free(ptr); +} + +static SCDetectTransformFromBase64Data *DetectTransformFromBase64DecodeParse(const char *str) +{ + SCDetectTransformFromBase64Data *tbd = SCTransformBase64Parse(str); + if (tbd == NULL) { + SCLogError("invalid transform_base64 values"); + } + return tbd; +} + +/** + * \internal + * \brief Base64 decode the input buffer + * \param det_ctx detection engine ctx + * \param s signature + * \param opts_str transform options, if any + * \retval 0 No decode + * \retval >0 Decoded byte count + */ +static int DetectTransformFromBase64DecodeSetup( + DetectEngineCtx *de_ctx, Signature *s, const char *opts_str) +{ + int r = -1; + + SCEnter(); + + SCDetectTransformFromBase64Data *b64d = DetectTransformFromBase64DecodeParse(opts_str); + if (b64d == NULL) + SCReturnInt(r); + + if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR) { + SCLogError("offset value must be a value, not a variable name"); + goto exit_path; + } + + if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR) { + SCLogError("byte value must be a value, not a variable name"); + goto exit_path; + } + + r = DetectSignatureAddTransform(s, DETECT_TRANSFORM_FROM_BASE64, b64d); + +exit_path: + if (r != 0) + DetectTransformFromBase64DecodeFree(de_ctx, b64d); + SCReturnInt(r); +} + +static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options) +{ + SCDetectTransformFromBase64Data *b64d = options; + const uint8_t *input = buffer->inspect; + const uint32_t input_len = buffer->inspect_len; + /* + * The output buffer is larger than needed. On average, + * a base64 encoded string is 75% of the encoded + * string. + */ + uint8_t output[input_len]; + uint32_t decode_length = input_len; + + DetectBase64Mode mode = b64d->mode; + uint32_t offset = b64d->offset; + uint32_t nbytes = b64d->nbytes; + + if (offset) { + if (offset > input_len) { + SCLogDebug("offset %d exceeds length %d; returning", offset, input_len); + return; + } + input += offset; + decode_length -= offset; + } + + if (nbytes) { + if (nbytes > decode_length) { + SCLogDebug("byte count %d plus offset %d exceeds length %d; returning", nbytes, offset, + input_len); + return; + } + decode_length = nbytes; + } + + PrintRawDataFp(stdout, input, input_len); + uint32_t decoded_length = 0; + uint32_t consumed = 0; + Base64Ecode code = + DecodeBase64(output, input_len, input, decode_length, &consumed, &decoded_length, mode); + if (code != BASE64_ECODE_ERR) { + PrintRawDataFp(stdout, output, decoded_length); + if (decoded_length) { + InspectionBufferCopy(buffer, output, decoded_length); + } + } +} + +#ifdef UNITTESTS +/* Simple success case -- check buffer */ +static int DetectTransformFromBase64DecodeTest01(void) +{ + const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ=="; + uint32_t input_len = strlen((char *)input); + const char *result = "This is Suricata"; + uint32_t result_len = strlen((char *)result); + SCDetectTransformFromBase64Data b64d = { + .nbytes = input_len, + .mode = DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT, + }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_len == result_len); + FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* Simple success case with RFC2045 -- check buffer */ +static int DetectTransformFromBase64DecodeTest01a(void) +{ + const uint8_t *input = (const uint8_t *)"Zm 9v Ym Fy"; + uint32_t input_len = strlen((char *)input); + const char *result = "foobar"; + uint32_t result_len = strlen((char *)result); + SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeRFC2045 }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_len == result_len); + FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* Decode failure case -- ensure no change to buffer */ +static int DetectTransformFromBase64DecodeTest02(void) +{ + const uint8_t *input = (const uint8_t *)"This is Suricata\n"; + uint32_t input_len = strlen((char *)input); + SCDetectTransformFromBase64Data b64d = { + .nbytes = input_len, + }; + InspectionBuffer buffer; + InspectionBuffer buffer_orig; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + buffer_orig = buffer; + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_offset == buffer_orig.inspect_offset); + FAIL_IF_NOT(buffer.inspect_len == buffer_orig.inspect_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* bytes > len so --> no transform */ +static int DetectTransformFromBase64DecodeTest03(void) +{ + const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ=="; + uint32_t input_len = strlen((char *)input); + + SCDetectTransformFromBase64Data b64d = { + .nbytes = input_len + 1, + }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* offset > len so --> no transform */ +static int DetectTransformFromBase64DecodeTest04(void) +{ + const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ=="; + uint32_t input_len = strlen((char *)input); + + SCDetectTransformFromBase64Data b64d = { + .offset = input_len + 1, + }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* partial transform */ +static int DetectTransformFromBase64DecodeTest05(void) +{ + const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ=="; + uint32_t input_len = strlen((char *)input); + const char *result = "This is S"; + uint32_t result_len = strlen((char *)result); + + SCDetectTransformFromBase64Data b64d = { + .nbytes = 12, + .mode = DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT, + }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_len == result_len); + FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* transform from non-zero offset */ +static int DetectTransformFromBase64DecodeTest06(void) +{ + const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ=="; + uint32_t input_len = strlen((char *)input); + const char *result = "s is Suricata"; + uint32_t result_len = strlen((char *)result); + + SCDetectTransformFromBase64Data b64d = { + .offset = 4, + .mode = DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT, + }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_len == result_len); + FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* partial decode */ +static int DetectTransformFromBase64DecodeTest07(void) +{ + /* Full string decodes to Hello World */ + const uint8_t *input = (const uint8_t *)"SGVs bG8 gV29y bGQ="; + uint32_t input_len = strlen((char *)input); + const char *result = "Hello Wor"; + uint32_t result_len = strlen((char *)result); + + SCDetectTransformFromBase64Data b64d = { .nbytes = input_len - 4, /* NB: stop early */ + .mode = Base64ModeRFC2045 }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_len == result_len); + FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0); + PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} + +/* input is not base64 encoded */ +static int DetectTransformFromBase64DecodeTest08(void) +{ + /* A portion of this string will be decoded */ + const uint8_t *input = (const uint8_t *)"This is not base64-encoded"; + uint32_t input_len = strlen((char *)input); + + SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeRFC2045 }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_len == 15); + // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} +static void DetectTransformFromBase64DecodeRegisterTests(void) +{ + UtRegisterTest("DetectTransformFromBase64DecodeTest01", DetectTransformFromBase64DecodeTest01); + UtRegisterTest( + "DetectTransformFromBase64DecodeTest01a", DetectTransformFromBase64DecodeTest01a); + UtRegisterTest("DetectTransformFromBase64DecodeTest02", DetectTransformFromBase64DecodeTest02); + UtRegisterTest("DetectTransformFromBase64DecodeTest03", DetectTransformFromBase64DecodeTest03); + UtRegisterTest("DetectTransformFromBase64DecodeTest04", DetectTransformFromBase64DecodeTest04); + UtRegisterTest("DetectTransformFromBase64DecodeTest05", DetectTransformFromBase64DecodeTest05); + UtRegisterTest("DetectTransformFromBase64DecodeTest06", DetectTransformFromBase64DecodeTest06); + UtRegisterTest("DetectTransformFromBase64DecodeTest07", DetectTransformFromBase64DecodeTest07); + UtRegisterTest("DetectTransformFromBase64DecodeTest08", DetectTransformFromBase64DecodeTest08); +} +#endif diff --git a/src/detect-transform-base64.h b/src/detect-transform-base64.h new file mode 100644 index 000000000000..fc0847bda9bd --- /dev/null +++ b/src/detect-transform-base64.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2024 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Jeff Lucovsky + */ + +#ifndef SURICATA_DETECT_TRANSFORM_BASE64_H +#define SURICATA_DETECT_TRANSFORM_BASE64_H + +/* prototypes */ +void DetectTransformFromBase64DecodeRegister(void); + +#endif /* SURICATA_DETECT_TRANSFORM_BASE64_H */ diff --git a/src/util-base64.c b/src/util-base64.c index ad42c9a703bc..104659eb9ed4 100644 --- a/src/util-base64.c +++ b/src/util-base64.c @@ -274,19 +274,19 @@ static inline Base64Ecode DecodeBase64RFC4648(uint8_t *dest, uint32_t dest_size, * \return Error code indicating success or failures with parsing */ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len, - uint32_t *consumed_bytes, uint32_t *decoded_bytes, Base64Mode mode) + uint32_t *consumed_bytes, uint32_t *decoded_bytes, DetectBase64Mode mode) { *decoded_bytes = 0; Base64Ecode ret = BASE64_ECODE_OK; switch (mode) { - case BASE64_MODE_RFC4648: + case Base64ModeRFC4648: ret = DecodeBase64RFC4648( dest, dest_size, src, len, consumed_bytes, decoded_bytes, false); break; - case BASE64_MODE_RFC2045: + case Base64ModeRFC2045: ret = DecodeBase64RFC2045(dest, dest_size, src, len, consumed_bytes, decoded_bytes); break; - case BASE64_MODE_STRICT: + case Base64ModeStrict: ret = DecodeBase64RFC4648( dest, dest_size, src, len, consumed_bytes, decoded_bytes, true); break; @@ -303,7 +303,7 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t consumed_bytes = 0, num_decoded = 0; \ uint8_t dst[dest_size]; \ Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \ - &consumed_bytes, &num_decoded, BASE64_MODE_RFC2045); \ + &consumed_bytes, &num_decoded, Base64ModeRFC2045); \ FAIL_IF(code != ecode); \ FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \ FAIL_IF(num_decoded != exp_decoded); \ @@ -315,7 +315,7 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t consumed_bytes = 0, num_decoded = 0; \ uint8_t dst[dest_size]; \ Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \ - &consumed_bytes, &num_decoded, BASE64_MODE_RFC4648); \ + &consumed_bytes, &num_decoded, Base64ModeRFC4648); \ FAIL_IF(code != ecode); \ FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \ FAIL_IF(num_decoded != exp_decoded); \ @@ -387,7 +387,7 @@ static int B64DecodeStringEndingSpaces(void) uint32_t consumed_bytes = 0, num_decoded = 0; uint8_t dst[10]; Base64Ecode code = DecodeBase64(dst, sizeof(dst), (const uint8_t *)src, 9, &consumed_bytes, - &num_decoded, BASE64_MODE_RFC2045); + &num_decoded, Base64ModeRFC2045); FAIL_IF(code != BASE64_ECODE_OK); FAIL_IF(num_decoded != 3); FAIL_IF(consumed_bytes != 4); diff --git a/src/util-base64.h b/src/util-base64.h index 744b849d87fd..4431d48a9e54 100644 --- a/src/util-base64.h +++ b/src/util-base64.h @@ -26,49 +26,12 @@ #define SURICATA_UTIL_BASE64_H_ #include "suricata-common.h" +#include "rust.h" /* Constants */ #define ASCII_BLOCK 3 #define B64_BLOCK 4 -typedef enum { - BASE64_MODE_RELAX, - /* If the following strings were to be passed to the decoder with RFC2045 mode, - * the results would be as follows. See the unittest B64TestVectorsRFC2045 in - * src/util-base64.c - * - * BASE64("") = "" - * BASE64("f") = "Zg==" - * BASE64("fo") = "Zm8=" - * BASE64("foo") = "Zm9v" - * BASE64("foob") = "Zm9vYg==" - * BASE64("fooba") = "Zm9vYmE=" - * BASE64("foobar") = "Zm9vYmFy" - * BASE64("foobar") = "Zm 9v Ym Fy" <-- Notice how the spaces are ignored - * BASE64("foobar") = "Zm$9vYm.Fy" # According to RFC 2045, All line breaks or *other - * characters* not found in base64 alphabet must be ignored by decoding software - * */ - BASE64_MODE_RFC2045, /* SPs are allowed during transfer but must be skipped by Decoder */ - BASE64_MODE_STRICT, - /* If the following strings were to be passed to the decoder with RFC4648 mode, - * the results would be as follows. See the unittest B64TestVectorsRFC4648 in - * src/util-base64.c - * - * BASE64("") = "" - * BASE64("f") = "Zg==" - * BASE64("fo") = "Zm8=" - * BASE64("foo") = "Zm9v" - * BASE64("foob") = "Zm9vYg==" - * BASE64("fooba") = "Zm9vYmE=" - * BASE64("foobar") = "Zm9vYmFy" - * BASE64("f") = "Zm 9v Ym Fy" <-- Notice how the processing stops once space is encountered - * BASE64("f") = "Zm$9vYm.Fy" <-- Notice how the processing stops once an invalid char is - * encountered - * */ - BASE64_MODE_RFC4648, /* reject the encoded data if it contains characters outside the base - alphabet */ -} Base64Mode; - typedef enum { BASE64_ECODE_ERR = -1, BASE64_ECODE_OK = 0, @@ -77,7 +40,7 @@ typedef enum { /* Function prototypes */ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len, - uint32_t *consumed_bytes, uint32_t *decoded_bytes, Base64Mode mode); + uint32_t *consumed_bytes, uint32_t *decoded_bytes, DetectBase64Mode mode); bool IsBase64Alphabet(uint8_t encoded_byte); #endif