Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 76 additions & 25 deletions src/extractors/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub struct Extractor {
pub exit_codes: Vec<i32>,
/// Set to true to disable recursion into this extractor's extracted files
pub do_not_recurse: bool,
/// Optional fallback extractor to try if the primary extractor fails
pub fallback: Option<Box<Extractor>>,
}

/// Stores information about a completed extraction
Expand Down Expand Up @@ -841,33 +843,31 @@ pub fn execute(
result.extractor = format!("{}_built_in", signature.name);
}

ExtractorType::External(cmd) => {
// Spawn the external extractor command
match spawn(
file_data,
file_path,
&output_directory,
signature,
extractor_definition.clone(),
) {
Err(e) => {
error!(
"Failed to spawn external extractor for '{}' signature: {}",
signature.name, e
);
ExtractorType::External(_) => {
// Try the primary extractor, then any fallbacks if it fails
let mut current_ext = Some(&extractor_definition);

while let Some(ext) = current_ext {
result = try_external_extract(
file_data,
file_path,
&output_directory,
signature,
ext,
);

if result.success {
break;
}

Ok(proc_info) => {
// Wait for the external process to exit
match proc_wait(proc_info) {
Err(_) => {
warn!("External extractor failed!");
}
Ok(ext_result) => {
result = ext_result;
// Set the extractor name to the name of the extraction utility
result.extractor = cmd.to_string();
}
// Try fallback extractor if available
current_ext = ext.fallback.as_deref();
if let Some(fallback) = current_ext {
if let ExtractorType::External(fallback_cmd) = &fallback.utility {
info!(
"Trying fallback extractor '{}' for '{}' signature",
fallback_cmd, signature.name
);
}
}
}
Expand Down Expand Up @@ -900,6 +900,50 @@ pub fn execute(
result
}

/// Attempt to run an external extractor, returning the extraction result.
fn try_external_extract(
file_data: &[u8],
file_path: &String,
output_directory: &String,
signature: &SignatureResult,
extractor: &Extractor,
) -> ExtractionResult {
let mut result = ExtractionResult {
..Default::default()
};

if let ExtractorType::External(cmd) = &extractor.utility {
match spawn(
file_data,
file_path,
output_directory,
signature,
extractor.clone(),
) {
Err(e) => {
error!(
"Failed to spawn external extractor '{}' for '{}' signature: {}",
cmd, signature.name, e
);
}

Ok(proc_info) => {
match proc_wait(proc_info) {
Err(_) => {
warn!("External extractor '{}' failed!", cmd);
}
Ok(ext_result) => {
result = ext_result;
result.extractor = cmd.to_string();
}
}
}
}
}

result
}

/// Spawn an external extractor process.
fn spawn(
file_data: &[u8],
Expand Down Expand Up @@ -971,6 +1015,13 @@ fn spawn(
.spawn()
{
Err(e) => {
// Clean up the carved file since the process failed to start
if let Err(rm_err) = fs::remove_file(&carved_file) {
warn!(
"Failed to clean up carved file '{}': {}",
carved_file, rm_err
);
}
error!(
"Failed to execute command {}{:?}: {}",
command, extractor.arguments, e
Expand Down
1 change: 1 addition & 0 deletions src/extractors/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ pub fn linux_kernel_extractor() -> extractors::common::Extractor {
"linux_kernel.elf".to_string(),
],
exit_codes: vec![0],
fallback: None,
}
}
11 changes: 10 additions & 1 deletion src/extractors/squashfs.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
use crate::extractors;

/// Describes how to run the sasquatch utility to extract SquashFS images
/// Describes how to run the sasquatch utility to extract SquashFS images,
/// with unsquashfs as a fallback for systems that only have the standard tool.
pub fn squashfs_extractor() -> extractors::common::Extractor {
extractors::common::Extractor {
utility: extractors::common::ExtractorType::External("sasquatch".to_string()),
extension: "sqsh".to_string(),
arguments: vec![extractors::common::SOURCE_FILE_PLACEHOLDER.to_string()],
// Exit code may be 0 or 2; 2 indicates running as not root, but otherwise extraction is ok
exit_codes: vec![0, 2],
// Fall back to unsquashfs if sasquatch is not installed
fallback: Some(Box::new(extractors::common::Extractor {
utility: extractors::common::ExtractorType::External("unsquashfs".to_string()),
extension: "sqsh".to_string(),
arguments: vec![extractors::common::SOURCE_FILE_PLACEHOLDER.to_string()],
exit_codes: vec![0, 2],
..Default::default()
})),
..Default::default()
}
}
Expand Down
1 change: 1 addition & 0 deletions src/extractors/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ pub fn uefi_extractor() -> extractors::common::Extractor {
* Recursing into this data would result in double extractions for no good reason.
*/
do_not_recurse: true,
fallback: None,
}
}