-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
file/swf: Use lzma-rs decompression instead of libhtp. v2 #8132
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -120,5 +120,6 @@ pub mod http2; | |
pub mod quic; | ||
pub mod bittorrent_dht; | ||
pub mod plugin; | ||
pub mod lzma; | ||
pub mod util; | ||
pub mod ffi; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
use lzma_rs::decompress::{Options, Stream}; | ||
use lzma_rs::error::Error; | ||
use std::io::{Cursor, Write}; | ||
|
||
/// Propagate lzma crate errors | ||
#[repr(C)] | ||
pub enum LzmaStatus { | ||
LzmaOk, | ||
LzmaIoError, | ||
LzmaHeaderTooShortError, | ||
LzmaError, | ||
LzmaXzError, | ||
} | ||
|
||
impl From<Error> for LzmaStatus { | ||
fn from(e: Error) -> LzmaStatus { | ||
match e { | ||
Error::IoError(_) => LzmaStatus::LzmaIoError, | ||
Error::HeaderTooShort(_) => LzmaStatus::LzmaHeaderTooShortError, | ||
Error::LzmaError(_) => LzmaStatus::LzmaError, | ||
Error::XzError(_) => LzmaStatus::LzmaXzError, | ||
} | ||
} | ||
} | ||
|
||
impl From<std::io::Error> for LzmaStatus { | ||
fn from(_e: std::io::Error) -> LzmaStatus { | ||
LzmaStatus::LzmaIoError | ||
} | ||
} | ||
|
||
/// Use the lzma algorithm to decompress a chunk of data. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn lzma_decompress( | ||
input: *const u8, input_len: &mut usize, output: *mut u8, output_len: &mut usize, | ||
allow_incomplete: bool, memlimit: usize, | ||
) -> LzmaStatus { | ||
let input = std::slice::from_raw_parts(input, *input_len); | ||
let output = std::slice::from_raw_parts_mut(output, *output_len); | ||
let output = Cursor::new(output); | ||
|
||
let options = Options { | ||
memlimit: Some(memlimit), | ||
allow_incomplete, | ||
..Default::default() | ||
}; | ||
|
||
let mut stream = Stream::new_with_options(&options, output); | ||
|
||
if let Err(e) = stream.write_all(input) { | ||
if !allow_incomplete { | ||
return e.into(); | ||
} | ||
} | ||
|
||
match stream.finish() { | ||
Ok(output) => { | ||
*output_len = output.position() as usize; | ||
LzmaStatus::LzmaOk | ||
} | ||
Err(e) => e.into(), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -117,12 +117,10 @@ SCEnumCharMap det_ctx_event_table[] = { | |
{ "Z_STREAM_ERROR", FILE_DECODER_EVENT_Z_STREAM_ERROR }, | ||
{ "Z_BUF_ERROR", FILE_DECODER_EVENT_Z_BUF_ERROR }, | ||
{ "Z_UNKNOWN_ERROR", FILE_DECODER_EVENT_Z_UNKNOWN_ERROR }, | ||
{ "LZMA_IO_ERROR", FILE_DECODER_EVENT_LZMA_IO_ERROR }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These needs new events in rules/http-events.rules There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see that all of the rules in Do you want me to define a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI, there is a known issue with these events, see https://redmine.openinfosecfoundation.org/issues/4482 (@jlucovsky seems I lost track of these) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack. So then fixing the file decoder events logging, and making rules to trigger on them, is a separate ticket / PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes |
||
{ "LZMA_HEADER_TOO_SHORT_ERROR", FILE_DECODER_EVENT_LZMA_HEADER_TOO_SHORT_ERROR }, | ||
{ "LZMA_DECODER_ERROR", FILE_DECODER_EVENT_LZMA_DECODER_ERROR }, | ||
{ "LZMA_MEMLIMIT_ERROR", FILE_DECODER_EVENT_LZMA_MEMLIMIT_ERROR }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we lose this mem limit error ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When lzma-rs exceeds the memory cap for decoding, it returns a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, would it be possible/simple for lzma-rs to do this ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be a major bump for them to change the public API for their It would be trivial though to check the error message. When lzma-rs produces this kind of error the error message is obvious. They have a test that does this here and it would be trivial to check the error message in our lzma.rs and map it to a different local error type. |
||
{ "LZMA_OPTIONS_ERROR", FILE_DECODER_EVENT_LZMA_OPTIONS_ERROR }, | ||
{ "LZMA_FORMAT_ERROR", FILE_DECODER_EVENT_LZMA_FORMAT_ERROR }, | ||
{ "LZMA_DATA_ERROR", FILE_DECODER_EVENT_LZMA_DATA_ERROR }, | ||
{ "LZMA_BUF_ERROR", FILE_DECODER_EVENT_LZMA_BUF_ERROR }, | ||
{ "LZMA_XZ_ERROR", FILE_DECODER_EVENT_LZMA_XZ_ERROR }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should only allow pure lzma, no xz wrapper, right ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the only reason this is here is because XZ errors are part of the public There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, maybe a debug validation to check that this is never reached ? |
||
{ "LZMA_UNKNOWN_ERROR", FILE_DECODER_EVENT_LZMA_UNKNOWN_ERROR }, | ||
{ | ||
"TOO_MANY_BUFFERS", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,9 +32,9 @@ | |
#include "util-misc.h" | ||
#include "util-print.h" | ||
|
||
#include <zlib.h> | ||
#include "rust.h" | ||
|
||
#include <htp/lzma/LzmaDec.h> | ||
#include <zlib.h> | ||
|
||
#define MAX_SWF_DECOMPRESSED_LEN 50000000 | ||
/* | ||
|
@@ -129,10 +129,6 @@ int FileSwfZlibDecompression(DetectEngineThreadCtx *det_ctx, | |
return ret; | ||
} | ||
|
||
static void *SzAlloc(ISzAllocPtr p, size_t size) { return malloc(size); } | ||
static void SzFree(ISzAllocPtr p, void *address) { free(address); } | ||
static const ISzAlloc suri_lzma_Alloc = { SzAlloc, SzFree }; | ||
|
||
/* ZWS format */ | ||
/* | ||
* | 4 bytes | 4 bytes | 4 bytes | 5 bytes | n bytes | 6 bytes | | ||
|
@@ -144,46 +140,30 @@ int FileSwfLzmaDecompression(DetectEngineThreadCtx *det_ctx, | |
{ | ||
int ret = 0; | ||
|
||
CLzmaDec strm; | ||
LzmaDec_Construct(&strm); | ||
ELzmaStatus status; | ||
|
||
if (compressed_data_len < LZMA_PROPS_SIZE + 8) { | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_FORMAT_ERROR); | ||
return 0; | ||
} | ||
ret = LzmaDec_Allocate(&strm, compressed_data, LZMA_PROPS_SIZE, &suri_lzma_Alloc); | ||
if (ret != SZ_OK) { | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_DECODER_ERROR); | ||
return 0; | ||
} | ||
LzmaDec_Init(&strm); | ||
compressed_data += LZMA_PROPS_SIZE + 8; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did these got removed ? Did you test this ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is advancing the input buffer past the header / properties, which were parsed out in You can see the relevant part of lzma-rs here. It starts by parsing the properties in the header, and then reads the rest, all within the As for testing, there are several tests in lzma-rs against the short header case and full decompression, suricata has several LZMA tests in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok thanks :-) |
||
compressed_data_len -= LZMA_PROPS_SIZE + 8; | ||
size_t inprocessed = compressed_data_len; | ||
size_t outprocessed = decompressed_data_len; | ||
|
||
ret = LzmaDec_DecodeToBuf(&strm, decompressed_data, &outprocessed, | ||
compressed_data, &inprocessed, LZMA_FINISH_ANY, &status, MAX_SWF_DECOMPRESSED_LEN); | ||
ret = lzma_decompress(compressed_data, &inprocessed, decompressed_data, &outprocessed, true, | ||
MAX_SWF_DECOMPRESSED_LEN); | ||
|
||
switch(ret) { | ||
case SZ_OK: | ||
case LzmaOk: | ||
ret = 1; | ||
break; | ||
case SZ_ERROR_MEM: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_MEMLIMIT_ERROR); | ||
case LzmaIoError: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_IO_ERROR); | ||
ret = 0; | ||
break; | ||
case SZ_ERROR_UNSUPPORTED: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_OPTIONS_ERROR); | ||
case LzmaHeaderTooShortError: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_HEADER_TOO_SHORT_ERROR); | ||
ret = 0; | ||
break; | ||
case SZ_ERROR_DATA: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_DATA_ERROR); | ||
case LzmaError: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_DECODER_ERROR); | ||
ret = 0; | ||
break; | ||
case SZ_ERROR_INPUT_EOF: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_BUF_ERROR); | ||
case LzmaXzError: | ||
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_LZMA_XZ_ERROR); | ||
ret = 0; | ||
break; | ||
default: | ||
|
@@ -192,6 +172,5 @@ int FileSwfLzmaDecompression(DetectEngineThreadCtx *det_ctx, | |
break; | ||
} | ||
|
||
LzmaDec_Free(&strm, &suri_lzma_Alloc); | ||
return ret; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -970,7 +970,7 @@ app-layer: | |
# auto will use http-body-inline mode in IPS mode, yes or no set it statically | ||
http-body-inline: auto | ||
|
||
# Decompress SWF files. | ||
# Decompress SWF files. Disabled by default. | ||
# Two types: 'deflate', 'lzma', 'both' will decompress deflate and lzma | ||
# compress-depth: | ||
# Specifies the maximum amount of data to decompress, | ||
|
@@ -979,7 +979,7 @@ app-layer: | |
# Specifies the maximum amount of decompressed data to obtain, | ||
# set 0 for unlimited. | ||
swf-decompression: | ||
enabled: yes | ||
enabled: no | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this needs some entry in doc//userguide/upgrade.rst There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need a redmine ticket entry @jasonish ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be a good idea. I think the reasoning for moving from enabled by default to disabled by default needs to be documented. So probably a ticket, and a small note in upgrade.rst. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. link to the ticket in the upgrade.rst? :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No problem. I will create a ticket for this and link it in the upgrade.rst. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ended up submitting a separate PR for this part. #8153 |
||
type: both | ||
compress-depth: 100kb | ||
decompress-depth: 100kb | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
allow_incomplete
is always true, maybe it should not be part of this function's argumentsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair. I think this was trying to maintain the kind of API that was used before (with
LZMA_FINISH_ANY
), but you are correct that if it is always true then it's not needed. I will remove it.