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
http2.errorcode uses an :ref:`unsigned 32-bit integer <rules-integer-keywords>`.
http2.errorcode is also a :ref:`multi-integer <multi-integers>`.
Examples::
http2.errorcode: NO_ERROR;

@ -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"

@ -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<u32> {
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<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]
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::<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> {

@ -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<bool,
match &frame.data {
HTTP2FrameTypeData::GOAWAY(goaway) => {
if !has_error_code {
let errcode: Option<parser::HTTP2ErrorCode> =
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<bool,
}
HTTP2FrameTypeData::RSTSTREAM(rst) => {
if !has_error_code {
let errcode: Option<parser::HTTP2ErrorCode> =
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 {

@ -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<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)]
pub struct HTTP2FrameGoAway {
pub errorcode: u32, //HTTP2ErrorCode

@ -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);
}
/**

Loading…
Cancel
Save