diff --git a/doc/userguide/rules/http2-keywords.rst b/doc/userguide/rules/http2-keywords.rst index 548f298cad..6497ffb523 100644 --- a/doc/userguide/rules/http2-keywords.rst +++ b/doc/userguide/rules/http2-keywords.rst @@ -36,6 +36,10 @@ http2.errorcode Match on the error code in a GOWAY or RST_STREAM frame +http2.errorcode uses an :ref:`unsigned 32-bit integer `. + +http2.errorcode is also a :ref:`multi-integer `. + Examples:: http2.errorcode: NO_ERROR; diff --git a/etc/schema.json b/etc/schema.json index d4f808a1ec..b7455a493d 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -2269,7 +2269,12 @@ "minProperties": 1, "properties": { "error_code": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "http2.errorcode" + ] + } }, "has_multiple": { "type": "string" @@ -2306,7 +2311,12 @@ "minProperties": 1, "properties": { "error_code": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "http2.errorcode" + ] + } }, "has_multiple": { "type": "string" diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index 305e76ec44..0212ec33cb 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -21,14 +21,14 @@ use super::http2::{ use super::parser; use crate::detect::uint::{ detect_match_uint, detect_parse_array_uint_enum, detect_uint_match_at_index, - DetectUintArrayData, DetectUintData, + DetectUintArrayData, DetectUintData, DetectUintIndex, DetectUintMode, }; +use crate::detect::EnumString; use crate::direction::Direction; use base64::{engine::general_purpose::STANDARD, Engine}; use std::ffi::CStr; use std::os::raw::c_void; use std::rc::Rc; -use std::str::FromStr; use suricata_sys::sys::DetectEngineThreadCtx; #[no_mangle] @@ -65,64 +65,60 @@ pub unsafe extern "C" fn SCHttp2ParseFrametype( return std::ptr::null_mut(); } -fn http2_tx_has_errorcode( - tx: &HTTP2Transaction, direction: Direction, code: u32, -) -> std::os::raw::c_int { - if direction == Direction::ToServer { - for i in 0..tx.frames_ts.len() { - match tx.frames_ts[i].data { - HTTP2FrameTypeData::GOAWAY(goaway) => { - if goaway.errorcode == code { - return 1; - } - } - HTTP2FrameTypeData::RSTSTREAM(rst) => { - if rst.errorcode == code { - return 1; - } - } - _ => {} - } - } - } else { - for i in 0..tx.frames_tc.len() { - match tx.frames_tc[i].data { - HTTP2FrameTypeData::GOAWAY(goaway) => { - if goaway.errorcode == code { - return 1; - } - } - HTTP2FrameTypeData::RSTSTREAM(rst) => { - if rst.errorcode == code { - return 1; - } - } - _ => {} - } - } +fn http2_tx_get_errorcode(f: &HTTP2Frame) -> Option { + match &f.data { + HTTP2FrameTypeData::GOAWAY(goaway) => Some(goaway.errorcode), + HTTP2FrameTypeData::RSTSTREAM(rst) => Some(rst.errorcode), + _ => None, } - return 0; } #[no_mangle] pub unsafe extern "C" fn SCHttp2TxHasErrorCode( - tx: *mut std::os::raw::c_void, direction: u8, code: u32, + tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void, ) -> std::os::raw::c_int { let tx = cast_pointer!(tx, HTTP2Transaction); - return http2_tx_has_errorcode(tx, direction.into(), code); + let ctx = cast_pointer!(ctx, DetectUintArrayData); + let frames = if direction & Direction::ToServer as u8 != 0 { + &tx.frames_ts + } else { + &tx.frames_tc + }; + return detect_uint_match_at_index::( + frames, + ctx, + http2_tx_get_errorcode, + tx.state >= HTTP2TransactionState::HTTP2StateClosed, + ); } #[no_mangle] pub unsafe extern "C" fn SCHttp2ParseErrorCode( str: *const std::os::raw::c_char, -) -> std::os::raw::c_int { +) -> *mut std::os::raw::c_void { let ft_name: &CStr = CStr::from_ptr(str); //unsafe if let Ok(s) = ft_name.to_str() { - if let Ok(x) = parser::HTTP2ErrorCode::from_str(s) { - return x as i32; + // special case for backward compatibility, now parsed as HTTP11_REQUIRED + if s.to_uppercase() == "HTTP_1_1_REQUIRED" { + let ctx = DetectUintArrayData:: { + du: DetectUintData { + arg1: parser::HTTP2ErrorCode::Http11Required.into_u(), + arg2: 0, + mode: DetectUintMode::DetectUintModeEqual, + }, + index: DetectUintIndex::Any, + start: 0, + end: 0, + }; + let boxed = Box::new(ctx); + return Box::into_raw(boxed) as *mut c_void; + } + if let Some(ctx) = detect_parse_array_uint_enum::(s) { + let boxed = Box::new(ctx); + return Box::into_raw(boxed) as *mut c_void; } } - return -1; + return std::ptr::null_mut(); } fn get_http2_priority(frame: &HTTP2Frame) -> Option { diff --git a/rust/src/http2/logger.rs b/rust/src/http2/logger.rs index 2197d7a83e..96b46a7b66 100644 --- a/rust/src/http2/logger.rs +++ b/rust/src/http2/logger.rs @@ -17,6 +17,7 @@ use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction}; use super::parser; +use crate::detect::EnumString; use crate::jsonbuilder::{JsonBuilder, JsonError}; use std; use std::collections::{HashMap, HashSet}; @@ -145,16 +146,10 @@ fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result { if !has_error_code { - let errcode: Option = - num::FromPrimitive::from_u32(goaway.errorcode); - match errcode { - Some(errstr) => { - js.set_string("error_code", &errstr.to_string().to_uppercase())?; - } - None => { - //use uint32 - js.set_string("error_code", &goaway.errorcode.to_string())?; - } + if let Some(errcode) = parser::HTTP2ErrorCode::from_u(goaway.errorcode) { + js.set_string("error_code", errcode.to_str())?; + } else { + js.set_string("error_code", &format!("unknown-{}", goaway.errorcode))?; } has_error_code = true; } else if !has_multiple { @@ -164,16 +159,10 @@ fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result { if !has_error_code { - let errcode: Option = - num::FromPrimitive::from_u32(rst.errorcode); - match errcode { - Some(errstr) => { - js.set_string("error_code", &errstr.to_string())?; - } - None => { - //use uint32 - js.set_string("error_code", &rst.errorcode.to_string())?; - } + if let Some(errcode) = parser::HTTP2ErrorCode::from_u(rst.errorcode) { + js.set_string("error_code", errcode.to_str())?; + } else { + js.set_string("error_code", &format!("unknown-{}", rst.errorcode))?; } has_error_code = true; } else if !has_multiple { diff --git a/rust/src/http2/parser.rs b/rust/src/http2/parser.rs index 0291a75214..93f59088e6 100644 --- a/rust/src/http2/parser.rs +++ b/rust/src/http2/parser.rs @@ -81,7 +81,9 @@ pub fn http2_parse_frame_header(i: &[u8]) -> IResult<&[u8], HTTP2FrameHeader> { } #[repr(u32)] +#[derive(EnumStringU32)] #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)] +#[suricata(enum_string_style = "LOG_UPPERCASE")] pub enum HTTP2ErrorCode { NoError = 0, ProtocolError = 1, @@ -99,37 +101,6 @@ pub enum HTTP2ErrorCode { Http11Required = 13, } -impl fmt::Display for HTTP2ErrorCode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl std::str::FromStr for HTTP2ErrorCode { - type Err = String; - - fn from_str(s: &str) -> Result { - let su = s.to_uppercase(); - let su_slice: &str = &su; - match su_slice { - "NO_ERROR" => Ok(HTTP2ErrorCode::NoError), - "PROTOCOL_ERROR" => Ok(HTTP2ErrorCode::ProtocolError), - "FLOW_CONTROL_ERROR" => Ok(HTTP2ErrorCode::FlowControlError), - "SETTINGS_TIMEOUT" => Ok(HTTP2ErrorCode::SettingsTimeout), - "STREAM_CLOSED" => Ok(HTTP2ErrorCode::StreamClosed), - "FRAME_SIZE_ERROR" => Ok(HTTP2ErrorCode::FrameSizeError), - "REFUSED_STREAM" => Ok(HTTP2ErrorCode::RefusedStream), - "CANCEL" => Ok(HTTP2ErrorCode::Cancel), - "COMPRESSION_ERROR" => Ok(HTTP2ErrorCode::CompressionError), - "CONNECT_ERROR" => Ok(HTTP2ErrorCode::ConnectError), - "ENHANCE_YOUR_CALM" => Ok(HTTP2ErrorCode::EnhanceYourCalm), - "INADEQUATE_SECURITY" => Ok(HTTP2ErrorCode::InadequateSecurity), - "HTTP_1_1_REQUIRED" => Ok(HTTP2ErrorCode::Http11Required), - _ => Err(format!("'{}' is not a valid value for HTTP2ErrorCode", s)), - } - } -} - #[derive(Clone, Copy, Debug)] pub struct HTTP2FrameGoAway { pub errorcode: u32, //HTTP2ErrorCode diff --git a/src/detect-http2.c b/src/detect-http2.c index 1b0a3ae125..f0fcab6b4f 100644 --- a/src/detect-http2.c +++ b/src/detect-http2.c @@ -123,6 +123,8 @@ void DetectHttp2Register(void) sigmatch_table[DETECT_HTTP2_ERRORCODE].AppLayerTxMatch = DetectHTTP2errorcodeMatch; sigmatch_table[DETECT_HTTP2_ERRORCODE].Setup = DetectHTTP2errorcodeSetup; sigmatch_table[DETECT_HTTP2_ERRORCODE].Free = DetectHTTP2errorcodeFree; + sigmatch_table[DETECT_HTTP2_ERRORCODE].flags = + SIGMATCH_INFO_UINT32 | SIGMATCH_INFO_MULTI_UINT | SIGMATCH_INFO_ENUM_UINT; #ifdef UNITTESTS sigmatch_table[DETECT_HTTP2_ERRORCODE].RegisterTests = DetectHTTP2errorCodeRegisterTests; #endif @@ -236,8 +238,10 @@ static int DetectHTTP2frametypeSetup (DetectEngineCtx *de_ctx, Signature *s, con return -1; void *dua8 = SCHttp2ParseFrametype(str); - if (dua8 == NULL) + if (dua8 == NULL) { + SCLogError("Invalid http2.frametype: %s", str); return -1; + } if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HTTP2_FRAMETYPE, (SigMatchCtx *)dua8, g_http2_match_buffer_id) == NULL) { @@ -269,27 +273,7 @@ static int DetectHTTP2errorcodeMatch(DetectEngineThreadCtx *det_ctx, const SigMatchCtx *ctx) { - uint32_t *detect = (uint32_t *)ctx; - - return SCHttp2TxHasErrorCode(txv, flags, *detect); - //TODOask handle negation rules -} - -static int DetectHTTP2FuncParseErrorCode(const char *str, uint32_t *ec) -{ - // first parse numeric value - if (ByteExtractStringUint32(ec, 10, (uint16_t)strlen(str), str) > 0) { - return 1; - } - - // it it failed so far, parse string value from enumeration - int r = SCHttp2ParseErrorCode(str); - if (r >= 0) { - *ec = r; - return 1; - } - - return 0; + return SCHttp2TxHasErrorCode(txv, flags, ctx); } /** @@ -304,24 +288,18 @@ static int DetectHTTP2FuncParseErrorCode(const char *str, uint32_t *ec) */ static int DetectHTTP2errorcodeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) { - uint32_t error_code; - if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0) return -1; - if (!DetectHTTP2FuncParseErrorCode(str, &error_code)) { - SCLogError("Invalid argument \"%s\" supplied to http2.errorcode keyword.", str); + void *dua32 = SCHttp2ParseErrorCode(str); + if (dua32 == NULL) { + SCLogError("Invalid http2.errorcode: %s", str); return -1; } - uint32_t *http2ec = SCCalloc(1, sizeof(uint32_t)); - if (http2ec == NULL) - return -1; - *http2ec = error_code; - - if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HTTP2_ERRORCODE, (SigMatchCtx *)http2ec, + if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HTTP2_ERRORCODE, (SigMatchCtx *)dua32, g_http2_match_buffer_id) == NULL) { - DetectHTTP2errorcodeFree(NULL, http2ec); + DetectHTTP2errorcodeFree(NULL, dua32); return -1; } @@ -335,7 +313,7 @@ static int DetectHTTP2errorcodeSetup (DetectEngineCtx *de_ctx, Signature *s, con */ void DetectHTTP2errorcodeFree(DetectEngineCtx *de_ctx, void *ptr) { - SCFree(ptr); + SCDetectU32ArrayFree(ptr); } /**