detect: http2.errorcode is now a generic integer

Ticket: 7889
pull/14025/head
Philippe Antoine 2 months ago committed by Victor Julien
parent 401b2fcae6
commit 969739d067

@ -36,6 +36,10 @@ http2.errorcode
Match on the error code in a GOWAY or RST_STREAM frame Match on the error code in a GOWAY or RST_STREAM frame
http2.errorcode uses an :ref:`unsigned 32-bit integer <rules-integer-keywords>`.
http2.errorcode is also a :ref:`multi-integer <multi-integers>`.
Examples:: Examples::
http2.errorcode: NO_ERROR; http2.errorcode: NO_ERROR;

@ -2269,7 +2269,12 @@
"minProperties": 1, "minProperties": 1,
"properties": { "properties": {
"error_code": { "error_code": {
"type": "string" "type": "string",
"suricata": {
"keywords": [
"http2.errorcode"
]
}
}, },
"has_multiple": { "has_multiple": {
"type": "string" "type": "string"
@ -2306,7 +2311,12 @@
"minProperties": 1, "minProperties": 1,
"properties": { "properties": {
"error_code": { "error_code": {
"type": "string" "type": "string",
"suricata": {
"keywords": [
"http2.errorcode"
]
}
}, },
"has_multiple": { "has_multiple": {
"type": "string" "type": "string"

@ -21,14 +21,14 @@ use super::http2::{
use super::parser; use super::parser;
use crate::detect::uint::{ use crate::detect::uint::{
detect_match_uint, detect_parse_array_uint_enum, detect_uint_match_at_index, 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 crate::direction::Direction;
use base64::{engine::general_purpose::STANDARD, Engine}; use base64::{engine::general_purpose::STANDARD, Engine};
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr;
use suricata_sys::sys::DetectEngineThreadCtx; use suricata_sys::sys::DetectEngineThreadCtx;
#[no_mangle] #[no_mangle]
@ -65,64 +65,60 @@ pub unsafe extern "C" fn SCHttp2ParseFrametype(
return std::ptr::null_mut(); return std::ptr::null_mut();
} }
fn http2_tx_has_errorcode( fn http2_tx_get_errorcode(f: &HTTP2Frame) -> Option<u32> {
tx: &HTTP2Transaction, direction: Direction, code: u32, match &f.data {
) -> std::os::raw::c_int { HTTP2FrameTypeData::GOAWAY(goaway) => Some(goaway.errorcode),
if direction == Direction::ToServer { HTTP2FrameTypeData::RSTSTREAM(rst) => Some(rst.errorcode),
for i in 0..tx.frames_ts.len() { _ => None,
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;
}
}
_ => {}
}
}
} }
return 0;
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn SCHttp2TxHasErrorCode( 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 { ) -> std::os::raw::c_int {
let tx = cast_pointer!(tx, HTTP2Transaction); let tx = cast_pointer!(tx, HTTP2Transaction);
return http2_tx_has_errorcode(tx, direction.into(), code); let ctx = cast_pointer!(ctx, DetectUintArrayData<u32>);
let frames = if direction & Direction::ToServer as u8 != 0 {
&tx.frames_ts
} else {
&tx.frames_tc
};
return detect_uint_match_at_index::<HTTP2Frame, u32>(
frames,
ctx,
http2_tx_get_errorcode,
tx.state >= HTTP2TransactionState::HTTP2StateClosed,
);
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn SCHttp2ParseErrorCode( pub unsafe extern "C" fn SCHttp2ParseErrorCode(
str: *const std::os::raw::c_char, 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 let ft_name: &CStr = CStr::from_ptr(str); //unsafe
if let Ok(s) = ft_name.to_str() { if let Ok(s) = ft_name.to_str() {
if let Ok(x) = parser::HTTP2ErrorCode::from_str(s) { // special case for backward compatibility, now parsed as HTTP11_REQUIRED
return x as i32; if s.to_uppercase() == "HTTP_1_1_REQUIRED" {
let ctx = DetectUintArrayData::<u32> {
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::<u32, parser::HTTP2ErrorCode>(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<u8> { fn get_http2_priority(frame: &HTTP2Frame) -> Option<u8> {

@ -17,6 +17,7 @@
use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction}; use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction};
use super::parser; use super::parser;
use crate::detect::EnumString;
use crate::jsonbuilder::{JsonBuilder, JsonError}; use crate::jsonbuilder::{JsonBuilder, JsonError};
use std; use std;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -145,16 +146,10 @@ fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result<bool,
match &frame.data { match &frame.data {
HTTP2FrameTypeData::GOAWAY(goaway) => { HTTP2FrameTypeData::GOAWAY(goaway) => {
if !has_error_code { if !has_error_code {
let errcode: Option<parser::HTTP2ErrorCode> = if let Some(errcode) = parser::HTTP2ErrorCode::from_u(goaway.errorcode) {
num::FromPrimitive::from_u32(goaway.errorcode); js.set_string("error_code", errcode.to_str())?;
match errcode { } else {
Some(errstr) => { js.set_string("error_code", &format!("unknown-{}", goaway.errorcode))?;
js.set_string("error_code", &errstr.to_string().to_uppercase())?;
}
None => {
//use uint32
js.set_string("error_code", &goaway.errorcode.to_string())?;
}
} }
has_error_code = true; has_error_code = true;
} else if !has_multiple { } else if !has_multiple {
@ -164,16 +159,10 @@ fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result<bool,
} }
HTTP2FrameTypeData::RSTSTREAM(rst) => { HTTP2FrameTypeData::RSTSTREAM(rst) => {
if !has_error_code { if !has_error_code {
let errcode: Option<parser::HTTP2ErrorCode> = if let Some(errcode) = parser::HTTP2ErrorCode::from_u(rst.errorcode) {
num::FromPrimitive::from_u32(rst.errorcode); js.set_string("error_code", errcode.to_str())?;
match errcode { } else {
Some(errstr) => { js.set_string("error_code", &format!("unknown-{}", rst.errorcode))?;
js.set_string("error_code", &errstr.to_string())?;
}
None => {
//use uint32
js.set_string("error_code", &rst.errorcode.to_string())?;
}
} }
has_error_code = true; has_error_code = true;
} else if !has_multiple { } else if !has_multiple {

@ -81,7 +81,9 @@ pub fn http2_parse_frame_header(i: &[u8]) -> IResult<&[u8], HTTP2FrameHeader> {
} }
#[repr(u32)] #[repr(u32)]
#[derive(EnumStringU32)]
#[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)] #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)]
#[suricata(enum_string_style = "LOG_UPPERCASE")]
pub enum HTTP2ErrorCode { pub enum HTTP2ErrorCode {
NoError = 0, NoError = 0,
ProtocolError = 1, ProtocolError = 1,
@ -99,37 +101,6 @@ pub enum HTTP2ErrorCode {
Http11Required = 13, 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<Self, Self::Err> {
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)] #[derive(Clone, Copy, Debug)]
pub struct HTTP2FrameGoAway { pub struct HTTP2FrameGoAway {
pub errorcode: u32, //HTTP2ErrorCode pub errorcode: u32, //HTTP2ErrorCode

@ -123,6 +123,8 @@ void DetectHttp2Register(void)
sigmatch_table[DETECT_HTTP2_ERRORCODE].AppLayerTxMatch = DetectHTTP2errorcodeMatch; sigmatch_table[DETECT_HTTP2_ERRORCODE].AppLayerTxMatch = DetectHTTP2errorcodeMatch;
sigmatch_table[DETECT_HTTP2_ERRORCODE].Setup = DetectHTTP2errorcodeSetup; sigmatch_table[DETECT_HTTP2_ERRORCODE].Setup = DetectHTTP2errorcodeSetup;
sigmatch_table[DETECT_HTTP2_ERRORCODE].Free = DetectHTTP2errorcodeFree; 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 #ifdef UNITTESTS
sigmatch_table[DETECT_HTTP2_ERRORCODE].RegisterTests = DetectHTTP2errorCodeRegisterTests; sigmatch_table[DETECT_HTTP2_ERRORCODE].RegisterTests = DetectHTTP2errorCodeRegisterTests;
#endif #endif
@ -236,8 +238,10 @@ static int DetectHTTP2frametypeSetup (DetectEngineCtx *de_ctx, Signature *s, con
return -1; return -1;
void *dua8 = SCHttp2ParseFrametype(str); void *dua8 = SCHttp2ParseFrametype(str);
if (dua8 == NULL) if (dua8 == NULL) {
SCLogError("Invalid http2.frametype: %s", str);
return -1; return -1;
}
if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HTTP2_FRAMETYPE, (SigMatchCtx *)dua8, if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HTTP2_FRAMETYPE, (SigMatchCtx *)dua8,
g_http2_match_buffer_id) == NULL) { g_http2_match_buffer_id) == NULL) {
@ -269,27 +273,7 @@ static int DetectHTTP2errorcodeMatch(DetectEngineThreadCtx *det_ctx,
const SigMatchCtx *ctx) const SigMatchCtx *ctx)
{ {
uint32_t *detect = (uint32_t *)ctx; return SCHttp2TxHasErrorCode(txv, flags, 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;
} }
/** /**
@ -304,24 +288,18 @@ static int DetectHTTP2FuncParseErrorCode(const char *str, uint32_t *ec)
*/ */
static int DetectHTTP2errorcodeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) static int DetectHTTP2errorcodeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{ {
uint32_t error_code;
if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0) if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1; return -1;
if (!DetectHTTP2FuncParseErrorCode(str, &error_code)) { void *dua32 = SCHttp2ParseErrorCode(str);
SCLogError("Invalid argument \"%s\" supplied to http2.errorcode keyword.", str); if (dua32 == NULL) {
SCLogError("Invalid http2.errorcode: %s", str);
return -1; return -1;
} }
uint32_t *http2ec = SCCalloc(1, sizeof(uint32_t)); if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HTTP2_ERRORCODE, (SigMatchCtx *)dua32,
if (http2ec == NULL)
return -1;
*http2ec = error_code;
if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HTTP2_ERRORCODE, (SigMatchCtx *)http2ec,
g_http2_match_buffer_id) == NULL) { g_http2_match_buffer_id) == NULL) {
DetectHTTP2errorcodeFree(NULL, http2ec); DetectHTTP2errorcodeFree(NULL, dua32);
return -1; return -1;
} }
@ -335,7 +313,7 @@ static int DetectHTTP2errorcodeSetup (DetectEngineCtx *de_ctx, Signature *s, con
*/ */
void DetectHTTP2errorcodeFree(DetectEngineCtx *de_ctx, void *ptr) void DetectHTTP2errorcodeFree(DetectEngineCtx *de_ctx, void *ptr)
{ {
SCFree(ptr); SCDetectU32ArrayFree(ptr);
} }
/** /**

Loading…
Cancel
Save