diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index e78c6d28d59f..979c8c08a4c4 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -716,6 +716,117 @@ pub unsafe extern "C" fn rs_http2_tx_get_header_names( return 0; } +fn http2_header_iscookie(direction: u8, hname: &[u8]) -> bool { + if let Ok(s) = std::str::from_utf8(hname) { + if direction & STREAM_TOSERVER != 0 { + if s.to_lowercase() == "cookie" { + return true; + } + } else { + if s.to_lowercase() == "set-cookie" { + return true; + } + } + } + return false; +} + +fn http2_header_trimspaces(value: &[u8]) -> &[u8] { + let mut start = 0; + let mut end = value.len(); + while start < value.len() { + if value[start] == b' ' || value[start] == b'\t' { + start += 1; + } else { + break; + } + } + while end > start { + if value[end - 1] == b' ' || value[end - 1] == b'\t' { + end -= 1; + } else { + break; + } + } + return &value[start..end]; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_http2_tx_get_headers( + tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, +) -> u8 { + let mut vec = Vec::new(); + let frames = if direction & STREAM_TOSERVER != 0 { + &tx.frames_ts + } else { + &tx.frames_tc + }; + for i in 0..frames.len() { + match &frames[i].data { + HTTP2FrameTypeData::HEADERS(hd) => { + for j in 0..hd.blocks.len() { + if !http2_header_iscookie(direction, &hd.blocks[j].name) { + // we do not escape linefeeds nor : in headers names + vec.extend_from_slice(&hd.blocks[j].name); + vec.push(':' as u8); + vec.push(' ' as u8); + vec.extend_from_slice(http2_header_trimspaces(&hd.blocks[j].value)); + vec.push('\r' as u8); + vec.push('\n' as u8); + } + } + } + _ => {} + } + } + if vec.len() > 0 { + tx.escaped.push(vec); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_http2_tx_get_headers_raw( + tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32, +) -> u8 { + let mut vec = Vec::new(); + let frames = if direction & STREAM_TOSERVER != 0 { + &tx.frames_ts + } else { + &tx.frames_tc + }; + for i in 0..frames.len() { + match &frames[i].data { + HTTP2FrameTypeData::HEADERS(hd) => { + for j in 0..hd.blocks.len() { + // we do not escape linefeeds nor : in headers names + vec.extend_from_slice(&hd.blocks[j].name); + vec.push(':' as u8); + vec.push(' ' as u8); + vec.extend_from_slice(&hd.blocks[j].value); + vec.push('\r' as u8); + vec.push('\n' as u8); + } + } + _ => {} + } + } + if vec.len() > 0 { + tx.escaped.push(vec); + let idx = tx.escaped.len() - 1; + let value = &tx.escaped[idx]; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return 1; + } + return 0; +} + #[no_mangle] pub unsafe extern "C" fn rs_http2_tx_get_header( tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32, @@ -1004,4 +1115,17 @@ mod tests { } assert_eq!(r2.1, "localhost".len()); } + + #[test] + fn test_http2_header_trimspaces() { + let buf0 = "nospaces".as_bytes(); + let r0 = http2_header_trimspaces(buf0); + assert_eq!(r0, "nospaces".as_bytes()); + let buf1 = " spaces\t".as_bytes(); + let r1 = http2_header_trimspaces(buf1); + assert_eq!(r1, "spaces".as_bytes()); + let buf2 = " \t".as_bytes(); + let r2 = http2_header_trimspaces(buf2); + assert_eq!(r2, "".as_bytes()); + } } diff --git a/src/detect-http-header.c b/src/detect-http-header.c index e5b66747b29c..722de79363fa 100644 --- a/src/detect-http-header.c +++ b/src/detect-http-header.c @@ -148,6 +148,27 @@ static uint8_t *GetBufferForTX(htp_tx_t *tx, uint64_t tx_id, return buf->buffer; } +static InspectionBuffer *GetBuffer2ForTX(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) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (rs_http2_tx_get_headers(txv, flow_flags, &b, &b_len) != 1) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + + return buffer; +} + /** \internal * \brief custom inspect function to utilize the cached headers */ @@ -397,7 +418,7 @@ static int DetectHttpHeaderSetupSticky(DetectEngineCtx *de_ctx, Signature *s, co { if (DetectBufferSetActiveList(s, g_http_header_buffer_id) < 0) return -1; - if (DetectSignatureSetAppProto(s, ALPROTO_HTTP1) < 0) + if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) return -1; return 0; } @@ -439,6 +460,16 @@ void DetectHttpHeaderRegister(void) PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP1, 0); /* not used, registered twice: HEADERS/TRAILER */ + DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); + DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, + GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataClient); + + DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, + HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); + DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, + GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer); + DetectBufferTypeSetDescriptionByName("http_header", "http headers"); diff --git a/src/detect-http-raw-header.c b/src/detect-http-raw-header.c index 87a4ba993c85..61604f0685a1 100644 --- a/src/detect-http-raw-header.c +++ b/src/detect-http-raw-header.c @@ -62,6 +62,9 @@ static int g_http_raw_header_buffer_id = 0; static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, const int list_id); +static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, + const int list_id); static int PrefilterMpmHttpHeaderRawRequestRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, @@ -105,6 +108,16 @@ void DetectHttpRawHeaderRegister(void) PrefilterMpmHttpHeaderRawResponseRegister, NULL, ALPROTO_HTTP1, 0); /* progress handled in register */ + DetectAppLayerInspectEngineRegister2("http_raw_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetData2); + DetectAppLayerInspectEngineRegister2("http_raw_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, + HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetData2); + + DetectAppLayerMpmRegister2("http_raw_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, + GetData2, ALPROTO_HTTP2, HTTP2StateDataClient); + DetectAppLayerMpmRegister2("http_raw_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, + GetData2, ALPROTO_HTTP2, HTTP2StateDataServer); + DetectBufferTypeSetDescriptionByName("http_raw_header", "raw http headers"); @@ -146,7 +159,7 @@ static int DetectHttpRawHeaderSetupSticky(DetectEngineCtx *de_ctx, Signature *s, { if (DetectBufferSetActiveList(s, g_http_raw_header_buffer_id) < 0) return -1; - if (DetectSignatureSetAppProto(s, ALPROTO_HTTP1) < 0) + if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) return -1; return 0; } @@ -192,6 +205,27 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, return buffer; } +static InspectionBuffer *GetData2(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) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (rs_http2_tx_get_headers_raw(txv, flow_flags, &b, &b_len) != 1) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + + return buffer; +} + typedef struct PrefilterMpmHttpHeaderRawCtx { int list_id; const MpmCtx *mpm_ctx;